Builder Pattern

Builder Pattern 은 복잡한 객체 생성 문제를 해결하는 창조적 디자인 패턴으로, Director, Builder, ConcreteBuilder, Product 의 4 가지 핵심 구성요소를 통해 단계별 객체 구성을 지원한다. 생성자 오버로딩 문제를 해결하고 불변 객체 생성을 가능하게 하며, 플루언트 인터페이스를 통한 메서드 체이닝으로 코드 가독성을 크게 향상시킨다. 현대 프레임워크와 라이브러리에서 널리 활용되고 있다.

핵심 개념

Builder Pattern 은 복잡한 객체의 생성 과정을 단계별로 분리하여, 동일한 생성 절차에서 다양한 표현의 객체를 생성할 수 있도록 하는 생성 패턴이다. 객체 생성이 여러 단계로 나뉘거나, 다양한 조합의 옵션이 존재할 때 적합하다.

핵심 정의

의도

복잡한 객체의 생성 과정과 표현 방법을 분리하여, 동일한 생성 절차로 다양한 형태의 객체를 단계별로 생성할 수 있도록 한다.

다른 이름 (Also Known As)

동기 (Motivation / Forces)

적용 가능성 (Applicability)

6.19 패턴이 해결하고자 하는 설계 문제

핵심 설계 문제들:

  1. 생성자 매개변수 급증 문제 (Telescoping Constructor Problem)

    • 여러 개의 선택적 매개변수로 인한 생성자 오버로딩
    • 매개변수 순서 혼동 및 가독성 저하
  2. 객체 상태 불일치 문제

    • 부분적으로 초기화된 객체의 위험성
    • 유효하지 않은 상태의 객체 생성 가능성
  3. 불변 객체 생성의 어려움

    • 복잡한 불변 객체 생성 시 중간 상태 노출
    • 스레드 안전성 확보의 복잡성

6.22 유사하거나 관련된 패턴들

패턴명관계차이점결합 사용
Factory Method대체 관계Factory 는 즉시 생성, Builder 는 단계적 생성빌더 생성 시 Factory Method 활용
Abstract Factory보완 관계Abstract Factory 는 관련 객체군 생성복잡한 객체 계층에서 함께 사용
Prototype대체 관계Prototype 은 복제 기반, Builder 는 구성 기반기본 템플릿 복제 후 빌더로 커스터마이징
Composite결합 관계Composite 트리 구조를 Builder 로 생성복잡한 트리 구조 객체 생성 시
Strategy결합 관계빌드 전략을 Strategy 패턴으로 구현다양한 빌드 알고리즘 선택 시

실무 구현 연관성

객체 생성 최적화 측면:

코드 품질 향상 측면:

아키텍처 설계 측면:

배경

Builder Pattern 은 다음과 같은 문제를 해결하기 위해 등장했다:

  1. 점층적 생성자 패턴 (Telescoping Constructor Pattern) 의 한계
    • 매개변수가 많아질수록 가독성 저하
    • 매개변수 순서 기억의 어려움
    • 코드 유지보수성 문제
  2. 자바빈 패턴 (JavaBeans Pattern) 의 문제점
    • 객체 일관성 (Consistency) 깨짐
    • 불변 객체 생성 불가
    • 스레드 안전성 문제

목적 및 필요성

주요 목적:

  1. 구성과 표현의 분리: 객체 생성 로직을 별도로 관리
  2. 단계적 구성: 복잡한 객체를 단계별로 안전하게 생성
  3. 코드 가독성 향상: 직관적인 객체 생성 API 제공
  4. 유연성 확보: 다양한 구성 옵션 지원

필요성:

주요 기능 및 역할

핵심 기능:

  1. 단계별 객체 구성

    • 필수 속성과 선택적 속성의 구분
    • 속성 설정 순서의 유연성 제공
    • 중간 상태 검증 가능
  2. 다양한 표현 생성

    • 동일한 구성 과정으로 다른 형태의 객체 생성
    • 구성 옵션에 따른 객체 변형
    • 조건부 속성 설정
  3. 유효성 검증

    • 빌드 단계에서의 일괄 검증
    • 비즈니스 규칙 적용
    • 에러 처리의 중앙화

특징

구분특징설명
구조적분리된 구성객체 생성 로직이 별도 클래스로 분리
행위적단계적 구성객체를 단계별로 구성하는 방식
인터페이스플루언트 API메서드 체이닝을 통한 직관적 사용
유연성다형성 지원다양한 빌더 구현체 지원
안전성불변성 보장완성된 객체의 불변성 확보

핵심 원칙

  1. Single Responsibility Principle (SRP)

    • 빌더는 객체 생성에만 집중
    • 각 빌더는 특정 타입의 객체 생성 담당
  2. Open/Closed Principle (OCP)

    • 새로운 빌더 추가 시 기존 코드 수정 불필요
    • 확장에는 열려있고 변경에는 닫혀있음
  3. Dependency Inversion Principle (DIP)

    • 클라이언트는 구체적 빌더가 아닌 인터페이스에 의존
    • 구현체 변경 시 클라이언트 코드 영향 최소화

주요 원리

  1. 위임 원리: Director 가 Builder 에게 구성 작업 위임
  2. 추상화 원리: Builder 인터페이스를 통한 구현체 추상화
  3. 조합 원리: 여러 구성 요소를 조합하여 최종 객체 생성
  4. 캡슐화 원리: 구성 로직을 빌더 내부에 캡슐화

설계 구조와 관계

Builder Pattern 의 설계 구조는 다음과 같은 관계를 형성한다:

graph LR
    subgraph "Client Layer"
        CL[Client Code]
    end
    
    subgraph "Builder Layer"
        DIR[Director]
        BI[Builder Interface]
        CB1[ConcreteBuilder A]
        CB2[ConcreteBuilder B]
    end
    
    subgraph "Product Layer"
        PA[Product A]
        PB[Product B]
    end
    
    CL --> DIR
    CL --> BI
    DIR --> BI
    BI --> CB1
    BI --> CB2
    CB1 --> PA
    CB2 --> PB
    
    style CL fill:#e1f5fe
    style DIR fill:#f3e5f5
    style BI fill:#e8f5e8
    style CB1 fill:#fff3e0
    style CB2 fill:#fff3e0
    style PA fill:#fce4ec
    style PB fill:#fce4ec

관계 설명:

작동 원리 및 방식

sequenceDiagram
    participant Client
    participant Director
    participant Builder
    participant Product
    
    Client->>Director: construct()
    Director->>Builder: buildPartA()
    Builder->>Builder: create PartA
    Director->>Builder: buildPartB()
    Builder->>Builder: create PartB
    Director->>Builder: buildPartC()
    Builder->>Builder: create PartC
    Director->>Builder: getResult()
    Builder->>Product: new Product(parts)
    Builder-->>Director: return Product
    Director-->>Client: return Product

작동 방식 설명:

  1. 초기화 단계: 클라이언트가 빌더 인스턴스 생성
  2. 구성 단계: 단계별로 객체 속성 설정
  3. 검증 단계: 빌드 전 유효성 검증 수행
  4. 생성 단계: 최종 객체 생성 및 반환
  5. 정리 단계: 빌더 상태 초기화 (재사용 시)

패턴 적용의 결과와 트레이드오프

긍정적 결과:

트레이드오프:

전체 구조

classDiagram
    class Director {
        -builder: Builder
        +construct()
        +setBuilder(builder: Builder)
    }
    
    class Builder {
        <<interface>>
        +buildPartA()
        +buildPartB()
        +buildPartC()
        +getResult() Product
    }
    
    class ConcreteBuilderA {
        -product: ProductA
        +buildPartA()
        +buildPartB()
        +buildPartC()
        +getResult() ProductA
    }
    
    class ConcreteBuilderB {
        -product: ProductB
        +buildPartA()
        +buildPartB()
        +buildPartC()
        +getResult() ProductB
    }
    
    class Product {
        <<abstract>>
    }
    
    class ProductA {
        -partA: String
        -partB: String
        -partC: String
    }
    
    class ProductB {
        -partA: Integer
        -partB: Boolean
        -partC: Object
    }
    
    Director --> Builder
    Builder <|-- ConcreteBuilderA
    Builder <|-- ConcreteBuilderB
    ConcreteBuilderA --> ProductA
    ConcreteBuilderB --> ProductB
    Product <|-- ProductA
    Product <|-- ProductB
필수 구성요소
구성요소기능역할특징/활용 시기
Builder (Interface)빌드 메서드 정의공통된 빌드 인터페이스 제공필수 구성요소. 다양한 ConcreteBuilder 에 대한 계약 역할 수행
ConcreteBuilder구체적 빌드 구현실제 객체 생성 로직 수행필수 구성요소. 제품 속성에 따라 다양한 구현 가능
Product최종 객체 생성빌더가 조립하는 결과물 객체필수 구성요소. 복잡한 내부 구조를 가질 수 있음
Director빌드 순서 제어빌드 프로세스 관리 및 표준화선택 구성요소. 복잡한 빌드 순서를 클라이언트에서 분리할 필요가 있을 때 사용
Abstract Product제품 타입 추상화다양한 제품 유형에 대한 공통 인터페이스 정의선택 구성요소. 여러 제품군 생성 시 활용
Builder Factory빌더 인스턴스 생성/선택 관리상황에 맞는 빌더 생성 및 선택 제어선택 구성요소. 설정 기반 동적 선택, 전략 패턴과 연계 가능

구현 기법

