로깅 간단히 알아보기

  • System.out.println()같은 시스템 콘솔로 출력하지 않고, 별도의 로깅 라이브러리를 이용해서 로그를 출력
  • 변수 출력할 때, + 를 이용하면 출력하지도 않는 문자열을 위해 문자열 더하기 연산이 이뤄나서 성능적으로 좋지 않다. 그러므로 변수를 출력할 때는 {}를 이용하자.
  • 성능도 System.out보다 좋기 때문에, 실무에서는 꼭 로그를 사용하자.
@Slf4j // private final Logger log = LoggerFactory.getLogger(this.getClass()); 자동으로 넣어줌
@RestController // RestController에서 반환하는 문자열은 뷰 이름이 아니라,그냥 문자열 그대로 반환
public class LogTestController {

    // private final Logger log = LoggerFactory.getLogger(this.getClass());

    @GetMapping("/log-test")
    public String logTest() {
        String name = "Spring";

        // 로컬 PC에서는 이정도 레벨로(모든 로그 출력). logging.level.hello.springmvc=trace
        log.trace("trace log = {}", name);

        // 개발 서버에서는 이정도 레벨로. logging.level.hello.springmvc=debug
        log.debug("debug log = {}", name);

        // 운영 서버에서 이정도 레벨로. 디폴트가 이 수준. logging.level.hello.springmvc=info
        log.info(" info log = {}", name);
        log.warn(" warn log = {}", name);
        log.error(" error log = {}", name);

        return "ok";
    }
}

 

 

 

 

HTTP Method 사용 목적
GET 데이터를 조회할 때. (= 서버에서 정보만 가져옴)
POST 데이터를 전송/저장할 때. (= 서버에 뭔가 보냄)
PUT 기존 데이터를 수정할 때
DELETE 데이터를 삭제할 때

📦 HTTP 요청 방식(method)

 

 

요청 매핑

  • 스프링이 요청을 어떻게 다루는지에 대한 매커니즘
@Slf4j
@RestController
public class MappingController {

    @RequestMapping(value = "/hello-basic", method = RequestMethod.GET)
    public String helloBasic() {
        log.info("helloBasic");

        return "ok";
    }

    @RequestMapping(value = "/mappingGetV1", method = RequestMethod.GET)
    public String mappingGetV1() {
        log.info("mappingGetV1");

        return "ok";
    }

    @GetMapping("/mappingGetV2")
    public String mappingGetV2() {
        log.info("mappingGetV2");

        return "ok";
    }

    /**
     * PathVariable 사용
     * 변수명이 같으면 생략 가능
     *
     * @PathVariable("userId") String userId -> @PathVariable String userId
     */
    // ?userId=userA는 쿼리 파라미터 방식. 해당 방식은 /mapping/userA
    @GetMapping("/mapping/{userId}")
    public String mappingPath(@PathVariable("userId") String data) {
        log.info("mappingPath userId = {}", data);

        return "ok";
    }

    /**
     * PathVariable 사용 다중
     */
    @GetMapping("/mapping/users/{userId}/orders/{orderId}")
    public String mappingPath(@PathVariable("userId") String userId, @PathVariable("orderId") Long orderId) {
        log.info("mappingPath userId={}, orderId={}", userId, orderId);
        return "ok";
    }

    /**
     * 파라미터로 추가 매핑
     * params="mode",
     * params="!mode"
     * params="mode=debug"
     * params="mode!=debug" (! = )
     * params = {"mode=debug","data=good"}
     */
    // http://localhost:8080/mapping-param?mode=debug
    // 이렇게 파라미터에 mode=debug가 있어야 호출될 수 있음
    @GetMapping(value = "/mapping-param", params = "mode=debug")
    public String mappingParam() {
        log.info("mappingParam");

        return "ok";
    }

    /**
     * 특정 헤더로 추가 매핑
     * headers="mode",
     * headers="!mode"
     * headers="mode=debug"
     * headers="mode!=debug" (! = )
     */
    // HTTP 요청 헤더에 mode=debug가 포함되어 있어야 이 핸들러가 실행.
    @GetMapping(value = "/mapping-header", headers = "mode=debug")
    public String mappingHeader() {
        log.info("mappingHeader");

        return "ok";
    }

