[ Spring ]/SpringMVC 1편

[Spring] HTTP 요청을 받는 방법 (요청 파라미터 / HTTP 요청 메시지)

쿠릉쿠릉 쾅쾅 2022. 2. 25. 14:15
728x90

 

 

 

HTTP 요청 데이터 조회

  • 클라이언트에서 서버로 요청 데이터를 전달할 때 3가지 방법이 존재한다.
    • GET - 쿼리 파라미터
      • /url?username=hello&age=20
      • 메시지 바디 없이, URL의 쿼리 파라미터에 데이터를 포함해서 전달
      • 예) 검색, 필터, 페이징 등에서 많이 사용한다.
    • POST - HTML Form
      • context-type : application/x-www-form-urlencoded
      • 메시지 바디에 쿼리 파리미터 형식으로 전달 (username=hello&age=20)
      • 예) 회원 가입, 상품 주문, HTML Form 사용
    • HTTP message body에 데이터를 직접 담아서 요청
      • HTTP API에서 주로 사용
      • 데이터 형식은 주로 JSON을 사용한다.
      • POST, PUT ,PATCH 방식으로 한다.

 

참고로 HttpServletRequest의 request.getParameter() 메서드로 GET 쿼리 파라미터 전송 방식 POST HTML Form 전송 방식의 데이터를 받을 수 있다. 이것을 간단히 요청 파라미터(request parameter) 조회라고 한다.

 


 

 요청 파라미터 

 

🔍 요청 파라미터 데이터 조회

package hello.springmvc.basic.request;

import hello.springmvc.basic.HelloData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

@Slf4j
@Controller
public class RequestParamController {

    /**
     * 반환 타입이 없으면서 이렇게 응답에 값을 직접 집어 넣으면, view 조회를 안한다.
     */
    @RequestMapping("/request-param-v1")
    public void requestParamV1(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String username = request.getParameter("username");
        int age = Integer.parseInt(request.getParameter("age"));

        log.info("username={}, age={}", username, age);

        response.getWriter().write("ok");
    }

    /**
     * @RequestParam 사용
     * - 파라미터 이름으로 바인딩
     *
     * @ResponseBody 추가
     * - View 조회를 무시하고, HTTP message body에 직접 내용 입력력     */
    @ResponseBody
    @RequestMapping("/request-param-v2")
    public String requestParamV2(
            @RequestParam("username") String memberName,
            @RequestParam("age") int memberAge) {

        log.info("username={}, age={}", memberName, memberAge);

        return "ok";
    }

    /**
     * @RequestParam 사용
     * HTTP 파라미터 이름이 변수 이름과 같으면 @RequestParam 애노테이션의 속성 값 생략 가능
     */
    @ResponseBody
    @RequestMapping("/request-param-v3")
    public String requestParamV3(
            @RequestParam String username,
            @RequestParam int age) {

        log.info("username={}, age={}", username, age);

        return "ok";
    }

    /**
     * @RequestParam 사용
     * String, int, Integer 등 단순 타입이면 @RequestParam 생략 가능
     * 그러나 웬만해서는 @RequestParam은 써주는걸 권장
     */
    @ResponseBody
    @RequestMapping("/request-param-v4")
    public String requestParamV4(String username, int age) {

        log.info("username={}, age={}", username, age);

        return "ok";
    }

    /**
     * @ModelAttribute 사용
     *
     */
    @ResponseBody
    @RequestMapping("/model-attribute-v1")
    public String modelAttributeV1(@ModelAttribute HelloData helloData) {

        log.info("helloData={}", helloData);
        return "ok";
    }

    /**
     * @ModelAttribute 생략
     * String, int 같은 단순 타입은 @RequestParam
     * argument resolver로 지정해둔 타입과 단순 타입을 제외하면 나머지는 @ModelAttribute
     */
    @ResponseBody
    @RequestMapping("/model-attribute-v2")
    public String modelAttributeV2(HelloData helloData) {
        log.info("helloData={}", helloData);
        return "ok";
    }

}

 

💡 첫 번째 방법  ㅡ request.getParameter()

@RequestMapping("/request-param-v1")

/**
 * 반환 타입이 없으면서 이렇게 응답에 값을 직접 집어 넣으면, view 조회를 안한다.
 */
