m1ndy5's coding blog

Jackson & Constructor 본문

백엔드 with java/spring

Jackson & Constructor

정민됴 2023. 7. 11. 23:20

프로젝트를 진행 중에 굉장히 난감한 문제를 겪었다.
문제는 아래와 같다.

// 댓글 다는 DTO
@Data
public class PostCommentReq {
    private final Long accountId;
    private final String content;
}

// 댓글 수정 DTO
@Data
public class PatchCommentReq {
    private final String content;
}

댓글 다는 DTO는 전혀 문제가 없이 잘 작동을 했지만 댓글 수정 DTO는 제대로 된 값을 계속해서 못받아오는 상황,,,!!!
일단 @Data 가 무엇인지 살펴보자!

 * Equivalent to {@code @Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode}.

@Data를 까보면 위와 같은 주석을 확인할 수 있는데, 해석해보자면
@Data는 @code @Getter @Setter @RequiredArgsConstructor @ToString @EqualsAndHashCode 와 똑같다 라는 뜻
여기서 생성자와 관련된 곳은 바로 @RequiredArgsConstructor이다.

 

@RequiredArgsConstructor 이란?

@RequiredArgsConstructor은 초기화 되지 않은 final 필드나, @NotNull 이 붙은 필드에 대해 생성자를 생성해준다.

 

 

여기서 알 수 있는 점은 댓글 다는 DTO와 댓글 수정 DTO 모두 final을 붙여주었고 @Data가 @RequiredArgsConstructor을 포함하고 있으니까 인자 있는 생성자가 잘 작동해야한다고 생각했다.

하지만 결과는 댓글 다는 DTO는 인자 있는 생성자가 호출됐지만 댓글 수정 DTO는 인자 있는 생성자를 호출하지 못했다.

 

 

이에 대해 몇가지 시도를 해보았는데

  1. 댓글 수정 DTO의 필드에 final를 빼보았다.
    final이 없기 때문에 기본 생성자로 호출이 된 것을 확인할 수 있었다.
  2. 필드를 2개 이상으로 바꿔보았다.
    이또한 인자 있는 생성자를 잘 호출하는 것을 확인할 수 있었다.

어떻게 된 것인지 알아보자!!

 

Jackson

json을 파싱하고 생성하는 라이브러리
내부적으로 Object Mapper class가 있어 json 파일을 파싱해서 자바 객체로 역직렬화 하거나, 자바 객체로부터 json을 직렬화시킬 수 있음
Jackson은 spring web dependency를 추가하면 자동으로 설치
Jackson은 Java Reflection을 사용

 

What is Java Reflection?

구체적인 클래스 타입을 알지 못해도 그 클래스의 메소드, 타입, 변수들에 접근할 수 있도록 해주는 자바 API
Java Reflection은 생성자의 인자 정보들을 가져올 수 없음


-> 따라서 기본 생성자 없이 파라미터가 있는 생성자만 존재한다면 java Reflection이 객체를 생성할 수 없게 됨
Jackson이 역직렬화를 할 때 반드시 기본 생성자가 필요한 이유였다.

 

그렇다면 왜 댓글 다는 DTO는 기본 생성자 없이 역직렬화가 가능했을까?

바로 jackson-module-parameter-names 모듈 덕분!
jackson-module-parameter-names 모듈은 기본 생성자가 없는 경우 필드에 데이터를 넣을 수 있는 다른 루트 검색 -> 인자가 있는 생성자로 바인딩

그래서 기본 생성자가 존재하지 않아도 다른 생성자에 역할을 위임해서 객체 바인딩 진행

 

그렇다면 여기서 또 아니 그래서 인자가 한 개인 생성자는 왜 바인딩이 안되는데!!
인자가 한개면 ObjectMapper가 모듈을 등록해도 역직렬화를 해주지 못한다고 함,,,(이유 아시는 분??ㅎㅎ)

 

그렇다면 어떻게 역직렬화를 해야할까?

기본생성자를 만들어주거나

@JsonCreator
public dto(String content){
    this.content = content;
}

@JsonCreator 을 사용해주면 된다고 한다.

References

https://da-nyee.github.io/posts/woowacourse-why-the-default-constructor-is-needed/
https://beaniejoy.tistory.com/76