URL/JSON은 어떻게 자바 객체로 변하나? ( 개념정리 )

KUN·2025년 6월 9일

목적

Spring MVC 내부 구조 중
ArgumentResolver,
ReturnValueHandler,
HttpMessageConverter,
Converter 등을 학습했다.
처음 접하는 개념들이 많아 이해가 쉽지 않았지만, 흐름을 잡는 데 집중했다.


순서

초급 <- 이부분만 다룰 것

  1. HttpMessageConverter
  2. ArgumentResolver (기본 흐름만)
  3. ConversionService (개념만)

중급

  1. Converter
  2. TypeConverter
  3. Formatter 날짜, 숫자 등 포맷 변환 필요할 때 사용
  4. WebMvcConfigurer.addFormatters() Converter/Formatter 등록법 학습

스프링 내부구조


Mess..Converter? Arg...Resolver?

개념

HttpMessageConverter - HTTP Body(예: JSON)를 자바 객체로 변환하거나, 자바 객체를 HTTP Body로 변환하는 컴포넌트

ArgumentResolver - HTTP 요청 데이터를 컨트롤러 메서드의 파라미터에 맞는 자바 타입으로 변환해 주는 컴포넌트
(예: @RequestParam, @PathVariable, @ModelAttribute 처리 담당)

ConversionService - 타입 간 변환을 위한 중앙 서비스로, 스프링 내부에서 다양한 변환
(예: String → Integer)을 일관되게 지원

쉽게 비유 해보자.

당신은 서빙하는 알바생이다.

손님은 미국인, 일본인, 중국인 등등 다양한 국적의 사람이 온다고 가정하자.
당신은 이 모든 사람의 언어를 알아들을 수 있다.

하지만, 셰프는 한국인이라 이 모든 언어들을 알아듣지 못한다.
셰프는 오직 '한국어로 적힌 정확한 주문서(자바 객체)'만 이해할 수 있다.

----- 일반적인 상황 (변환기 없음) ------
주문 : 여기 워터멜론 하나 주세요~
당신 : 여기 워터멜론 하나 주문 들어왔습니다.
주방 : ?;; 멜론? 그런건 메뉴판에 없어! (주방이 이해 못함 : 변환 실패)

			부가 설명:
스프링은 기본적으로 [내가 만든 등급]을 모른다. 나는 BLUE_STAR 등급을 만들었지만,
'String 에서 VIP_CODE 로 변환하는 법'을 모르는데
그걸 변환 시키게 해주는 것이 Converter 역할이며, 
그걸 관리하는게 'ConverterService' 이다.

HttpMessageConverter

주문 : (워터멜론 달라는 쪽지를 HTTP Body에 담아 보냄 -: JSON)

당신 : (쪽지를 읽으며 변환)"아, 수박 하나 주문이구나!" (@RequestBody - 자바 객체 변환)

주방 :~ 수박, 알겠어~ (주방이 이해함)

ConversionService

주문 : 저는 블루스타 등급이에요 (특수 코드 전달 - "등급코드=BLUE_STAR")

당신 : (ConversionService라는 '변환 메뉴얼' 참고해서) 
"BLUE_STAR → VIP 고객 등급"으로 변환
VIP 고객 등급 주문 들어왔습니다~

주방 : 아 VIP 고객이 오셨구나?

부가 설명

public class VIPCode { > 이와 같은 VIP Code 가 존재한다고 가정하자.
    private String grade;

    public VIPCode(String grade) {
        this.grade = grade;
    }

    public String getGrade() {
        return grade;
    }
}

이걸 어떻게 변환해야하나?
만약 요청이 /welcome?code=BLUE_STAR 라고 들어왔다.

@GetMapping("/welcome")
public String welcome(@RequestParam VIPCode code) { <- 이렇게 받을 수 있는가?
    ...
}

정답: 못받는다.
그래서 우리가 직접 Converter를 구현하고, 이를 스프링 빈으로 등록해서
스프링이 변환 방법을 알 수 있도록 만들어준다.

@Component
public class StringToVIPCodeConverter implements Converter<String, VIPCode> {
    @Override
    public VIPCode convert(String source) {
        return new VIPCode(source);  이렇게 넣어주면 위의 코드가 실행이 된다.
    }
} 이렇게 하면 변환이 성공적으로 진행 된다는 뜻 이다.

ArgumentResolver

가정

위 이미지 때문에 헷갈렸던 부분을 정리하고자 한다.
나는 처음에 저런 다이어그램을 보고 이렇게 생각했다.

요청이 들어오고 그 요청이 ArgumentResolver로 간다.
그 다음 ArgumentResolver 가 @RequestParm , @RequestBody 등등을 확인하고 각자 역할에 맞게 실행된다.
RequestBody 는 HttpMessageConverter 로 이동하며,
RequestParam, PathVariable 등등을 처리하는 것은 ConversionService 로 이동한다.

라고 이해하고 있다.

내가 이해하는 게 맞나?

처음단계는 일단 요청이 들어왔고,

RequestMappingHandlerAdapter.class

각각 처리하는 클레스들 ( RequestResponseBody, RequestParam, PathVariable ) 등등... 그리고 상속받는 걸 들어가 본다면....

내부구조

공통적으로 HanlderMethodArgumentResolver 을 구현받고 있다.

정리해보자

요청 → DispatcherServlet
    ↓
HandlerAdapter (RequestMappingHandlerAdapter)
    ↓
각 파라미터마다 호환되는지 체크( 아래와 같은 메서드 실행 )

※ 여기서 resolver 종류:
    - @RequestParam → RequestParamMethodArgumentResolver → ConversionService 사용
    - @PathVariable → PathVariableMethodArgumentResolver → ConversionService 사용
    - @RequestBody → **RequestResponseBodyMethodProcessor** → HttpMessageConverter 사용
    - @ModelAttribute → ModelAttributeMethodProcessor → ConversionService 사용

배운 점

처음에는 ArgumentResolver가 모든 파라미터를 처리하고,
그 안에서 ConversionService와 HttpMessageConverter도 같이 쓰는 줄 알았다.

하지만 조사해보니,

  • @RequestBody는 RequestResponseBodyMethodProcessorHttpMessageConverter를 직접 사용하고,

  • @RequestParam과 @PathVariable은 일반 ArgumentResolverConversionService를 사용한다는 걸 알게 됐다.

  • 처음에는 @RequestParam으로 커스텀 객체는 못받는 줄 알았으나,
    하지만, Converter 인터페이스를 구현해서 변환 방법을 등록해주면 커스텀 객체도 받을 수 있다는 사실을 알게 되었다.

profile
배우노라, 실험하노라, 기록하노라

0개의 댓글