본문 바로가기
Spring/스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술

스프링 MVC - HTTP 요청 메시지(헤더, 데이터) 조회 정리

by hk27 2022. 1. 31.
스프링 MVC에서는 HTTP 요청 메시지를 어떻게 조회하나요?

안녕하세요.

오늘은 스프링 MVC로 HTTP 요청 메시지의 헤더와 데이터를 조회하는 방법을 알아보겠습니다. 

 

앞서 스프링 MVC를 도입하지 않고 서블릿의 HttpServletRequest로 요청 메시지를 조회하는 방법을 알아보았습니다. 궁금하신 분은 아래 게시글을 참고해주세요. 

 

HttpServletRequest로 HTTP 요청 메시지 조회하기
HttpServletRequest로 HTTP 요청 데이터 조회하기

 

어노테이션 기반의 스프링 MVC는 더욱 편리한 요청 메시지 조회 기능을 제공합니다.

먼저 헤더를 어떻게 조회하는지 알아봅시다.

 

헤더 조회

헤더를 조회하는 코드를 봅시다. 

@Slf4j
@RestController
public class RequestHeaderController {

    @RequestMapping("/headers")
    public String headers(HttpServletRequest request, HttpServletResponse response,
                          HttpMethod httpMethod, Locale locale,
                          @RequestHeader MultiValueMap<String, String> headerMap,
                          @RequestHeader("host") String host,
                          @CookieValue(value="myCookie", required = false) String cookie){
        log.info("request = {}", request);
        log.info("response = {}", response);
        log.info("httpMethod = {}", httpMethod);
        log.info("locale = {}", locale);
        log.info("headerMap = {}", headerMap);
        log.info("host = {}", host);
        log.info("myCookie = {}", cookie);

        return "ok";
    }
}

컨트롤러는 메소드의 파라미터로 HttpServletRequest, HttpServletResponse, HttpMethod, Locale 등의 객체를 받아올 수 있고 이를 사용해서 헤더 정보를 조회할 수 있습니다. 

또한 @RequestHeader 어노테이션을 사용하면 헤더를 편리하게 조회할 수 있습니다.

@RequestHeader의 속성을 지정하지 않고 맵 타입(Map<String, String>, MultiValueMap<String, String>) 객체로 데이터를 받으면 모든 헤더 정보가 조회됩니다. 

혹은 헤더 이름을 지정해서 @RequestHeader("name") String headerInfo 로 이용하면 특정 헤더 정보를 조회할 수 있습니다. 

@CookieValue로 특정 쿠기 값을 조회할 수도 있습니다. 

코드를 실행한 결과는 아래와 같습니다.

