Builder Pattern
자바의 빌더 패턴은 복잡한 객체를 생성할 때, 생성자 대신 마치 조립하듯이 객체를 단계별로 생성하는 디자인 패턴이다.
쉽게 말해 서브웨이 주문하기와 같다고 생각하면 된다. 빵, 미트, 채소, 소스를 원하는 옵션을 하나씩 선택해 최종적으로 샌드위치(객체)를 만들어내는 방식이다.




왜 필요한가요?(등장 배경)
빌더 패턴을 사용하지 않을 경우 2가지로 객체를 생성할 수 있다.
점층적 생성자 패턴(기존 방식의 문제점)
필드가 많아지면 생성자의 파라미터는 그만큼 많아지기 때문이다.
// 이름, 나이, 전화번호, 주소, 이메일, 키, 몸무게...
Member member = new Member("철수", 20, "010-1234-5678", "서울", null, 175, 70);
- 가독성 최악: 서울이 주소인지 이름인지. 175가 키인지 몸무게인지 한눈에 안 들어온다.
- 순서 실수: 순서를 헷갈려서 키 자리에 몸무게를 넣어도 에러가 안 난다.(같은 int 이기 때문)
자바 빈즈 패턴(Setter 사용)
점층적 생성 패턴의 단점을 보완하기 기본 생성자를 만들고 set 메서드로 값을 채우는 방식이다.
매개변수가 없는 생성자로 객체 생성 후 Setter 메서드를 이용해 클래스 필드의 초깃값을 설정하는 방식이다.
Member member = new Member();
member.setName("철수");
member.setAge(20);
- 객체의 불완전성: setName 까지만 하고 setAge를 실수로 입력하지 않으면, 나이가 없는 대학생이 학교에서 수업을 듣는 것과 같다. 이럴 경우 객체는 일관성이 무너진 상태이기 때문에 다른 곳에서 인스턴스를 사용할 경우 런타임 예외가 발생할 수 있다.
- 불변성 깨짐: 언제 어디서든 set으로 값을 바꿀 수 있기 때문에 객체가 안전하지 않다.
이러한 문제들을 해결하기 위해 별도의 빌더 클래스를 만들어 메서드를 통해 step-by-step으로 값을 입력받은 후에 최종적으로 build() 메서드로 하나의 인스턴스를 생성하여 리턴하는 패턴이 등장했다.
빌더 패턴 사용법
Member member = Member.builder()
.name("철수")
.age(20)
.phone("010-1234-5678")
.address("서울")
.build(); // 최종 객체 생성
메서드 체이닝: . 을 찍으며 계속 연결하는 방식이다
가독성: 어떤 값에 무엇이 들어가는지 명확하다.
순서무관: 나이를 먼저 넣든 이름을 먼저 넣든 상관없다.
구현 방법
수동으로 구현하는 방법과 lombok으로 자동 생성하는 방법이 있다.
public class Member {
private String name;
private int age;
private String phone;
// 1. 생성자는 private으로 막음 (외부에서 new Member() 금지)
private Member(Builder builder) {
this.name = builder.name;
this.age = builder.age;
this.phone = builder.phone;
}
// 2. static 내부 클래스로 Builder 정의
public static class Builder {
private String name;
private int age;
private String phone;
// 3. 각 메서드는 값을 세팅하고 자기 자신(Builder)을 반환 (체이닝을 위해)
public Builder name(String name) {
this.name = name;
return this;
}
public Builder age(int age) {
this.age = age;
return this;
}
public Builder phone(String phone) {
this.phone = phone;
return this;
}
// 4. 마지막에 실제 객체를 만들어 반환
public Member build() {
return new Member(this);
}
}
// 5. 외부에서 빌더 시작점 제공
public static Builder builder() {
return new Builder();
}
}
// Member m = Member.builder().name("A").build();
lombok 라이브러리 중 @Builder 어노테이션 하나로도 빌더 패턴을 구현할 수 있다.
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder // 이거 하나면 끝
public class Member {
private String name;
private int age;
private String phone;
}
// 사용법은 같다
// Member m = Member.builder().name("A").build();
빌더 패턴의 장단점
장점
- 가독성: 어떤 필드에 어떤 값이 들어가는지 명확하다.
- 유연성: 필요한 데이터만 설정 가능하다.(생성자 오버로딩 필요 없음)
- 불변성: 객체를 생성한 후 값을 변경하지 못하게(final) 만들 수 있다.
단점
- 코드양(lombok 없을 경우): 직접 코딩하면 코드가 길어진다.
- 성능: 객체 생성 시 빌더 객체도 하나 더 만들어지므로 미세한 오버헤드가 발생할 수 있다.
오버헤드가 발생하는 이유는 목표 객체를 만들기 위해, '빌더'라는 중간 객체를 하나 더 만들어야 하기 때문이다.- 메모리 사용량 증가: 객체를 하나 만들려는데 굳이 빌더 객체를 2개 만들어야 하기 때문에 2개의 객체 공간이 필요하다.
- 메서드 호출 비용 증가: 생성자는 한 번에 값을 다 넣지만, 빌더는 .name(), .age() 등 메서드를 여러 번 거쳐야 하므로 CPU 연산이 아주 미세하게 많아진다.
- 가비지 컬렉션(CG) 부담: 생성된 빌더 객체는 build()가 끝나는 순간 필요 없는 쓰레기가 된다. CG가 이를 청소해야 하는 일이 늘어난다.
그럼 사용하지 말아야 하나요?
아니요, 실무에서는 안심하고 사용한다.
이유
- 현대의 하드웨어 : 요즘 서버의 서버의 성능(CPU, 메모리)은 매우 뛰어납니다. 객체 하나 더 만드는 비용은 '먼지'만큼 작다.
- JVM의 최적화 : 자바 가상 머신(JVM)은 수명 짧은 객체(빌더 같은)를 생성하고 없애는 데 도가 텄다.
성능 차이를 체감하기 힘들다. - 가독성의 이득 : 오버헤드로 인한 성능 저하(0.00001초 손해)보다, 코드를 깔끔하게 짜서 개발자가 버그를 줄이고 유지보수하기 쉬워지는 이득(수십 시간 절약)이 훨씬 크다.
결론적으로 초당 수백만 번 객체를 찍어내야 하는 극한의 고성능 게임 엔진이나 임베디드 시스템이 아니라면, 웹 개발(Spring Boot)에서는 오버헤드를 전혀 걱정하지 않고 빌더 패턴을 사용할 수 있다.
면접 답변식 요약
빌더 패턴은 복잡한 객체를 단계별로 생성할 수 있도록 도와주는 생성 디자인 패턴입니다.
생성자의 파라미터가 많을 때 발생하는 가독성 저하와 순서 오류를 해결하기 위해 사용합니다. 메서드 체이닝을 통해 어떤 필드에 어떤 값이 들어가는지 명확히 알 수 있고, Setter를 열지 않아도 되므로 객체의 불변성(Immutability)을 확보할 수 있다는 큰 장점이 있습니다.
실무에서는 Lombok의 @Builder 어노테이션을 활용하여 보일러플레이트 코드 없이 간편하게 적용하여 사용하고 있습니다.
'Daily Dev Q&A 정리 템플릿' 카테고리의 다른 글
| 25.12.15 RDBMS는 무엇일까? (1) | 2025.12.15 |
|---|---|
| 25.12.12 프레임워크가 무엇이고 무엇을 사용했나요? 라는 질문에 대해 정리하기 (0) | 2025.12.14 |
| 25.12.10 자바에 제네릭을 왜 사용할까? (1) | 2025.12.10 |
| 25.12.09 자바의 접근 제어자(Access Modifier)에 대하여 (0) | 2025.12.09 |
| 25.12.08 자바의 Collections Framework에 대해 설명해보기 (0) | 2025.12.08 |