    /**
     * Content-Type 헤더 기반 추가 매핑 Media Type
     * consumes="application/json"
     * consumes="!application/json"
     * consumes="application/*"
     * consumes="*\/*"
     * MediaType.APPLICATION_JSON_VALUE
     */
    // HTTP 요청 헤더의 Content-Type이 application/json으로 설정되어야 호출 될 수 있음.
    // 즉, 클라이언트가 JSON 데이터를 보낼 때만 이 메서드가 응답하는 구조
    @PostMapping(value = "/mapping-consume", consumes = "application/json")
    public String mappingConsumes() {
        log.info("mappingConsumes");

        return "ok";
    }

    /**
     * Accept 헤더 기반 Media Type
     * produces = "text/html"
     * produces = "!text/html"
     * produces = "text/*"
     * produces = "*\/*"
     */
    // HTTP 요청 헤더의 Accept이 text/html로 설정되어야 호출될 수 있음.
    // 스프링은 서버가 어떤 형식의 응답을 생성할 수 있는지 미리 명시
    @PostMapping(value = "/mapping-produce", produces = "text/html")
    public String mappingProduces() {
        log.info("mappingProduces");

        return "ok";
    }
}

 

 

 

 

요청 매핑 - API 예시

  • 회원 목록 조회: GET /users
  • 회원 등록: POST /users
  • 회원 조회: GET  /users/{userId}
  • 회원 수정: PATCH /users/{userId} 
  • 회원 삭제: DELETE /users/{userId}
@RestController
@RequestMapping("/mapping/users")
public class MappingClassController {

    @GetMapping
    public String users() {
        return "get users";
    }

    @PostMapping
    public String addUser() {
        return "post user";
    }

    @GetMapping("/{userId}")
    public String findUser(@PathVariable("userId") String userId) {
        return "get userId= " + userId;
    }

    @PatchMapping("/{userId}")
    public String updateUser(@PathVariable("userId") String userId) {
        return "update userId= " + userId;
    }

    @DeleteMapping("/{userId}")
    public String deleteUser(@PathVariable("userId") String userId) {
        return "delete userId= " + userId;
    }
}

 

 

 

 

HTTP 요청 - 기본, 헤더 조회

  • HTTP 요청의 헤더, 쿠키, 메타정보 등을 스프링 방식으로 편하게 추출
  • 기존 서블릿에서 HttpServletRequest 객체에서 .getXXX()로 추출하지 않아도 된다.
@Slf4j
@RestController
public class RequestHeaderController {

    @RequestMapping("/headers")
    public String header(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("header host={}", host);
        log.info("myCookie={}", cookie);

        return "ok";
    }
}

 

 

 

 

 

HTTP 요청 파라미터 - 쿼리 파라미터, HTML Form

  • GET - 쿼리 파라미터
  • POST - HTML Form
  • 위 두가지 요청 파라미터는 동일한 형식이므로, request.getParameter()를 통해 모두 조회할 수 있다.
@Slf4j
@Controller
public class RequestParamController {

    @RequestMapping("/request-param-v1")
    public void requestParamV1(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String username = request.getParameter("username");
        int age = Integer.parseInt(request.getParameter("age"));

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

        PrintWriter w = response.getWriter();
        w.write("ok");
    }
}

 

 

✅ 스프링으로 개선되는 버전

@Slf4j
@Controller
public class RequestParamController {

    @ResponseBody // 반환하는 문자열이 논리 경로인 viewName이 아니고, 그냥 http 응답 바디에 문자열 자체를 반환
    @RequestMapping("/request-param-v2")
    public String requestParamV2(@RequestParam("username") String username,
                                 @RequestParam("age") int age) {

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

        return "ok";
    }