@RequestMapping("/request-param-v1")
public void requestParamV1(HttpServletRequest request, HttpServletResponse response) throws IOException {
    String username = request.getParameter("username");
    int age = Integer.parseInt(request.getParameter("age"));

    log.info("username={}, age={}", username, age);

    response.getWriter().write("ok");
}
  • request.getParameter() 메서드를 통해 GET 쿼리파라미터 방식이든 POST HTML Form 방식이든 둘 다 요청 데이터를 가져올 수 있다.

 

 

 

💡 두 번째 방법 ㅡ @RequestParam(name="xxx")

@RequestMapping("/request-param-v2")

/**
 * @RequestParam 사용
 * - 파라미터 이름으로 바인딩
 *
 * @ResponseBody 추가
 * - View 조회를 무시하고, HTTP message body에 직접 내용 입력력     */
@ResponseBody
@RequestMapping("/request-param-v2")
public String requestParamV2(
        @RequestParam("username") String memberName,
        @RequestParam("age") int memberAge) {

    log.info("username={}, age={}", memberName, memberAge);

    return "ok";
}
  • @RequestParam의 name 속성이 파라미터 이름으로 사용된다.
    • @RequestParam("username") String memberNameString meberName = request.getParameter("username") 은 서로 같은 문장이다.
  • @ResponseBody
    • 클래스 레벨에서 @Controller 애노테이션이 사용되고, 메서드 레벨에서 서 @RequestMapping 애노테이션이 사용되고 있을 때, 메서드 반환 타입이 String 타입일 경우 반환 값으로 View 조회를 하게 된다. 하지만 @ResponseBody가 메서드 레벨에 붙어있을 경우 반환타입이 String 이여도 반환값은 View 조회를 안거치고 바로 HTTP Response 메시지 body 내용이 된다.

 

 💡 세 번째 방법 - @RequestParam 

@RequestMapping("/request-param-v3")

/**
 * @RequestParam 사용
 * HTTP 파라미터 이름이 변수 이름과 같으면 @RequestParam 애노테이션의 속성 값 생략 가능
 */
@ResponseBody
@RequestMapping("/request-param-v3")
public String requestParamV3(
        @RequestParam String username,
        @RequestParam int age) {

    log.info("username={}, age={}", username, age);

    return "ok";
}
  • HTTP 파라미터 이름과 변수 이름이 같을 경우 @RequestParam 애노테이션의 속성 값 생략 가능

 

💡 네 번째 방법 - @RequestParam 생략

@RequestMapping("/request-param-v4")

/**
 * @RequestParam 사용
 * String, int, Integer 등 단순 타입이면 @RequestParam 생략 가능
 * 그러나 웬만해서는 @RequestParam은 써주는걸 권장
 */
@ResponseBody
@RequestMapping("/request-param-v4")
public String requestParamV4(String username, int age) {

    log.info("username={}, age={}", username, age);

    return "ok";
}
  • @RequestParam의 파라미터 타입이 String, int, Integer 처럼 단순 타입일 경우 @RequestParam 애노테이션도 생략 가능하다.
  • @RequestParam 애노테이션을 생략하면 스프링 MVC는 내부에서 'required=false'를 적용한다.
  • 그러나 @RequestParam 애노테이션을 생략하지 말고 요청 파라미터에서 데이터를 읽는 다는 것을 명확하게 표기 해주는게 더 좋을 것 같다. 

 

 💡 다섯 번째 방법 - ModelAttribute 사용 

package hello.springmvc.basic;

import lombok.Data;

@Data
public class HelloData {
    private String username;
    private int age;
}
  • 데이터를 담을 객체 생성
  • 요청 파라미터의 변수명과 객체의 필드 변수 명은 일치해야 한다.
  • 롬복 @Data
    • @Getter, @Setter, @ToString, @EqualsAndHashCode, @RequiredArgsConstrcutor 를 자동으로 적용 시켜준다.

 

@RequestMapping("/model-attribute-v1")

/**
 * @ModelAttribute 사용
 *
 */