구현 방식설명장점대표 사례 / 사용 언어
Classic GoF BuilderDirector 가 빌드 순서를 제어하고, Builder 인터페이스로 구현 분리빌드 과정의 명확한 단계화, 복잡한 객체 조립에 적합Java, Python (TextConverter 등)
Fluent Builder메서드 체이닝 방식으로 직관적 객체 생성 (withX().withY().build())코드 가독성, 선언적 스타일, 순서 유연성JavaScript, Java (HttpRequest 등)
Effective Java 스타일내부 정적 클래스 방식, 필수/선택 속성 구분 명확불변 객체, 명확한 생성자 구조, 코드 응집도 높음Java (Pizza 예제 등)
Internal BuilderProduct 클래스 내부에 Builder 중첩 정의외부 노출 없이 캡슐화, 작은 객체군에 적합Java (Effective Java), Kotlin
External BuilderProduct 외부에 Builder 클래스를 분리하여 설계여러 Product 간 공유 및 확장 용이Java, C++, JS
Functional Builder / DSL함수형 언어에서 빌더를 함수 조합 또는 DSL 형태로 구현불변성 중심 설계, 간결하고 명시적인 객체 구성Python, Kotlin DSL, JavaScript
Generic Builder제네릭을 활용해 타입 안정성 보장, 유연한 객체 구성 지원타입 안전성, 재사용성, 공통 빌더 구현에 적합Java, TypeScript
Step Builder단계별 타입을 반환하여 필수 설정 순서를 강제하는 구현필수값 누락 방지, 유효성 보장Java, Kotlin
Lombok @Builder어노테이션 기반 자동 생성개발 편의성, 코드 자동화, 빠른 적용Java + Lombok
Validation 포함 Builderbuild() 시점에 유효성 검사 수행 (필수 필드 누락, 형식 검증 등)안정성 확보, 런타임 오류 방지모든 언어 가능 (Fluent + Validation)
Immutable Builderbuild() 이후 객체 변경 불가. Setter 미제공스레드 안전성, 신뢰 가능한 상태 보장Java, Kotlin, Scala
Builder Factory다양한 빌더 생성/선택을 팩토리에서 처리설정 기반 선택, DI/Strategy 와 연계 가능Java, Spring, 설정 기반 시스템

Classic GoF Builder (Director 포함)

설명: 전통적 형태의 Builder. Builder 인터페이스, ConcreteBuilder, Product, Director 구성.
예시 (Python):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# Product
class House:
    def __init__(self):
        self.parts = []

    def add(self, part):
        self.parts.append(part)

    def show(self):
        print(f"House parts: {self.parts}")

# Builder Interface
class HouseBuilder:
    def build_foundation(self): pass
    def build_walls(self): pass
    def build_roof(self): pass
    def get_result(self): pass

# Concrete Builder
class WoodenHouseBuilder(HouseBuilder):
    def __init__(self):
        self.house = House()

    def build_foundation(self):
        self.house.add("Wood Foundation")

    def build_walls(self):
        self.house.add("Wood Walls")

    def build_roof(self):
        self.house.add("Wood Roof")

    def get_result(self):
        return self.house

# Director
class HouseDirector:
    def construct(self, builder: HouseBuilder):
        builder.build_foundation()
        builder.build_walls()
        builder.build_roof()
        return builder.get_result()

# Client
builder = WoodenHouseBuilder()
director = HouseDirector()
house = director.construct(builder)
house.show()

Fluent Builder (Method Chaining)

설명: withX().withY().build() 형태의 연쇄 호출로 구성하는 현대적 스타일
예시 (JavaScript):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class Car {
  constructor(builder) {
    this.make = builder.make;
    this.model = builder.model;
    this.color = builder.color;
  }

  static get Builder() {
    return class {
      constructor() {
        this.make = "Unknown";
        this.model = "Unknown";
        this.color = "White";
      }

      setMake(make) {
        this.make = make;
        return this;
      }

      setModel(model) {
        this.model = model;
        return this;
      }

      setColor(color) {
        this.color = color;
        return this;
      }

      build() {
        return new Car(this);
      }
    };
  }
}

// 사용
const car = new Car.Builder()
  .setMake("Tesla")
  .setModel("Model S")
  .setColor("Black")
  .build();
console.log(car);

내부 클래스 기반 Builder (Effective Java 스타일)

설명: Product 클래스 안에 static Builder 클래스를 중첩하여 구성
예시 (JavaScript 유사):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
class Pizza {
  constructor(builder) {
    this.dough = builder.dough;
    this.cheese = builder.cheese;
    this.toppings = builder.toppings;
  }

  static get Builder() {
    return class {
      constructor(dough) {
        this.dough = dough;
        this.cheese = "mozzarella";
        this.toppings = [];
      }

      addTopping(topping) {
        this.toppings.push(topping);
        return this;
      }

      setCheese(cheese) {
        this.cheese = cheese;
        return this;
      }

      build() {
        return new Pizza(this);
      }
    };
  }
}

// 사용 예시
const pizza = new Pizza.Builder("thin")
  .setCheese("cheddar")
  .addTopping("pepperoni")
  .addTopping("mushroom")
  .build();
console.log(pizza);

Step Builder (타입 안전 빌더)

설명: 필수 단계를 순차적으로 강제하는 빌더, 컴파일 타임 타입 안정성 확보에 유리
예시 (TypeScript 스타일):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
class User {
  constructor(
    public username: string,
    public email: string,
    public age?: number
  ) {}
}

// 단계별 Builder 인터페이스
interface UsernameStep {
  setUsername(username: string): EmailStep;
}

interface EmailStep {
  setEmail(email: string): OptionalStep;
}

interface OptionalStep {
  setAge(age: number): BuildStep;
  build(): User;
}

interface BuildStep {
  build(): User;
}

class UserBuilder implements UsernameStep, EmailStep, OptionalStep, BuildStep {
  private username: string;
  private email: string;
  private age?: number;

  setUsername(username: string): EmailStep {
    this.username = username;
    return this;
  }

  setEmail(email: string): OptionalStep {
    this.email = email;
    return this;
  }

  setAge(age: number): BuildStep {
    this.age = age;
    return this;
  }

  build(): User {
    return new User(this.username, this.email, this.age);
  }
}

// 사용 예시
const user = new UserBuilder()
  .setUsername("hyun")
  .setEmail("hyun@example.com")
  .setAge(30)
  .build();

Functional Builder (Python DSL)

설명: 함수형 스타일로 빌더를 구성. 빌더 함수들을 조합하여 최종 객체 구성.
예시 (Python):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Query:
    def __init__(self):
        self.select_clause = []
        self.from_clause = None
        self.where_clause = []

    def __str__(self):
        return f"SELECT {', '.join(self.select_clause)} FROM {self.from_clause} WHERE {' AND '.join(self.where_clause)}"

def select(*columns):
    def inner(q):
        q.select_clause.extend(columns)
        return q
    return inner

def from_table(table):
    def inner(q):
        q.from_clause = table
        return q
    return inner

def where(condition):
    def inner(q):
        q.where_clause.append(condition)
        return q
    return inner

# 빌더 함수 조합
query = Query()
query = (select("name", "age")(query))
query = (from_table("users")(query))
query = (where("age > 20")(query))

print(query)

장점

카테고리항목설명
1. 코드 구조 및 가독성코드 가독성 향상플루언트 인터페이스 및 체이닝 방식으로 직관적이고 읽기 쉬운 코드 작성 가능
클라이언트와 생성 로직 분리클라이언트는 최종 객체에만 집중하고, 생성 과정은 빌더 내부로 숨겨 캡슐화
유연한 객체 표현동일한 빌더로 다양한 속성 조합의 객체 생성 가능
2. 복잡성 관리복잡한 객체 생성 단순화단계별 생성 절차 분리로 인해 복잡한 생성 과정도 명확하게 관리 가능
복잡한 객체 구성 관리선택적 속성, 중첩 구조 등 복잡한 객체 조합을 체계적으로 처리할 수 있음
3. 안전성 및 불변성매개변수 순서 독립성생성자와 달리, 속성 설정 순서에 의존하지 않아 가독성과 안정성 향상
불변 객체 생성 보장모든 속성 설정 후 객체 생성 → 변경 불가능한 객체로 스레드 안전성 확보
객체 불완전성 방지build() 호출을 통해 모든 필드가 세팅되었는지 검증 가능
유효성 검증 중앙화생성 로직 내에서 일괄적으로 필드 검증 가능 (필수 조건 누락 방지 등)
4. 확장성과 유지보수성확장성새로운 속성이나 유형의 빌더를 추가하더라도 기존 코드 영향 없이 확장 가능
재사용성빌더 구성 로직의 모듈화로 공통 패턴의 재사용이 가능, 코드 중복 감소

단점과 문제점 및 해결방안

단점

카테고리단점 항목설명해결 방안
설계 복잡도클래스 수 증가Builder, Director, Product 등 구성 요소로 인한 클래스 수 증가자동 생성 도구 (Lombok, CodeGen) 활용, 필요한 경우에만 도입
코드 복잡성 증가단순한 객체에도 Builder 적용 시 코드 구조가 오히려 복잡해짐기준 마련 (예: 매개변수 4 개 이상), 간단한 객체엔 생성자/팩토리 사용
리소스 사용성능 오버헤드메서드 체이닝, 중간 객체 생성 등으로 인한 성능 저하인라인 최적화, Lightweight Builder, 불필요한 체이닝 최소화
메모리 사용량 증가빌더 객체 및 중간 상태 값 저장으로 인한 GC 부하빌더 재사용 시 초기화 메서드 추가, 빌더 수명 관리
검증/안정성런타임 오류 가능성필수 필드 누락, 잘못된 필드 순서 등으로 인한 런타임 오류 발생 가능@NonNull, 타입 힌트, Step Builder 등으로 컴파일 타임 검증 강화
적용 한계과도한 설계간단한 객체까지 과도하게 적용되면 오히려 오버엔지니어링적용 기준 명확화, 단일 속성 객체엔 일반 생성자나 팩토리 활용

문제점

