@Builder 어노테이션과 default 생성자

고범수·2023년 8월 8일
3

Spring Boot

목록 보기
9/12

Spring Boot 3.1.2
Jackson-core 2.15.2

상황

Spring Boot에서 다음과 같은 Dto를 이용하여 @ReqeustBody를 통해 Json을 역직렬화 하려고 한다. 그러면,

import lombok.Builder;
import lombok.Getter;
import lombok.ToString;

@Getter
@ToString
@Builder
public class DemoDto {
    private String demoName;
}

아래와 같은 에러 메시지를 보여준다.

Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.example.demo.dto.DemoDto` (although at least one Creator exists): cannot deserialize from Object value (no delegate- or property-based Creator)]

이전에 글에서 살펴봤듯, Jackson은 역직렬화 시에 Reflection을 이용하고, 이 때 기본 생성자가 사용된다. 따라서, 기본 생성자가 필요한데, 위 DemoDto에 기본 생성자가 자동 생성 될 줄 알았다.

원인

@Builder 어노테이션은 생성자 혹은 메소드에 적용 할수 있고, 클래스에 적용할 수도 있다. 클래스에 적용하는 경우, @AllArgsConstructor(access = AccessLevel.PAKAGE)를 클래스에 적용하는 것처럼 동작한다. 다만, @Builder 어노테이션을 클래스에 적용하는 경우는 생성자가 하나도 없는 경우에만 전체 필드를 parameter로 가지는 생성자를 자동생성한다. 만약 어떤 생성자가 존재한다면 all-args constructor가 존재한다고 가정하고 코드를 생성하기 때문에 컴파일러 에러가 발생한다.

즉, 위 상황에서는 클래스에 적용된 @Builder 어노테이션에 의해 모든 필드를 파라미터로 가지는 생성자가 생성되어 기본 생성자가 자동 생성되지 않아 생긴 문제였다.

import lombok.Builder;
import lombok.Getter;
import lombok.ToString;

@Getter
@ToString
@Builder
public class DemoDto {
    private String demoName;
    private Long demoAge;

    private DemoDto(String demoNam, String a, String n) {
    }
}

위와 같이 생성자가 정의되어 있다면, 다음과 같은 에러를 볼 수 있다.

다른 경우들

1. 데이터 타입이 일치하는 생성자

package com.example.demo.dto;

import lombok.Builder;
import lombok.Getter;
import lombok.ToString;

@Getter
@ToString
@Builder
public class DemoDto {
    private String demoName;
    private Long demoAge;

    private DemoDto(String b, Long a) {
    }
}

위와 같은 경우에는 컴파일 에러가 발생하지 않고, 요청시 매핑도 올바르게 된다.

2. 데이터 타입이 일치하지 않는 생성자

package com.example.demo.dto;

import lombok.Builder;
import lombok.Getter;
import lombok.ToString;

@Getter
@ToString
@Builder
public class DemoDto {
    private String demoName;
    private Long demoAge;

    private DemoDto(String b, Integer a) {
    }
}

위 코드는 다음과 같은 에러를 볼 수 있다.

추가로 알아둘 사실

1.데이터 타입이 일치하는 생성자의 결과를 보면 알 수 있겠지만, 사실 역직렬화는 기본 생성자 없이도 동작한다. 정확히는 Spring Boot와 jackson-module-parameter-names 모듈을 사용하면 기본 생성자 없이도 동작하고, 아마 Spring Boot Web 의존성에 포함되어있을 것이다.

이렇게 되면 생성자에 추가적인 어노테이션을 적용할 필요 없이 파라미터가 존재하는 생성자와 JSON property가 자동 매핑된다.

그런데 왜 DemoDto는 기본 생성자가 필요하다는 메시지의 에러가 발생했을까? 답은 단일 필드를 가지는 경우에는 @JsonCreator를 적용한 파라미터를 가지는 생성자가 필수이기 때문이다. 이것을 원치 않는다면 기본 생성자를 생성하여 역직렬화를 수행하도록 해야한다.

https://github.com/FasterXML/jackson-databind/issues/1498

학습할 내용

  • Json 직렬화/역직렬화 시의 동작 방식

1개의 댓글

comment-user-thumbnail
2023년 8월 8일

감사합니다. 이런 정보를 나눠주셔서 좋아요.

답글 달기