엔티티에서 @Builder 사용할 때 불변성에 대한 고찰

2026. 1. 31. 21:35·Java

엔티티에서@Builder를 사용해 보면서 생긴 문제점이 있다.
엔티티는 불변성을 유지해줘야 한다는 점을 간과했다는 점이다.

불변성이란?

변경을 허용하지 않는 성질로
불변성을 유지하는 객체란 변경 불가한 객체를 뜻한다.
 
불변객체는 객체를 생성 후 객체에 저장된 값을 수정하려고 시도한다면,
값이 수정되는 것이 아닌 아예 새로운 객체가 생성되어 저장된다.

예로 들면 문자열이 있다.

String a = "hello"; // 불변객체
String b = a.toUpperCase(); // a는 변하지 않고, 새 객체가 생성됨

StringBuilder sb = new StringBuilder("hello"); // 가변객체
sb.append(" world"); // 기존 객체 자체가 변경됨

 
@Setter는 가변객체에서 사용된다.

Emp emp = new Emp();              // 기본 생성자로 객체 생성
emp.setEmpId(empDto.getEmpId());  // 하나씩 setter 호출

객체의 변경을 허용하기에 가변객체에 해당한다.

근데 @Builder도 내부적으로 setter처럼 필드에 값을 저장하는데 반환타입이 있는 것뿐이다.

  • @Setter : 객체를 생성하고 필드에 하나하나 값을 넣음.
  • @Builder : 한 개씩 필드에 값을 넣고, 모아서 한 번에 객체를 생성함.

아래 접은 글은 Emp 엔티티 클래스에 @Builder를 붙였을 때,
자동생성되는 내부 클래스의 예시이다. 

더보기

사원테이블(Emp)에는 3개의 필드가 있다고 가정했다.
사원번호 : emp_id
사원명 : emp_name
이메일 : email

@Entity
// 나머지 어노테이션 생략
public class Emp{
	private String empid;
	/// ... 나머지 필드 생략

            public static class EmpBuilder {
                private String empId;
                private String empName;
                private String email;

                public EmpBuilder empId(String empId) {
                    this.empId = empId; // setter-like
                    return this;
                }

                public EmpBuilder empName(String empName) {
                    this.empName = empName; // setter-like
                    return this;
                }

                public Emp build() {
                    return new Emp(empId, empName, email); // 마지막에 객체 생성
                }
            }
}

 

Builder 방식은 최종적으로 생성되는 엔티티의 불변성을 직접적으로 깨지는 않는다.
다만 엔티티를 생성하는 과정에서 가변적인 중간 객체(Builder)를 사용하게 되며,
이로 인해 엔티티 생성 과정 자체가 단계적으로 열리게 된다.

중간 객체는 가변객체, 최종 반환 객체는 불변 객체

예를들어 Emp class에 @Builder를 사용했다면
중간 객체는 내부클래스인 EmpBuilder 객체를 의미하고
build() 메서드로 반환되는 최종 객체는 외부클래스인 Emp 객체를 의미한다.

즉, 결과로 만들어지는 Emp 엔티티는 불변일 수 있지만,
그 생성 과정에는 가변 상태가 포함된다.
이 점에서 엔티티처럼 생성 시점의 일관성과 제약이 중요한 객체에는
@Builder 방식이 적합하지 않을 수 있다.

실제 런타임 환경에서 Builder의 중간 상태에 외부에서 접근하는 것은
구조적으로 어렵지만,
엔티티 생성 과정이 가변 객체를 포함한다는 점 자체가
설계 관점에서는 주의할 부분이 된다.

 

이러한 이유로 엔티티는 생성 시점의 상태와 규칙을 명확히 드러낼 수 있는
파라미터 생성자나 static factory 메서드를 통해 생성하는 것이 바람직하다.

static factory 메서드를 사용하면
엔티티 생성에 필요한 필수 값과 도메인 규칙을 한 곳에 모을 수 있고,
엔티티가 항상 일관된 상태로 생성되도록 강제할 수 있다.

@Builder의 원래 용도

그럼 @Builder는 어디에 쓰이느냐?

 

바로 기존에도 메서드체이닝을 사용하던 클래스이다.
그 메서드체이닝에 get 같은 코드를 줄여주려고 사용하는 것이다.
예를 들면 로그인에 사용되는 Spring Security 코드가 있다.

즉, @Builder는 객체 자체의 불변성을 보장하기 위한 도구라기보다는,
복잡한 옵션 조합이나 메서드 체이닝을 가독성 있게 표현하기 위한 도구이다.

정리하면, @Builder는 최종 객체의 불변성을 깨지는 않지만,
엔티티 생성 과정에 가변 중간 상태를 도입한다는 점에서
도메인 규칙과 일관성이 중요한 엔티티에는 적합하지 않다.

'Java' 카테고리의 다른 글

[Spring Boot] Entity <-> DTO 변환 방법 4가지, Setter부터 @Builder, static Factory 메서드 패턴, 상속까지  (0) 2026.01.23
카멜케이스와 스네이크케이스 언제 사용할까?  (0) 2026.01.08
Spring Boot 버전 2 부터 자동화된 필터 기능들  (0) 2025.12.31
[Java] 날짜 타입(LocalDate, Date) 다루기, Servlet, Spring, Spring Boot  (0) 2025.09.01
백준 1931번 회의실 지정,Java 풀이  (4) 2025.07.11
'Java' 카테고리의 다른 글
  • [Spring Boot] Entity <-> DTO 변환 방법 4가지, Setter부터 @Builder, static Factory 메서드 패턴, 상속까지
  • 카멜케이스와 스네이크케이스 언제 사용할까?
  • Spring Boot 버전 2 부터 자동화된 필터 기능들
  • [Java] 날짜 타입(LocalDate, Date) 다루기, Servlet, Spring, Spring Boot
MvA
MvA
백엔드 개발자 김재현입니다. 주로 공부하면서 느낀점을 기록합니다.
  • MvA
    Man vs Ai
    MvA
  • 전체
    오늘
    어제
    • 분류 전체보기 (94)
      • Java (6)
      • Python (8)
        • 딥러닝 (1)
        • 머신러닝 (7)
      • JavaScript (2)
      • 내배캠 (60)
      • 개인 프로젝트 (11)
      • 책 후기 (5)
      • 기타 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    내일배움캠프
    아키텍처
    배포
    딥러닝
    Riot API
    TiL
    머신러닝
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
MvA
엔티티에서 @Builder 사용할 때 불변성에 대한 고찰
상단으로

티스토리툴바