@ResponseBody
@RequestMapping("/model-attribute-v1")
public String modelAttributeV1(@ModelAttribute HelloData helloData) {

    log.info("helloData={}", helloData);
    return "ok";
}
  • @RequestParam은 1:1 매핑이다. @ModelAttribute은 객체 매핑이다.
  • @ModelAttribute는 객체 매핑이기 때문에 요청 파라미터로 받을 데이터의 key값이 객체에 변수명으로 정의되어 있어야 한다. 
  • @ModelAttribute 애노테이션을 사용하면 객체 하나로 여러 개의 데이터를 받을 수 있다.
  • @ModelAttribute 애노테이션을 적용할 객체는 getter, setter 메서드가 모두 정의되어 있어야 한다.

 

💡 여섯 번째 방법 - ModelAttribute 생략

@RequestMapping("/model-attribute-v2")

/**
 * @ModelAttribute 생략
 * String, int 같은 단순 타입은 @RequestParam
 * argument resolver로 지정해둔 타입과 단순 타입을 제외하면 나머지는 @ModelAttribute
 */
@ResponseBody
@RequestMapping("/model-attribute-v2")
public String modelAttributeV2(HelloData helloData) {
    log.info("helloData={}", helloData);
    return "ok";
}
  • @ModelAttribute 애노테이션을 생략할 수 있다.
  • 스프링은 내부에서 String, int, Integer 같은 단순 타입은 @RequestParam을 사용한다. 단순 타입과 argument resolver로 지정해둔 타입을 제외하고서는 나머지 타입은 @ModelAttirbute를 사용한다.

 

 

 

🔍 요청 파라미터의 필수 파라미터

package hello.springmvc.basic.request;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

@Slf4j
@Controller
public class RequestParamController {

    /**
     * @RequestParam.required 사용
     * 필수 파라미터 여부
     * 속성값인 required의 기본값은 true다.
     */
    @ResponseBody
    @RequestMapping("/request-param-required")
    public String requestParamRequired(
            @RequestParam(required = true) String username,  // 필수 파라미터
            @RequestParam(required = false) int age) {

        log.info("username={}, age={}", username, age);

        return "ok";
    }


    /**
     * @RequestParam.defaultValue 사용
     * 파라미터에 값이 없을 경우 기본값을 적용할 수 있다.
     */
    @ResponseBody
    @RequestMapping("/request-param-default")
    public String requestParamDefault(
            @RequestParam(defaultValue = "guest") String username,
            @RequestParam(defaultValue = "-1") int age) {

        log.info("username={}, age={}", username, age);

        return "ok";
    }

    /**
     * @RequestParam Map, MultiValueMap
     * Map(key=value)
     * MultiValue(key=[value1, value2,...])
     */
    @ResponseBody
    @RequestMapping("/request-param-map")
    public String requestParamMap(@RequestParam Map<String, Object> paramMap) {

        log.info("username={}, age={}", paramMap.get("username"), paramMap.get("age"));

        return "ok";
    }

}

 

💡 파라미터 필수 여부

@RequestMapping("/request-param-required")

/**
 * @RequestParam.required 사용
 * 필수 파라미터 여부
 * 속성값인 required의 기본값은 true다.
 */
@ResponseBody
@RequestMapping("/request-param-required")
public String requestParamRequired(
        @RequestParam(required = true) String username,  // 필수 파라미터
        @RequestParam(required = false) int age) {

    log.info("username={}, age={}", username, age);

    return "ok";
}
  • @RequestParam(required=true) 또는 @RequestParam(required=false)
    • 파라미터 필수 여부를 나타낸다.
    • 기본값은 true다.

 

 💡 파라미터 기본값 적용 

 @RequestMapping("/request-param-default")

/**
 * @RequestParam.defaultValue 사용
 * 파라미터에 값이 없을 경우 기본값을 적용할 수 있다.
 */
@ResponseBody
@RequestMapping("/request-param-default")
public String requestParamDefault(
        @RequestParam(defaultValue = "guest") String username,
        @RequestParam(defaultValue = "-1") int age) {

    log.info("username={}, age={}", username, age);

    return "ok";
}
  • @RequestParam(defaultValue="")
    • 파라미터 값이 없을 경우 defaultValue를 사용하여 기본 값을 적용할 수 있다.
    • defaultValue 속성을 사용하면 required 속성을 사용안해도 된다.

 

💡 파라미터를 Map, MultiValueMap으로 조회하기

@RequestMapping("/request-param-map")

/**
 * @RequestParam Map, MultiValueMap
 * Map(key=value)
 * MultiValue(key=[value1, value2,...])
 */