카테고리문제 항목원인영향탐지/진단예방 방법해결 기법
상태 관리빌더 상태 오염빌더 인스턴스 재사용 시 이전 상태가 남아 있음잘못된 객체 생성단위 테스트, 상태 점검 로직빌더 재사용 금지, 상태 초기화 로직 구현reset(), Immutable Builder 도입
중간 상태 노출build() 호출 전 객체를 외부에서 참조 가능불완전한 객체 사용상태 접근 제한 분석build() 후에만 객체 반환deep copy, immutable copy 적용
유효성 및 안정성필수 필드 누락체이닝 방식에서 누락된 필수 필드에 대한 검증 부재런타임 예외 발생필드 null 체크, 빌더 사용 로그 분석필수 필드 구분, 체이닝 강제 순서 구현Step Builder, Required Field Validator 적용
부분 객체 생성모든 필드를 세팅하지 않아 불완전한 상태의 객체 생성됨잘못된 결과 반환런타임 검증, QA 시나리오 확인build() 내 유효성 검사Fluent Validation, assertComplete() 메서드 추가
설계 일관성Director 의존성 과도Builder 사용 시 Director 가 빌드 순서를 강제하게 됨설계 유연성 저하역할 분석, 코드 리뷰단순 빌더는 Client 에서 직접 체이닝Director 제거 또는 단일 빌더 방식 사용
재사용성유사한 빌더 클래스 중복도메인마다 유사한 빌더가 반복 구현됨유지보수 부담, 코드 중복 증가코드 중복 분석 도구추상 Builder 설계, Generic Builder 사용Builder Registry/Template 적용
DI 통합 문제의존성 주입 복잡화DI 컨테이너가 Builder 인스턴스를 관리하지 못하거나 조합 어려움객체 생성 흐름 분리DI 로그 분석, 초기화 에러 추적Factory + Builder 패턴 조합Spring Bean Factory 안에 빌더 감싸기

도전 과제

카테고리도전 과제원인영향해결 방안
아키텍처/설계과도한 클래스 수 증가Director, Builder, ConcreteBuilder 등의 구조적 구성요소 다수유지보수 복잡성 증가단순한 경우 Director 생략, 내부 정적 Builder 사용
역할 분리 모호성Builder, Director, Client 간 책임 불명확설계 일관성 저하단일 책임 원칙 적용 (SRP), 역할 명확히 구분
Product 변경 시 Builder 동기화 필요Product 필드 변경이 Builder 에 즉각 영향동기화 누락 시 런타임 오류 발생테스트 자동화, Code Generation 도구 (Lombok 등) 활용
재사용성/중복빌더 클래스 중복유사한 도메인 객체마다 Builder 를 각각 구현코드 중복 증가공통 추상 Builder 또는 Generic Builder 패턴 도입
생성 로직 재사용 어려움각 Builder 마다 로직이 캡슐화되어 있음중복된 구현 증가빌더 상속 구조 도입, Builder Pooling or Registry 설계 적용
성능/자원 최적화메모리 사용량 증가빌더 객체 생성, 불변성 확보를 위한 중간 객체 누적GC 압박, 메모리 낭비빌더 객체 재사용, 약한 참조 (WeakRef), Object Pooling
빌더 호출 오버헤드체이닝에 따른 메서드 호출 스택 증가생성 속도 저하인라인 최적화, 가벼운 빌더 도입 (Lightweight Builder)
객체 중복 생성빌더와 최종 객체 간 데이터 중복리소스 낭비Copy-on-write 방식, Prototype 패턴 결합
타입 안전성필수 필드 누락 위험체이닝 방식은 선택적 필드와 구분이 어려움객체 상태 불완전Step Builder, Required Field Check, @NonNull, IDE 지원
테스트/검증유효성 검사 누락build() 호출 전 상태가 유효한지 보장되지 않음예외 발생, 논리 오류build() 내부에 필드 조합 검증 로직 추가, Fluent Validation 기법 적용
테스트 작성 복잡성복잡한 체이닝 로직 테스트 어려움테스트 품질 저하Test Data Builder 패턴 활용, 각 빌더 단계별 유닛 테스트 설계
현대 기술 환경MSA 환경에서 빌더 일관성 유지 어려움서비스 간 DTO 생성 방식 불일치인터페이스 불일치, 유지보수 어려움공통 Builder 모듈화 (Shared Library), OpenAPI 기반 코드 생성 도입
클라우드 환경의 메모리 제한서버리스/컨테이너 환경에서 오버헤드 민감비용 증가, 성능 저하Lightweight Builder, Builder 객체 풀링 적용
함수형 프로그래밍과의 충돌가변 상태 관리 불가, 불변성 요구전통 빌더 적용 곤란Immutable Builder, Functional Builder (람다/함수 조합 기반 빌더) 도입
문서화/가독성체이닝 순서 불명확빌더 사용법이 직관적이지 않음오용 위험, 디버깅 어려움JavaDoc/TSDoc 활용, IDE 용 Docstring 주석 및 예시 추가
빌더 사용법 표준 부재팀 간 작성 방식 상이코드 스타일 혼란팀별 빌더 작성 가이드 수립, 코딩 컨벤션 포함

분류에 따른 종류 및 유형

분류 기준유형설명주요 사용 사례
구현 방식Classic (GoF) BuilderDirector, Builder, Product 로 구성된 전통적 구조복잡한 객체 생성 로직, 파서/컴파일러 등
Fluent Builder메서드 체이닝 기반 선언형 빌더 (.withX().withY().build())API 클라이언트, 설정 객체, REST 요청 빌더 등
Step Builder단계별 인터페이스로 강제 흐름 설계, 타입 안전성 강화필수 필드가 많은 복잡 객체, 오류 방지 필요 시
Lombok @Builder애노테이션 기반 빌더 자동 생성Java DTO, Config 클래스 등
Effective Java Builder내부 정적 클래스를 활용한 실용적 빌더 구조Immutable 객체 생성
복잡도Simple Builder단일 객체, 비교적 필드가 적은 경우에 적합Value Object, 단순 DTO
Complex Builder유효성 검사, 조건부 조합 등 복잡한 로직 포함도메인 객체, 설정 객체
Composite Builder다수의 하위 요소 조합으로 구성된 복합 객체 생성UI 컴포넌트, 문서 생성, Aggregates
타입 안전성Basic Builder일반적인 빌더, 순서 제한 없음일반 객체 빌더
Step Builder각 단계마다 다른 타입 반환으로 순서 강제데이터 누락 방지, 안전성 우선 객체
Generic Builder제네릭 기반으로 다양한 타입의 객체를 안전하게 구성다양한 템플릿 객체 생성 시
사용 범위Internal BuilderProduct 클래스 내부에 정적 Builder 클래스를 정의클래스 전용 빌더
External Builder독립된 외부 클래스에서 Builder 정의공유/확장 가능한 빌더 구조
Domain-Specific Builder특정 도메인에 최적화된 빌더 (예: DSL, SQL 쿼리 등)QueryDSL, Flutter 위젯 구성 등
구조 스타일Director 기반Builder 호출 순서를 캡슐화하는 Director 존재전통적 GoF 구조에서 사용
No DirectorClient 가 Builder 직접 호출유연성과 단순성 중시 구조
응용 형태Function Composition함수형 언어에서 빌더 기능을 함수 조합 형태로 표현Python, Kotlin 등 함수형 스타일 구성 시
DSL 기반 Builder선언형 Domain-Specific Language 형태로 객체 구성Kotlin DSL, Gradle, Terraform 설정 등
생성 대상Single Product Builder하나의 객체만 생성일반 객체 생성
Multi Product Builder다양한 변형 객체 또는 복수 객체를 생성조건별 메시지, UI 템플릿 등

실무 사용 예시

카테고리적용 분야구체적 사용 예시설명 및 효과
백엔드 개발DTO/VO 생성API 요청/응답 객체, 설정 객체 등필수/선택 필드 구분, 불변 객체 생성, 코드 가독성 향상
API 개발HTTP Request/ResponseRetrofit, OkHttp, RestTemplate다양한 옵션의 요청/응답 객체 구성, 유연한 파라미터 설정
데이터베이스SQL Query 생성QueryDSL, JPA Criteria API, MyBatis, Jooq동적 SQL 쿼리 조합, 복잡한 쿼리 작성의 안정성과 가독성 확보
설정 관리설정 객체 빌더Spring Boot 설정 객체, Docker 설정환경별 설정 값 분리 및 설정 객체화, 유효성 검사 포함 가능
UI 개발UI 컴포넌트 생성React JSX, Flutter Widget, Android Dialog다양한 속성을 조합한 복합 위젯 생성, 선언적 구성 방식, 재사용성 확보
문서 생성텍스트/HTML 문서 생성MarkdownBuilder, HTMLBuilder다양한 포맷의 문서를 공통 로직으로 단계별 구성 가능
테스트테스트 데이터 구성Test Data Builder, Fixture Object다양한 테스트 상태의 객체 손쉽게 생성, 테스트 코드 가독성 및 유지보수성 향상
게임 개발캐릭터/오브젝트 생성RPG 캐릭터 빌더, 속성 + 스킬 + 장비 조합 시스템조건 기반의 복잡한 객체 생성 로직 구성 가능
메시징 시스템메시지 객체 생성Kafka, RabbitMQ, Custom Message Builder메시지 헤더, 바디 구성 등 메시지 형식 표준화 및 직관적 구성
빌드 시스템빌드 스크립트 구성Gradle, Maven DSL, Code Generator선언형 DSL 스타일 빌더 사용, 설정 코드 가독성 향상
기타 자동화환경 구성 스크립트Infrastructure-as-Code DSL구성 요소 단계적 빌드, 재사용 및 조건부 구성 편의 제공

활용 사례

사례 1: 전자상거래 주문 시스템

시스템 구성:

1
2
3
4
5
Client → OrderDirector → OrderBuilder → Order
            PaymentBuilder → Payment
            ShippingBuilder → Shipping

활용 시나리오:

온라인 쇼핑몰에서 복잡한 주문 생성 과정을 Builder Pattern 으로 구현:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// 주문 생성
Order order = Order.builder()
    .customerId("CUST001")
    .orderDate(LocalDate.now())
    .items(Arrays.asList(item1, item2, item3))
    .shippingAddress("서울시 강남구…")
    .paymentMethod(PaymentMethod.CREDIT_CARD)
    .discountCode("SUMMER2025")
    .priority(OrderPriority.STANDARD)
    .build();

Workflow:

  1. 주문 초기화: 고객 정보와 기본 설정
  2. 상품 추가: 구매할 상품 목록 설정
  3. 배송 정보: 주소와 배송 옵션 설정
  4. 결제 정보: 결제 방법과 할인 적용
  5. 최종 검증: 주문 데이터 유효성 검사
  6. 주문 생성: 완성된 Order 객체 반환

사례 2: User Registration 요청 처리 시스템

요구 사항:

