dev_beomgeun
꾸준하게 차근차근
dev_beomgeun
전체 방문자
오늘
어제
  • 분류 전체보기 (170)
    • 전공 (0)
      • 운영체제 (0)
      • 알고리즘 (0)
      • 자료구조 (0)
      • 데이터베이스 (0)
      • 네트워크 (0)
    • 개발 공부 (32)
      • 웹 (6)
      • 리눅스 (4)
      • 머신러닝 (1)
      • 스프링 (17)
      • Git (2)
      • AWS (2)
    • 개발 도서, 강의 (3)
      • 스프링 입문을 위한 자바 객체지향의 원리와 이해 (0)
      • 모든 개발자를 위한 HTTP 웹 기본 지식(김영한.. (2)
      • 스프링 부트와 AWS로 혼자 구현하는 웹서비스 (1)
    • 문제 풀이 (118)
      • 백준 알고리즘 (72)
      • 프로그래머스 알고리즘, SQL (38)
      • Hackerrank SQL (8)
    • 프로젝트 기록 (4)
      • 캡스톤 종합설계 (4)
      • 반려하루 프로젝트 (0)
    • 활동 기록 (12)
      • 네이버 부스트캠프 (7)
      • 취준 & 코테 (4)
      • 소프트웨어 마에스트로 13기 (1)
    • 이것저것 (1)

블로그 메뉴

  • 홈
  • 깃허브
  • 링크드인
  • 방명록

공지사항

인기 글

태그

  • dp
  • 서블릿
  • 백준 DP
  • 일기
  • 부스트캠프
  • HackerRank mysql
  • 네이버 부스트캠프
  • c++
  • 기록
  • 그래프탐색
  • 반성
  • 백준
  • 프로그래머스 SQL
  • 회고
  • 스프링
  • AI Tech
  • Hackerrank
  • 프로그래머스
  • Baekjoon
  • BFS

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
dev_beomgeun

꾸준하게 차근차근

[SpringBoot x JPA] List 초기화에서 Builder 패턴 사용 시 NullPointerException
개발 공부/스프링

[SpringBoot x JPA] List 초기화에서 Builder 패턴 사용 시 NullPointerException

2022. 8. 8. 20:40
728x90

문제 상황

평소와 같이 1:N의 관계인 Entity를 생성하고 1에 해당하는 클래스에서 @OneToMany(mappedBy = "family")를 했습니다.

그리고 해당 List를 new ArrayList<>()로 미리 초기화를 해줬습니다. (Null 방지)

그리고 service 레이어에서 insertNewMember 메서드를 실행시켰는데, 바로 NullPointerException 발생..

해당 List가 Null 상태인 것을 발견했습니다. 

 

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
public class Family extends BaseTimeEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long familyId;

    @Column(nullable = false, unique = true, length = 50)
    private String familyName;

    @OneToMany(mappedBy = "family", cascade = CascadeType.ALL)
    private List<FamilyMember> familyMemberList = new ArrayList<>();

    public static Family createFamily(CreateFamilyRequest dto) {
        return Family.builder()
                .familyName(dto.getFamilyName())
                .build();
    }

    public void insertNewMember(FamilyMember familyMember) {
        familyMemberList.add(familyMember);
    }
}

원인 분석

우선 디버깅을 통해서 insertNewMember에 넘어오는 파라미터 값인 FamilyMember를 살펴봤습니다.

그리고 해당 FamilyMember는 위의 static 메서드인 createFamily를 통해서 생성됐고 빌더 패턴을 이용했습니다.

 

클래스 필드 초기화

기본적으로 클래스로부터 객체가 생성될 때, 필드는 기본 초기값으로 설정됩니다.

초기값과 다르게 생성하고 싶다면, 필드에 미리 값을 지정해주는 방법과 생성자를 통해 전달하는 방법이 있습니다.

그리고 알아낸 사실은 빌더 패턴을 이용할 경우, 미리 지정해준 값은 무시되고 자동 초기값으로 생성되는 것이었습니다.

 

