생성 패턴, 무엇을 언제 고를까

Factory Method, Abstract Factory, Builder, Prototype, Singleton — 생성 패턴 다섯 가지를 비교하고 상황별 선택 기준을 정리한다.

2026-08-13 · 최초 발행 2025-10-14

new 호출이 코드 곳곳에 흩어져 있으면, 생성 로직 하나를 바꿀 때마다 호출부를 전부 뒤져야 한다. GoF가 정의한 생성(Creational) 패턴은 객체 생성 과정을 캡슐화해 클라이언트와 생성 로직을 분리하는 설계 방식이다. 객체 수명주기 관리, 의존성 역전(DIP) 실현, 테스트 용이성 향상에 기여하며, 마이크로서비스나 클라우드 네이티브, DI 컨테이너 환경에서 객체 구성의 일관성과 변경 내성을 높이는 핵심 기법으로 쓰인다.

생성 패턴이 풀려는 문제

생성 패턴은 조건 분기, 캐싱, 프로토타입 복제, 단계적 조립처럼 다양한 생성 로직을 인터페이스 뒤로 숨기는 데 목적이 있다. 생성 책임을 전담하는 구성요소를 두면 SRP·OCP·DIP 준수가 강화되고, 테스트 대역(mock·fake) 주입이 쉬워지며, 생성 실패나 예외 흐름을 표준화할 수 있다.

생성 책임을 분리했을 때 달라지는 것들

  • 생성 책임의 캡슐화: 생성자 호출, 옵션 파라미터, 검증, 리소스 확보를 전담 모듈로 옮기고, 클라이언트는 인터페이스 기반 생성 진입점에만 의존한다.
  • 다양성과 확장성: 타입 결정, 환경별 구현 교체, 기능 플래그·컨피그 기반 분기가 가능해지고, 신규 제품군을 추가할 때 기존 호출부 수정을 최소화할 수 있다.
  • 수명주기와 리소스 관리: 싱글톤 초기화, 지연 로딩, 풀링·캐시 정책과 결합해 비용을 최적화하고, 생성 실패 시 롤백·정리 절차를 표준화한다.
  • 테스트와 운영 편의성: 팩토리나 빌더 주입으로 테스트 대역을 쉽게 대체하고, 메트릭·로깅·트레이싱을 생성 경계에 집중적으로 삽입할 수 있다.
  • 규칙과 정책의 중앙화: 유효성 검사, 보안 정책, 버전 호환 로직을 생성 경로에 모아두면 정책이 바뀔 때 단일 지점만 수정하면 된다.

생성 패턴 중 무엇을 고를까

  • Factory Method: 상위 클래스가 생성 인터페이스를 정의하고 하위 클래스가 구체 타입 생성을 결정한다. 제품군이 유사하나 생성 세부가 달라지는 경우에 적합하다.
  • Abstract Factory: 관련 제품군(테마, 플랫폼 세트)을 일관되게 생성하는 인터페이스를 제공한다. 제품군 일관성(호환성)을 강제해야 하는 UI·플랫폼 포팅 상황에 유리하다.
  • Builder: 복잡한 객체를 단계적으로 구성하고 최종 조립을 분리한다. 불변 객체, 다수의 선택적 파라미터, 유효성 검사가 필요할 때 적합하다.
  • Prototype: 기존 인스턴스를 복제(clone)해 새 객체를 생성한다. 생성 비용이 크거나 런타임에 구성 복제를 자주 수행해야 할 때 유용하다.
  • Singleton: 애플리케이션 전역에서 단일 인스턴스를 제공한다. 설정, 레지스트리, 캐시 등 공유 상태가 필요한 경우에 한정적으로 사용한다.

패턴별 비교

패턴 성능 확장성 일관성 안정성 운영 편의
Factory Method 중상
Abstract Factory
Builder
Prototype 중하
Singleton 중하(동시성 이슈) 중하(테스트 난이)

생성 흐름의 공통 메커니즘

다섯 패턴은 세부 구현이 다르지만, 클라이언트가 요청하면 팩토리(혹은 빌더)가 검증·정책·로깅을 처리한 뒤 제품을 생성하거나 복제해 반환한다는 흐름은 공통적이다. 타입을 인식하지 못하면 예외를 반환한다.

ProductFactory/BuilderClientProductFactory/BuilderClientalt[타입 인식 성공][미등록 타입]create(type, config)검증/정책/로깅 처리인스턴스 생성 또는 복제Product 반환예외 반환(IllegalArgumentException)

도입 절차