구성 요소:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class User {
    private final String username;
    private final String password;
    private final String email;
    private final String address;
    private final String phone;
    private final boolean marketingConsent;

    private User(Builder builder) {
        this.username = builder.username;
        this.password = builder.password;
        this.email = builder.email;
        this.address = builder.address;
        this.phone = builder.phone;
        this.marketingConsent = builder.marketingConsent;
    }

    public static class Builder {
        private final String username;
        private final String password;
        private final String email;
        private String address = "";
        private String phone = "";
        private boolean marketingConsent = false;

        public Builder(String username, String password, String email) {
            this.username = username;
            this.password = password;
            this.email = email;
        }

        public Builder address(String address) { this.address = address; return this; }
        public Builder phone(String phone) { this.phone = phone; return this; }
        public Builder marketingConsent(boolean consent) {
	        this.marketingConsent = consent; 
	        return this;
        }

        public User build() {
            return new User(this);
        }
    }
}

Workflow:

  1. 컨트롤러에서 User.Builder(username, password, email) 시작
  2. 옵션 필드에 따라 .address(), .phone() 등 체이닝
  3. build() 호출로 User 객체 생성

사례 3: 문서 생성

설명: Builder Pattern 은 복잡한 객체를 단계별로 생성하는 데 사용되며, 예를 들어 다양한 형식의 문서 생성에 활용된다.

시스템 구성:

작동 방식:

차이점:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# Builder 인터페이스: 문서 작성 단계 정의
class DocumentBuilder:
    def start_document(self):
        pass

    def add_title(self, title: str):
        pass

    def add_paragraph(self, text: str):
        pass

    def end_document(self):
        pass

    def get_result(self) -> str:
        pass


# ConcreteBuilder 1: HTML 문서 생성기
class HTMLBuilder(DocumentBuilder):
    def __init__(self):
        self.parts = []

    def start_document(self):
        self.parts.append("<html><body>")

    def add_title(self, title: str):
        self.parts.append(f"<h1>{title}</h1>")

    def add_paragraph(self, text: str):
        self.parts.append(f"<p>{text}</p>")

    def end_document(self):
        self.parts.append("</body></html>")

    def get_result(self) -> str:
        return "\n".join(self.parts)


# ConcreteBuilder 2: Markdown 문서 생성기
class MarkdownBuilder(DocumentBuilder):
    def __init__(self):
        self.parts = []

    def start_document(self):
        self.parts.append("# 문서 시작")

    def add_title(self, title: str):
        self.parts.append(f"# {title}")

    def add_paragraph(self, text: str):
        self.parts.append(text)

    def end_document(self):
        self.parts.append("_문서 끝_")

    def get_result(self) -> str:
        return "\n\n".join(self.parts)


# Director: Builder 사용 순서 정의
class DocumentDirector:
    def __init__(self, builder: DocumentBuilder):
        self.builder = builder

    def construct(self):
        self.builder.start_document()
        self.builder.add_title("Builder 패턴 소개")
        self.builder.add_paragraph("Builder 패턴은 복잡한 객체를 단계별로 생성할 수 있게 해준다.")
        self.builder.add_paragraph("여기서는 HTML과 Markdown으로 문서를 생성하는 예제를 다룬다.")
        self.builder.end_document()


# Client 코드
if __name__ == "__main__":
    # HTML 생성
    html_builder = HTMLBuilder()
    director = DocumentDirector(html_builder)
    director.construct()
    print("== HTML 문서 ==")
    print(html_builder.get_result())

    print("\n")

    # Markdown 생성
    md_builder = MarkdownBuilder()
    director = DocumentDirector(md_builder)
    director.construct()
    print("== Markdown 문서 ==")
    print(md_builder.get_result())

구현 예시

시스템 구성: Spring Security 에서는 웹 보안 설정을 위해 Builder Pattern 을 광범위하게 활용한다.

graph TB
    A[HttpSecurity] --> B[AuthorizeHttpRequestsConfigurer]
    A --> C[FormLoginConfigurer]
    A --> D[CsrfConfigurer]
    A --> E[SessionManagementConfigurer]
    
    B --> F[AuthorizationFilter]
    C --> G[UsernamePasswordAuthenticationFilter]
    D --> H[CsrfFilter]
    E --> I[SessionManagementFilter]
    
    style A fill:#e3f2fd
    style B fill:#f3e5f5
    style C fill:#e8f5e8
    style D fill:#fff3e0
    style E fill:#fce4ec

활용 사례 Workflow:

flowchart TD
    A[Spring Security Configuration] --> B[HttpSecurity Builder 생성]
    B --> C[authorizeHttpRequests 설정]
    C --> D[formLogin 설정]
    D --> E[csrf 설정]
    E --> F[sessionManagement 설정]
    F --> G[build 메서드 호출]
    G --> H[SecurityFilterChain 생성]
    H --> I[필터 체인 등록]
    
    style A fill:#e1f5fe
    style H fill:#e8f5e8
    style I fill:#f3e5f5

Builder Pattern 의 역할:

  1. 설정 단계 분리: 각 보안 기능별로 독립적 설정
  2. 유연한 조합: 필요한 보안 기능만 선택적 적용
  3. 체인 방식: 메서드 체이닝으로 직관적 설정
  4. 타입 안전성: 컴파일 타임에 설정 오류 검출

Spring Security 사례를 바탕으로 한 보안 설정 빌더를 Python 과 Javascript 로 구현

Python

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
# Security Configuration Builder Pattern 구현

from abc import ABC, abstractmethod
from typing import Dict, List, Optional, Callable
from dataclasses import dataclass
from enum import Enum

# 열거형 정의
class SessionCreationPolicy(Enum):
    IF_REQUIRED = "if_required"
    STATELESS = "stateless"
    ALWAYS = "always"

class HttpMethod(Enum):
    GET = "GET"
    POST = "POST"
    PUT = "PUT"
    DELETE = "DELETE"

# 설정 데이터 클래스들
@dataclass
class AuthRule:
    """인증 규칙을 나타내는 데이터 클래스"""
    pattern: str
    roles: List[str]
    methods: List[HttpMethod]

@dataclass
class FormLoginConfig:
    """폼 로그인 설정을 나타내는 데이터 클래스"""
    login_page: str
    success_url: str
    failure_url: str
    username_parameter: str = "username"
    password_parameter: str = "password"

@dataclass
class CsrfConfig:
    """CSRF 설정을 나타내는 데이터 클래스"""
    enabled: bool
    token_repository: Optional[str] = None
    ignore_patterns: List[str] = None

@dataclass
class SessionConfig:
    """세션 관리 설정을 나타내는 데이터 클래스"""
    creation_policy: SessionCreationPolicy
    max_sessions: int
    max_session_prevents_login: bool

# Product: 최종 보안 설정 객체
class SecurityFilterChain:
    """보안 필터 체인 - Builder Pattern의 Product"""
    
    def __init__(self, 
                 auth_rules: List[AuthRule],
                 form_login: Optional[FormLoginConfig] = None,
                 csrf_config: Optional[CsrfConfig] = None,
                 session_config: Optional[SessionConfig] = None):
        self.auth_rules = auth_rules
        self.form_login = form_login
        self.csrf_config = csrf_config
        self.session_config = session_config
        
        # 유효성 검증
        self._validate()
    
    def _validate(self):
        """설정 유효성 검증"""
        if not self.auth_rules:
            raise ValueError("최소 하나의 인증 규칙이 필요합니다.")
        
        for rule in self.auth_rules:
            if not rule.pattern:
                raise ValueError("인증 규칙에는 패턴이 필요합니다.")
    
    def apply_security(self, request_path: str, method: HttpMethod) -> bool:
        """요청에 대한 보안 검사 수행"""
        for rule in self.auth_rules:
            if self._matches_pattern(request_path, rule.pattern):
                return self._check_authorization(rule, method)
        return False
    
    def _matches_pattern(self, path: str, pattern: str) -> bool:
        """URL 패턴 매칭 (간단한 와일드카드 지원)"""
        if pattern.endswith("**"):
            return path.startswith(pattern[:-2])
        return path == pattern
    
    def _check_authorization(self, rule: AuthRule, method: HttpMethod) -> bool:
        """인증 확인 (실제 구현에서는 사용자 세션 확인 등)"""
        return method in rule.methods

# Builder 인터페이스
class SecurityConfigurer(ABC):
    """보안 설정자 인터페이스 - Builder Pattern의 Builder"""
    
    @abstractmethod
    def and_return(self) -> 'HttpSecurityBuilder':
        """부모 빌더로 돌아가기"""
        pass

# Concrete Builder들
class AuthorizeHttpRequestsConfigurer(SecurityConfigurer):
    """HTTP 요청 인증 설정자"""
    
    def __init__(self, parent: 'HttpSecurityBuilder'):
        self.parent = parent
        self.auth_rules: List[AuthRule] = []
    
    def request_matchers(self, pattern: str) -> 'RequestMatcherConfigurer':
        """요청 매처 설정"""
        return RequestMatcherConfigurer(self, pattern)
    
    def any_request(self) -> 'RequestConfigurer':
        """모든 요청에 대한 설정"""
        return RequestConfigurer(self, "/**")
    
    def add_rule(self, rule: AuthRule):
        """인증 규칙 추가"""
        self.auth_rules.append(rule)
    
    def and_return(self) -> 'HttpSecurityBuilder':
        """부모 빌더로 돌아가기"""
        self.parent.auth_rules = self.auth_rules
        return self.parent

class FormLoginConfigurer(SecurityConfigurer):
    """폼 로그인 설정자"""
    
    def __init__(self, parent: 'HttpSecurityBuilder'):
        self.parent = parent
        self.config = FormLoginConfig(
            login_page="/login",
            success_url="/",
            failure_url="/login?error"
        )
    
    def login_page(self, page: str) -> 'FormLoginConfigurer':
        """로그인 페이지 설정"""
        self.config.login_page = page
        return self
    
    def default_success_url(self, url: str) -> 'FormLoginConfigurer':
        """로그인 성공 URL 설정"""
        self.config.success_url = url
        return self
    
    def failure_url(self, url: str) -> 'FormLoginConfigurer':
        """로그인 실패 URL 설정"""
        self.config.failure_url = url
        return self
    
    def and_return(self) -> 'HttpSecurityBuilder':
        """부모 빌더로 돌아가기"""
        self.parent.form_login_config = self.config
        return self.parent