@Builder
@NoArgsConstructor
@AllArgsConstructor
@Getter
public class BuilderTest {
    private String test = "test1";
    private int num = 5;
    private List<Integer> testList = new ArrayList<>();
}

테스트를 위해 BuilderTest라는 클래스를 만들고 필드는 각각 String, int, List로 미리 값을 지정해줬습니다.

    @DisplayName("빌더 패턴을 사용할 경우 기본 초기값이 무시된다")
    @Test
    void Test() {
        BuilderTest instanceFromDefaultConstructor = new BuilderTest();
        assertThat(instanceFromDefaultConstructor.getTest()).isEqualTo("test1");
        assertThat(instanceFromDefaultConstructor.getNum()).isEqualTo(5);
        assertThat(instanceFromDefaultConstructor.getTestList()).isNotNull();

        BuilderTest instanceFromBuilder = BuilderTest.builder().build();
        assertThat(instanceFromBuilder.getTest()).isNull();
        assertThat(instanceFromBuilder.getNum()).isEqualTo(0);
        assertThat(instanceFromBuilder.getTestList()).isNull();
    }

 

테스트 결과, 기본 생성자로 생성한 인스턴스의 값은 미리 지정해준 값으로 생성되지만

빌더를 이용해서 생성한 인스턴스는 필드 자료형의 기본 초기값으로 생성이 되는 것을 확인할 수 있었습니다.

 

결과

따라서 빌더 패턴을 통해서 인스턴스를 만들었는데, 해당 ArrayList는 값을 전달해주지 않아서 기본 초기값인 Null로 되어 있었고 해당 Null에 add 메서드를 실행해서 NullPointerException이 발생했던 것입니다.

 

    public static Family createFamily(CreateFamilyRequest dto, String invitationCode) {
        return Family.builder()
                .familyName(dto.getFamilyName())
                .familyMemberList(new ArrayList<>())
                .build();
    }

빌더로 생성 시 List도 new ArrayList로 초기화해줬더니 더 이상 Null 문제가 발생하지 않았습니다.

 

@Builder.Default

@Builder
@NoArgsConstructor
@AllArgsConstructor
@Getter
public class BuilderTest {
    private String test = "test1";
    private int num = 5;
    
    @Builder.Default
    private List<Integer> testList = new ArrayList<>();
}

저렇게 매번 Builder로 객체 생성 시 초기화해주는 것은 번거롭기도 하고, 놓칠 수 있습니다.

@Builder.Default 어노테이션을 이용하면 Builder를 통해 생성한 인스턴스의 기본 값을 세팅할 수 있습니다.

728x90
저작자표시 비영리 변경금지 (새창열림)

'개발 공부 > 스프링' 카테고리의 다른 글

[Github Actions] CI 스크립트에 Redis 환경 추가하기  (0) 2022.10.02
[SpringBoot x JPA] Soft Delete #1 Select (해당 Status 조건 추가 조회 방법) - @Where  (1) 2022.09.11
[SpringBoot] response.sendRedirect(url)가 동작하지 않는 경우  (0) 2022.07.09
[SpringBoot] Custom Exception 에러 처리 및 리팩토링 (Custom Exception)  (0) 2022.06.27
[SpringBoot] Error - Parameter 0 of constructor in ~ required a bean of type 'java.lang.String' that could not be found.  (3) 2022.06.26
    '개발 공부/스프링' 카테고리의 다른 글
    • [Github Actions] CI 스크립트에 Redis 환경 추가하기
    • [SpringBoot x JPA] Soft Delete #1 Select (해당 Status 조건 추가 조회 방법) - @Where
    • [SpringBoot] response.sendRedirect(url)가 동작하지 않는 경우
    • [SpringBoot] Custom Exception 에러 처리 및 리팩토링 (Custom Exception)
    dev_beomgeun
    dev_beomgeun
    백엔드 개발을 하며 얻은 지식과 경험을 공유합니다. 현재 카카오페이에서 백엔드 엔지니어로 일하고 있습니다.

    티스토리툴바