먼저 제품군 범위, 생성 조건, 구성 파라미터 명세서를 작성하고 동시성·메모리·초기화 지연·트랜잭션 경계 같은 비기능 요구를 정의한다. 다음으로 Product 인터페이스와 Factory·Builder 계약을 확정하고, 제품군 일관성이 필요하면 Abstract Factory, 복잡한 조립이면 Builder, 생성 비용이 크면 Prototype, 하위 클래스별 생성 차이면 Factory Method, 제한적 전역 인스턴스면 Singleton을 고른다. 생성 전 검증 → 생성 → 후처리(등록·캐시) → 실패 시 리소스 정리 순서를 확립하고, Singleton은 이중 검사 잠금과 초기화 안전, 불변 상태 우선 적용으로 동시성을 다룬다. 마지막으로 DI 컨테이너에 등록해 프로필·환경별로 바인딩하고, 생성 경로에 로깅·메트릭·트레이싱을 삽입해 실패율과 지연 시간을 계측한다.

Builder로 보는 예제

다섯 패턴 중 조립 단계가 가장 잘 드러나는 Builder로 예를 든다. 환경은 OpenJDK 17, Maven·Gradle 프로젝트, 단위테스트는 JUnit 5를 전제한다.

// 불변 HTTP 요청 객체의 Builder 예시
public final class HttpRequest {
    private final String method;
    private final String url;
    private final int timeoutMs;
    private final Map<String, String> headers;

    private HttpRequest(Builder b) {
        this.method = b.method;
        this.url = b.url;
        this.timeoutMs = b.timeoutMs;
        this.headers = Collections.unmodifiableMap(new HashMap<>(b.headers));
        validate();
    }
    private void validate() {
        if (url == null || url.isBlank()) throw new IllegalArgumentException("url required");
        if (timeoutMs < 0) throw new IllegalArgumentException("timeout must be >= 0");
    }
    public static class Builder {
        private String method = "GET";
        private String url;
        private int timeoutMs = 3000;
        private Map<String, String> headers = new HashMap<>();

        public Builder url(String url) { this.url = url; return this; }
        public Builder method(String method) { this.method = method; return this; }
        public Builder timeout(int ms) { this.timeoutMs = ms; return this; }
        public Builder header(String k, String v) { this.headers.put(k, v); return this; }
        public HttpRequest build() { return new HttpRequest(this); }
    }
}
// 사용
HttpRequest req = new HttpRequest.Builder()
    .url("https://api.example.com/items")
    .method("POST")
    .header("Authorization", "Bearer token")
    .timeout(5000)
    .build();

실무에서 계열을 고르는 방식

  • 멀티 테넌트·멀티 클라우드 드라이버 구성에서는 Abstract Factory로 클라우드 공급자별 스토리지·큐·시크릿 제품군을 일관되게 생성하고, 환경 변수·프로필 기반 바인딩으로 배포 간 교체를 자동화한다.
  • API 클라이언트·SDK 구성에서는 Builder로 인증, 재시도, 타임아웃, 프록시 같은 옵션을 단계적으로 조립하고, 공통 유효성 검사와 표준 헤더 삽입을 빌더 내부 정책으로 중앙화한다.
  • 규칙 엔진이나 템플릿 복제에서는 Prototype으로 룰 템플릿을 복제하고 소수 필드만 수정해 고성능 초기화를 구현한다. 깊은 복사 규칙을 명시해 공유 참조로 인한 부작용을 막는다.
  • 플러그인 아키텍처에서는 Factory Method로 플러그인 등록·발견·생성을 표준화해 신규 플러그인을 무중단으로 추가하고, 실패한 플러그인은 격리한 뒤 생성 실패 리트라이 정책을 적용한다.
  • 설정·레지스트리 싱글톤에서는 읽기 전용 불변 객체와 초기화 안전 보장으로 동시성 이슈를 최소화하고, 테스트 격리를 위해 리셋 훅이나 DI 기반 대체 구현을 둔다.

생성 패턴 도입의 효과

생성 패턴을 도입하면 생성 관련 코드 중복이 3060% 감소하고, 생성 실패율·예외율을 가시화하면 MTTR 단축에 도움이 된다. 변경 범위가 줄어들면서 코드 수정 라인수(Churn)도 2040% 감소한다. 정성적으로는 결합도가 낮아지고 제품군 일관성이 올라가며 테스트 용이성이 향상된다. 운영 관측성과 정책 중앙화 효과로 유지보수성도 좋아진다.

생성 패턴디자인 패턴BuilderFactory MethodSingleton