@RequestBody는 기본 생성자가 필요없다.

2022. 8. 11. 23:17Spring

나는 지금까지 @RequestBody를 통해 JSON 요청이나 응답할 때, 기본 생성자가 꼭 필요하다고 알고 있었다.

 

아래 코드는 위 조건을 반영한 코드이다.

@Getter
public class HelloDto {
    private String name;
    private boolean use;
    private String nullable;

    public HelloDto() {}
}

@RequestBody 에서 @Setter가 없어도 되는 이유, 기본 생성자가 필요한 이유에 대한 블로그 글이 엄청 많기 때문에 이미 아는 내용일 수도 있다.

 

Spring Boot가 JSON을 매핑하는 구현체로 Jackson 라이브러리를 사용하기 때문이다. 정확히는 Jackson의 ObjectMapper.

 

getter setter 의 prefix를 때고 필드명과 비교해 리플렉션을 이용해 값을 할당한다. 이 과정에서 기본 생성자가 필요하다.

 

그래서 나는 DTO에는 final 을 쓸 수 없다고 알고 있었다.


하지만 최근 코드 리뷰에서 final을 사용하라는 조언을 받아서 확인해본 결과 final을 사용할 수 있다는 것을 알게 되었다.

 

다음 코드를 사용해도 정상적으로 매핑된다.

@Getter
public class HelloDto {
    private final String name;
    private final boolean use;
    private final String nullable;

    public HelloDto(String name, boolean use, String nullable) {
        this.name = name;
        this.use = use;
        this.nullable = nullable;
    }
}

테스트 결과 정상적으로 값이 매핑되고 있다.

 

이것이 가능한 이유는 @JsonCreator이다. 매핑이 필요한 필드를 가진 생성자에 해당 애노테이션을 사용해 Jackson이 이를 인식해서 필드를 매핑할 수 있게 한다.

 

위 코드에 애노테이션이 없어도 동작하는 이유는 생성자가 한 개일 때는 생략이 가능하기 때문이다.

 

이 기능을 가능하게 해주는 아래 라이브러리이다.

 

com.fasterxml.jackson.module:jackson-module-parameter-names

덕분에 DTO도 불변 객체로 만들 수 있게 되었다.


추가 내용

 

만약 인텔리제이를 사용하고 있다면 동작하지 않는 경우가 있다.

 

인텔리제이에서 빌드는(Preference - build) 기본 설정인 Gradle, 인텔리제이 자체 빌드툴인 IntelliJ IDEA 가 있다.

 

이 때 IntelliJ IDEA 사용하면 적용되지 않는다. 빌드 방법을 유지한채 사용하고자 한다면 다음과 같이 해야 한다.

@JsonCreator
public UserLoginData(@JsonProperty("email") String email, @JsonProperty("password") String password) {
    this.email = email;
    this.password = password;
}

참고

https://bbbicb.tistory.com/46

https://stackabuse.com/definitive-guide-to-jackson-objectmapper-serialize-and-deserialize-java-objects/

https://velog.io/@park2348190/Jackson-ObjectMapper에서-기본-생성자-없이-Deserialization-하기#jackson과-reflection

http://daplus.net/java-objectmapper를-사용하여-기본-생성자없이-불변-객체를-de-serialize/

https://skyblue300a.tistory.com/13

https://juhi.tistory.com/68