    // 버전 이슈와, 비직관적이라서 잘 사용 X
//    @ResponseBody
//    @RequestMapping("/request-param-v3")
//    public String requestParamV3(@RequestParam String username,
//                                 @RequestParam int age) {
//
//        log.info("username = {}, age = {}", username, age);
//
//        return "ok";
//    }

    // 버전 이슈와, 비직관적이라서 잘 사용 X
//    @ResponseBody
//    @RequestMapping("/request-param-v4")
//    public String requestParamV4(String username, int age) {
//        log.info("username = {}, age = {}", username, age);
//
//        return "ok";
//    }

    // username = 인 경우, 빈 문자가 들어와버림. 즉, 오류가 발생 안해버림.
    @ResponseBody
    @RequestMapping("/request-param-required") // username 파라미터가 무조건 들어와야 함
    public String requestParamRequired(@RequestParam(value = "username", required = true) String username,
                                 @RequestParam(value = "age", required = false) Integer age) {

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

        return "ok";
    }

    // 파라미터 안들어오면, 디폴트 값으로 대체. username = 인(빈 문자) 경우에도 디폴트로 값 넣어줌. 굿.
    @ResponseBody
    @RequestMapping("/request-param-default")
    public String requestParamDefault(@RequestParam(value = "username", defaultValue = "guest") String username,
                                       @RequestParam(value = "age", defaultValue = "-1") int age) {

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

        return "ok";
    }

    // 요청 파라미터 전부 가져오기.
    // 파라미터의 값이 중복될 수 있을 것 같으면, MultiValueMap을 사용하자.
    @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";
    }
}

 

 

 

 

HTTP 요청 파라미터 - @ModelAttribute

// 마법처럼 HelloData 객체가 생성되고, 요청 파라미터 값도 모두 들어가 있음 -> 프로퍼티
@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";
}

 

  • @ModelAttribute HelloData helloData
    스프링이 HelloData 객체를 먼저 생성함 (기본 생성자 필요)
  • HTTP 요청 파라미터 username=seol&age=24
    → 파라미터 이름을 기반으로, setUsername("seol"), setAge(24) 호출
  • 자바빈 프로퍼티 규약 기반 덕분에 가능:
    • 필드는 private, 게터/세터는 public
    • setXxx(...)를 호출할 수 있으면 됨
  • 결과적으로 객체가 채워진 채로 컨트롤러에 전달

 

 

 

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

  • HttpEntity를 통해, HTTP header, body 정보를 편리하게 조회.
  • 요청 파라미터를 조회하는 @RequestParam, @ModelAttribute와는 관계가 없음.
  • 메시지 바디 정보 직접 반환할 수 있고, 헤더 정보 포함 가능함. view 조회는 없음.
  • @RequestBody를 사용하면, 더욱 편하게 메시지 바디를 조회 가능.(바디만 조회함)
    -> HttpMessageConverter가 메시지 바디 전체를 문자열로 변환하고 파라미터로 넘겨줌.
  • 헤더 정보가 필요하면, @RequestBody 말고, HttpEntity나 @RequestHeader를 사용하면 됨.
@Slf4j
@Controller
public class RequestBodyStringController {

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

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

        PrintWriter w = response.getWriter();
        w.write("ok");
    }

    @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");
    }

    @PostMapping("/request-body-string-v3")
    public HttpEntity<String> requestBodyStringV3(HttpEntity<String> httpEntity) throws IOException {
        String messageBody = httpEntity.getBody();
        log.info("messageBody = {}", messageBody);

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

    @ResponseBody
    @PostMapping("/request-body-string-v4")
    public String requestBodyStringV4(@RequestBody String messageBody) {
        log.info("messageBody = {}", messageBody);

        return "ok";
    }
}

 

 

방법 메시지 바디 HTTP 헤더 응답 방식
v1 (Servlet API) 직접 읽음 직접 처리 response.getWriter()
v2 (InputStream) 주입됨 직접 처리 Writer 사용
v3 (HttpEntity) getBody() getHeaders() HttpEntity 반환
v4 (@RequestBody) 자동 주입 헤더 불가 -> @RequestHeader 써야 문자열 직접 반환

 

 

 

 

 

