✅ @Entity ✅ @NoArgsConstructor ✅ @AllArgsConstructor ✅ @Builder
객체에 데이터를 넣어주는 방법(어노테이션)은 @Setter, @NoArgsConstructor, @AllArgsConstructor 등이 있다.
하지만 각 어노테이션은 문제가 있다.
@Setter
👉 객체가 무분별하게 변경될 가능성 있음
@AllArgsConstructor
👉 객체 내부의 인스턴스멤버들을 모두 가지고 있는 생성자를 생성 (매우 위험)
@NoArgsConstructor
👉 기본 생성자의 접근 제어자가 불명확함
Setter는 그 의도가 분명하지 않고 객체를 언제든지 변경할 수 있는 상태가 되어서 객체의 안전성이 보장받기 힘들다.
특히 엔티티에서는 @Setter를 사용 시 해당 변경 가능성이 어디서 누구에 의해 발생했는지 추적하기가 힘들다.
따라서 값 변경이 필요한 경우 의미 있는 메서드를 생성하여 사용하는 것이 좋다.
클래스에 존재하는 모든 필드에 대한 생성자를 자동으로 생성하는데, 인스턴스 멤버의 선언 순서에 영향을 받기 때문에 두 변수의 순서를 바꾸면 생성자의 입력 값 순서도 바뀌게 되어 검출되지 않는 치명적인 오류를 발생시킬 수 있다.
따라서 생성자에 @Builder를 붙임으로써 @AllArgsConstructor를 쓰지 않는 것이 좋다.
@Entity가 붙은 JPA가 관리하는 객체는 기본 생성자(파라미터가 없는 public 또는 protected 생성자)가 필수이다. 따라서 자동으로 기본 생성자를 생성해주는 @NoArgsConstructor를 많이 사용한다.
하지만 아무런 값도 갖지 않는 의미 없는 객체의 생성을 주의해야 한다.
따라서 기본 생성자(NoArgsConstructor)의 접근 제어를 PROCTECTED 로 설정하여 아무런 값도 갖지 않는 의미 없는 객체의 생성을 막을 뿐만 아니라, 무분별한 객체 생성에 대해 한번 더 체크하는 것이 좋다.
//@NoArgsConstructor(access = AccessLevel.PROTECTED) 을 사용하면
Member member = new Member(); //컴파일 에러 발생
그렇다면 의미 있는 객체 생성은 어떻게 할까?
클래스 자체에 @Builder를 붙이지 말고, 의미 있는 생성자에 @Builder를 붙여 사용하면 된다.
@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;
}
실제 프로젝트에서 사용한 사진 객체의 구현 코드이다.
@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);
}
}
출처