@ResponseBody
@RequestMapping("/request-param-map")
public String requestParamMap(@RequestParam Map<String, Object> paramMap) {

    log.info("username={}, age={}", paramMap.get("username"), paramMap.get("age"));

    return "ok";
}
  • @RequestParam Map
    • 파라미터 값이 1개가 확실하면 Map으로 사용한다.
  • @RequestParam MultiValueMap 
    • 하나의 key 값에 value값이 여러개 일 경우 MultiValueMap을 사용한다.
    • 잘 쓰이지 않는다. 

 

 


 

 

HTTP 요청 메시지

  • HTTP message body에 데이터를 직접 담아서 요청한다.
  • HTTP API에서 주로 사용한다.
  • JSON 데이터 형식을 주로 사용한다.
  • POST, PUT, PATCH 방식을 주로 사용한다.
  • 요청 파라미터와 다르게 HTTP 메시지 바디를 통해 데이터가 직접 넘어오는 경우는 @RequestParam 또는 @ModelAttribute를 사용할 수 없다.

 

 

🔍 HTTP 요청 메시지가 단순 텍스트 일 때 조회하는 방법

package hello.springmvc.basic.request;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
import java.nio.charset.StandardCharsets;

@Slf4j
@Controller
public class RequestBodyStringController {

    @PostMapping("/request-body-string-v1")
    public void requestBodyStringV1(HttpServletRequest request, HttpServletResponse response) throws IOException {
        ServletInputStream inputStream = request.getInputStream();
        String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);

        log.info("messageBoyd={}", messageBody);

        response.getWriter().write("ok");
    }

    /**
     * InputStream(Reader) : HTTP 요청 메시지 바디의 내용을 직접 조회
     * OutputStream(Writer) : HTTP 응답 메시지의 바디에 직접 결과 출력
     */
    @PostMapping("/request-body-string-v2")
    public void requestBodyStringV1(InputStream inputStream, Writer responseWriter) throws IOException {
        String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);

        log.info("messageBody={}", messageBody);

        responseWriter.write("ok");
    }

    /**
     * HttpEntity : HTTP header, body 정보를 편하게 조회
     * - 메시지 바디 정보를 직접 조회할 수 있다.
     * - HttpMessageConvert 사용 -> StringHttpMessageConverter 적용
     *
     * 응답에도 HttpEntity 사용 가능
     * - 메시지 바디 정보 직접 반환 (view 조회x)
     * - HttpMessageConverter 사용 -> StringHttpMessageConverter 적용
     */
    @PostMapping("/request-body-string-v3")
    public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity) {

        String messageBody = httpEntity.getBody();

        log.info("messageBody={}", messageBody);

        return new HttpEntity<>("ok");
    }

    /**
     * @RequsteBody
     * - 메시지 바디 정보를 직접 조회
     *  - HttpMessageConverter 사용 -> StringHttpMessageConverter 적용
     *
     * @ResponseBody
     * - 메시지 바디 정보 직접 반환 (view 조회x)
     *  - HttpMessageConverter 사용 -> StringHttpMessageConverter 적용
     */
    @ResponseBody
    @PostMapping("/request-body-string-v4")
    public String requestBodyStringV4(@RequestBody String messageBody) {
        log.info("messageBody={}", messageBody);
        return "ok";
    }


}

 

💡 첫 번째 방법

 @PostMapping("/request-body-string-v1")

@PostMapping("/request-body-string-v1")
public void requestBodyStringV1(HttpServletRequest request, HttpServletResponse response) throws IOException {
    ServletInputStream inputStream = request.getInputStream();
    String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);

    log.info("messageBoyd={}", messageBody);

    response.getWriter().write("ok");
}

 

💡 두 번째 방법

@PostMapping("/request-body-string-v2")

/**
 * InputStream(Reader) : HTTP 요청 메시지 바디의 내용을 직접 조회
 * OutputStream(Writer) : HTTP 응답 메시지의 바디에 직접 결과 출력
 */
@PostMapping("/request-body-string-v2")
public void requestBodyStringV1(InputStream inputStream, Writer responseWriter) throws IOException {
    String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);

    log.info("messageBody={}", messageBody);

    responseWriter.write("ok");
}
  • InputStream(Reader) : HTTP 요청 메시지 바디의 내용을 직접 조회
  • OutputStream(Writer) : HTTP 응답 메시지의 바디에 직접 결과 출력

 

 

