인프런/스프링 MVC 1편

11)RequestParam 처리 방법 , HttpRequest 응답 방법

backend dev 2023. 1. 26.

Http 요청파라미터-@ModelAttribute

요청된 파라미터 처리 - @ModelAttribute

실제 개발을 하면 요청 파라미터를 받아서 필요한 객체를 만들고 그 객체에 값을 넣어주어야한다. 

보통 다음과 같은 코드를 작성할 것이다.

@RequestParam String username;
@RequestParam int age;
HelloData data = new HelloData();
data.setUsername(username);
data.setAge(age);

스프링은 이 과정을 완전히 자동화해주는 @ModelAttribute 기능을 제공한다.

 

먼저 요청 파라미터를 바인딩 받을 객체를 만들자  (bind의 사전적의미 = 묶다)

 

바인딩 받을 객체 HelloData

@Data // @Getter , @Setter , @ToString , @EqualsAndHashCode , @RequiredArgsConstructor 를 자동으로 적용해주는 어노테이션이다.
public class HelloData {
    private String username;
    private int age;
}

@ToString은 객체를 출력하기위한 toString을 오버라이드해서 더 깔끔하게 자동으로 만들어준다.

@Model 안의 @EqualsAndHashCode는 무엇일까?

 

Lombok @Data의 @EqualsAndHashCode이 뭐하는 애일까?

lombok 라이브러리를 애용하다보면, 자주 사용하게 되는 애노테이션들이 있다. (너무 편리하게 애용하고 있다)하지만, 그마저도 자주쓰는 애노테이션끼리 묶어 @Data라는 애노테이션이 있는데, @Get

velog.io

 

@RequestParam을 이용했으면 다음과 같이 파라미터를 받아 객체를 생성하고 사용했을것이다

@ResponseBody
@RequestMapping("/model-attribute-v1")
public String modelAttributeV1(
    @RequestParam String username,
    @RequestParam int age) {

    HelloData helloData = new HelloData();
    helloData.setUsername(username);
    helloData.setAge(age);
    log.info("username = {} , age = {}", helloData.getUsername(), helloData.getAge());
    log.info("@Tostring이 toString 메소드를 깔끔하게 오버라이드해줌 , {}", helloData.toString());
    return "ok";
}

 

@ModelAttribute를 사용하게되면

@ResponseBody
@RequestMapping("/model-attribute-v1")
public String modelAttributeV1(@ModelAttribute HelloData helloData) {
    log.info("username = {} , age = {}", helloData.getUsername(), helloData.getAge());
    log.info("hellodata = {}", helloData.toString());
    return "ok";
}

다음과 같이 @RequestParam으로 파라미터를 받아 변수에 넣고,

파라미터를 바인딩할 객체를 생성해서 값을 넣는 과정이 전부 사라진다.

1.요청 파라미터의 이름으로 Hellodata객체의 프로퍼티를 찾는다. ?username=a&age=10 이런식으로 들어왔다면 

username과 age를 이용해서 찾는다.

 

2. 그리고 해당 프로퍼티의 setter를 호출해서 파라미터값을 입력(바인딩)해준다.

username으로 setUsername()을찾고 , age로 setAge()를 찾아 값을 넣어준다.

 

위와같이 Spring동작하므로 바인딩할 객체에는 Setter가 필수다!

(setter가 없으면 값이 들어가지않는다.)

 

객체의 어떤 객체변수(속성)가  getter와 setter 둘다 가졌다면 , 프로퍼티라고 부른다

프로퍼티의 읽기와 쓰기는 일반적으로 게터(getter)와 세터(setter) 메소드 호출로 변환된다.

age에 ab값을 넣었더니 바인딩 오류가 발생하였다.

@ModelAttribute의 생략

@ResponseBody
@RequestMapping("/model-attribute-v2")
public String modelAttributeV2(HelloData helloData) { //@ModelAttribute는 생략가능하다.
    log.info("username = {} , age = {}", helloData.getUsername(), helloData.getAge());
    log.info("hellodata = {}", helloData.toString());
    return "ok";
}

 

@ModelAttribute는 생략할 수 있다.

그런데 @RequestParam도 생략가능하니까 혼란이 발생할 수 있다.

 

그래서 스프링은 생략시 다음과 같은 규칙을 적용한다.

1. String,int,Integer와 같은 단순타입은 @RequestParam을 적용하고

2. 나머지는 @ModelAttribute를 적용한다 ( 하지만 argument resolver로 지정해둔 타입은 @ModelAttribute가 적용되지않는다  ex) HttpServletResponse 이런애들이 적용되어있음)

여기까지가 요청된 파라미터의 대해 알아본것이다.

GET 방식으로 들어오는 쿼리 파라미터와

POST방식으로 들어오는 HTML Form 방식의 파라미터에 대해 

어떻게 받아서 처리하는지를 배운것이다.


이제부터는 Http message body에 데이터를 직접 담아서 요청온것을 처리하는법에 배운다.

 

Http요청 메시지 - 단순텍스트

V1 HttpServletRequest, Response 사용

 

http 메시지 바디를 통해 넘어온 데이터는 @RequestParam,@ModelAttribute를 사용할 수 없다.

(물론 HTML Form 형식은 쿼리파라미터형식으로 사용가능하다)

 

