올바른 JPA Entity 생성자 사용 방법

강신현·2022년 9월 6일
0

[프로젝트] PlevUp

목록 보기
3/3

✅ @Entity ✅ @NoArgsConstructor ✅ @AllArgsConstructor ✅ @Builder


문제

객체에 데이터를 넣어주는 방법(어노테이션)은 @Setter, @NoArgsConstructor, @AllArgsConstructor 등이 있다.
하지만 각 어노테이션은 문제가 있다.

@Setter 👉 객체가 무분별하게 변경될 가능성 있음
@AllArgsConstructor 👉 객체 내부의 인스턴스멤버들을 모두 가지고 있는 생성자를 생성 (매우 위험)
@NoArgsConstructor 👉 기본 생성자의 접근 제어자가 불명확함


해결

1. @Setter (사용 x)

Setter는 그 의도가 분명하지 않고 객체를 언제든지 변경할 수 있는 상태가 되어서 객체의 안전성이 보장받기 힘들다.
특히 엔티티에서는 @Setter를 사용 시 해당 변경 가능성이 어디서 누구에 의해 발생했는지 추적하기가 힘들다.

따라서 값 변경이 필요한 경우 의미 있는 메서드를 생성하여 사용하는 것이 좋다.


2. @AllArgsConstructor (사용 x)

클래스에 존재하는 모든 필드에 대한 생성자를 자동으로 생성하는데, 인스턴스 멤버의 선언 순서에 영향을 받기 때문에 두 변수의 순서를 바꾸면 생성자의 입력 값 순서도 바뀌게 되어 검출되지 않는 치명적인 오류를 발생시킬 수 있다.

따라서 생성자에 @Builder를 붙임으로써 @AllArgsConstructor를 쓰지 않는 것이 좋다.


3. @NoArgsConstructor(access = AccessLevel.PROTECTED)로 변경

@Entity가 붙은 JPA가 관리하는 객체는 기본 생성자(파라미터가 없는 public 또는 protected 생성자)가 필수이다. 따라서 자동으로 기본 생성자를 생성해주는 @NoArgsConstructor를 많이 사용한다.
하지만 아무런 값도 갖지 않는 의미 없는 객체의 생성을 주의해야 한다.

따라서 기본 생성자(NoArgsConstructor)의 접근 제어를 PROCTECTED 로 설정하여 아무런 값도 갖지 않는 의미 없는 객체의 생성을 막을 뿐만 아니라, 무분별한 객체 생성에 대해 한번 더 체크하는 것이 좋다.

//@NoArgsConstructor(access = AccessLevel.PROTECTED) 을 사용하면

Member member = new Member(); //컴파일 에러 발생

@Builder

그렇다면 의미 있는 객체 생성은 어떻게 할까?

클래스 자체에 @Builder를 붙이지 말고, 의미 있는 생성자에 @Builder를 붙여 사용하면 된다.


실제 코드

1. 잘못된 코드

@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Photo {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name="photoIdx")
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="boardIdx")
    private Board board;

    private String fileName;
}

2. 바른 코드

실제 프로젝트에서 사용한 사진 객체의 구현 코드이다.

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Photo {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name="photoIdx")
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="boardIdx")
    private Board board;

    private String fileName;

    @Builder
    public Photo(Board board, String fileName) {
        this.fileName = fileName;

        this.board = board;
        board.addPhoto(this);
    }
}

출처

https://velog.io/@mooh2jj/%EC%98%AC%EB%B0%94%EB%A5%B8-%EC%97%94%ED%8B%B0%ED%8B%B0-Builder-%EC%82%AC%EC%9A%A9%EB%B2%95

profile
땅콩의 모험 (server)

0개의 댓글