HTTP 요청 메시지 - JSON

  • HTTP 요청시에 content-type이 application/json인지 꼭 확인해야 한다. 그래야 JSON을 처리할 수 있는 HTTP 메시지 컨버터가 실행된다.
  • @RequestBody 요청
    • JSON 요청 -> HTTP 메시지 컨버터 -> 객체
  • @ResponseBody 응답
    • 객체 -> HTTP 메시지 컨버터 -> 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("username = {}, age = {}", helloData.getUsername(), helloData.getAge());

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

    @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("username = {}, age = {}", helloData.getUsername(), helloData.getAge());

        return "ok";
    }

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

        return "ok";
    }

    // 요청 헤더까지 함께 보고 싶을 때 유용
    @ResponseBody
    @PostMapping("/request-body-json-v4")
    public String requestBodyJsonV4(HttpEntity<HelloData> helloData) throws IOException {
        HelloData messageBody = helloData.getBody();
        log.info("username = {}, age = {}", messageBody.getUsername(), messageBody.getAge());

        return "ok";
    }

    // 응답도 JSON으로 내보내야 할 때
    @ResponseBody
    @PostMapping("/request-body-json-v5")
    public HelloData requestBodyJsonV5(@RequestBody HelloData helloData) throws IOException {
        log.info("username = {}, age = {}", helloData.getUsername(), helloData.getAge());

        return helloData;
    }
}

 

버전 방식 어노테이션/타입 요청  바디 처리 응답 처리 헤더 접근
v1 서블릿 직접 처리 HttpServletRequest, InputStream 수동으로 Stream → String → 객체 파싱 response.getWriter().write() request.getHeader() 등 직접
v2 @RequestBody String @RequestBody String 문자열 자동 주입, 객체 변환은 수동 (ObjectMapper) 문자열 반환 X 직접 처리 필요
v3 @RequestBody 객체 @RequestBody HelloData JSON → 객체 자동 변환 (HttpMessageConverter) 문자열 직접 반환 X
v4 HttpEntity<객체> HttpEntity<HelloData> 자동 변환, getBody()로 바디 접근 new HttpEntity<>("ok") 직접 작성 getHeaders()로 헤더 가능
v5 객체 반환 (응답까지 자동) @RequestBody + return 객체 자동 변환 객체 → JSON 자동 변환 X

 

 

 

 

HTTP 응답 - 정적 리소스, 뷰 템플릿

✅ 정적 리소스

  • src/main/resources/static 또는 /public, /resources, /META-INF/resources 등 클래스패스 하위 폴더에 HTML, JS, CSS 을 넣는다.
  • 브라우저 요청이 들어오면 가공 없이 그대로 파일이 응답됨
  • 예: /static/basic/hello-form.htmlhttp://localhost:8080/basic/hello-form.html

✅ 뷰 템플릿

  • 템플릿 파일 경로: src/main/resources/templates/
  • 컨트롤러에서 Model 객체에 데이터 담고 → 논리 뷰 이름 반환 → 뷰 리졸버가 실제 템플릿 파일 찾아 렌더링
  • 예: return "response/hello"/templates/response/hello.html 렌더링
@Controller
public class ResponseViewController {

    @RequestMapping("/response-view-v1")
    public ModelAndView responseViewV1() {
        ModelAndView mav = new ModelAndView("response/hello")
                .addObject("data", "hello1");

        return mav;
    }

    @RequestMapping("/response-view-v2")
    public String responseViewV2(Model model) {
        model.addAttribute("data", "hello2");

        return "response/hello";
    }

    // 사용하지 말자. 이런 것도 있다 정도로 알자.
    @RequestMapping("/response/hello")
    public void responseViewV3(Model model) {
        model.addAttribute("data", "hello3");
    }
}

 

 

분류 방식 특징
정적 리소스 /static, /public 등 - HTML, CSS, JS 파일 등 가공 없이 그대로 응답됨
- 스프링이 아니라 톰캣 정적 리소스 핸들러가 처리함
뷰 템플릿 Model + return viewName - 컨트롤러가 Model에 데이터 담고,
return "논리 뷰 이름" → 뷰 리졸버가 HTML 렌더링
뷰 템플릿 (void) return 타입 없음
(URL → 뷰 이름 추론)
- 요청 URL로부터 뷰 이름 추론 → 명시성 부족하여 비추천

 

 

 

 

 

