엔티티 생성 방식, 언제 어떤 걸 써야 할까?
이번 팀 프로젝트에서 도메인 모델을 구성하면서, 엔티티를 어떤 방식으로 생성할지 여러 방식이 오갔다.
어떤 사람은 생성자를 직접 정의했고, 어떤 사람은 @Builder
를 사용했다. 그래서 이번 기회에 각 방식의 특성과 장단점, 그리고 언제 어떤 걸 쓰는 게 좋을지 정리해보려고 한다.
정적 팩토리 메서드, 직접 생성자 정의, Lombok 빌더 등은 모두 상황에 따라 장점이 분명한 도구들이다.
이번 글에서는 그 기준을 나름대로 정리하고, 협업 관점에서 어떤 방식이 더 적합할지도 고민해봤다.
그리고, 팀원들이 읽을 글이기 때문에 빠르게 읽고 이해하기 위해 최대한 간결하게 작성해보았다.
생성자 직접 정의 – 가장 단순하고 안정적인 방식
언제 사용할까?
- 필드 수가 적고 (2~3개)
- 필수값만 받고 불변성을 지켜야 할 때
- 외부 라이브러리에 의존하지 않고 순수한 방식으로 만들고 싶을 때
예시
public class User {
private final String name;
private final int age;
protected User() {}
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
장점
- 객체 생성 시 필수 필드가 누락되지 않는다.
- 외부 라이브러리 없이도 가능해서 직관적이고 명시적이다.
단점
- 필드가 많아지면 인자 순서를 실수하기 쉽다.
- 선택적 값 처리가 불편하다.
Lombok + @Builder – 가독성, 유연함
언제 사용할까?
- 필드가 많고, 일부는 선택 사항일 때
- 테스트 코드나 DTO에서 유연한 생성 방식이 필요할 때
- 프로젝트 전반에 Lombok을 적극 활용하고 있을 때
예시
@Getter
@NoArgsConstructor(access = PROTECTED)
@AllArgsConstructor
@Builder
public class Post {
private String title;
private String content;
private boolean published;
}
Post post = Post.builder()
.title("정적 팩토리 패턴")
.published(true)
.build();
장점
- 필드명을 직접 보고 설정하므로 실수가 적다.
- 선택 필드가 많아도 생성자 오버로딩 없이 처리 가능하다.
단점
- 필드가 1~2개인 경우 오히려 장황할 수 있다.
@NoArgsConstructor
를 누락하면 JPA에서 오류 발생- 생성자/메서드가 명시적으로 안 보여서 디버깅 시 혼란이 생길 수 있다.
- 필드를 빼먹어도 문법상 오류가 없기에, 실수할 여지가 많다.
정적 팩터리 메서드 – 의도를 담는 생성 방식
언제 사용할까?
- “이 객체가 왜 생성되는지” 의도를 분명히 드러내고 싶을 때
- 생성 시점에 검증, 초기화, 정책 분기가 필요한 경우
- 생성자를 감추고, 객체 생성 방식을 통제하고 싶을 때
예시
public class Member {
private final String name;
private final boolean isVip;
private Member(String name, boolean isVip) {
this.name = name;
this.isVip = isVip;
}
public static Member vip(String name) {
return new Member(name, true);
}
public static Member normal(String name) {
return new Member(name, false);
}
}
Member member = Member.vip("홍길동");
장점
- 메서드 이름으로 의도를 명확히 표현할 수 있다.
- 검증 로직, 기본값 설정, 정책 분기를 담기 좋다.
- 같은 시그니처로 여러 방식의 객체 생성이 가능하다.
단점
- public 생성자가 없으면 상속이 어렵다.
- Javadoc이나 컨벤션 없이는 API 문서에서 잘 안 보인다.
- 일부 개발자에겐 익숙하지 않은 방식일 수 있다.
도구는 많지만, 목적에 따라 골라야 한다
이 세 가지 방식은 각각 장점이 뚜렷하다. 그리고 그중 절대적인 정답은 없다.
중요한 건 왜 이 방식을 썼는지를 설명할 수 있는 것이다.
상황 | 추천 방식 |
---|---|
필드가 적고 필수값만 받을 때 | 생성자 직접 정의 |
필드가 많고 선택 필드가 있을 때 | Lombok + @Builder |
생성의 의미를 명확히 전달하거나 검증이 필요할 때 | 정적 팩터리 메서드 |
그리고 무엇보다 중요한 건, 팀 차원에서 일관된 규칙을 갖고 사용하는 것.
혼자서 여러 방식을 섞어 쓰기보다는, 팀원들과 기준을 맞추고 통일하는 게 훨씬 효율적이다.
팀의 상황에 따른 나의 판단
우리 팀은 백엔드 개발자 8명이 함께 개발 중이다. 각자 맡은 도메인을 개발하면서도, 서로의 도메인과 연결되는 상황이 많다.
그래서 코드의 일관성이 효율적인 협업에 있어서 매우 중요하다고 판단했다.
Lombok의 @Builder
는 가독성 면에서 뛰어나지만, 컴파일 시점에 필수값 누락을 잡아내기 어렵다는 단점이 있다.
초기 프로젝트 구성원이 아직 익숙하지 않은 부분도 있어, 실수를 줄이는 것을 우선순위로 두었다.
그래서 나는 다음과 같이 제안한다.
정적 팩터리 메서드 + Lombok 기본 어노테이션만 사용 (@Getter, @NoArgsConstructor 등)
예시
@Entity
@Getter
@Table(name = "study_group_members")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class StudyGroupMember extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "group_member_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id", nullable = false)
private Member member;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "study_group_id", nullable = false)
private StudyGroup studyGroup;
@Column(name = "is_allowed", nullable = false)
private Boolean isAllowed = false;
private StudyGroupMember(Member member, StudyGroup studyGroup) {
this.member = member;
this.studyGroup = studyGroup;
}
public static StudyGroupMember of(Member member, StudyGroup studyGroup) {
return new StudyGroupMember(member, studyGroup);
}
}
생성자는 최소한으로 열어두고, 정적 메서드를 통해 명확한 의도와 안정성을 확보하는 방식이다.
팀원 간 공유가 쉽고, 실수 가능성도 줄일 수 있어 협업에 적합하다고 판단했다. 또한, 추후 검증 로직이 들어가더라도 깔끔하게 유지하기 좋다고 판단하였다.
정적 팩토리 메서드에 대한 글을 쓴 적이 있는데, 다음 글을 참고하면 좋을 것 같다.
출처
생성자 직접 정의 (Constructor)
- Oracle Java 튜토리얼: Providing Constructors for Your Classes(Oracle Documentation)
- 위키백과: Constructor (object-oriented programming)(Wikipedia)
Lombok @Builder
- Lombok 공식 문서: @Builder
- Baeldung 튜토리얼: Using Lombok’s @Builder Annotation(Baeldung)
정적 팩토리 메서드 (Static Factory Method)
- Joshua Bloch, Effective Java 3rd Edition, Item 1: Consider static factory methods instead of constructors
- Baeldung 튜토리얼: Java Constructors vs Static Factory Methods(Baeldung)
- Medium 블로그: Item - 1 Effective Java Consider Static Factory Methods instead of Constructors(Medium)