💡 세 번째 방법

@PostMapping("/request-body-string-v3")

/**
 * HttpEntity : HTTP header, body 정보를 편하게 조회
 * - 메시지 바디 정보를 직접 조회할 수 있다.
 * - HttpMessageConvert 사용 -> StringHttpMessageConverter 적용
 *
 * 응답에도 HttpEntity 사용 가능
 * - 메시지 바디 정보 직접 반환 (view 조회x)
 * - HttpMessageConverter 사용 -> StringHttpMessageConverter 적용
 */
@PostMapping("/request-body-string-v3")
public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity) {

    String messageBody = httpEntity.getBody();

    log.info("messageBody={}", messageBody);

    return new HttpEntity<>("ok");
}
  • HttpEntity 클래스는 HTTP 요청 또는 응답에 해당하는 HttpHeader와 HttpBody를 포함하는 클래스다. 
    • 요청일 때는 정보를 조회하고, 응답일 때는 메시지 바디 정보, 헤더 정보를 직접 반환(view 조회x)한다.
    • Headers 정보 조회 : HttpEntity#getHeaders()
    • body 내용 조회 : HttpEntity#getBody()
    • HttpEntity는 @RequestBody와 기능적으로 비슷하나 추가적으로 헤더 정보를 가져올 수 있다.
    • HttpEntity 클래스를 상속받아 구현한 클래스는 RequestEntity와 ResponseEntity가 있다.
      • RequestEntity : HttpMethod, url 정보를 얻을 수 있다. (요청일 때 사용)
      • ResponseEntity : HTTP 상태 코드 설정 가능 (응답일 때 사용)
        • ex) return new ResponseEntity<String>("Hello World", responseHeaders, HttpStatus.CREATED)

 

스프링 MVC는 HTTP 메시지 바디를 읽어서 문자나 객체로 변환해서 전달해준다. 이 때 HTTP 메시지 컨버터(HttpMessageConverter)라는 기능을 사용한다.

 

 💡 네 번째 방법 - @ReqeustBody 

@PostMapping("/request-body-string-v4")

/**
 * @RequsteBody
 * - 메시지 바디 정보를 직접 조회
 *  - HttpMessageConverter 사용 -> StringHttpMessageConverter 적용
 *
 * @ResponseBody
 * - 메시지 바디 정보 직접 반환 (view 조회x)
 *  - HttpMessageConverter 사용 -> StringHttpMessageConverter 적용
 */
@ResponseBody
@PostMapping("/request-body-string-v4")
public String requestBodyStringV4(@RequestBody String messageBody) {
    log.info("messageBody={}", messageBody);
    return "ok";
}
  • @RequestBody 애노테이션을 사용하면 HTTP 메시지 바디 정보를 편리하게 조회할 수 있다.
    • @ReqeustBody는 요청 메시지를 자바 객체로 변환한다. 이 때 HttpMessageConveter가 사용된다.
  • 헤더 정보가 필요하다면 HttpEntity를 사용하거나 @RequestHeader 애노테이션을 사용하면 된다.
  • @ResponseBody 애노테이션을 사용하여 반환값을 바로 HTTP 메시지 바디에 전달했다.

 

@RequestBody 애노테이션는 HTTP 요청 메시지에 담긴 값들을 자바 객체로 변환시켜서 객체에 저장시킨다.

@ResponseBody 애노테이션는 자바 객체를 Http 응답 메시지의 객체를 변환하여 클라이언트로 전송시킨다.  

 

 

🔍 HTTP 요청 메시지가 JSON 일 때 조회하는 방법

package hello.springmvc.basic.request;

import com.fasterxml.jackson.databind.ObjectMapper;
import hello.springmvc.basic.HelloData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;


/**
 * 조회할 JSON 데이터
 * {"username":"hello", "age":20}
 * (헤더 정보)content-type : application/json
 */

@Slf4j
@Controller
public class RequestBodyJsonController {

    private ObjectMapper objectMapper = new ObjectMapper();

    @PostMapping("/request-body-json-v1")
    public void requestBodyJsonV1(HttpServletRequest request, HttpServletResponse response) throws IOException {
        ServletInputStream inputStream = request.getInputStream();
        String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);

        log.info("messageBody={}", messageBody);

        HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
        log.info("helloData={}", helloData);