HTTP 응답 - HTTP API, 메시지 바디에 직접 입력

  • 뷰 템플릿이나 정적 리소스가 아닌, HTTP 메시지 바디 자체에 데이터(문자열, JSON 등)를 직접 담아 반환하는 방식
  • @RestController로 구현하다가, 상태 코드나 응답 헤더를 세밀하게 제어하고 싶다면, ResponseEntity<T>를 이용.
@Slf4j
// @Controller
// @ResponseBody
@RestController // -> @Controller, @ResponseBody 들어가 있음. Rest API 만들 때 사용하는 컨트롤러
public class ResponseBodyController {

    @GetMapping("/response-body-string-v1")
    public void responseBodyV1(HttpServletResponse response) throws IOException {
        response.getWriter().write("ok");
    }

    @GetMapping("/response-body-string-v2")
    public ResponseEntity<String> responseBodyV2() throws IOException {
        return new ResponseEntity<>("ok", HttpStatus.OK);
    }

    // @ResponseBody
    @GetMapping("/response-body-string-v3")
    public String responseBodyV3() {
        return "ok";
    }

    @GetMapping("/response-body-json-v1")
    public ResponseEntity<HelloData> responseBodyJsonV1() {
        HelloData helloData = new HelloData();
        helloData.setUsername("userA");
        helloData.setAge(20);

        return new ResponseEntity<>(helloData, HttpStatus.OK);
    }

    // 동적으로 응답 상태 반환하려면, ResponseEntity 사용
    @ResponseStatus(HttpStatus.OK)
    // @ResponseBody
    @GetMapping("/response-body-json-v2")
    public HelloData responseBodyJsonV2() {
        HelloData helloData = new HelloData();
        helloData.setUsername("userA");
        helloData.setAge(20);

        return helloData;
    }
}

 

 

 

 

 

HTTP 메시지 컨버터

  • HTTP 메시지 바디와 Java 객체 간의 변환을 담당하는 컴포넌트
    • 요청 시 (역직렬화): @RequestBody, HttpEntity 등을 사용하면 클라이언트가 전송한 메시지 바디(JSON, XML, plain text 등)를 String, byte[], 객체 등 Java 타입으로 변환
    • 응답 시 (직렬화): @ResponseBody, ResponseEntity, @RestController 등을 사용하면 컨트롤러가 반환한 Java 객체를 JSON, 문자열 등으로 변환하여 HTTP 메시지 바디에 기록

✅ @ResponseBody를 사용시

  • 문자열이나 객체를 반환했을 때, 그걸 HTTP 메시지 바디에 메시지 컨버터가 적절한 형식으로 변환해서 실어주게 됨.
  • viewResolver 대신에 HttpMessageConverter가 동작
  • 기본 문자처리: StringHttpMessageConverter
  • 기본 객체처리: MappingJackson2HttpMessageConverter
  • byte 처리 등등 기타 여러 HttpMessageConverter가 기본으로 등록되어 있음

@ResponseBody 내부 동작 흐름

 

 

대상 타입 컨버터 지원 미디어 타입 예시 요청 예시 응답
파일 등 바이너리 변환 0 = ByteArrayHttpMessageConverter application/octet-stream, */* @RequestBody
byte[] data
return byte[];
(예: 이미지 파일)
문자열 변환 1 = StringHttpMessageConverter text/plain, */*,
application/json
@RequestBody
String data
return "ok";
JSON등 객체 변환 2 = MappingJackson2HttpMessageConverter application/json @RequestBody
HelloData data
return helloData; (JSON 자동 변환)

 

