나는 그동안 컨트롤러에서 요청으로 들어오는 Request Dto에 생각 없이 @RequestBody를 붙여주고, 해당 클래스를 구현해왔다. (공장식으로 코드를 찍어냈다.)
하지만 최근에 어떤 로직을 통해 Json으로 들어온 요청이 Request Dto 클래스로 매핑되는지 궁금해져서,
이번 기회에 공부하여 기록으로 남기고자 한다.
RequestBody어노테이션을 캡쳐해 보았다.
요청으로 들어오는 Body를 HttpMessageConverter
가 해당 요청의 type에 따라 변환해준다고 쓰여 있다.
보통 요청은 Json으로 들어오므로, Json을 convert해주는 MappingJackson2HttpMessageConverter
클래스를 살펴보자.
MappingJackson2HttpMessageConverter
클래스다. 해당 클래스를 살펴보면 별로 메소드가 없는 걸 볼 수 있다. Jackson 라이브러리의 ObjectMapper를 이용하여 JSON을 읽고 쓸 수 있다고 하더니만, 해당 클래스에는 ObjectMapper가 없어서, 궁금해서 해당 클래스가 상속하는 AbstractJackson2HttpMessageConverter
클래스에 타고 들어가 보았다.
여기 있었구나 ! 해당 클래스가 필드로 ObjectMapper를 갖고 있었다.
지금까지의 내용을 정리해 보자면,
📝그렇다면 ObjectMapper가 어떤식으로 JSON을 객체로 매핑하는 것일까?
자바의 getter 혹은 setter 메소드에서 get / set prefix를 제거하고, 그 이후 첫글자를 소문자로 바꾸어서 매핑시킨다고 한다.
따라서 Json - > Java Object로 역직렬화 시에는 기본 생성자와 getter 혹은 setter 둘 중 하나만 있어도 reflection을 이용하여 기본 생성자를 생성한 이후 getter / setter필드를 ObjectMapper에서 읽어서 JSON필드 → 자바 객체 필드로 매핑시킨다고 한다.
그런데 기본 생성자는 왜 필요한 걸까 ? 다음 코드에서 알 수 있다.
BeanDeserializer
클래스의 deserializeFromObject
메소드 내부에서 각 분기문에 따라 bean을 생성하는데, 만약 기본생성자가 존재한다면 위의 분기문을 지나치고, 맨 아래의 메소드가 실행된다. 이는 기본생성자를 통해 빈 객체를 만든다는 뜻이다.
대부분의 블로그들은 RequestBody에서 기본생성자가 무조건 필요하다고들 한다.
그러나 이는 사실이 아니며, ParameterNamesModule이라는 자바 라이브러리를 이용해, 매개 변수 이름을 통해서도 역직렬화가 가능하다고 한다. 하지만 지금은 기본적인 내용을 공부하는 중이므로 나중에 더 자세히 알아보도록 한다.
자바 객체를 objectMapper를 이용하여 object → JSON으로 직렬화 한다.
먼저 AbstractJackson2HttpMessageConverter
에서 내부적으로 objectWriter.writeValue
를 호출한다. 이때 value는 jackson으로 직렬화 될 자바 객체이다.
다음으로는 writeValue 메소드 내부에서 serialize
메소드를 호출한다.
해당 메소드에서 조금 타고 들어가 보면 BeanSerializerBase
클래스의 serializeFields가 나온다.
이 메소드가 핵심이였다. 해당 메소드의 for문에서 직렬화 할 필드를 담고있는 prop배열에서 하나씩 필드들을 직렬화 시킨다.
해당 필드들에 접근하기 위해 _accessorMethod에서는 레코드의 getter를 사용하고 있었다. Dto가 일반 클래스였다면 getter메소드를 별도로 선언해야 할 듯 싶다.