        response.getWriter().write("ok");
    }

    /**
     * @RequestBody 사용
     * HttpMessageConverter 사용 -> StringHttpMessageConverter 적용
     * 
     * @ResponseBody
     * - 모든 메서드에 @ResponseBody 적용
     * - 메시지 바디 정보 직접 번환 (view 조회)
     * - HttpMessageConverter 적용 -> StringHttpMessageConverter 적용
     */
    
    @ResponseBody
    @PostMapping("/request-body-json-v2")
    public String requestBodyJsonV2(@RequestBody String messageBody) throws IOException {

        log.info("messageBody={}", messageBody);
        HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
        log.info("helloDate={}", helloData);

        return "ok";
    }

    /**
     * @RequestBody 사용
     * HttpMessageConverter 사용 -> MappingJackson2HttpMessageConverter
     * context-type : application/json 확인
     */
    
    @ResponseBody
    @PostMapping("/request-body-json-v3")
    public String requestBodyJsonV3(@RequestBody HelloData helloData) {
        log.info("helloData={}", helloData);

        return "ok";
    }

    @ResponseBody
    @PostMapping("/request-body-json-v4")
    public String requestBodyJsonV4(HttpEntity<HelloData> httpEntity) {
        HelloData helloData = httpEntity.getBody();

        log.info("helloDate={}", helloData);

        return "ok";
    }

    /**
     * @RequestBody 사용
     * HttpMessageConverter 사용 -> MappingJackson2HttpMessageConverter
     * context-type : application/json 확인
     * 
     * @ResponseBody 사용
     * - 메시지 바디 정보 직접 반환 (view 조회x)
     * - HttpMessageConverter 사용 -> MappingJackson2HttpMessageConverter
     * Accept : application/json 확인
     */
    @ResponseBody
    @PostMapping("/request-body-json-v5")
    public HelloData requestBodyJsonV5(@RequestBody HelloData helloData) {
        
        log.info("helloData={}", helloData);
        
        return helloData;
    }
}

 

💡 첫 번째 방법

@PostMapping("/request-body-json-v1")

private ObjectMapper objectMapper = new ObjectMapper();

@PostMapping("/request-body-json-v1")
public void requestBodyJsonV1(HttpServletRequest request, HttpServletResponse response) throws IOException {
    ServletInputStream inputStream = request.getInputStream();
    String messageBody = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8);

    log.info("messageBody={}", messageBody);

    HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
    log.info("helloData={}", helloData);

    response.getWriter().write("ok");
}
  • HttpServletRequest를 사용해서 직접 HTTP 메시지 바디에서 데이터를 읽어와서, 문자로 변환한다.
  • 문자로 된 JSON 데이터를 Jackson 라이브러리인 objectMapper를 사용하여 자바 객체로 변환한다.

 

💡 두 번째 방법

@PostMapping("/request-body-json-v2")

 private ObjectMapper objectMapper = new ObjectMapper();

/**
 * @RequestBody 사용
 * HttpMessageConverter 사용 -> StringHttpMessageConverter 적용
 *
 * @ResponseBody
 * - 모든 메서드에 @ResponseBody 적용
 * - 메시지 바디 정보 직접 번환 (view 조회)
 * - HttpMessageConverter 적용 -> StringHttpMessageConverter 적용
 */

@ResponseBody
@PostMapping("/request-body-json-v2")
public String requestBodyJsonV2(@RequestBody String messageBody) throws IOException {

    log.info("messageBody={}", messageBody);
    HelloData helloData = objectMapper.readValue(messageBody, HelloData.class);
    log.info("helloDate={}", helloData);

    return "ok";
}
  • @RequestBody 애노테이션을 사용해서 HTTP 메시지에서 데이터를 꺼내고 파라미터 변수인 messageBody에 저장한다. 
  • 문자로 된 JSON 데이터를 messageBody를 objectMapper를 통해서 자바 객체로 변환한다.

 

 💡 세 번째 방법 

@PostMapping("/request-body-json-v3")