✅ 스프링 MVC는 다음의 경우에 HTTP 메시지 컨버터를 적용

  • HTTP 요청: @RequestBody, HttpEntity(RequestEntity)  canRead()로 메시지 컨버터가 해당 클래스, 미디어타입을 지원하는지 체크하고, read()로 메시지 읽기 가능
  • HTTP 응답: @ResponseBody, HttpEntity(ResponseEntity) CanWrite()로 메시지 컨버터가 해당 클래스, 미디어 타입을 지원하는지 체크하고. write()로 메시지 쓰기 가능

 

 

 

 

요청 매핑 핸들러 어뎁터 구조

  • 요청 파라미터 처리: 핸들러 메서드의 파라미터 생성 -> Argument Resolver
    • @RequestParamRequestParamMethodArgumentResolver
    • @PathVariablePathVariableMethodArgumentResolver
    • HttpServletRequestServletRequestMethodArgumentResolver
    • @RequestBody → ✅ RequestResponseBodyMethodProcessor (uses HttpMessageConverter.read())
    • HttpEntity<T> → ✅ HttpEntityMethodProcessor (uses HttpMessageConverter.read())

  • 핸들러 실행 후 반환값 처리: 클라이언트에 보낼 응답 생성 -> Return Value Handler
    • String (View 이름)  → ViewNameMethodReturnValueHandler
    • ModelAndViewModelAndViewMethodReturnValueHandler
    • @ResponseBody → ✅ RequestResponseBodyMethodProcessor (uses HttpMessageConverter.write())
    • HttpEntity<T> → ✅ HttpEntityMethodProcessor (uses HttpMessageConverter.write())

RequestMappingHandlerAdapter 동작 방식

 

 

 

다시, HTTP 메시지 컨버터

  • 대부분의 ArgumentResolver는 단순히 쿼리 파라미터, 경로 변수, HttpServletRequest 등을 통해 값을 뽑음
  • 그 중 일부, 메시지 바디가 필요한 상황에서만 HttpMessageConverter를 사용해서 값을 읽어들임
  • 즉, ArgumentResolver나 ReturnValueHandler가 메시지 바디에 접근할 필요가 있을 때, 내부적으로 HttpMessageConverter를 사용

요청 매핑 핸들러 어뎁터와 HTTP 메시지 컨버터 내부 구조

 

 

✅ 요청 시 메시지 컨버터를 사용하는 ArgumentResolver

  • RequestResponseBodyMethodProcessor
    @RequestBody 처리용
    내부에서 메시지 바디를 꺼내기 위해 HttpMessageConverter.read() 호출
  • HttpEntityMethodProcessor
    HttpEntity<T>, RequestEntity<T> 처리용
    마찬가지로 메시지 바디 읽기 위해 메시지 컨버터 사용

 

✅ 응답 시 메시지 컨버터를 사용하는 ReturnValueHandler

  • RequestResponseBodyMethodProcessor
    → @ResponseBody 붙은 메서드 결과 처리
    → 반환값을 HttpMessageConverter.write()로 HTTP 메시지 바디에 기록
  • HttpEntityMethodProcessor
    → HttpEntity<T>, ResponseEntity<T> 응답 처리용
    → 메시지 바디, 헤더, 상태코드까지 전부 이쪽에서 작성

 

요청 → DispatcherServlet
→ HandlerMapping (컨트롤러 선택)
→ RequestMappingHandlerAdapter (핸들러 어댑터)

1. ArgumentResolver 목록 탐색
→ supportsParameter() → resolveArgument()
→ 메시지 바디 필요 시, HttpMessageConverter.read()

2. 핸들러 메서드 실행

3. ReturnValueHandler 목록 탐색
→ supportsReturnType() → handleReturnValue()
→ 메시지 바디 필요 시, HttpMessageConverter.write()

 

 

 

매개변수 리졸버가 컨트롤러에 필요한 매개변수를, 요청 정보를 기반으로 생성해서 넘겨준다.
그리고 컨트롤러가 반환하는 값도 ReturnValueHandler가 적절한 방식으로 변환해준다.
메시지 바디를 읽거나 쓸 필요가 있는 경우에만, 내부적으로 HttpMessageConverter가 동작해서 변환을 수행한다.