class CsrfConfigurer(SecurityConfigurer):
    """CSRF 설정자"""
    
    def __init__(self, parent: 'HttpSecurityBuilder'):
        self.parent = parent
        self.config = CsrfConfig(enabled=True)
    
    def disable(self) -> 'CsrfConfigurer':
        """CSRF 비활성화"""
        self.config.enabled = False
        return self
    
    def ignore_request_matchers(self, *patterns: str) -> 'CsrfConfigurer':
        """CSRF 검사 제외 패턴 설정"""
        self.config.ignore_patterns = list(patterns)
        return self
    
    def and_return(self) -> 'HttpSecurityBuilder':
        """부모 빌더로 돌아가기"""
        self.parent.csrf_config = self.config
        return self.parent

class SessionManagementConfigurer(SecurityConfigurer):
    """세션 관리 설정자"""
    
    def __init__(self, parent: 'HttpSecurityBuilder'):
        self.parent = parent
        self.config = SessionConfig(
            creation_policy=SessionCreationPolicy.IF_REQUIRED,
            max_sessions=1,
            max_session_prevents_login=False
        )
    
    def session_creation_policy(self, policy: SessionCreationPolicy) -> 'SessionManagementConfigurer':
        """세션 생성 정책 설정"""
        self.config.creation_policy = policy
        return self
    
    def maximum_sessions(self, max_sessions: int) -> 'SessionManagementConfigurer':
        """최대 세션 수 설정"""
        self.config.max_sessions = max_sessions
        return self
    
    def and_return(self) -> 'HttpSecurityBuilder':
        """부모 빌더로 돌아가기"""
        self.parent.session_config = self.config
        return self.parent

# 헬퍼 클래스들
class RequestMatcherConfigurer:
    """요청 매처 설정 헬퍼"""
    
    def __init__(self, parent: AuthorizeHttpRequestsConfigurer, pattern: str):
        self.parent = parent
        self.pattern = pattern
    
    def has_role(self, role: str) -> AuthorizeHttpRequestsConfigurer:
        """역할 기반 접근 권한 설정"""
        rule = AuthRule(
            pattern=self.pattern,
            roles=[role],
            methods=list(HttpMethod)
        )
        self.parent.add_rule(rule)
        return self.parent
    
    def has_any_role(self, *roles: str) -> AuthorizeHttpRequestsConfigurer:
        """다중 역할 기반 접근 권한 설정"""
        rule = AuthRule(
            pattern=self.pattern,
            roles=list(roles),
            methods=list(HttpMethod)
        )
        self.parent.add_rule(rule)
        return self.parent

class RequestConfigurer:
    """요청 설정 헬퍼"""
    
    def __init__(self, parent: AuthorizeHttpRequestsConfigurer, pattern: str):
        self.parent = parent
        self.pattern = pattern
    
    def authenticated(self) -> AuthorizeHttpRequestsConfigurer:
        """인증 필요 설정"""
        rule = AuthRule(
            pattern=self.pattern,
            roles=["USER"],  # 기본 사용자 역할
            methods=list(HttpMethod)
        )
        self.parent.add_rule(rule)
        return self.parent
    
    def permit_all(self) -> AuthorizeHttpRequestsConfigurer:
        """모든 접근 허용"""
        rule = AuthRule(
            pattern=self.pattern,
            roles=[],  # 빈 역할 = 모든 접근 허용
            methods=list(HttpMethod)
        )
        self.parent.add_rule(rule)
        return self.parent

# 메인 빌더 클래스
class HttpSecurityBuilder:
    """HTTP 보안 설정 빌더 - Builder Pattern의 ConcreteBuilder"""
    
    def __init__(self):
        self.auth_rules: List[AuthRule] = []
        self.form_login_config: Optional[FormLoginConfig] = None
        self.csrf_config: Optional[CsrfConfig] = None
        self.session_config: Optional[SessionConfig] = None
    
    def authorize_http_requests(self, 
                               configurer: Callable[[AuthorizeHttpRequestsConfigurer], AuthorizeHttpRequestsConfigurer]) -> 'HttpSecurityBuilder':
        """HTTP 요청 인증 설정"""
        auth_configurer = AuthorizeHttpRequestsConfigurer(self)
        configurer(auth_configurer)
        # configurer 함수가 and_return()을 호출하여 설정이 완료됨
        return self
    
    def form_login(self, 
                   configurer: Callable[[FormLoginConfigurer], FormLoginConfigurer]) -> 'HttpSecurityBuilder':
        """폼 로그인 설정"""
        login_configurer = FormLoginConfigurer(self)
        configurer(login_configurer)
        return self
    
    def csrf(self, 
             configurer: Callable[[CsrfConfigurer], CsrfConfigurer]) -> 'HttpSecurityBuilder':
        """CSRF 설정"""
        csrf_configurer = CsrfConfigurer(self)
        configurer(csrf_configurer)
        return self
    
    def session_management(self, 
                          configurer: Callable[[SessionManagementConfigurer], SessionManagementConfigurer]) -> 'HttpSecurityBuilder':
        """세션 관리 설정"""
        session_configurer = SessionManagementConfigurer(self)
        configurer(session_configurer)
        return self
    
    def build(self) -> SecurityFilterChain:
        """최종 보안 필터 체인 생성"""
        return SecurityFilterChain(
            auth_rules=self.auth_rules,
            form_login=self.form_login_config,
            csrf_config=self.csrf_config,
            session_config=self.session_config
        )

# Director (선택사항) - 공통 설정 템플릿
class SecurityConfigurationDirector:
    """보안 설정 디렉터 - 일반적인 설정 패턴 제공"""
    
    @staticmethod
    def basic_web_security() -> SecurityFilterChain:
        """기본 웹 보안 설정"""
        return (HttpSecurityBuilder()
                .authorize_http_requests(lambda auth: 
                    auth.request_matchers("/public/**").permit_all()
                        .request_matchers("/admin/**").has_role("ADMIN")
                        .any_request().authenticated()
                        .and_return())
                .form_login(lambda form: 
                    form.login_page("/login")
                        .default_success_url("/dashboard")
                        .and_return())
                .csrf(lambda csrf: csrf.and_return())
                .build())
    
    @staticmethod
    def api_security() -> SecurityFilterChain:
        """API 보안 설정 (무상태)"""
        return (HttpSecurityBuilder()
                .authorize_http_requests(lambda auth: 
                    auth.request_matchers("/api/public/**").permit_all()
                        .any_request().authenticated()
                        .and_return())
                .csrf(lambda csrf: csrf.disable().and_return())
                .session_management(lambda session: 
                    session.session_creation_policy(SessionCreationPolicy.STATELESS)
                           .and_return())
                .build())

# 사용 예시와 테스트
def demonstrate_builder_pattern():
    """Builder Pattern 사용 예시"""
    
    print("=== Builder Pattern 보안 설정 예시 ===\n")
    
    # 1. 수동 빌더 사용 (Fluent Interface)
    print("1. 수동 빌더 설정:")
    security_chain = (HttpSecurityBuilder()
                     .authorize_http_requests(lambda auth: 
                         auth.request_matchers("/admin/**").has_role("ADMIN")
                             .request_matchers("/user/**").has_any_role("USER", "ADMIN")
                             .request_matchers("/public/**").permit_all()
                             .any_request().authenticated()
                             .and_return())
                     .form_login(lambda form: 
                         form.login_page("/custom-login")
                             .default_success_url("/home")
                             .failure_url("/login?error=true")
                             .and_return())
                     .csrf(lambda csrf: 
                         csrf.ignore_request_matchers("/api/**")
                             .and_return())
                     .session_management(lambda session: 
                         session.session_creation_policy(SessionCreationPolicy.IF_REQUIRED)
                                .maximum_sessions(2)
                                .and_return())
                     .build())
    
    print(f"생성된 인증 규칙 수: {len(security_chain.auth_rules)}")
    print(f"폼 로그인 페이지: {security_chain.form_login.login_page}")
    print(f"CSRF 활성화: {security_chain.csrf_config.enabled}")
    print()
    
    # 2. Director 사용 (사전 정의된 템플릿)
    print("2. Director를 통한 기본 웹 보안 설정:")
    basic_security = SecurityConfigurationDirector.basic_web_security()
    print(f"기본 설정 규칙 수: {len(basic_security.auth_rules)}")
    
    print("\n3. Director를 통한 API 보안 설정:")
    api_security = SecurityConfigurationDirector.api_security()
    print(f"API 설정 규칙 수: {len(api_security.auth_rules)}")
    print(f"세션 정책: {api_security.session_config.creation_policy.value}")
    print()
    
    # 3. 보안 검사 테스트
    print("4. 보안 검사 테스트:")
    test_cases = [
        ("/admin/users", HttpMethod.GET, "관리자 페이지"),
        ("/user/profile", HttpMethod.POST, "사용자 페이지"),
        ("/public/info", HttpMethod.GET, "공개 페이지"),
        ("/protected", HttpMethod.GET, "보호된 페이지")
    ]
    
    for path, method, description in test_cases:
        is_authorized = security_chain.apply_security(path, method)
        print(f"{description} ({path}): {'허용' if is_authorized else '차단'}")

if __name__ == "__main__":
    demonstrate_builder_pattern()

Javascript

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
// JavaScript Builder Pattern - HTTP Client 구현

// 열거형 정의
const HttpMethod = Object.freeze({
    GET: 'GET',
    POST: 'POST',
    PUT: 'PUT',
    DELETE: 'DELETE',
    PATCH: 'PATCH'
});

const ContentType = Object.freeze({
    JSON: 'application/json',
    FORM: 'application/x-www-form-urlencoded',
    XML: 'application/xml',
    TEXT: 'text/plain'
});

const AuthType = Object.freeze({
    BASIC: 'basic',
    BEARER: 'bearer',
    API_KEY: 'apikey'
});