request = org.apache.catalina.connector.RequestFacade@51bb7541
response = org.apache.catalina.connector.ResponseFacade@153a58e3
httpMethod = GET
locale = ko_KR
headerMap ={host=[localhost:8080],
connection=[keep-alive],
user-agent=[Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36],
accept=[text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9],
accept-language=[ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7],
cookie=[username-localhost-8889="2|1:0|10:1641466132|23:username-localhost- ... .. ... ]}
host = localhost:8080
myCookie = null

 

요청 파라미터(request parameter) 조회 - @RequestParam

요청 파라미터는 Get 방식의 쿼리 파라미터와 Post 방식의 HTML Form의 메시지 바디 쿼리 파라미터를 의미합니다.

Get 방식의 쿼리 파라미터는 URL에 전달되는 데이터로, http://localhost:8080/request-param?username=hello&age=20처럼 이용됩니다. 

Post 방식의 HTML Form 메시지 바디의 쿼리 파라미터는 말 그대로 HTML Form 요청 데이터가 전송될 때 메시지 바디에 저장된 쿼리 파라미터를 의미합니다.

아래 사진에서 메시지 바디 부분에 저장된 username=kim과 age=20이 쿼리 파라미터입니다[1]. 

쿼리 파라미터 데이터를 조회하는 것을 요청 파라미터 조회라고 합니다. 

요청 파라미터를 조회하는 방법을 알아봅시다. 

 

    @ResponseBody
    @RequestMapping("/request-param-v1")
    public String requestParamV1(@RequestParam("username") String membername,
                                 @RequestParam("age") int memberAge){
        log.info("username={}, age={}", membername, memberAge);
        return "ok";
    }

    @ResponseBody
    @RequestMapping("/request-param-v2")
    public String requestParamV2(@RequestParam String username,
                                 @RequestParam int age){
        log.info("username={}, age={}", username, age);
        return "ok";
    }

    @ResponseBody
    @RequestMapping("/request-param-v3")
    public String requestParamV3(String username, int age){
        log.info("username={}, age={}", username, age);
        return "ok";
    }

첫 번째 방법은 코드의 3~4번째 줄처럼 @RequestParam("requestParameterName") type paramName을 사용하는 방법입니다. 요청 파라미터 중 requestParameterName을 갖는 파라미터의 값이 paramName에 저장됩니다.

예를 들어서 username=kim이라는 요청 파라미터가 들어오면

@RequestParam("username") String membername -> membername = kim 이 됩니다. 

 

두 번째 방법은 코드의 11~12번째 줄처럼 @RequestParam type paramName을 사용하는 방법입니다.

이 방법은 요청 파라미터의 데이터 이름과 변수명이 같을 때 편리하게 사용할 수 있습니다. 

@RequestParam String username -> username = kim

 

세 번째 방법은 코드의 19번째 줄처럼 @RequestParam 어노테이션을 쓰지 않고 타입과 변수명만 지정하는 방법입니다. 같은 이름의 요청 파라미터가 들어오면 데이터가 저장됩니다. 

String username -> username = kim

 

세 번째 방법이 가장 간단하지만 @RequestParam 어노테이션이 없으면 명확하게 요청 파라미터의 데이터임을 인지하기 어려울 수 있기 때문에, 어노테이션을 명시한 첫 번째, 두 번째 방법을 사용하는 것도 좋습니다. 

또한 어노테이션을 생략하면 요청 파라미터가 필수가 아닙니다.

예를 들어서 userName 값이 생략되어서 들어오면 @RequestParam을 사용한 컨트롤러는 MissingServletRequestParameterException 에러가 나지만, 어노테이션을 생략한 컨트롤러는 username을 null으로 두고 수행됩니다. 

 

@RequestParam 어노테이션을 사용한 컨트롤러도 요청 파라미터를 필수가 아니도록 지정할 수 있습니다.

어노테이션의 속성으로 required를 지정하면 됩니다. 코드를 봅시다.

    @ResponseBody
    @RequestMapping("/request-param-required")
    public String requestParamRequired(@RequestParam(required = true) String username,
                                       @RequestParam(required = false) Integer age,
                                       @RequestParam(defaultValue = "0") int id) {
        log.info("username={}, age={}", username, age);
        return "ok";
    }

required는 true나 false로 지정할 수 있고, 값이 필수인지 여부를 나타냅니다. 기본값은 true이기 때문에 요청 파라미터가 필수로 입력되어야 합니다. false이면 요청 파라미터가 필수가 아니며, null입니다.

따라서 required=false이면 변수의 타입이 null 값을 받을 수 없는 기본형(primitive type)이면 안 됩니다. 따라서 int 대신 Integer를 사용해야 합니다.

defalutValue도 지정할 수 있습니다. 단어 그대로 기본 값이며, 쿼리 파라미터값이 들어오지 않으면 defaultValue 값으로 지정합니다. 

 

요청 파라미터 - @ModelAttribute

실제로 웹을 만들 때는 요청 파라미터를 받아서 객체를 만드는 경우가 많을 것입니다.

예를 들어서 String username, int age 요청 파라미터를 받아서 username과 age 필드를 갖는 객체를 만들려면 데이터를 받아서 하나하나 지정해서 객체를 만들어야 합니다.

그러나 스프링에서는 매우 편리한 @ModelAttribute 어노테이션을 지원합니다.

요청 파라미터를 읽어서 자동으로 객체를 만드는 어노테이션입니다.

예시 코드를 봅시다.

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

    @ResponseBody
    @RequestMapping("/model-attribute-v2")
    public String modelAttributeV2(HelloData helloData) {
        log.info("username={}, age={}", helloData.getUsername(), helloData.getAge());
        return "ok";
    }
    
@Getter @Setter
public class HelloData {
    private String username;
    private int age;
}

위 코드의 세 번째 줄처럼 @ModelAttribute Class param 으로 사용하면 객체가 만들어져서 저장됩니다.

스프링 MVC는 @ModelAttribute가 있으면 객체를 생성하고 setter를 호출해 파라미터 값을 입력합니다. 

10번째 줄처럼 @ModelAttribute 어노테이션을 생략할 수도 있습니다.

스프링은 String, int, Integer 같은 단순 타입이면 @RequestParam이 생략되었다고 파악해서 값을 바인딩하고, 그 외의 타입이면 @ModelAttribute가 생략되었다고 파악해서 객체를 만듭니다. 

 

HTTP 요청 데이터 - 단순 텍스트

다음으로 HTTP 요청 메시지의 데이터로 단순 텍스트가 넘어올 때, 읽는 방법입니다.

@RequestBody 어노테이션을 이용하면 간편하게 데이터를 받을 수 있습니다.

예시 코드를 봅시다.

    @ResponseBody
    @PostMapping("/request-body-string")
    public String requestBodyString(@RequestBody String messageBody){
        log.info("message Body = {}", messageBody);
        return "ok";
    }

컨트롤러에서 파라미터로 @RequestBody로 데이터를 받을 수 있습니다.

 

HTTP 요청 데이터 - JSON

다음으로 HTTP 요청 메시지의 데이터로 JSON 데이터가 넘어올 때, 받는 방법입니다.

JSON 데이터도 @RequestBody로 받을 수 있고, String으로 값을 받으면 "{"username": "hello", "age": 30}"처럼 값이 받아집니다. 

만약 @ModelAttribute처럼 바로 객체를 만들고 싶으면 객체 클래스로 데이터를 받으면 됩니다. 

예시 코드를 봅시다.

    @ResponseBody
    @PostMapping("/request-body-json")
    public String requestBodyJson(@RequestBody HelloData data) throws IOException {
        log.info("username = {}, age = {}", data.getUsername(), data.getAge());
        return "ok";
    }

세 번째 줄처럼 @RequestBody HelloData data로 데이터를 받으면 data 객체가 만들어집니다. 

 

이번 게시글에서는 스프링 MVC로 여러 어노테이션을 사용해 HTTP 요청 메시지를 조회하는 방법을 알아보았습니다.

다음 게시글에서는 스프링 MVC로 HTTP 응답 메시지를 만드는 방법을 알아보겠습니다. 

 

인프런  '스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술' 강의를 듣고 공부하며 정리한 자료입니다. 

잘못된 부분은 피드백 주시면 감사하겠습니다. 

글 읽어주셔서 감사합니다 :-)

 

참고 자료

[0] 스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술, 섹션 6. 스프링 MVC - 기본 기능 https://www.inflearn.com/course/스프링-mvc-1

[1] 모든 개발자를 위한 HTTP 웹 기본 지식, https://www.inflearn.com/course/http-%EC%9B%B9-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC

댓글