생성 - 3. Builder

mskimdev·2026년 5월 19일

Design Pattern

목록 보기
3/13

Builder 패턴

new User("김자바", null, null, 25, null, true, false)

생성자에 파라미터가 많아지면 이런 코드가 나온다. null이 몇 개인지, 25가 나이인지 ID인지, truefalse가 뭘 의미하는지 전혀 알 수 없다. Builder는 이 문제를 해결하기 위한 패턴이다.


Builder 패턴이란

복잡한 객체를 단계별로 조립하듯 만드는 패턴이다. 필요한 속성만 골라서 이름을 붙여가며 설정할 수 있고, 생성자에 인자를 잔뜩 넣는 것보다 훨씬 읽기 쉽다.


기본 구현

public class User {
    private final String name;
    private final String email;
    private final int age;
    private final boolean isAdmin;

    // 생성자는 Builder만 호출할 수 있게 private
    private User(Builder builder) {
        this.name = builder.name;
        this.email = builder.email;
        this.age = builder.age;
        this.isAdmin = builder.isAdmin;
    }

    // 정적 중첩 클래스로 Builder 정의
    public static class Builder {
        private String name;
        private String email;
        private int age;
        private boolean isAdmin = false; // 기본값 설정 가능

        public Builder name(String name) {
            this.name = name;
            return this; // 메서드 체이닝을 위해 자기 자신 반환
        }

        public Builder email(String email) {
            this.email = email;
            return this;
        }

        public Builder age(int age) {
            this.age = age;
            return this;
        }

        public Builder isAdmin(boolean isAdmin) {
            this.isAdmin = isAdmin;
            return this;
        }

        public User build() {
            return new User(this);
        }
    }
}
User user = new User.Builder()
        .name("김자바")
        .email("java@example.com")
        .age(25)
        .build();

각 메서드가 이름을 갖고 있어서 어떤 값이 어디에 들어가는지 한눈에 보인다. 필요 없는 속성은 그냥 생략하면 된다.


메서드 체이닝

return this를 통해 Builder 메서드들을 연결해서 쓸 수 있다. 이걸 메서드 체이닝(Method Chaining) 이라고 한다.

// 체이닝 없이 쓰면
Builder builder = new User.Builder();
builder.name("김자바");
builder.age(25);
User user = builder.build();

// 체이닝으로 쓰면
User user = new User.Builder()
        .name("김자바")
        .age(25)
        .build();

필수 값 검증

build() 시점에 필수 값이 있는지 검사할 수 있다.

public User build() {
    if (name == null || name.isBlank()) {
        throw new IllegalStateException("name은 필수입니다.");
    }
    return new User(this);
}

생성자에서 검증하면 어떤 파라미터가 문제인지 파악하기 어렵지만, Builder에서는 메서드 이름으로 명확하게 알 수 있다.


Lombok @Builder

실제로 매번 Builder 클래스를 직접 작성하는 건 번거롭다. Lombok 라이브러리의 @Builder 어노테이션을 쓰면 위 코드를 자동으로 생성해준다.

import lombok.Builder;

@Builder
public class User {
    private String name;
    private String email;
    private int age;
    private boolean isAdmin;
}
User user = User.builder()
        .name("김자바")
        .email("java@example.com")
        .age(25)
        .build();

코드 양이 크게 줄어든다. Builder 패턴을 직접 구현할 일은 많지 않지만, 구조를 이해하고 있으면 Lombok이 뭘 대신 해주는지 파악하는 데 도움이 된다.


언제 쓰는가

  • 생성자 파라미터가 많고, 그중 일부는 선택적일 때
  • 같은 타입의 파라미터가 여러 개라 순서를 실수하기 쉬울 때
  • 객체 생성 과정이 단계별로 나뉘어야 할 때

파라미터가 많은 생성자를 보면서 "이게 뭐지?" 싶었던 적이 있다면, Builder가 그 답이다. 무엇을 만들고 있는지 코드만 봐도 읽히는 것, 그게 Builder가 주는 가장 큰 이점이다.

profile
<- 개발 공부하는 나

0개의 댓글