Factory Method 패턴: 생성 책임을 서브클래스에 위임하는 이유

Factory Method 패턴의 참여자 구조와 확장 방식, Java 구현 예제, Simple Factory·Abstract Factory와의 선택 기준을 정리한다.

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

새로운 알림 채널 하나를 추가하는데 기존 분기문을 전부 손봐야 한다면, 생성 로직이 잘못된 곳에 있다는 신호다. Factory Method 패턴은 객체 생성 로직을 서브클래스에 위임해 확장성과 결합도 감소를 동시에 달성한다. 제품 계층 구조와 생성자 계층 구조를 분리해, 변경에는 닫히고 확장에는 열린 구조를 지향한다.

생성 책임을 서브클래스로 넘긴다는 것

핵심 의도는 객체 생성 책임을 상위 Creator가 아니라 하위 ConcreteCreator에 위임하는 것이다. Product 인터페이스를 기준으로 클라이언트와 구현체 사이의 결합을 최소화하며, OCP(개방-폐쇄 원칙)와 DIP(의존성 역전 원칙)를 구현하는 수단으로 쓰인다.

참여자 구조

Product는 생성 대상의 추상 인터페이스를 정의하고, ConcreteProduct는 그 실제 구현체다. Creator는 factoryMethod()를 선언하고 템플릿 훅을 제공하며, ConcreteCreator가 factoryMethod()를 오버라이드해 구체 인스턴스를 생성한다.

createsProductConcreteProductAConcreteProductBCreator+factoryMethod() : ProductConcreteCreatorA+factoryMethod() : ProductConcreteCreatorB+factoryMethod() : Product

캡슐화와 확장, 결합도까지

클라이언트는 Product 인터페이스에만 의존하고 구체 클래스는 알 필요가 없다. Creator의 템플릿 메서드 내부에는 로그, 모니터링, 트랜잭션 경계 같은 공통 부가 기능을 삽입할 수 있다. 신규 타입을 추가할 때는 ConcreteProduct와 ConcreteCreator만 늘리면 되므로 기존 클라이언트 수정이 최소화되고 회귀 리스크가 줄어든다. 인터페이스 기반 대체 가능성 덕분에 단위 테스트에서 목·스텁을 주입하기 쉽고, 생성자를 교체해 환경별 구성을 차등화할 수 있다. 생성 시점, 캐시·풀링, 재시도 정책은 Creator 계층에서 집중 관리하며, 스레드 세이프티를 위한 동기화·락 범위도 여기서 명시적으로 다룬다.

실무에서 자주 쓰는 지점

  • 외부 결제·메시징 게이트웨이 클라이언트 선택에서는 런타임 구성이나 지역·고객군 기준으로 알맞은 ConcreteCreator를 고르고, 장애·지연 시 폴백 Creator로 대체해 가용성을 확보한다.
  • 스토리지 드라이버와 리포지토리 확장에서는 파일, S3, GCS 등 드라이버별 Product 구현을 교체하고, 멀티테넌트 환경에서는 테넌트별 Creator 바인딩으로 분리한다.
  • 플러그인 아키텍처에서는 플러그인별 Product를 등록하고 Creator를 리플렉션이나 서비스 로더로 자동 탐색해, 신규 플러그인을 배포할 때 코어 수정을 최소화한다.

적용 흐름

입력으로는 채널·지역·테넌트 같은 구성 파라미터, 요청 컨텍스트, 폴백 정책이 들어온다. Creator를 선택하고 factoryMethod()를 호출해 Product를 생성하며, 생성이 실패하면 예외를 매핑하고 재시도나 폴백 Creator를 호출한다. 결과는 Product 인터페이스로 반환되어 호출부가 동일한 프로토콜로 처리한다. 캐시나 풀을 사용한다면 락 획득 → 캐시 조회 → 미존재 시 생성 → 캐시 등록 → 락 해제 순서를 따르고, 생성 비용이 높은 객체는 객체 풀과 함께 쓰는 편이 좋다.