그리고 http 메시지 바디를 통해 데이터가 들어오므로 Http Method도 Post,Put,Patch를 사용한다.

(@PostMapping, @PutMapping, @PatchMapping)

GET방식으로도 Body를 받을 수 있지만 실무에서는 그렇게 사용하지않는다.

@Slf4j
@Controller
public class RequestBodyStringController {

    @PostMapping("/request-body-string-v1") //Http 메세지 Body에 값이 들어올것이므로 @PostMapping으로 받는다.
    public void requestBodyString(HttpServletRequest request, HttpServletResponse response)
        throws IOException {
        ServletInputStream inputStream = request.getInputStream(); //request객체의 inputStream을 받고
        String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); //메시지의 바디를 받는다.
        log.info("messageBody={}", messageBody);
        response.getWriter().write("ok");
    }

}

 

 

 

V2 InputStream,Writer 사용

Spring은 메소드 인자로 엄청 다양하게 지원하지만 그중에 InputStream과 Writer도 지원해준다.

parameter(파라미터) ==> 매개변수 

매개변수는 함수를 정의할때 들어오는값을 설정하는 그게 파라미터(매개변수)

public void add(int a, int b) {
    
}

여기서는 a,b가 파라미터

argument ==> 전달인자

add(10,20); 이렇게하면 여기서는 10,20이 전달인자(argument)

 

@PostMapping("/request-body-string-v2")
public void requestBodyStringV2(InputStream inputStream, Writer responseWriter) //이렇게 원하는 전달인자를 받으면 된다.
    throws IOException {
    String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);
    log.info("messageBody={}", messageBody);
    responseWriter.write("ok");

}

이런식으로 수정이되고, 잘 동작한다.

 

 

V3 HttpEntity, RequestEntity, ResponseEntity 사용

HttpEntity는 Http 헤더와 바디정보를 편하게 조회할 수 있는 객체이다! 파라미터와는 상관없음!

파라미터로 데이터가 들어오는 경우는 @RequestParam,@ModelAttribute를 이용한다.

@PostMapping("/request-body-string-v3")
    public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity) // HttpEntity<String> 객체를 만들면 Http메시지바디에있는값을 "String"으로 바꿔준다.
        throws IOException { // 반환도 HttpEntity<String>를 해준다, HttpEntity는 http메시지를 의미한다고 생각하면된다.
//        String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); 이와같은코드가 HttpMessageConverter로인해 자동으로 진행됨
        String body = httpEntity.getBody(); //들어온 Http메시지 바디의 값을 저장하고
        log.info("messageBody={}", body); // 출력해보고
        return new HttpEntity<>("ok"); //Http엔티티를 만들어 바디를 채워서 리턴해준다.
    }

잘 동작한다.

헤더와 바디정보를 쉽게 조회할 수 있는 HttpEntity를 상속한 

RequestEntity와 ResponseEntity가 있다.

 

RequestEntity는 헤더,바디정보 뿐만아니라 HttpMethod,URL 정보까지 조회가능하다. ( 요청에서 사용한다)

ResponseEntity는 헤더와 바디정보 뿐만 아니라 Http상태코드까지 설정이 가능하다. ( 응답에서 사용한다.)

 

RequestEntity는 요청받은 값들을 조회할때 사용

ResponseEntity는 응답에 대한 http응답메시지의 내부값들을 설정하고 반환할때 사용

 

RequestEntity,ResponseEntity 사용해보기

@PostMapping("/request-body-string-v3-2")
public ResponseEntity<String> requestBodyStringV3_2(RequestEntity<String> requestEntity)
    throws IOException {
    String method = requestEntity.getMethod().toString();
    String type = requestEntity.getType().toString();
    String URL = requestEntity.getUrl().toString();
    String headers = requestEntity.getHeaders().toString();
    String body = requestEntity.getBody();

    System.out.println("method = " + method);
    System.out.println("type = " + type);
    System.out.println("URL = " + URL);
    System.out.println("headers = " + headers);
    System.out.println("body = " + body);


    return new ResponseEntity<>("ok", HttpStatus.OK);//헤더도 넣을수있음
}

헤더를 넣거나 받을때는 MultiValueMap을 이용한다.

 

v4 - @ResponseBody, @RequestBody

@ResponseBody //해당 어노테이션이 있으면, 반환되는 문자열값을 http응답메시지 바디에 넣어준다.
@PostMapping("/request-body-string-v4")
public String requestBodyStringV4(@RequestBody String messageBody) { //@RequestBody 어노테이션을 이용하여 바디에 들어온값을 받을 수 있다.
    //@RequestBody 어노테이션덕에 바디에 들어온 String값이 messageBody변수에 담긴다.
    log.info("messageBody ={}", messageBody);
    return "ok";
}

@RequestBody

@RequestBody를 사용하면 HTTP 메시지 바디 정보를 편리하게 조회할 수 있다.

참고로 헤더정보가 필요하다면 HttpEntity를 사용하거나, @RequestHeader를 사용하면 된다.

이렇게 메시지 바디를 직접 조회하는 기능은 요청 파라미터를 조회하는 @RequestParam,@ModelAttribute와 전혀 관계가 없다.

요청 파라미터 ==> PathVariable을 통해 들어오거나, 쿼리스트링형식으로 들어오는 값들 

 

@ResponseBody

댓글