[Spring] Request Dto, Response Dto

구범모·2023년 9월 15일
0

@RequestBody

나는 그동안 컨트롤러에서 요청으로 들어오는 Request Dto에 생각 없이 @RequestBody를 붙여주고, 해당 클래스를 구현해왔다. (공장식으로 코드를 찍어냈다.)

하지만 최근에 어떤 로직을 통해 Json으로 들어온 요청이 Request Dto 클래스로 매핑되는지 궁금해져서,

이번 기회에 공부하여 기록으로 남기고자 한다.

RequestBody어노테이션을 캡쳐해 보았다.

요청으로 들어오는 Body를 HttpMessageConverter가 해당 요청의 type에 따라 변환해준다고 쓰여 있다.

보통 요청은 Json으로 들어오므로, Json을 convert해주는 MappingJackson2HttpMessageConverter

클래스를 살펴보자.

MappingJackson2HttpMessageConverter 클래스다. 해당 클래스를 살펴보면 별로 메소드가 없는 걸 볼 수 있다. Jackson 라이브러리의 ObjectMapper를 이용하여 JSON을 읽고 쓸 수 있다고 하더니만, 해당 클래스에는 ObjectMapper가 없어서, 궁금해서 해당 클래스가 상속하는 AbstractJackson2HttpMessageConverter 클래스에 타고 들어가 보았다.

여기 있었구나 ! 해당 클래스가 필드로 ObjectMapper를 갖고 있었다.

지금까지의 내용을 정리해 보자면,

📝
1. RequestBody로 들어온 요청은 각 요청의 타입에 맞게 HttpMessageConverter를 구현하는 컨버터가 동작한다.
2. Json이 요청으로 들어오면, HttpMessageConverter를 구현하는 MappingJackson2HttpMessageConverter가 동작한다.
3. 해당 컨버터의 부모클래스인 AbstractJackson2HttpMessageConverter에서 ObjectMapper를 필드로 갖고있고, JSON을 읽고 쓸 수 있는 read / write 메소드가 구현되어 있다.(자세한 것은 해당 클래스에서 살펴볼 수 있다.)

그렇다면 ObjectMapper가 어떤식으로 JSON을 객체로 매핑하는 것일까?

ObjectMapper가 JSON필드 → 자바 객체 필드로 매핑하는 법

출처 : https://jenkov.com/tutorials/java-json/jackson-objectmapper.html#how-jackson-objectmapper-matches-json-fields-to-java-fields

자바의 getter 혹은 setter 메소드에서 get / set prefix를 제거하고, 그 이후 첫글자를 소문자로 바꾸어서 매핑시킨다고 한다.

따라서 Json - > Java Object로 역직렬화 시에는 기본 생성자와 getter 혹은 setter 둘 중 하나만 있어도 reflection을 이용하여 기본 생성자를 생성한 이후 getter / setter필드를 ObjectMapper에서 읽어서 JSON필드 → 자바 객체 필드로 매핑시킨다고 한다.

@RequestBody 사용시 기본 생성자

그런데 기본 생성자는 왜 필요한 걸까 ? 다음 코드에서 알 수 있다.

BeanDeserializer 클래스의 deserializeFromObject 메소드 내부에서 각 분기문에 따라 bean을 생성하는데, 만약 기본생성자가 존재한다면 위의 분기문을 지나치고, 맨 아래의 메소드가 실행된다. 이는 기본생성자를 통해 빈 객체를 만든다는 뜻이다.

사실 기본 생성자가 필요 없다고?

대부분의 블로그들은 RequestBody에서 기본생성자가 무조건 필요하다고들 한다.

그러나 이는 사실이 아니며, ParameterNamesModule이라는 자바 라이브러리를 이용해, 매개 변수 이름을 통해서도 역직렬화가 가능하다고 한다. 하지만 지금은 기본적인 내용을 공부하는 중이므로 나중에 더 자세히 알아보도록 한다.

@ResponseBody

자바 객체를 objectMapper를 이용하여 object → JSON으로 직렬화 한다.

먼저 AbstractJackson2HttpMessageConverter에서 내부적으로 objectWriter.writeValue를 호출한다. 이때 value는 jackson으로 직렬화 될 자바 객체이다.

다음으로는 writeValue 메소드 내부에서 serialize메소드를 호출한다.

해당 메소드에서 조금 타고 들어가 보면 BeanSerializerBase클래스의 serializeFields가 나온다.

이 메소드가 핵심이였다. 해당 메소드의 for문에서 직렬화 할 필드를 담고있는 prop배열에서 하나씩 필드들을 직렬화 시킨다.

해당 필드들에 접근하기 위해 _accessorMethod에서는 레코드의 getter를 사용하고 있었다. Dto가 일반 클래스였다면 getter메소드를 별도로 선언해야 할 듯 싶다.

결론

RequestBody를 역직렬화 하기 위해 필요한 것

  • 기본 생성자(엄밀히 말하면 무조건 필요하지는 않다.)
  • getter / setter 둘 중 하나.

ResponseBody를 직렬화 하기 위해 필요한 것

  • getter

reference

profile
우상향 하는 개발자

0개의 댓글