실행 가능한 예제

전제조건은 JDK 17 설치다. 파일명은 FactoryMethodDemo.java.

// javac FactoryMethodDemo.java && java FactoryMethodDemo sms
public class FactoryMethodDemo {

    interface Notifier {
        void send(String to, String msg);
    }

    static class EmailNotifier implements Notifier {
        public void send(String to, String msg) {
            System.out.println("[EMAIL] to=" + to + " msg=" + msg);
        }
    }

    static class SmsNotifier implements Notifier {
        public void send(String to, String msg) {
            System.out.println("[SMS] to=" + to + " msg=" + msg);
        }
    }

    static abstract class NotifierCreator {
        public final Notifier newNotifier() {
            // 공통 부가 기능 지점(로그/메트릭/권한 검사 등)
            return create();
        }
        protected abstract Notifier create();
    }

    static class EmailNotifierCreator extends NotifierCreator {
        protected Notifier create() { return new EmailNotifier(); }
    }

    static class SmsNotifierCreator extends NotifierCreator {
        protected Notifier create() { return new SmsNotifier(); }
    }

    public static void main(String[] args) {
        String channel = args.length > 0 ? args[0] : "email";
        NotifierCreator creator = switch (channel) {
            case "sms" -> new SmsNotifierCreator();
            case "email" -> new EmailNotifierCreator();
            default -> {
                System.err.println("Unknown channel: " + channel + " -> fallback=email");
                yield new EmailNotifierCreator();
            }
        };
        Notifier notifier = creator.newNotifier();
        notifier.send("alice@example.com", "Hello Factory Method");
    }
}

컴파일과 실행은 javac FactoryMethodDemo.java 다음 java FactoryMethodDemo email로 한다.

Simple Factory와 Abstract Factory 사이에서

패턴 성능 확장성 일관성 안정성 운영 편의
Simple Factory 분기 기반으로 빠른 경량 생성 기존 팩토리 수정 필요, 보통 수준 생성 로직 중앙집중 일관성 확보 팩토리 단일 장애점 우려 코드 단순, 운영 용이
Factory Method 동적 바인딩 오버헤드 경미 클래스 추가로 비침투적 확장 인터페이스 강제, 구현 교체 용이 테스트 용이, 회귀 리스크 감소 모듈 단위 배포/교체 유연
Abstract Factory 제품군 생성에도 유사 성능 제품군 확장에 매우 유리 제품군 단위 구성 일관성 구성 불일치 예방 효과 초기 설계 복잡도 상승

단순 분기 수준의 요구사항이라면 Simple Factory가 비용 효율적이고, 제품군 단위 일관성이 필요하면 Abstract Factory와 조합하는 편이 낫다.

Creator에 공통 단면을 얹을 때 치르는 값

Creator에 로깅·메트릭·리트라이·서킷브레이커 같은 공통 단면을 삽입하고, 채널→Creator 매핑을 구성으로 외부화하면 재배포 없이 확장할 수 있다. 캐시·풀을 쓸 때는 동기화 범위를 최소화해 락 경합을 줄인다.

다만 클래스 수가 늘어나면 가독성이 떨어질 수 있고, 과도하게 계층화하면 의사결정이 지연되고 성능도 저하될 수 있다.

Factory Method 도입 효과

정량적으로는 신규 타입을 추가할 때 기존 코드 수정 라인 수가 02줄 수준에 그치고, 공통 단면을 템플릿화하면 생성 로직 재사용률이 40% 이상 증가한다. 팩토리 경계에 테스트를 집중하면 회귀 결함률이 2030% 감소한다. 정성적으로는 변경 영향 범위가 지역화되어 예측 가능성이 오르고, 테스트 대체 용이성으로 품질 보증이 강화되며, 모듈화된 배포 전략으로 운영 민첩성이 높아진다.

Factory Method디자인 패턴생성 패턴OCPDIP