// Product: HTTP 요청 객체
class HttpRequest {
    /**
     * HTTP 요청을 나타내는 클래스 - Builder Pattern의 Product
     */
    constructor(builder) {
        // 필수 필드
        this.url = builder.url;
        this.method = builder.method;
        
        // 선택적 필드
        this.headers = { ...builder.headers };
        this.queryParams = { ...builder.queryParams };
        this.body = builder.body;
        this.timeout = builder.timeout;
        this.authentication = builder.authentication ? { ...builder.authentication } : null;
        this.interceptors = [...builder.interceptors];
        this.retryConfig = builder.retryConfig ? { ...builder.retryConfig } : null;
        
        // 생성 시점에 유효성 검증
        this._validate();
        
        // 불변성 보장
        Object.freeze(this.headers);
        Object.freeze(this.queryParams);
        Object.freeze(this.authentication);
        Object.freeze(this.interceptors);
        Object.freeze(this.retryConfig);
        Object.freeze(this);
    }
    
    _validate() {
        if (!this.url) {
            throw new Error('URL은 필수 항목입니다.');
        }
        
        if (!this.url.startsWith('http://') && !this.url.startsWith('https://')) {
            throw new Error('유효한 HTTP URL이 아닙니다.');
        }
        
        if (!Object.values(HttpMethod).includes(this.method)) {
            throw new Error(`지원하지 않는 HTTP 메서드: ${this.method}`);
        }
        
        // POST, PUT, PATCH 메서드에서 body가 있는 경우 Content-Type 검증
        if (['POST', 'PUT', 'PATCH'].includes(this.method) && 
            this.body && 
            !this.headers['Content-Type']) {
            console.warn('Body가 있는 요청에는 Content-Type 헤더를 설정하는 것이 좋습니다.');
        }
    }
    
    /**
     * 실제 HTTP 요청 실행 (시뮬레이션)
     */
    async execute() {
        console.log(`Executing ${this.method} request to ${this.url}`);
        
        // 인터셉터 실행 (요청 전)
        let processedRequest = this;
        for (const interceptor of this.interceptors) {
            if (interceptor.onRequest) {
                processedRequest = await interceptor.onRequest(processedRequest);
            }
        }
        
        try {
            // 실제로는 fetch API 등을 사용
            const response = await this._simulateHttpCall(processedRequest);
            
            // 인터셉터 실행 (응답 후)
            let processedResponse = response;
            for (const interceptor of this.interceptors) {
                if (interceptor.onResponse) {
                    processedResponse = await interceptor.onResponse(processedResponse);
                }
            }
            
            return processedResponse;
        } catch (error) {
            // 재시도 로직
            if (this.retryConfig && this.retryConfig.maxRetries > 0) {
                return this._retryRequest(error, 0);
            }
            throw error;
        }
    }
    
    async _simulateHttpCall(request) {
        // HTTP 호출 시뮬레이션
        await new Promise(resolve => setTimeout(resolve, 100));
        
        return {
            status: 200,
            statusText: 'OK',
            data: {
                message: 'Success',
                request: {
                    url: request.url,
                    method: request.method,
                    headers: request.headers
                }
            }
        };
    }
    
    async _retryRequest(error, attempt) {
        if (attempt >= this.retryConfig.maxRetries) {
            throw error;
        }
        
        const delay = this.retryConfig.delay * Math.pow(2, attempt); // 지수 백오프
        console.log(`Retrying request in ${delay}ms (attempt ${attempt + 1}/${this.retryConfig.maxRetries})`);
        
        await new Promise(resolve => setTimeout(resolve, delay));
        
        try {
            return await this._simulateHttpCall(this);
        } catch (retryError) {
            return this._retryRequest(retryError, attempt + 1);
        }
    }
    
    toString() {
        return `HttpRequest[${this.method}] ${this.url}`;
    }
}

// Builder 클래스들
class HttpRequestBuilder {
    /**
     * HTTP 요청 빌더 - Builder Pattern의 ConcreteBuilder
     */
    constructor(url) {
        // 필수 필드
        this.url = url;
        this.method = HttpMethod.GET;
        
        // 선택적 필드들 초기화
        this.headers = {};
        this.queryParams = {};
        this.body = null;
        this.timeout = 30000; // 30초 기본값
        this.authentication = null;
        this.interceptors = [];
        this.retryConfig = null;
    }
    
    // HTTP 메서드 설정
    get() {
        this.method = HttpMethod.GET;
        return this;
    }
    
    post() {
        this.method = HttpMethod.POST;
        return this;
    }
    
    put() {
        this.method = HttpMethod.PUT;
        return this;
    }
    
    delete() {
        this.method = HttpMethod.DELETE;
        return this;
    }
    
    patch() {
        this.method = HttpMethod.PATCH;
        return this;
    }
    
    // 헤더 설정
    header(key, value) {
        this.headers[key] = value;
        return this;
    }
    
    headers(headerObject) {
        Object.assign(this.headers, headerObject);
        return this;
    }
    
    contentType(type) {
        this.headers['Content-Type'] = type;
        return this;
    }
    
    accept(type) {
        this.headers['Accept'] = type;
        return this;
    }
    
    // 쿼리 파라미터 설정
    queryParam(key, value) {
        this.queryParams[key] = value;
        return this;
    }
    
    queryParams(paramObject) {
        Object.assign(this.queryParams, paramObject);
        return this;
    }
    
    // 요청 본문 설정
    body(data) {
        this.body = data;
        return this;
    }
    
    json(data) {
        this.body = JSON.stringify(data);
        this.contentType(ContentType.JSON);
        return this;
    }
    
    form(data) {
        if (data instanceof FormData) {
            this.body = data;
            // FormData인 경우 브라우저가 Content-Type을 자동 설정
        } else {
            this.body = new URLSearchParams(data).toString();
            this.contentType(ContentType.FORM);
        }
        return this;
    }
    
    // 인증 설정
    basicAuth(username, password) {
        const credentials = btoa(`${username}:${password}`);
        this.authentication = {
            type: AuthType.BASIC,
            credentials: credentials
        };
        this.header('Authorization', `Basic ${credentials}`);
        return this;
    }
    
    bearerToken(token) {
        this.authentication = {
            type: AuthType.BEARER,
            token: token
        };
        this.header('Authorization', `Bearer ${token}`);
        return this;
    }
    
    apiKey(key, value, location = 'header') {
        this.authentication = {
            type: AuthType.API_KEY,
            key: key,
            value: value,
            location: location
        };
        
        if (location === 'header') {
            this.header(key, value);
        } else if (location === 'query') {
            this.queryParam(key, value);
        }
        return this;
    }
    
    // 타임아웃 설정
    timeout(milliseconds) {
        this.timeout = milliseconds;
        return this;
    }
    
    // 인터셉터 추가
    interceptor(interceptorFn) {
        this.interceptors.push(interceptorFn);
        return this;
    }
    
    // 재시도 설정
    retry(maxRetries, delay = 1000) {
        this.retryConfig = {
            maxRetries: maxRetries,
            delay: delay
        };
        return this;
    }
    
    // 최종 객체 생성
    build() {
        return new HttpRequest(this);
    }
}

// 특화된 빌더들
class GetRequestBuilder extends HttpRequestBuilder {
    constructor(url) {
        super(url);
        this.method = HttpMethod.GET;
    }
    
    // GET 요청에서는 body 설정 메서드들을 제한
    body() {
        throw new Error('GET 요청에서는 body를 설정할 수 없습니다.');
    }
    
    json() {
        throw new Error('GET 요청에서는 JSON body를 설정할 수 없습니다.');
    }
    
    form() {
        throw new Error('GET 요청에서는 form body를 설정할 수 없습니다.');
    }
}

class PostRequestBuilder extends HttpRequestBuilder {
    constructor(url) {
        super(url);
        this.method = HttpMethod.POST;
    }
}

// Director 클래스 - 공통 패턴들
class HttpClientDirector {
    /**
     * HTTP 클라이언트 디렉터 - 일반적인 요청 패턴 제공
     */
    
    static jsonApiRequest(baseUrl, endpoint) {
        return new HttpRequestBuilder(`${baseUrl}${endpoint}`)
            .contentType(ContentType.JSON)
            .accept(ContentType.JSON)
            .timeout(10000);
    }
    
    static restApiRequest(baseUrl, endpoint, apiKey) {
        return this.jsonApiRequest(baseUrl, endpoint)
            .apiKey('X-API-Key', apiKey, 'header')
            .retry(3, 1000);
    }
    
    static authApiRequest(baseUrl, endpoint, token) {
        return this.jsonApiRequest(baseUrl, endpoint)
            .bearerToken(token)
            .interceptor({
                onRequest: async (request) => {
                    console.log(`[Auth API] Sending request to ${request.url}`);
                    return request;
                },
                onResponse: async (response) => {
                    if (response.status === 401) {
                        console.warn('[Auth API] Authentication failed');
                    }
                    return response;
                }
            });
    }
}

// 팩토리 함수들 (편의성 제공)
class HttpClient {
    /**
     * HTTP 클라이언트 팩토리 - 편의 메서드 제공
     */
    
    static request(url) {
        return new HttpRequestBuilder(url);
    }
    
    static get(url) {
        return new GetRequestBuilder(url);
    }
    
    static post(url) {
        return new PostRequestBuilder(url);
    }
    
    static put(url) {
        return new HttpRequestBuilder(url).put();
    }
    
    static delete(url) {
        return new HttpRequestBuilder(url).delete();
    }
    
    static patch(url) {
        return new HttpRequestBuilder(url).patch();
    }
}

