josolha
JPA의 @Builder , @Builder.Default 본문
문제사항
Spring Boot 및 Lombok을 사용한 프로젝트에서 Board 엔티티 클래스에 빌더 패턴을 적용하다.
Lombok의 @Builder 어노테이션을 하고 애플리케이션 실행 중에 NullPointerException이 발생하는 문제에 직면했다.
@Entity
@Table(name = "board")
@Getter
@Builder
public class Board extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// ... 생략 ...
@OneToMany(mappedBy = "board", cascade = CascadeType.ALL)
private List<Tag> tags = new ArrayList<>();
// ... 생략 ...
}
위에는 해당 Board 엔티티이고 서비스단에서 board 객체 안에 빌더로 생성한 tag 객체를 주입하면서 에러가 발생했다.
문제는 tags와 같은 컬렉션 필드에 대한 초기화가 이루어지지 않아서 NullPointerException 에러를 마주했다.
문제 원인
@Builder 어노테이션은 편리해서 여기저기 마구잡이로 사용 했지만 여기서 문제가 있었다.
바로 컬렉션 필드에 대한 자동 초기화를 제공하지 않는다는 것이다.
이로 인해 빌더를 통해 생성된 객체에서 해당 컬렉션 필드에 접근하려고 할 때 NullPointerException이 발생한다.
Board 클래스에 여러 컬렉션 필드(List<Tag>, List<Comment> 등)가 있었는데
Lombok의 @Builder를 사용했을 때, 이 컬렉션들이 자동으로 초기화되지 않아 빌더를 사용해 객체를 생성할 때,
이러한 필드들이 자동으로 초기화가 되지 않아 NullPointerException을 마주했다.
구체적으로 예를 들면서 이해해보자.
예를 들어 다음과 같은 클래스가 있을 때
@Builder
public class Example {
private List<String> items = new ArrayList<>();
}
Example.builder().build()를 통해 객체를 생성하면,
items 필드는 new ArrayList<>()로 초기화된 것이 아니라 Null로 설정된다.
이유는 Lombok의 @Builder 어노테이션을 사용할 때 특정 필드의 초기화 동작에 대한 중요한 점이 있다.
기본적으로 @Builder는 클래스의 필드에 대해 명시적으로 제공된 초기값을 무시한다.
대신, 빌더를 사용하여 객체를 생성할 때 해당 필드에 값이 제공되지 않으면,
그 필드는 기본 자바 초기화(예: 객체는 null, 기본 타입은 0, false 등)를 따른다.
여기서 @Builder.Default 어노테이션을 사용하면,
Lombok은 해당 필드에 대해 별도의 처리를 하여 명시적으로 제공된 초기값을 유지한다.
이 어노테이션을 사용하면 빌더를 통해 객체를 생성할 때 해당 필드에 값이 제공되지 않으면,
명시적으로 설정된 초기값(예: new ArrayList<>())을 사용한다.
@Builder
public class Example {
@Builder.Default
private List<String> items = new ArrayList<>();
}
따라서 이 경우, Example.builder().build()를 통해 객체를 생성하면 items 필드는 new ArrayList<>()로 초기화된다.
결과
Lombok의 @Builder 어노테이션과 함께 @Builder.Default를 사용하여 컬렉션 필드를 자동으로 초기화하도록 설정했다.
이렇게 하면 빌더를 통해 객체를 생성할 때 컬렉션 필드들이 자동으로 빈 리스트로 초기화가 된다.
다음은 변경된 코드의 예시이다.
@Entity
@Table(name = "board")
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Board extends BaseEntity {
// ... 다른 필드 ...
@OneToMany(mappedBy = "board", cascade = CascadeType.ALL)
@Builder.Default
private List<Comment> comments = new ArrayList<>();
// ... 다른 컬렉션 필드들 ...
}
이렇게 하면 해당 오류를 해결할 수 있었다.
결론
Lombok의 @Builder를 사용할 때 컬렉션과 같이 초기화가 필요한 필드에 대해서는 @Builder.Default를 사용하여 명시적인 초기값을 유지하도록 해야 한다. 이렇게 하면 빌더를 통해 생성된 객체에서도 해당 필드가 올바르게 초기화된 상태를 가질 수 있다.
'Spring' 카테고리의 다른 글
Eco-reading 트러블 슈팅(실시간 알림 기능) (1) | 2023.11.27 |
---|---|
AWS S3 (이미지 다운로드 에러) (1) | 2023.11.20 |
스프링 (AOP) (0) | 2023.11.09 |
NCT PROJECT 트러블 슈팅(시큐리티) (1) | 2023.10.09 |
JWT를 위해 (0) | 2023.08.21 |