@ResponseBody
@PostMapping("/request-body-json-v3")
public String requestBodyJsonV3(@RequestBody HelloData helloData) {
    log.info("helloData={}", helloData);

    return "ok";
}
  • @RequestBody 애노테이션은 직접 만든 객체를 지정할 수 있다.
  • HttpEntity<T> 클래스 / @ResponseBody 애노테이션을 사용하면 HTTP 메시지 컨버터가 HTTP 메시지 바디의 내용을 우리가 원하는 문자나 객체로 변환해준다.
  • HTTP 메시지 컨버터는 문자 뿐만 아니라 JSON 객체로 변환도 해준다.
  • @RequestBody는 생략 불가능하다.
    • @RequestBody를 생략하면 단순 타입일 경우에는 @RequestParam 애노테이션이, 나머지 그 외 타입일 경우에는 @ModelAttribute 애노테이션이 자동으로 적용 되므로 HTTP 메시지 바디가 아닌 요청 파라미터로 처리하게 된다.
    • 따라서, @RequestBody 애노테이션을 생략하면 안된다.
  • HTTP 요청시 content-type이 application/json인지 꼭 확인할 것. 그래야 JSON으로 처리할 수 있는 HTTP 메시지 컨버터가 실행된다.

 

 

💡 네 번째 방법

@PostMapping("/request-body-json-v4")

@ResponseBody
@PostMapping("/request-body-json-v4")
public String requestBodyJsonV4(HttpEntity<HelloData> httpEntity) {
    HelloData helloData = httpEntity.getBody();

    log.info("helloDate={}", helloData);

    return "ok";
}
  • HttpEntity<T> 클래스 사용

 

 

 💡 다섯 번째 방법 

@PostMapping("/request-body-json-v5")

/**
 * @RequestBody 사용
 * HttpMessageConverter 사용 -> MappingJackson2HttpMessageConverter
 * context-type : application/json 확인
 *
 * @ResponseBody 사용
 * - 메시지 바디 정보 직접 반환 (view 조회x)
 * - HttpMessageConverter 사용 -> MappingJackson2HttpMessageConverter
 * Accept : application/json 확인
 */
@ResponseBody
@PostMapping("/request-body-json-v5")
public HelloData requestBodyJsonV5(@RequestBody HelloData helloData) {

    log.info("helloData={}", helloData);

    return helloData;
}
  • @ResponseBody 애노테이션을 사용할 때, 반환 타입을 객체로 지정하면 응답할 때 HTTP 메시지 바디에 직접 넣어 줄 수 있다. 
  • @ReqeustBody 대신에 HttpEntity<T>로도 가능하다.
  • @RequestBody 요청
    • JSON 요청 → HTTP 메시지 컨버터 → 자바 객체
  • @ResponseBody 응답
    • 객체 → HTTP 메시지 컨버터 → JSON 응답

 

 


 

요청 파라미터 vs HTTP 메시지 바디

  • 요청 파라미터를 조회하는 기능 : @RequestParam / @ModelAttribute
  • HTTP 메시지 바디를 직접 조회하는 기능 : @RequestBody / HttpEntity<T>

 

🔍 @ResponseBody

  • @ResponseBody 애노테이션을 사용하면 응답 결과를 HTTP 메시지 바디에 직접 담아서 잔달할 수 있다. (view 사용 x)

 

 

 

 

 

 

 

 

 

 


👀 참고 자료

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-mvc-1/dashboard

 

스프링 MVC 1편 - 백엔드 웹 개발 핵심 기술 - 인프런 | 강의

웹 애플리케이션을 개발할 때 필요한 모든 웹 기술을 기초부터 이해하고, 완성할 수 있습니다. 스프링 MVC의 핵심 원리와 구조를 이해하고, 더 깊이있는 백엔드 개발자로 성장할 수 있습니다., -

www.inflearn.com

 

https://galid1.tistory.com/769

 

Spring MVC - @ModelAttribute의 장점(@RequestParam와 @ModelAttribute)

이번 시간에는 사용자의 전달값을 핸들러의 매개변수로 매핑할때 사용되는 @RequestParam 과 @ModelAttribute 에 대해 알아보도록 하겠습니다. 1. 사용법과 예제 우선 각각의 어노테이션의 사용법과 예

galid1.tistory.com

 

https://medium.com/webeveloper/reqeustbody%EC%99%80-responsebody-%EC%96%B8%EC%A0%9C-%EC%82%AC%EC%9A%A9%ED%95%A0%EA%B9%8C-2efcab364edb

 

@ReqeustBody와 @ResponseBody 언제 사용할까?

비동기 통신을 위한 어노테이션

medium.com

 

728x90