// 사용 예시
async function demonstrateBuilderPattern() {
    console.log('=== JavaScript Builder Pattern HTTP Client 예시 ===\n');
    
    try {
        // 1. 기본 GET 요청
        console.log('1. 기본 GET 요청:');
        const getRequest = HttpClient.get('https://api.example.com/users')
            .queryParam('page', 1)
            .queryParam('limit', 10)
            .accept(ContentType.JSON)
            .timeout(5000)
            .build();
        
        console.log(getRequest.toString());
        const getResponse = await getRequest.execute();
        console.log('응답:', getResponse.status, getResponse.statusText);
        console.log();
        
        // 2. 인증이 포함된 POST 요청
        console.log('2. 인증이 포함된 POST 요청:');
        const postRequest = HttpClient.post('https://api.example.com/users')
            .bearerToken('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...')
            .json({
                name: 'John Doe',
                email: 'john@example.com',
                role: 'user'
            })
            .retry(2, 500)
            .interceptor({
                onRequest: async (request) => {
                    console.log('요청 전 인터셉터: 로깅 추가');
                    return request;
                }
            })
            .build();
        
        console.log(postRequest.toString());
        const postResponse = await postRequest.execute();
        console.log('응답:', postResponse.status, postResponse.statusText);
        console.log();
        
        // 3. Director를 사용한 API 요청
        console.log('3. Director를 사용한 REST API 요청:');
        const apiRequest = HttpClientDirector
            .restApiRequest('https://api.service.com', '/v1/data', 'api-key-123')
            .get()
            .queryParams({
                category: 'technology',
                sort: 'date'
            })
            .build();
        
        console.log(apiRequest.toString());
        const apiResponse = await apiRequest.execute();
        console.log('응답:', apiResponse.status, apiResponse.statusText);
        console.log();
        
        // 4. 복잡한 폼 데이터 요청
        console.log('4. 복잡한 폼 데이터 요청:');
        const formData = new FormData();
        formData.append('title', 'New Article');
        formData.append('content', 'Article content here...');
        formData.append('file', new Blob(['file content'], { type: 'text/plain' }));
        
        const formRequest = HttpClient.post('https://api.example.com/articles')
            .basicAuth('admin', 'password')
            .form(formData)
            .timeout(30000)
            .build();
        
        console.log(formRequest.toString());
        console.log('폼 데이터 포함된 요청 생성 완료');
        console.log();
        
        // 5. 체이닝과 조건부 설정
        console.log('5. 조건부 설정을 포함한 요청:');
        const isDevelopment = true;
        const conditionalRequest = HttpClient.request('https://api.example.com/config')
            .get()
            .contentType(ContentType.JSON);
        
        // 조건부 설정
        if (isDevelopment) {
            conditionalRequest
                .queryParam('debug', 'true')
                .timeout(60000);
        } else {
            conditionalRequest
                .retry(5, 2000)
                .timeout(10000);
        }
        
        const finalRequest = conditionalRequest.build();
        console.log(finalRequest.toString());
        console.log('조건부 설정 적용 완료');
        
    } catch (error) {
        console.error('오류 발생:', error.message);
    }
}

// 빌더 패턴의 유연성 시연
function demonstrateFlexibility() {
    console.log('\n=== Builder Pattern 유연성 시연 ===\n');
    
    // 동일한 기본 설정에서 다양한 요청 생성
    const baseBuilder = HttpClient.request('https://api.example.com')
        .bearerToken('token-123')
        .timeout(15000)
        .retry(2, 1000);
    
    // 1. 사용자 목록 조회
    const getUsersRequest = baseBuilder
        .get()
        .queryParam('endpoint', '/users')
        .accept(ContentType.JSON)
        .build();
    
    // 2. 새 사용자 생성
    const createUserRequest = HttpClient.request('https://api.example.com')
        .bearerToken('token-123')
        .timeout(15000)
        .retry(2, 1000)
        .post()
        .json({
            name: 'New User',
            email: 'new@example.com'
        })
        .build();
    
    // 3. 사용자 정보 업데이트
    const updateUserRequest = HttpClient.request('https://api.example.com')
        .bearerToken('token-123')
        .timeout(15000)
        .retry(2, 1000)
        .put()
        .json({
            id: 123,
            name: 'Updated Name'
        })
        .build();
    
    console.log('다양한 요청 생성:');
    console.log('1.', getUsersRequest.toString());
    console.log('2.', createUserRequest.toString());
    console.log('3.', updateUserRequest.toString());
}

// 실행
if (typeof window === 'undefined') {
    // Node.js 환경
    demonstrateBuilderPattern().then(() => {
        demonstrateFlexibility();
    });
} else {
    // 브라우저 환경
    window.demonstrateBuilderPattern = demonstrateBuilderPattern;
    window.demonstrateFlexibility = demonstrateFlexibility;
    window.HttpClient = HttpClient;
    window.HttpClientDirector = HttpClientDirector;
    
    console.log('Builder Pattern HTTP Client가 로드되었습니다.');
    console.log('demonstrateBuilderPattern() 함수를 호출해보세요.');
}

실무에서 효과적으로 적용하기 위한 고려사항 및 주의할 점

카테고리고려 항목설명권장사항
적용 판단 기준객체 복잡도 판단매개변수 수, 조건 분기, 설정 조합이 많을 때에만 적용매개변수 4 개 이상 또는 선택 필드가 많을 경우 사용
간단 객체에의 남용 방지단순한 객체에 사용 시 코드 오버헤드 발생간단한 경우 생성자나 정적 팩토리 메서드로 대체
설계 원칙SRP 및 책임 분리 유지빌더가 생성 외 기능 (유효성, 상태 저장 등) 을 갖게 되면 단일 책임 원칙 위배생성, 상태 관리, 검증 책임은 각각 분리
인터페이스 일관성빌더 체이닝/API 사용 방식이 프로젝트마다 다르면 혼란 유발코딩 컨벤션 및 빌더 설계 가이드 마련
클래스 수 증가 관리Product, Builder, Director 등 클래스가 증가할 수 있음Lombok @Builder, 코드 생성기 활용
객체 일관성 및 유효성필수/선택 필드 명확 구분필수 필드 누락 시 잘못된 객체 생성 가능성 있음생성자에 필수값, 체이닝에 선택값 배치
build() 시 유효성 검증중간 상태에서의 불완전 객체 방지 필요build() 에서 모든 필드 조합 검증 또는 유효성 로직 포함
불변성 보장생성된 객체가 변경되면 안정성 및 예측성 저하모든 필드 final, setter 제거, Immutable 객체 반환
테스트 및 유지보수테스트 데이터 생성테스트 시 다양한 상태의 객체 필요Test Data Builder 패턴 활용, 단위 테스트 분리
Product 와 Builder 간 구조 동기화Product 구조 변경 시 빌더도 함께 수정 필요자동화 툴 사용 또는 Product 기반 코드 생성 적용
변경 영향도 관리빌더 인터페이스 변경 시 다른 코드에 미치는 영향 존재하위 호환성 고려 설계, 인터페이스 분리 적용
성능 최적화메모리 사용량Builder 는 일시적인 객체로 GC 부담 증가 가능풀링, 재사용 가능 구조 설계 또는 빌더 리셋 메서드 구현
체이닝 오버헤드/지연 생성불필요한 객체 생성이나 호출 순서 오류 발생 가능Lazy Initialization, 체이닝 최적화
클래스 동기화/성능 이슈멀티스레드 환경에서 공유 시 안전성 문제Thread-Safe Builder 혹은 복사 전략, ThreadLocal 적용
문서화 및 가독성API 사용법 명시체이닝 순서나 필수 메서드 등 문서가 부족할 경우 오용 위험IDE 인텔리센스 지원 + JavaDoc/TSDoc 주석 활용
가독성 향상체이닝이 지나치게 길거나 복잡할 경우 가독성 저하명확한 메서드 명명, 단계별 메서드 분리, Fluent API 설계
팀 적용 관점교육 및 패턴 숙지 필요개발자마다 빌더 사용 방식이 상이할 수 있음팀 가이드라인 수립, 내부 교육 또는 코드 리뷰 강화
팀 표준화/자동화 적용공통 빌더 템플릿 또는 도구 활용 필요Lombok, Kotlin DSL, 코드 생성기 도입 검토

6.23 테스트 전략

단위 테스트 접근법:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# Builder Pattern 테스트 예시
import unittest

class TestHttpRequestBuilder(unittest.TestCase):
    
    def test_basic_request_creation(self):
        """기본 요청 생성 테스트"""
        request = HttpClient.get("https://api.example.com")
        self.assertEqual(request.url, "https://api.example.com")
        self.assertEqual(request.method, "GET")
    
    def test_fluent_interface(self):
        """플루언트 인터페이스 테스트"""
        request = (HttpClient.post("https://api.example.com")
                   .header("Content-Type", "application/json")
                   .timeout(5000)
                   .build())
        
        self.assertEqual(request.headers["Content-Type"], "application/json")
        self.assertEqual(request.timeout, 5000)
    
    def test_validation_rules(self):
        """유효성 검증 테스트"""
        with self.assertRaises(ValueError):
            HttpClient.get("").build()  # 빈 URL
        
        with self.assertRaises(ValueError):
            HttpClient.get("invalid-url").build()  # 잘못된 URL
    
    def test_immutability(self):
        """불변성 테스트"""
        request = HttpClient.get("https://api.example.com").build()
        original_headers = request.headers.copy()
        
        # 헤더 수정 시도 (실패해야 함)
        with self.assertRaises(TypeError):
            request.headers["New-Header"] = "value"

6.24 리팩토링 전략

기존 생성자를 Builder 로 리팩토링:

  1. 1 단계: 기존 생성자 유지하면서 Builder 추가
  2. 2 단계: 클라이언트 코드를 점진적으로 Builder 로 전환
  3. 3 단계: 기존 생성자를 Deprecated 처리
  4. 4 단계: 기존 생성자 제거 및 Builder 전용화

리팩토링 체크리스트:

6.25 활용 시 흔한 실수

주요 실수 유형:

  1. 빌더 상태 오염

    • 문제: 빌더 인스턴스 재사용 시 이전 상태 잔존
    • 해결: 빌더 리셋 메서드 구현 또는 새 인스턴스 생성
  2. 불완전한 유효성 검증

    • 문제: 빌드 시점이 아닌 사용 시점에 오류 발견
    • 해결: build() 메서드에서 완전한 검증 수행
  3. 과도한 빌더 적용

    • 문제: 단순한 객체에도 빌더 패턴 적용
    • 해결: 복잡도 기준 수립 (매개변수 4 개 이상)
  4. 메모리 누수

    • 문제: 빌더에서 참조하는 대용량 객체 미해제
    • 해결: 빌드 완료 후 참조 정리

최적화하기 위한 고려사항 및 주의점

