💻 하나씩 차곡차곡/Back-end

[Spring] @Builder란? (Feat. 정적 생성 메서드)

뚜루리 2025. 4. 18. 08:54
728x90
320x100

@Builder는 롬복(Lombok)에서 제공하는 애노테이션 중 하나로,

복잡한 객체를 간편하고 가독성 좋게 생성할 수 있도록 도와줌.


 

@Builder를 사용하는 이유?

[기존방식]

User user = new User("홍길동", "hello@naver.com", 25, "서울");
  • 생성자에 들어가는 파라미터 순서가 헷갈릴 수 있음
  • 어떤 값이 어떤 필드에 들어가는지 직관적이지 않음=

 

[@Builder 방식]

User user = User.builder()
    .name("홍길동")
    .email("hello@naver.com")
    .age(25)
    .address("서울")
    .build();
  • 파라미터 순서 상관 없음
  • 어떤 필드에 어떤 값이 들어가는지 명확함
  • 선택적으로 필드를 설정할 수 있음

가독성 높고 안전함!


 

사용 방법

import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class User {
    private String name;
    private String email;
    private int age;
    private String address;

}
  • 엔티티 클래스 상단에 @Builder 어노테이션 붙임.
 
 
User user = User.builder()
    .name("홍길동")
    .email("hong@gmail.com")
    .age(30)
    .build();
 
 
 
 

🔒 주의할 점

  • @Builder는 기본 생성자를 생성해주지 않음 → 필요하면 @NoArgsConstructor 따로 써줘야 함.
  • JPA Entity에는 직접 쓰는 것보다 DTO에서 주로 사용하는 걸 추천

 

@Builder를 Entity에 쓰는 게 위험할 수 있는 이유

항목 설명
무분별한 생성 우려 @Builder는 모든 필드를 포함하기 때문에 id, createdAt 같은 관리 대상 필드까지 외부에서 설정할 수 있음 → 실수 가능성 증가
JPA 라이프사이클 무시 JPA는 생성자, 프록시, 영속성 컨텍스트 관리에 민감한데, Builder는 이를 우회하여 객체를 만들 수 있어 예상치 못한 문제가 생길 수 있음
불변성 저해 엔티티는 보통 생성 이후 일부 필드만 변경(Setter)하는데, Builder는 초기화 목적 외에도 객체를 새로 만들 가능성을 줘서 유지보수가 어려워질 수 있음

 


📌 그렇다면 엔티티에는 @Builder 대신에 어떤걸 사용하는 게 좋을까?

[정적 생성 메서드]

 

  • 정적 생성 메서드는 new 대신 명시적인 이름의 메서드로 객체 생성하고, 생성자의 의도를 명확히 표현할 수 있어 엔티티 클래스에 더 적합함.
    public static User create(String email, String encodedPassword, String nickname) {
        User user = new User();
        user.email = email;
        user.password = encodedPassword;
        user.nickname = nickname;
        user.role = Role.USER;
        user.status = UserStatus.ACTIVE;
        return user;
    }

 


🔔 정리하자면....

  • Entity에는 @Builder를 직접 사용하지 않는 게 좋다: JPA가 사용하는 생성자와 충돌하거나, 필드 초기화 누락 위험 있음.
  • Entity에는 정적 팩토리 메서드 사용 권장: 불변성 보장 + 생성 책임 명확.
  • DTO에는 @Builder 사용 많이 함: API 응답, 테스트용 객체 만들 때 유용

 


📌  정적 생성 메서드 + @Builder 조합도 있음!

package ddururi.bookbookclub.domain.user.dto;

import ddururi.bookbookclub.domain.user.entity.User;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
@AllArgsConstructor
public class UserResponse {
    private Long id;
    private String email;
    private String nickname;
    private String role;

    public static UserResponse from(User user) {
        return UserResponse.builder()
                .id(user.getId())
                .email(user.getEmail())
                .nickname(user.getNickname())
                .role(user.getRole().name())
                .build();
    }
}
  1. 정적 생성 메서드 패턴: from(User user)
    • 객체 생성 방식을 한눈에 의미 전달.
    • User → UserResponse로 변환하는 명확한 목적을 가짐.
  2. @Builder 사용: UserResponse.builder()...build();
    • 가독성 좋고, 필드 많을 때 유지보수 편리.
    • 필드 순서와 상관없이 필요한 값만 골라서 세팅 가능.

 

💡 의도를 명확하게 표현 from()이란 이름만 봐도 'User 객체로부터 UserResponse를 만든다'는 걸 알 수 있음.
🔧 변환 책임을 캡슐화 DTO 변환 로직이 깔끔하게 UserResponse 안에 들어있음.
🧱 Builder로 가독성과 유연성 확보 필드 많아도 가독성이 유지됨. 파라미터 순서 실수 방지.
♻️ 테스트와 유지보수 쉬움 나중에 필드가 추가되어도 한 줄만 더 쓰면 됨. 생성자 바꾸는 것보다 부담 적음.
🔍 명시적인 팩토리 메서드 활용 of, from, ofDto, fromEntity 등 이름만으로 의도 파악 쉬움.

 

즉, 정적 생성 메서드 + @Builder 조합 =

"객체 생성의 책임은 명확히 하면서도, 가독성과 유지보수를 챙기자"는 실용적인 접근에서 비롯됨.!

 

 
728x90
320x100