카테고리최적화 항목설명권장사항
성능객체 생성 비용Builder 및 Product 객체 생성이 반복되면 비용 증가팩토리 래핑, 캐싱, 불필요한 생성 최소화
메서드 체이닝 오버헤드체이닝 방식이 성능에 영향을 줄 수 있음인라인 최적화, 체이닝 최소화, 컴파일러 최적화 신뢰
빌드 결과 캐싱동일 빌드 입력에 대해 반복 생성 방지결과 객체 캐싱 및 Flyweight 적용 가능
메모리 최적화빌더 객체 일시적 생성GC 에 부담이 될 수 있는 임시 객체 다수 생성사용 후 참조 해제, reset 메서드 구현, 객체 풀 적용
불필요한 중간 객체빌드 과정에서 불필요한 필드/객체 생성 발생 가능Lazy Initialization, Copy-on-Write, 중간 값 생략 전략 적용
빌더 재사용성Stateless 하지 않으면 오작동 위험 있음상태 없는 빌더로 구현하거나 풀링 전략 사용
타입 안전성체이닝 순서 의존성필드 설정 순서가 잘못되면 런타임 오류 가능Step Builder, 필수 필드 강제, @NonNull, IDE 타입 힌트 사용
유효성 및 일관성build() 내부 검증 비용유효성 로직이 복잡할수록 build() 호출 비용 증가필수/선택 필드 구분, 유효성 최소화, 사전 검증 단계 분리
객체 상태 일관성build() 전 객체 상태 불완전 가능성중간 상태 노출 방지, 불변 객체 반환, clone 기반 재생성 전략
코드 품질중복 빌더 구현유사 객체마다 빌더가 중복 구현됨추상 빌더 계층 도입, 공통 빌더 추상화 구조 사용
확장성플러그인 기반 확장기능 확장 시 빌더 구조 확장이 어렵거나 깨지기 쉬움확장 포인트 인터페이스 도입, 인터셉터/Decorator 적용 가능
테스트테스트 친화성복잡한 빌더는 테스트하기 어려움테스트 전용 빌더 제공, 빌더 상태 추적 기능 추가
배포/툴 통합코드 생성 도구 활용반복적인 빌더 구현은 유지보수에 부담됨Lombok @Builder, Kotlin DSL, Annotation Processor 활용
불변성 보장상태 변경 허용 시 일관성 문제불변성이 없으면 사이드 이펙트 발생 가능build() 결과는 Immutable Object 로 구성, 컬렉션은 불변 컬렉션으로 처리

주제와 관련하여 주목할 내용

카테고리주제항목설명
설계 패턴 이해Builder Pattern객체 구성생성 과정을 단계별로 분리하여 복잡한 객체 생성 지원
유연성동일한 생성 절차로 다양한 객체 표현 가능
재사용성생성 로직 재사용으로 코드 중복 감소
인터페이스 설계단계별 메서드를 명확히 정의하여 명확한 계약 제공
Director객체 생성 과정을 조정하는 관리자 역할
ConcreteBuilder실제 객체 생성 로직 구현 클래스
불변성/타입 안전성Immutable Object빌더를 통한 불변 객체 생성 가능
Phantom Types타입 시스템을 활용한 단계 안전 보장 (컴파일 타임 검증)
프레임워크 활용Spring FrameworkSpring Security HttpSecurity Builder보안 설정을 빌더 방식으로 구성
Spring BootAuto Configuration Builder자동 설정 시 빌더 스타일의 구성 적용
Spring CoreBeanDefinitionBuilderXML 없이 자바 코드로 Bean 구성
언어/도구 기반 지원Lombok@Builder 애노테이션컴파일 타임에 빌더 코드를 자동 생성
KotlinNamed Parameters + DSL빌더 없이도 선언형 방식으로 객체 구성 가능
GradleBuild Script DSL선언형 DSL 스타일로 빌드 스크립트 구성
웹/클라이언트 개발HTTP ClientOkHttp, RetrofitREST API 요청 구성을 위한 빌더 구조 활용
React (JSX)Component Builder컴포넌트 구성 시 빌더 스타일 패턴 적용
데이터 처리/DBQuery BuilderJPA Criteria API동적 쿼리를 위한 빌더 기반 DSL 구성
JSON/XML Builder문서 생성 DSLJSON, XML 구조 생성을 위한 빌더 패턴 적용
테스트 전략Test Data Builder테스트 객체 구성 자동화테스트 전용 객체 생성 시 코드 간결화와 재사용성 확보
도메인 모델링DDD BuilderAggregate Builder복잡한 애그리게이트 객체 생성 시 빌더 활용
함수형/반응형 프로그래밍Functional Builder함수형 빌더 구현람다와 클로저 기반 선언형 빌더 구조 구현
Reactive Builder비동기 빌더RxJava, WebFlux 등과 결합하여 반응형 흐름에 적합한 빌더 적용
성능 최적화Zero-Copy Builder메모리 복사 제거메모리 오버헤드 없이 객체 구성
Builder Pool Pattern빌더 객체 재사용풀링을 통한 메모리 및 생성 비용 절감
검증/안전성Validation Builderbuild 시 검증객체 생성 전 유효성 검증 내장 가능
패턴 간 비교/조합Abstract Factory vs Builder생성 방식 비교제품군 vs 복잡 조립 차이 구분
Prototype vs Builder복제 vs 조립생성 전략의 목적 차이에 따른 적합성 판단
Builder + Prototype복사 + 조립 조합공통 기본 틀 복사 후 빌더로 커스터마이징

추가 학습 내용

카테고리주제설명
기본 개념Builder Pattern (GoF)객체 생성 과정을 단계별로 분리하여 다양한 표현을 가능하게 하는 생성 패턴
Director생성 과정을 조정하여 일관된 절차로 제품을 생성하는 역할
ConcreteBuilder실제 생성 로직을 정의하는 구체적인 빌더 구현체
Product빌더가 생성하는 최종 결과 객체
고급 구조Step Builder Pattern생성 순서를 타입 시스템으로 강제하여 타입 안전성을 확보하는 빌더 구조
내부/외부 Builder내부 클래스 또는 외부 클래스로 빌더를 구현하는 방식 차이
Fluent Interface메서드 체이닝을 통해 선언적이고 가독성 높은 API 를 설계하는 방식
Method Chaining객체 생성 과정을 연속된 메서드 호출로 연결하는 구현 기법
성능 최적화Builder Pool PatternBuilder 인스턴스를 풀링하여 재사용함으로써 객체 생성을 최적화
Memory-Efficient Builder불필요한 객체 생성을 줄이고 메모리 효율을 높이기 위한 전략
함수형 접근Functional Builder람다 표현식, 클로저 등을 활용한 선언적 빌더 구현 방식
동시성Thread-Safe Builder멀티스레드 환경에서도 안전하게 사용할 수 있도록 동기화된 빌더 설계
테스트 전략Test Data Builder테스트에 필요한 객체 생성을 자동화하고 반복 코드 제거
Fixture Builder테스트 고정 데이터 (Fixture) 를 빌더로 구성하는 방식
도메인 모델링Aggregate BuilderDDD 에서 복잡한 애그리게이트 객체 생성을 담당하는 빌더 구조
설정/DTO 빌더설정 파일, DTO 객체 등의 생성 시 빌더 패턴을 활용한 실무 예제
API 설계Fluent API자연어에 가까운 표현으로 직관적인 API 를 설계하는 기법
생성 패턴 비교Factory vs Builder제품군 생성 (Factory) vs 복잡한 객체 조립 (Builder) 의 책임 분리 비교
Prototype vs Builder객체 복제 중심 (Prototype) 과 조립 중심 (Builder) 의 차이 비교
불변 객체 설계Immutable Object + Builder생성 후 상태가 바뀌지 않는 객체의 안전한 빌더 조합 방식
프레임워크/자동화 도구Lombok, Kotlin DSLJava/Kotlin 에서 빌더 자동 생성 및 선언형 DSL 스타일 빌더 구현 방법
코드 생성기빌더 코드 생성을 자동화하는 도구 사용법 (예: Swagger Codegen, Lombok 등)
DSL 및 구성 도구SQL DSL (JOOQ, QueryDSL)빌더 기반 DSL 로 SQL 쿼리를 안전하고 선언적으로 생성하는 도구
JSON/XML BuilderJSON, XML 등 문서 구조 생성을 위한 빌더 기반 파서 설계
UI Builder (React, Flutter)선언형 UI 프레임워크에서의 빌더 패턴 활용 방식
소프트웨어 원칙SRP (단일 책임 원칙)빌더 클래스는 객체 생성 책임만 가지도록 구성
설계/품질 원칙Clean Code빌더 패턴을 활용한 가독성 높은 설계와 유지보수 중심 개발 철학 적용
성능/메모리 관리Object Pooling빌더/생성 객체 재사용을 통한 메모리 오버헤드 최소화
학습 필수 요소GoF 23 패턴전체적인 디자인 패턴 체계 내에서 Builder 의 위치 이해
SOLID빌더 패턴의 구조적 설계를 위한 객체지향 원칙 기반 분석
아키텍처 전략CQRS + BuilderCommand 객체 생성을 위한 빌더 패턴 결합 전략
Domain-Driven Design복잡한 도메인 모델에서 빌더를 활용한 객체 생성 전략

용어 정리

카테고리용어설명
패턴 구성요소Builder객체 생성 단계 (메서드) 를 정의하는 추상 인터페이스 또는 베이스 클래스
ConcreteBuilderBuilder 를 구현하며 실제 Product 를 생성하는 클래스
DirectorBuilder 의 메서드를 호출하여 객체 생성 순서를 제어하는 관리자 역할 클래스 (선택적)
Product빌더 패턴을 통해 최종적으로 생성되는 복잡한 객체
설계 기법Fluent Interface메서드 체이닝을 통해 선언적·가독성 높은 API 를 구성하는 인터페이스 스타일
Method Chaining각 메서드가 자기 자신을 반환하여 연속적으로 호출 가능하게 하는 기법
Step Builder각 단계마다 다른 인터페이스를 반환하여 빌드 순서를 강제하고 컴파일 타임 검증 제공
프로그래밍 개념Immutable Object한 번 생성된 후 내부 상태가 변경되지 않는 객체로, 안정성과 예측 가능성 제공
Telescoping Constructor생성자 매개변수가 점점 늘어나는 안티패턴으로, 빌더 패턴이 이를 해결
최적화 기법Object Pooling생성된 객체를 재사용하여 메모리 및 성능을 최적화하는 기법

참고 및 출처