Factory Method Pattern

Factory Method 패턴은 객체 생성의 책임을 상위 클래스가 아닌 하위 클래스에 위임하여, 클라이언트가 어떤 클래스의 인스턴스를 만들 것인지 모른 채 객체를 생성할 수 있게 한다. 추상 클래스는 인터페이스만 정의하고, 실제 객체 생성을 하위 클래스에서 담당함으로써 확장성과 유연성을 확보한다. GoF 의 23 가지 디자인 패턴 중 가장 널리 사용되는 생성 패턴으로, “Virtual Constructor” 라고도 불린다.

이 패턴은 Creator 와 Product 라는 두 개의 주요 계층구조를 통해 객체 생성 로직을 캡슐화하며, Template Method 패턴과 유사한 구조를 가진다. 프레임워크 설계, UI 컴포넌트 생성, 데이터베이스 연결 관리 등 다양한 영역에서 활용되며, 2025 년 현재 마이크로서비스 아키텍처와 AI/ML 시스템에서 그 중요성이 더욱 부각되고 있다.

특히, 다형성과 DIP(Dependency Inversion Principle) 원칙에 따라 유연하고 유지보수하기 쉬운 아키텍처 설계에 핵심 역할을 수행한다.

핵심 개념

Factory Method Pattern (팩토리 메서드 패턴) 은 객체 생성을 위한 인터페이스를 정의하고, 인스턴스화할 클래스는 서브클래스가 결정하도록 하는 생성 패턴이다.

핵심 구성 요소:

패턴의 본질

의도 (Intent)

객체 생성을 위한 인터페이스를 정의하되, 어떤 클래스의 인스턴스를 생성할지는 서브클래스가 결정하도록 위임한다.

다른 이름 (Also Known As)

동기 (Motivation / Forces)

적용 가능성 (Applicability)

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

핵심 문제:

배경

Factory Method Pattern 은 객체 지향 설계에서 " 어떤 클래스의 인스턴스를 생성할지 " 에 대한 결정을 미루는 필요성에서 탄생했습니다. 직접적인 생성자 호출은 클라이언트 코드와 구체 클래스 간의 강한 결합을 만들어 유지보수성과 확장성을 해칩니다.

목적 및 필요성

주요 목적:

필요성:

주요 기능 및 역할

특징

핵심 원칙

설계 원칙 준수

  1. 단일 책임 원칙 (SRP): 객체 생성 로직을 별도 클래스로 분리
  2. 개방 - 폐쇄 원칙 (OCP): 새로운 제품 타입 추가 시 기존 코드 수정 불필요
  3. 의존 역전 원칙 (DIP): 구체적인 클래스가 아닌 추상화에 의존
  4. 리스코프 치환 원칙 (LSP): 서브클래스가 기본 클래스를 완전히 대체 가능

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

긍정적 결과:

트레이드오프:

관련 패턴과의 연관성

Abstract Factory Pattern 과의 관계:

Builder Pattern 과의 결합:

Singleton Pattern 과의 결합:

현대적 구현 기법

함수형 프로그래밍 접근:

1
2
3
4
5
6
7
8
# 함수형 팩토리 예시
def create_storage_factory(provider: str):
    factories = {
        'aws': lambda config: S3Storage(**config),
        'azure': lambda config: BlobStorage(**config),
        'gcp': lambda config: GCPStorage(**config)
    }
    return factories.get(provider)

의존성 주입과의 결합:

주요 원리 및 작동 방식

classDiagram
    class Creator {
        <<abstract>>
        +operation()
        +factoryMethod() Product
    }
    
    class ConcreteCreatorA {
        +factoryMethod() ConcreteProductA
    }
    
    class ConcreteCreatorB {
        +factoryMethod() ConcreteProductB
    }
    
    class Product {
        <<interface>>
        +doSomething()
    }
    
    class ConcreteProductA {
        +doSomething()
    }
    
    class ConcreteProductB {
        +doSomething()
    }
    
    Creator <|-- ConcreteCreatorA
    Creator <|-- ConcreteCreatorB
    Creator ..> Product : creates
    ConcreteCreatorA ..> ConcreteProductA : creates
    ConcreteCreatorB ..> ConcreteProductB : creates
    Product <|-- ConcreteProductA
    Product <|-- ConcreteProductB

작동 원리:

  1. 클라이언트가 Creator 의 operation() 메서드 호출
  2. operation() 내부에서 factoryMethod() 호출
  3. ConcreteCreator 가 factoryMethod() 구현체 실행
  4. 해당하는 ConcreteProduct 인스턴스 생성 및 반환
  5. 생성된 객체로 비즈니스 로직 수행

구조 및 아키텍처

구성 요소

구분구성요소기능역할특징
필수Creator (생성자 클래스)팩토리 메서드 선언, 제품 사용 로직 정의객체 생성 인터페이스 제공, 비즈니스 로직 캡슐화추상 클래스 또는 인터페이스로 정의
ConcreteCreator (구체적 생성자)특정 제품 생성 책임 수행실제 객체 생성 로직 구현Creator 를 상속하여 팩토리 메서드 오버라이딩
Product (제품 인터페이스)제품 공통 연산 정의클라이언트 코드와 제품 구현 간 결합 방지인터페이스 또는 추상 클래스로 정의
ConcreteProduct (구체적 제품)실제 기능 수행Product 인터페이스의 구체 구현Product 를 구현한 실제 클래스
선택ParameterizedFactoryMethod매개변수에 따라 다양한 제품 생성하나의 팩토리 메서드로 여러 제품 처리조건 분기 또는 enum 기반 생성, 런타임 결정 가능

Creator 의 역할:

ConcreteCreator 의 역할:

Product 계층구조의 역할:

클래스 구조 다이어그램

classDiagram
    class Creator {
      <<abstract>>
      + factoryMethod(): Product
      + someOperation(): string
    }
    class ConcreteCreator {
      + factoryMethod(): Product
    }
    class Product {
      <<interface>>
      + usefulOperation(): string
    }
    class ConcreteProduct1 {
      + usefulOperation(): string
    }
    class ConcreteProduct2 {
      + usefulOperation(): string
    }

    Creator <|-- ConcreteCreator
    Product <|.. ConcreteProduct1
    Product <|.. ConcreteProduct2
    ConcreteCreator --> Product : creates

구현 기법

구현 기법정의 및 설명구성 요소 요약목적/의도장점유의사항 / 단점활용 사례
표준 Factory MethodCreator 추상 클래스에 factoryMethod() 를 선언하고, ConcreteCreator 에서 이를 오버라이딩하여 객체 생성- Creator (추상 클래스)
- ConcreteCreator
- Product / ConcreteProduct
상속 기반으로 타입 안전성과 구조적 확장성 확보- 타입 안전성
- 구조적 명확성
- 재사용 가능
- 클래스 수 증가
- 제품 수만큼 서브클래스 생성 필요
로깅 시스템, 메시지 파서, GUI 위젯 생성 등 복잡한 객체 생성
파라미터화된 FactoryfactoryMethod(param) 에 전달된 매개변수에 따라 서로 다른 ConcreteProduct 를 반환 (조건 분기 기반)- Creator 클래스 하나
- 조건부 생성 로직
- Enum/상수로 타입 정의
하나의 팩토리에서 여러 제품 타입 동적 처리- 코드 중복 제거
- 클래스 수 감소
- 조건 기반 유연성
- SRP 위반 우려
- 분기 조건이 많아지면 유지보수 어려움
문서 타입, 결제 수단, 알림 타입 등 다양한 제품군 처리
등록 기반 Factory제품 생성 함수를 미리 등록한 후 문자열이나 키를 통해 해당 생성자를 조회하여 객체 생성 (클래스 이름 → 생성자 매핑)- Registry(Map)
- 키 (String) - 생성자 (Supplier)
- create(key)
런타임 유연성 확보, 동적 확장 가능- 런타임 제품 등록/제거 가능
- 플러그인 시스템에 유리
- 키 - 생성자 매핑 관리 필요
- 잘못된 키 전달 시 예외 발생
플러그인 시스템, UI 컴포넌트 생성기, DSL 해석기
정적 팩토리 메서드생성자 대신 의미 있는 static 메서드로 객체를 생성하는 방식. 생성자 대신 명시적 메서드명을 사용함- private 생성자
- static create 메서드
- 메서드 오버로딩
의미 명확한 객체 생성, 생성자 제한 및 제어- 네이밍 직관적
- 인스턴스 재사용 가능
- 싱글턴·캐싱과 조합 가능
- 상속 불가
- 인터페이스 기반 확장에는 적합하지 않음
Optional.of(), List.of() 등 Java 객체 생성 API
템플릿 메서드 결합형Creator 의 비즈니스 로직 내에서 factoryMethod() 를 호출하도록 설계. 객체 생성만 팩토리 메서드로 분리됨- Creator: 비즈니스 로직 포함
- factoryMethod() 분리
생성 절차 일부만 유연하게 변경 가능- 핵심 로직은 공통, 생성만 다양화
- 코드 중복 방지
- 전체 객체 흐름이 고정되어야 함
- 커스터마이징이 제한적일 수 있음
문서 생성 워크플로우, 공통 처리 이후 단계적 분기 처리
함수형 팩토리JavaScript, Python 등에서 고차 함수, 클로저, 람다 등을 이용해 객체 생성을 함수로 캡슐화하여 동적으로 처리- 생성자 함수 또는 lambda
- 키 - 함수 맵핑
- create(key)
간결한 표현, 동적 확장- 코드 간결
- 동적 구성 유연
- 고차 함수로 커스터마이징 용이
- 명시적 타입 제한 어려움
- 복잡한 구조에는 부적합
프론트엔드 컴포넌트 팩토리, 파서, 라우터 등
의존성 주입 기반 Factory팩토리를 외부에서 생성하거나 주입받아 사용하는 방식. Factory Method 자체는 고정되어 있고, 실제 생성자는 런타임 시 주입됨- 외부 DI 컨테이너
- Interface 기반 팩토리
- 생성자 또는 Setter 주입
Factory 관리 책임 외부화로 결합도 감소- 테스트 용이성↑
- DI 컨테이너와의 자연스러운 통합
- 외부 DI 환경 필요
- 구조가 과도하게 복잡해질 수 있음
Spring, FastAPI 등 DI 환경의 서비스 팩토리

표준 Factory Method 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
from abc import ABC, abstractmethod

# Product 인터페이스
class Logger(ABC):
    @abstractmethod
    def log(self, message: str):
        pass

# ConcreteProduct 1
class FileLogger(Logger):
    def log(self, message: str):
        print(f"[FileLogger] {message}")

# ConcreteProduct 2
class DatabaseLogger(Logger):
    def log(self, message: str):
        print(f"[DatabaseLogger] {message}")

# Creator (추상 팩토리 클래스)
class LoggerFactory(ABC):
    @abstractmethod
    def create_logger(self) -> Logger:
        pass

# ConcreteCreator 1
class FileLoggerFactory(LoggerFactory):
    def create_logger(self) -> Logger:
        return FileLogger()

# ConcreteCreator 2
class DatabaseLoggerFactory(LoggerFactory):
    def create_logger(self) -> Logger:
        return DatabaseLogger()

# 클라이언트 코드
def log_message(factory: LoggerFactory, msg: str):
    logger = factory.create_logger()
    logger.log(msg)

log_message(FileLoggerFactory(), "파일에 기록")
log_message(DatabaseLoggerFactory(), "DB에 기록")

🎯 특징: 명확한 상속 구조, 제품 추가 시 Creator 서브클래스 확장

매개변수화된 Factory Method (조건 분기 기반)

 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
class Document:
    def render(self):
        raise NotImplementedError()

class PDFDocument(Document):
    def render(self):
        print("Rendering PDF")

class WordDocument(Document):
    def render(self):
        print("Rendering Word")

# 단일 팩토리 클래스: 매개변수 기반 제품 결정
class DocumentFactory:
    def create_document(self, doc_type: str) -> Document:
        if doc_type == "PDF":
            return PDFDocument()
        elif doc_type == "Word":
            return WordDocument()
        else:
            raise ValueError(f"Unknown document type: {doc_type}")

# 클라이언트
factory = DocumentFactory()
doc = factory.create_document("PDF")
doc.render()

🎯 특징: 서브클래스 대신 조건 분기 사용으로 단순화 가능 (단 SRP 위반 우려)

등록 기반 Factory Method (레지스트리 기반, 동적 확장)

 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
from typing import Callable

class Shape:
    def draw(self):
        raise NotImplementedError()

class Circle(Shape):
    def draw(self):
        print("Circle drawn")

class Square(Shape):
    def draw(self):
        print("Square drawn")

# 등록 기반 팩토리
class ShapeFactory:
    def __init__(self):
        self._creators: dict[str, Callable[[], Shape]] = {}

    def register(self, shape_type: str, creator: Callable[[], Shape]):
        self._creators[shape_type] = creator

    def create(self, shape_type: str) -> Shape:
        creator = self._creators.get(shape_type)
        if not creator:
            raise ValueError(f"Unknown shape: {shape_type}")
        return creator()

# 팩토리 사용
factory = ShapeFactory()
factory.register("circle", Circle)
factory.register("square", Square)

shape = factory.create("square")
shape.draw()

🎯 특징: 동적 등록/확장에 적합, 플러그인 아키텍처에 자주 사용

정적 팩토리 메서드 (Static Method 기반)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class Connection:
    def __init__(self, db_type: str):
        self.db_type = db_type

    @staticmethod
    def create_mysql():
        return Connection("MySQL")

    @staticmethod
    def create_postgres():
        return Connection("PostgreSQL")

# 사용
conn1 = Connection.create_mysql()
print(f"Connected to {conn1.db_type}")

🎯 특징: 명확한 생성 메서드명 제공, 생성자 은닉 가능

함수형 팩토리 (고차 함수 기반)

 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
from typing import Callable

class Notification:
    def send(self):
        raise NotImplementedError()

class EmailNotification(Notification):
    def send(self):
        print("Sending Email")

class SMSNotification(Notification):
    def send(self):
        print("Sending SMS")

# 함수형 팩토리 맵
notification_factory: dict[str, Callable[[], Notification]] = {
    "email": EmailNotification,
    "sms": SMSNotification,
}

# 클라이언트
def create_notification(ntype: str) -> Notification:
    return notification_factory[ntype]()

notif = create_notification("email")
notif.send()

🎯 특징: 코드 간결성 우수, 람다나 클로저와 결합 용이

의존성 주입 기반 Factory (DI 컨테이너 사용 가정)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 제품
class Service:
    def execute(self):
        print("Service executed")

# 팩토리 역할 수행 클래스
class ServiceFactory:
    def __init__(self, implementation: Service):
        self._impl = implementation

    def get_service(self) -> Service:
        return self._impl

# 클라이언트 코드: DI 주입 가정
factory = ServiceFactory(Service())
svc = factory.get_service()
svc.execute()

🎯 특징: DI 컨테이너와 통합 용이, 테스트와 유연성 극대화

장점

카테고리항목설명
1. 결합도 감소느슨한 결합 (Loose Coupling)클라이언트가 구체적인 클래스가 아닌 추상 인터페이스에 의존함으로써, 의존성 감소 및 변경에 대한 유연성 확보
DIP 준수Creator 가 구체 Product 가 아닌 추상 Product 에 의존함으로써 Dependency Inversion Principle을 구현
캡슐화 (Encapsulation)객체 생성 로직을 팩토리 내부에 감추어 클라이언트는 구체 구현 세부사항을 몰라도 사용 가능
2. 책임 분리단일 책임 원칙 (SRP)객체 생성 책임과 객체 사용 책임을 분리함으로써 각 클래스의 책임 명확화
객체 생성 캡슐화객체 생성 방식을 클라이언트 코드에서 분리하여 응집도를 높이고 재사용성을 강화
3. 확장성개방 - 폐쇄 원칙 준수 (OCP)새로운 제품 타입을 기존 코드를 변경하지 않고 확장 가능 (새로운 ConcreteProduct 및 Creator 서브클래스만 추가하면 됨)
구조 확장 용이성다양한 제품군/서비스를 추가할 때 기존 로직을 건드리지 않고 Creator 하위 클래스만 확장 가능
4. 테스트성테스트 용이성 (Testability)Mock 팩토리 또는 테스트 전용 팩토리 구성으로 단위 테스트 작성이 쉬움
의존성 주입과 결합 용이생성된 객체를 외부에서 주입 가능해 테스트 더블 삽입이나 의존성 대체가 쉬움
5. 구조적 품질코드 재사용성팩토리 메서드를 여러 클라이언트에서 재사용 가능하며, 동일한 생성 로직을 중앙화하여 관리 가능
구현 유연성 제공다양한 생성 전략 (지연 생성, 캐싱, 객체 풀 등) 을 팩토리 내부에 적용할 수 있어 유연한 객체 생성 방식 구현 가능

단점과 문제점 그리고 해결방안

카테고리항목문제 설명원인영향탐지/진단예방 및 해결 방안
설계 복잡성클래스 수 증가Product, Creator, ConcreteCreator 등 다수 클래스 필요제품 수가 많을수록 클래스 쌍이 반복 생성됨구조 과잉, 진입 장벽 증가코드 리뷰, 클래스 계층 분석Abstract Factory 도입, Registry 패턴, Simple Factory 또는 함수형 팩토리 적용
복잡성 증가단순 객체 생성에도 추상화로 인해 과도한 구조추상화 수준 불균형오히려 가독성 및 생산성 저하설계 복잡도 평가 지표객체 생성 복잡도 평가 후 필요 시 적용, 빌더/템플릿/전략 패턴과 결합
상속 강제성ConcreteCreator 는 Creator 를 반드시 상속해야 함상속 기반 구현 구조의 제한다형성 제약, 테스트 불편구조 정적 분석 도구컴포지션 기반 팩토리 또는 고차 함수 팩토리 적용
유지보수성팩토리 클래스 폭발제품 추가 시마다 Creator/Factory 서브클래스 증가제품 수요 증가, 단일 책임 원칙 준수 미흡패키지 난해, 유지보수 비용 증가클래스 수, 메트릭 측정 도구Template Method, BaseFactory 추출, Registry 기반 팩토리 활용
팩토리 비대화하나의 팩토리에 너무 많은 생성 책임이 집중됨조건 분기 또는 고정 로직 집중화OCP 위반, 유연성 저하코드 복잡도 분석팩토리 분할, Chain of Factory 또는 Abstract Factory 패턴 적용
순환 의존성Creator ↔ Product 의존 관계가 발생하는 경우Product 내부에서 Creator 참조컴파일 에러, 무한 루프 위험의존성 분석 도구의존 방향 재설계, 이벤트 기반 통신, 의존성 주입 (DI) 적용
테스트성테스트 복잡성팩토리 별로 다양한 테스트 시나리오 필요생성 대상 분기 증가, 테스트 환경 구축 어려움테스트 코드 중복, 커버리지 관리 어려움커버리지 분석, 테스트 전략 분석MockFactory, 테스트 전용 팩토리, DI 기반 구성
객체 상태 검증 어려움생성된 객체 내부 상태 확인이 어려움팩토리 은닉 구조로 인해 직접 생성자 접근 제한됨객체 단위 테스트 난이도 상승테스트 더블 사용 유무 분석인터페이스 기반 테스트, 상태 검증 로직 도입
성능 최적화생성 비용 오버헤드생성시마다 객체 초기화 및 연결 구성 필요팩토리 호출이 매번 객체를 새로 생성메모리/시간 오버헤드프로파일링 도구Lazy Initialization, 캐싱, Object Pool 적용
메서드 호출 오버헤드다형성 구조로 인한 간접 호출 누적인터페이스 - 구현체 분리 구조성능 저하, 실시간성 시스템에 불리함호출 경로 트레이싱Hot Path 최적화, 정적 팩토리 (static factory) 적용
구조적 제약제네릭 타입 한계제네릭 언어에서 팩토리 타입 매핑이 복잡런타임 타입 소거 및 제약타입 안정성 저하, 코드 가독성 저하정적 분석 도구, 타입 힌트 활용타입 힌트, Factory Injection, 고차 함수 기반 팩토리 적용
비동기 팩토리 필요JS/Python 등에서 비동기 객체 생성이 필요한 경우async 환경에서 동기 팩토리는 부적절Future/Callback 관리 복잡비동기 호출 경로 정리Async Factory 구성 (async def), 콜백 기반 또는 Promise/Future 기반 구현 적용

도전 과제 및 해결책

카테고리도전 과제설명권장 해결 방안
설계 복잡성클래스 수 증가제품 수에 따라 Creator / Product 클래스가 폭증패키지 정리, Simple Factory, Abstract Factory 패턴과 결합, 코드 생성기 사용
설계 복잡성 증가단순 객체에도 복잡한 구조 유발 가능사전 복잡도 분석, 전략/템플릿/레지스트리 패턴과 결합 고려
OCP 위반 가능성Creator 내부 조건문 도입 시 확장에 취약Creator 서브클래싱 유지, 조건문 최소화
유지보수 및 확장성유사한 팩토리 로직 중복ConcreteFactory 구현부가 반복될 수 있음BaseFactory 추출 또는 Template Method 패턴 적용
런타임 팩토리 선택 어려움컴파일 타임 결정으로 유연성 저하Factory Registry, DI 컨테이너, 설정 기반 매핑
제품 추가 시 변경 전파 위험Interface 구조 설계가 미흡하면 변경 파급SRP, ISP 준수 및 Product 인터페이스 안정성 확보
성능 최적화객체 생성 비용다형성과 동적 객체 생성 오버헤드객체 풀링 (Object Pool), 지연 초기화 (Lazy Init), 캐싱 메커니즘
메서드 호출 성능팩토리 구조로 인한 호출 계층 증가 가능성성능 병목 지점 프로파일링, Static Factory 고려
운영 및 동시성멀티스레드 환경에서 동기화 문제팩토리 호출 중 race condition, 중복 생성 가능성Thread-safe 구현, 불변 객체 생성, ThreadLocal Factory
버전/플랫폼 종속 문제구체 팩토리가 플랫폼 또는 버전에 종속될 가능성공통 인터페이스 유지 및 플랫폼 추상화
테스트 용이성테스트 복잡성 증가팩토리 로직과 Product 동시 테스트 어려움MockFactory, StubFactory, 테스트 전용 Creator 구성
객체 상태 테스트 어려움팩토리에서 생성한 객체의 내부 상태 확인 어려움인터페이스 중심 테스트, 테스트 더블 (Test Double) 활용
언어/플랫폼 특성제네릭 타입 처리 어려움제네릭 언어에서 타입 기반 매핑이 번거로움Factory Injection, 고차 함수 (HOF) 활용
동적 언어의 타입 안정성 부족런타임 오류 가능성이 높음정적 타입 힌트, Mypy 등 정적 분석 도구 활용, 타입 검증 메타 프로그래밍 적용
비동기 팩토리 메서드 필요JS/Python 등에서 비동기 객체 생성을 처리할 필요 있음async factory, 콜백 패턴 또는 Future 기반 구현

분류 기준에 따른 종류 및 유형

분류 기준유형설명적용 시 고려사항
1. 구현 방식추상 클래스 기반Creator 를 추상 클래스로 선언하고 서브클래스가 팩토리 메서드를 구현상속 구조가 명확할 때 적합, 계층 설계 유지 쉬움
인터페이스 기반Creator 를 인터페이스로 정의하고 유연하게 다양한 구현 제공다중 구현 또는 동적 주입이 필요한 경우 유리
클래식 서브클래스 팩토리Creator 의 서브클래스에서 생성 책임 수행 (GoF 원형 구조)단일 책임 원칙 (SRP) 기반의 구조화 가능
고차 함수 팩토리생성 함수를 인자로 전달받아 팩토리 기능 수행함수형 언어, 전략적 팩토리 교체 필요 시 유용
Registry 기반 팩토리생성자나 함수 객체를 Map 등에 등록하여 런타임에 룩업하여 생성확장성 높은 구조, 플러그인 시스템에 적합
2. 생성 방식정적 팩토리 메서드static 메서드로 객체 생성, 주로 of(), getInstance() 형식간단한 객체나 유틸리티 객체 생성에 효과적
인스턴스 메서드 기반팩토리 메서드를 인스턴스 메서드로 구현, 객체 상태를 활용한 생성 로직 포함상태 기반 생성 전략에 적합
3. 생성 시점즉시 생성형팩토리 호출 시 바로 객체 생성가벼운 객체, 빠른 반응성이 필요한 경우
지연 생성형실제 필요 시점까지 객체 생성을 미룸 (Lazy Initialization)메모리 최적화, 무거운 객체에 적합
4. 매개변수 사용매개변수형 팩토리전달된 인자를 기반으로 생성할 제품 타입을 결정 (switch-case, enum, key)조건 분기 통제 필요, 테스트 용이성 확보
타입별 전용 팩토리각 제품군마다 전용 생성 메서드 또는 Creator 를 분리하여 구성타입 안정성, 명시적 타입 처리 필요 시 유리
5. 등록/확장 방식정적 등록제품과 생성자 매핑이 컴파일 타임에 결정됨타입 안전성과 예측 가능한 구조 보장
동적 등록런타임에 새로운 제품 타입 및 생성자 등록 가능 (플러그인, 설정 기반 팩토리)시스템 확장성/유연성 확보, 설정 또는 리플렉션 필요
Factory Registry타입별 Creator 를 Map<String, Creator> 로 관리하여 실행 시 선택제품군이 많은 경우 유용, 전략 또는 설정 기반 연동 구조 가능
6. 반환 구조단일 객체 반환단일 제품 인스턴스를 생성 후 반환대부분의 팩토리에 해당되는 기본 방식
객체 풀 / 싱글턴 반환동일 객체를 재사용하거나 풀링 방식으로 반환 (Object Pool, Singleton)생성 비용 높은 객체 최적화에 효과적
7. 언어 특성동적 팩토리Python, JavaScript 등 동적 언어 기반, 런타임 생성 및 타입 유연성 활용리플렉션, 메타프로그래밍 활용 가능
정적 팩토리Java, C++ 등 정적 타입 언어 기반, 컴파일 타임 안전성 강조제네릭, 오버로딩, 인터페이스 기반 구조 활용

실무 적용 예시

분야적용 사례구체 예시 / 기술 스택적용 목적 및 효과
프레임워크Bean 및 Handler 생성Spring BeanFactory, HandlerAdapterDI 와 결합하여 유연한 객체 관리 및 요청 처리
데이터베이스DB 커넥션/드라이버 팩토리JDBC DriverManager, Hibernate Dialect FactoryDB 벤더 교체 대응, 커넥션 생명주기 관리
UI/그래픽UI 컴포넌트 및 LookAndFeel 생성Java Swing, JavaFX, Android ViewFactory플랫폼별 UI 추상화 및 커스터마이징 용이
로깅 시스템로거 객체 생성SLF4J, Log4j LoggerFactory다양한 로깅 구현체를 인터페이스 기반으로 유연하게 전환 가능
파싱/입출력파서 객체 생성Jackson, Gson, XML Parser FactoryJSON, XML, CSV 등 포맷에 따라 파서 자동 선택
웹 요청 처리요청 핸들러, 필터 등 동적 생성Spring DispatcherServlet, Servlet FilterFactoryHTTP 요청 타입에 맞는 핸들러 동적 생성
게임 개발게임 내 유닛, 아이템 팩토리UnitFactory, ItemFactory게임 밸런스 조정, 객체 확장성 및 스폰 전략 구성
마이크로서비스서비스 인스턴스 생성 및 연결Service Discovery + Factory동적 서비스 연결, 부하 분산, 장애 격리
로그 수집/분석Logger, Appender 팩토리Logstash, Fluentd 플러그인 팩토리로깅 전략 및 대상 (LogFile, TCP 등) 에 따라 유연하게 생성
파일 처리파일 파서 또는 변환기 생성Apache POI, OpenCSV, Tika파일 확장자별 파서/리더 자동 생성
ORM/DAO 계층DAO 및 세션 팩토리MyBatis, Hibernate SessionFactoryDB 접근 객체의 추상화 및 유연한 관리
테스트Mock, Stub, Spy 객체 생성Mockito, JMock 팩토리테스트 격리 및 시나리오별 테스트 객체 주입
플러그인 시스템팩토리 레지스트리 기반 플러그인 로딩Eclipse ExtensionPoint, Spring SPI런타임 확장성 확보, 구현체 분리 및 캡슐화
다국어/지역화Locale 기반 객체 팩토리java.util.Calendar, ResourceBundle Factory지역 설정에 따른 동적 객체 제공
보안/암호화암호화/권한 객체 생성JWTProvider, CipherFactory정책 기반 보안 객체 및 알고리즘 구현체 선택

활용 사례

사례 1: 멀티 클라우드 환경에서 스토리지 서비스 추상화

시스템 구성:

graph TB
    Client[클라이언트 애플리케이션]

    %% Factory Layer
    subgraph "Factory Layer"
        CSF[CloudStorageFactory]
        AWSF[AWSStorageFactory]
        AzureF[AzureStorageFactory]
        GCPF[GCPStorageFactory]
    end

    %% Product Layer
    subgraph "Product Layer"
        CS[CloudStorage Interface]
        S3[S3Storage]
        Blob[BlobStorage]
        GCS[GCPStorage]
    end

    %% 관계 정의는 subgraph 밖에서
    Client --> CSF
    CSF --> AWSF
    CSF --> AzureF
    CSF --> GCPF

    AWSF --> S3
    AzureF --> Blob
    GCPF --> GCS

    CS --> S3
    CS --> Blob
    CS --> GCS

워크플로우:

  1. 클라이언트가 설정된 클라우드 타입으로 팩토리 요청
  2. 해당 클라우드의 ConcreteFactory 가 스토리지 객체 생성
  3. 생성된 스토리지 객체로 파일 업로드/다운로드 수행
  4. 클라이언트는 구체적인 클라우드 API 를 알 필요 없음

Factory Method 의 역할:

유무에 따른 차이점:

사례 2: 다양한 데이터베이스 (MySQL, Oracle 등) 연결 객체 생성

시스템 구성:

Workflow:

  1. 클라이언트는 DatabaseConnectionFactory 의 createConnection() 호출
  2. Concrete Creator 가 실제 Connection 객체 생성
  3. 클라이언트는 DatabaseConnection 인터페이스로 사용

차이점:

사례 3: GUI 위젯

시스템 구성: GUI 프레임워크에서 Factory Method 이용해 Button 인터페이스 기반으로 플랫폼 별 버튼 생성

구성도:

sequenceDiagram
    Client->>WindowsGUIFactory: createButton()
    WindowsGUIFactory->>WindowsButton: new()
    Client->>WindowsButton: render()

워크플로우:

1
Client → GUIFactory → createButton() → WindowsButton or LinuxButton 생성 → UI 렌더링

Factory 적용 유무 비교

사례 4: 알림 (Notification) 서비스

요구사항:

시스템 구성

1
Client → NotifierFactory → ConcreteFactory → Notifier (Email/Slack/SMS)

Workflow:

  1. Client 는 설정 또는 인자에 따라 Factory 선택
  2. Factory 의 createNotifier() 호출로 구현체 반환
  3. send(message) 메서드로 메시지 전송

코드 구현:

 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
from abc import ABC, abstractmethod

# 1. Product Interface
class Notifier(ABC):
    @abstractmethod
    def send(self, message: str):
        pass

# 2. Concrete Product
class EmailNotifier(Notifier):
    def send(self, message: str):
        print(f"[Email] {message}")

class SlackNotifier(Notifier):
    def send(self, message: str):
        print(f"[Slack] {message}")

class SMSNotifier(Notifier):
    def send(self, message: str):
        print(f"[SMS] {message}")

# 3. Creator (Factory)
class NotifierFactory(ABC):
    @abstractmethod
    def create_notifier(self) -> Notifier:
        pass

# 4. Concrete Factories
class EmailNotifierFactory(NotifierFactory):
    def create_notifier(self) -> Notifier:
        return EmailNotifier()

class SlackNotifierFactory(NotifierFactory):
    def create_notifier(self) -> Notifier:
        return SlackNotifier()

class SMSNotifierFactory(NotifierFactory):
    def create_notifier(self) -> Notifier:
        return SMSNotifier()

# 5. Client 코드
def notify(factory: NotifierFactory, message: str):
    notifier = factory.create_notifier()
    notifier.send(message)

# 사용 예시
if __name__ == "__main__":
    notify(EmailNotifierFactory(), "메일 전송 완료")
    notify(SlackNotifierFactory(), "슬랙 알림 전송")
    notify(SMSNotifierFactory(), "SMS 발송 성공")

사례 5: 다양한 결제 수단 객체 생성

시스템 구성:

Workflow:

  1. 클라이언트는 PaymentFactory 의 인터페이스만 사용
  2. 실제 결제 객체 생성은 하위 팩토리에서 결정
  3. 새로운 결제 수단 추가 시 기존 코드 수정 없이 Factory/Payment 만 추가

역할: 결제 객체 생성 책임 분리, 확장성/유지보수성 강화

1
2
3
[Client] → [PaymentFactory] → [CreditCardFactory] → [CreditCardPayment]
                                   └→ [PaypalFactory] → [PaypalPayment]

코드 예시:

 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
from abc import ABC, abstractmethod

# 1. Product 인터페이스
class Payment(ABC):
    @abstractmethod
    def pay(self, amount: float):
        pass

# 2. Concrete Products
class CreditCardPayment(Payment):
    def pay(self, amount: float):
        print(f"[CreditCard] {amount:f}원을 신용카드로 결제합니다.")

class PaypalPayment(Payment):
    def pay(self, amount: float):
        print(f"[PayPal] {amount:f}원을 PayPal로 결제합니다.")

class CryptoPayment(Payment):
    def pay(self, amount: float):
        print(f"[Crypto] {amount:f}원을 암호화폐로 결제합니다.")

# 3. Creator (Factory 인터페이스)
class PaymentFactory(ABC):
    @abstractmethod
    def create_payment(self) -> Payment:
        pass

# 4. Concrete Creators
class CreditCardFactory(PaymentFactory):
    def create_payment(self) -> Payment:
        return CreditCardPayment()

class PaypalFactory(PaymentFactory):
    def create_payment(self) -> Payment:
        return PaypalPayment()

class CryptoFactory(PaymentFactory):
    def create_payment(self) -> Payment:
        return CryptoPayment()

# 5. 클라이언트
def process_payment(factory: PaymentFactory, amount: float):
    payment = factory.create_payment()
    payment.pay(amount)

# 사용 예시
if __name__ == "__main__":
    process_payment(CreditCardFactory(), 10000)
    process_payment(PaypalFactory(), 20000)
    process_payment(CryptoFactory(), 5000)

구현 예시

Python: Database Connection

 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
from abc import ABC, abstractmethod

# Product
class DatabaseConnection(ABC):
    @abstractmethod
    def connect(self):
        pass

# Concrete Products
class MySQLConnection(DatabaseConnection):
    def connect(self):
        print("MySQL 연결 완료")

class OracleConnection(DatabaseConnection):
    def connect(self):
        print("Oracle 연결 완료")

# Creator
class DatabaseConnectionFactory(ABC):
    @abstractmethod
    def create_connection(self) -> DatabaseConnection:
        pass

# Concrete Creators
class MySQLConnectionFactory(DatabaseConnectionFactory):
    def create_connection(self):
        return MySQLConnection()

class OracleConnectionFactory(DatabaseConnectionFactory):
    def create_connection(self):
        return OracleConnection()

# Client
def client(factory: DatabaseConnectionFactory):
    conn = factory.create_connection()
    conn.connect()

client(MySQLConnectionFactory())
client(OracleConnectionFactory())

Python: Parser

  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
from abc import ABC, abstractmethod
from typing import Dict, Any
import json
import yaml
import xml.etree.ElementTree as ET

# 추상 제품 클래스
class ConfigParser(ABC):
    @abstractmethod
    def parse(self, config_string: str) -> Dict[str, Any]:
        """설정 문자열을 파싱하여 딕셔너리로 반환합니다."""
        pass
    
    @abstractmethod
    def get_format(self) -> str:
        """파서의 형식을 반환합니다."""
        pass

# 구체적인 제품 클래스들
class JSONConfigParser(ConfigParser):
    def parse(self, config_string: str) -> Dict[str, Any]:
        try:
            return json.loads(config_string)
        except json.JSONDecodeError as e:
            raise ValueError(f"JSON 파싱 오류: {str(e)}")
            
    def get_format(self) -> str:
        return "JSON"

class YAMLConfigParser(ConfigParser):
    def parse(self, config_string: str) -> Dict[str, Any]:
        try:
            return yaml.safe_load(config_string)
        except yaml.YAMLError as e:
            raise ValueError(f"YAML 파싱 오류: {str(e)}")
            
    def get_format(self) -> str:
        return "YAML"

class XMLConfigParser(ConfigParser):
    def parse(self, config_string: str) -> Dict[str, Any]:
        try:
            root = ET.fromstring(config_string)
            return self._element_to_dict(root)
        except ET.ParseError as e:
            raise ValueError(f"XML 파싱 오류: {str(e)}")
            
    def get_format(self) -> str:
        return "XML"
        
    def _element_to_dict(self, element: ET.Element) -> Dict[str, Any]:
        result = {}
        for child in element:
            if len(child) == 0:
                result[child.tag] = child.text
            else:
                result[child.tag] = self._element_to_dict(child)
        return result

# 팩토리 클래스
class ConfigParserFactory:
    _parsers = {}
    
    @classmethod
    def register_parser(cls, format_type: str, parser_class: type) -> None:
        """새로운 파서를 등록합니다."""
        cls._parsers[format_type.lower()] = parser_class
        
    @classmethod
    def get_parser(cls, format_type: str) -> ConfigParser:
        """요청된 형식의 파서를 생성하여 반환합니다."""
        parser_class = cls._parsers.get(format_type.lower())
        if not parser_class:
            raise ValueError(f"지원하지 않는 형식입니다: {format_type}")
        return parser_class()

# 파서 등록
ConfigParserFactory.register_parser("json", JSONConfigParser)
ConfigParserFactory.register_parser("yaml", YAMLConfigParser)
ConfigParserFactory.register_parser("xml", XMLConfigParser)

# 사용 예시
def parse_config(format_type: str, config_string: str) -> Dict[str, Any]:
    try:
        parser = ConfigParserFactory.get_parser(format_type)
        print(f"{parser.get_format()} 파서를 사용하여 파싱을 시작합니다.")
        return parser.parse(config_string)
    except Exception as e:
        print(f"파싱 중 오류 발생: {str(e)}")
        raise

# 테스트
if __name__ == "__main__":
    # JSON 설정 파싱
    json_config = '{"name": "test", "value": 123}'
    result = parse_config("json", json_config)
    print("JSON 결과:", result)
    
    # YAML 설정 파싱
    yaml_config = """
    name: test
    value: 123
    """
    result = parse_config("yaml", yaml_config)
    print("YAML 결과:", result)

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
// 추상 제품 클래스 (인터페이스)
class PaymentProcessor {
    process(amount) {
        throw new Error('process 메서드를 구현해야 합니다.');
    }
    
    getType() {
        throw new Error('getType 메서드를 구현해야 합니다.');
    }
}

// 구체적인 제품 클래스들
class CreditCardProcessor extends PaymentProcessor {
    constructor(apiKey) {
        super();
        this.apiKey = apiKey;
    }
    
    async process(amount) {
        console.log(`신용카드 결제 처리 중: $${amount}`);
        // 실제 신용카드 결제 처리 로직
        return {
            success: true,
            type: 'credit_card',
            amount,
            timestamp: new Date()
        };
    }
    
    getType() {
        return 'Credit Card';
    }
}

class PayPalProcessor extends PaymentProcessor {
    constructor(clientId, clientSecret) {
        super();
        this.clientId = clientId;
        this.clientSecret = clientSecret;
    }
    
    async process(amount) {
        console.log(`PayPal 결제 처리 중: $${amount}`);
        // 실제 PayPal 결제 처리 로직
        return {
            success: true,
            type: 'paypal',
            amount,
            timestamp: new Date()
        };
    }
    
    getType() {
        return 'PayPal';
    }
}

// 팩토리 클래스
class PaymentProcessorFactory {
    static _processors = new Map();
    static _configs = new Map();
    
    static registerProcessor(type, processorClass, config) {
        this._processors.set(type.toLowerCase(), processorClass);
        if (config) {
            this._configs.set(type.toLowerCase(), config);
        }
    }
    
    static createProcessor(type) {
        const processorClass = this._processors.get(type.toLowerCase());
        if (!processorClass) {
            throw new Error(`지원하지 않는 결제 방식입니다: ${type}`);
        }
        
        const config = this._configs.get(type.toLowerCase());
        return new processorClass(Object.values(config));
    }
}

// 결제 프로세서 등록
PaymentProcessorFactory.registerProcessor('credit_card', CreditCardProcessor, {
    apiKey: 'test_api_key'
});

PaymentProcessorFactory.registerProcessor('paypal', PayPalProcessor, {
    clientId: 'test_client_id',
    clientSecret: 'test_client_secret'
});

// 결제 처리 서비스
class PaymentService {
    async processPayment(type, amount) {
        try {
            const processor = PaymentProcessorFactory.createProcessor(type);
            console.log(`${processor.getType()} 프로세서를 사용하여 결제를 시작합니다.`);
            return await processor.process(amount);
        } catch (error) {
            console.error('결제 처리 중 오류 발생:', error.message);
            throw error;
        }
    }
}

// 사용 예시
async function main() {
    const paymentService = new PaymentService();
    
    try {
        // 신용카드 결제
        const creditCardResult = await paymentService.processPayment('credit_card', 100);
        console.log('신용카드 결제 결과:', creditCardResult);
        
        // PayPal 결제
        const paypalResult = await paymentService.processPayment('paypal', 150);
        console.log('PayPal 결제 결과:', paypalResult);
    } catch (error) {
        console.error('결제 처리 실패:', error.message);
    }
}

main();

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

카테고리항목설명권장사항
1. 설계 단계패턴 적용 판단객체 종류가 적을 경우 오히려 복잡도 증가제품 수가 3 개 이상이거나 제품군이 자주 변경될 때 적용
Creator 인터페이스 설계Creator 는 반드시 추상화되어야 함createProduct() 등으로 표준화, SRP/OCP 기반 설계
Product 인터페이스 설계모든 Product 들이 공통 구현 가능한 기능만 포함DIP 기반으로 불필요한 메서드 포함 방지
계층 구조 설계Creator ↔ Product 간 병렬 계층 구조 유지 필요1:1 매핑 유지, 필요한 경우 Abstract Factory 패턴 결합
인터페이스 안정성인터페이스 변경이 전체 구현체에 영향을 줄 수 있음기능 최소화 및 변경 가능성 고려한 설계
2. 구현 단계명명 규칙객체 생성 메서드는 의미 있는 명명 필요createXxx, buildYyy 등 도메인 기반 규칙 일관화
조건 분기 최소화if/else 문이 많아지면 Factory 의미 퇴색Creator 서브클래스 분리 또는 전략 패턴 결합
오류 처리잘못된 요청에 대한 예외 처리 및 메시지 제공의미 있는 예외 타입 정의 + 로깅 + API 문서화
문서화각 팩토리의 목적과 생성 조건 명확히 설명JavaDoc, Swagger 등 도구를 통한 상세 설명 작성
3. 유지보수성클래스 수 관리Product/Creator 수 증가 시 유지보수 어려움Registry 패턴 또는 DI 기반 팩토리 관리
팩토리 확장성제품 추가 시 Factory 수정 필요설정 기반 팩토리 또는 리플렉션 기반 동적 등록 고려
코드 복잡도 관리팩토리 중복 정의 및 의미 없는 추상화 방지단순 객체는 Static Factory 또는 DI 적용으로 간결화
버전 관리인터페이스 변경 시 하위 호환성 문제 발생Deprecated 처리 + 마이그레이션 가이드 제공
4. 테스트/운영테스트 용이성인터페이스 기반 테스트 시 유리Mock/Stub 팩토리, 의존성 주입을 통한 테스트 환경 구성
로깅 및 모니터링객체 생성 추적 및 오류 원인 분석 필요생성 로그 수집, APM, Metrics 등 연계
운영 중 성능 모니터링고비용 객체 생성 시 성능 저하 발생 가능프로파일링 도구 활용, 객체 풀링 및 캐시 적용
객체 생성 비용 최적화무거운 객체 반복 생성 방지Singleton, Object Pool, Prototype 패턴과의 조합
5. DI/플러그인 연계외부 의존성 주입팩토리를 외부에서 주입받아 유연성 확보Spring @Bean, NestJS @Injectable() 등 DI 컨테이너 기반 구성
설정 기반 팩토리 선택외부 설정 또는 전략 패턴 기반 팩토리 자동 선택환경 변수, 설정 파일 (YAML/JSON), 전략 키 기반 분기
플러그인 구조/동적 확장런타임에 팩토리 추가 및 교체가 필요한 경우리플렉션, SPI(Service Provider Interface), Plugin Registry 패턴 적용

테스트 환경에서 팩토리 패턴 적용 방법

목적:

적용 전략

전략설명예시
Test Factory 구현테스트용 팩토리를 별도 구현TestNotificationFactoryMockEmailNotifier 반환
팩토리 인터페이스 분리DI 컨테이너로 주입 가능하게 설계Spring @Profile, NestJS module override
Factory Registry Mocking런타임에 팩토리 Map 교체FactoryRegistry.replace("payment", MockPaymentFactory)
Provider 패턴팩토리 대신 Provider 를 통해 주입Provider<DatabaseClient> 로 변경 유도
Configuration Based Swap설정 파일에 따라 팩토리 교체application-test.yml 에 test factory 설정

테스트 예시

 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
interface PaymentProcessor {
    void process();
}

class RealPaymentProcessor implements PaymentProcessor {
    public void process() { /* 실제 결제 */ }
}

class PaymentProcessorFactory {
    public PaymentProcessor create() {
        return new RealPaymentProcessor();
    }
}

@Test
void testPaymentProcessing() {
    PaymentProcessor mock = Mockito.mock(PaymentProcessor.class);
    PaymentProcessorFactory testFactory = Mockito.mock(PaymentProcessorFactory.class);
    Mockito.when(testFactory.create()).thenReturn(mock);

    // 테스트 대상에 팩토리 주입
    OrderService service = new OrderService(testFactory);
    service.checkout();

    Mockito.verify(mock).process();
}

테스트 환경에서의 고려사항

항목설명권장 사항
생성 주체 분리new 사용 지양팩토리 또는 DI 컨테이너 사용
테스트 팩토리 명확화테스트 전용 팩토리를 별도 구현TestXxxFactory, MockXxxProvider
설정 분리테스트 환경별 구성 분리application-test.yml, jest.config.js
주입 전략Factory 를 인터페이스 기반으로 설계유닛 테스트 내 Mock 주입 가능 구조 확보

활용 시 흔한 실수

항목설명해결 방안
과도한 적용단순 객체 생성에 Factory Method 를 도입해 구조만 복잡해짐복잡성과 확장성 사이의 균형 평가, 단순 생성은 일반 함수로 처리
부적절한 인터페이스 설계Product 인터페이스가 너무 구체적이거나 너무 추상적실제 클라이언트 요구 중심으로 인터페이스 설계
팩토리 메서드에 부수효과 존재객체 생성 외 다른 작업 수행 (로깅, 상태 변경 등) 으로 SRP 위반객체 생성을 순수하게 분리, 생성 외 로직은 외부로 이동
실수 1: 과도한 적용
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 단순 문자열 포장을 위해 과하게 팩토리 적용
class StringWrapper:
    def __init__(self, text: str):
        self.text = text

class StringFactory:
    def create_string(self, text: str) -> StringWrapper:
        return StringWrapper(text)

s = StringFactory().create_string("hello")
print(s.text)

🔻 문제점: 단순 생성인데도 팩토리 구조가 과하게 도입되어 복잡도 증가

개선 코드 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# 단순한 객체는 팩토리 없이 생성자 호출 또는 헬퍼 함수 사용
def create_string_wrapper(text: str) -> 'StringWrapper':
    return StringWrapper(text)

class StringWrapper:
    def __init__(self, text: str):
        self.text = text

s = create_string_wrapper("hello")
print(s.text)

해결: 구조 단순화, 불필요한 클래스를 제거하여 가독성과 유지보수성 향상

실수 2: 부적절한 인터페이스 설계
1
2
3
4
5
6
7
8
# Product 인터페이스가 너무 구체적
class Animal:
    def walk_on_four_legs(self):  # 모든 동물이 네 발로 걷지는 않음
        pass

class Bird(Animal):
    def walk_on_four_legs(self):
        raise NotImplementedError("Birds don't walk on four legs")

🔻 문제점: 인터페이스 설계가 실제 사용자의 목적/맥락을 반영하지 않음

개선 코드 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
from abc import ABC, abstractmethod

class Animal(ABC):
    @abstractmethod
    def move(self):  # 더 일반적인 행위로 추상화
        pass

class Dog(Animal):
    def move(self):
        print("Dog walks on four legs")

class Bird(Animal):
    def move(self):
        print("Bird flies")

def describe(animal: Animal):
    animal.move()

describe(Dog())
describe(Bird())

해결: 클라이언트 (사용자) 가 요구하는 행위 중심 인터페이스로 개선하여 유연성 확보

실수 3: 팩토리 메서드의 부수효과
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Logger:
    def log(self, message: str):
        print(f"[LOG] {message}")

class LoggerFactory:
    def create_logger(self):
        logger = Logger()
        print("Logger created")  # 부수효과
        logger.log("Logger is ready")  # 부수효과
        return logger

logger = LoggerFactory().create_logger()
logger.log("작업 시작")

🔻 문제점: 객체 생성 이외의 부수작업 (로깅, 출력 등) 이 팩토리 메서드에 혼합되어 있음

개선 코드 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
class Logger:
    def log(self, message: str):
        print(f"[LOG] {message}")

class LoggerFactory:
    def create_logger(self) -> Logger:
        return Logger()  # 생성만 수행

# 생성과 초기화 책임 분리
logger = LoggerFactory().create_logger()
print("Logger created")
logger.log("Logger is ready")
logger.log("작업 시작")

해결: 단일 책임 원칙(SRP) 준수, 테스트와 재사용성 향상

성능을 최적화하기 위한 고려사항

카테고리항목설명권장사항
1. 메모리 최적화객체 풀링자주 사용되며 생성 비용이 높은 객체를 재사용Object Pool 패턴, 팩토리와 결합
지연 초기화 (Lazy Loading)사용 시점까지 객체 생성을 미루어 메모리 낭비 방지Singleton + Lazy Initialization, Proxy 패턴 활용
캐싱동일한 조건의 객체를 메모리에 보관하여 재사용HashMap 캐시, 불변 객체 재사용
메모리 누수 방지객체 생명주기 미관리로 인한 메모리 누수 방지WeakReference, 정리 로직 (cleanup) 구현
2. 실행 성능생성 로직 단순화복잡한 조건 분기, 불필요한 동적 로직 제거조건 분리, 로직 캡슐화
팩토리 캐싱Factory 인스턴스의 재사용을 통한 반복 생성 비용 절감Factory Singleton 또는 DI 컨테이너 등록
조기 유효성 검증잘못된 인자에 대해 생성 전에 빠르게 실패Factory 메서드 내 매개변수 검사 도입
3. 구조 관리중앙 팩토리 레지스트리팩토리 인스턴스를 중앙에서 관리Map 기반 Registry 또는 Service Loader 사용
인터페이스 최소화과도한 추상화로 인한 오버헤드 방지꼭 필요한 인터페이스만 유지
생성자 호출 최소화new 키워드의 중복 사용 방지팩토리 내부로 캡슐화
4. 확장성동적 로딩런타임에 팩토리를 추가/등록 가능하게 구성Reflection 기반 Registry, Plugin 아키텍처 활용
DI/IoC 결합팩토리를 DI 컨테이너에 등록해 유지보수와 성능 향상Spring @Bean, NestJS @Injectable() 등 활용
전략/플러그인 조합다양한 전략/서비스를 런타임에 교체 가능Strategy 패턴 + Factory 조합
5. 동시성/안정성스레드 안전성멀티스레드 환경에서 데이터 충돌 방지ThreadLocal, 불변 객체, synchronized factory
부하 분산다수의 팩토리 인스턴스를 통해 처리 분산Factory Pool 구성, 로드 밸런싱 로직 적용
6. 테스트/운영테스트 객체 주입테스트 용 팩토리 또는 MockFactory 구현테스트 환경에서 유연한 객체 생성 지원
재사용 가능성팩토리/객체를 범용적으로 재사용 가능하도록 설계공통 인터페이스 및 생성 규칙 표준화
유지보수성 확보팩토리 구조가 과하게 복잡해지지 않도록 유지최소 구성 유지 + 의미 있는 추상화 수준 유지
7. 보안/제약조건타입 안전성런타임 오류 방지를 위해 타입 제약 강화Java/TypeScript 제네릭 활용, Python 타입 힌트
보안 객체 생성권한 또는 정책에 따라 생성 제한 필요보안 컨텍스트에 따른 팩토리 구현
설정 기반 로딩외부 설정 파일/환경변수 기반 객체 생성YAML/JSON 설정 + 팩토리 매핑

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

카테고리주제항목/키워드설명
1. 핵심 개념Factory Method Pattern핵심 원리, 구조, 장점/단점, 실무 적용객체 생성 책임 위임으로 결합도 감소 및 확장성 향상, 실무 전반에 활용
생성자 분리new 연산자를 팩토리 내부로 숨김클라이언트 코드 단순화 및 SRP 준수
2. 설계 원칙SOLID 원칙DIP, OCP, SRP추상화에 의존하여 확장에 열리고 수정에 닫힌 구조 실현
느슨한 결합Creator-Product 구조 분리구조적으로 의존성을 줄이고 유연성 확보
3. 생성 패턴 비교Simple Factory정적 메서드 + 조건문간단하지만 OCP 위반 가능
Abstract Factory제품군 생성관련 객체군 생성 책임 위임, Factory Method 와 조합 사용 가능
Builder Pattern단계적 객체 조립복잡한 객체 조립 시 Factory 와 결합 가능
Prototype Pattern객체 복제 기반 생성상태 기반 복제에 적합한 구조
4. 테스트 전략테스트 더블Mock, Stub, Fake인터페이스 기반 팩토리를 통한 테스트 객체 주입 용이
Mock Factory테스트 목적 전용 팩토리 구현테스트 시점에 조건별 객체 주입 가능
Test Double 팩토리다양한 테스트 시나리오 구성유지보수 및 테스트 자동화 향상
5. 프레임워크 활용DI/IoC 연계Spring, Guice, NestJS 등과 통합팩토리 기반 객체 주입 자동화, @Bean, @Factory 활용 가능
Constructor Injection생성자 기반 DI팩토리 생성 객체를 의존성으로 주입
6. 고급 구현 전략Generic Factory타입 안전성 확보제네릭 기반 팩토리 구현으로 컴파일 타임 검증 가능
Reflection-based Factory런타임 동적 클래스 로딩유연성 극대화 (단, 보안/성능 유의)
Annotation-driven Factory메타데이터 기반 자동 팩토리프레임워크나 도구와의 통합에 용이
Registry Factory팩토리 맵 등록/검색 구조플러그인 및 모듈 동적 로딩에 활용
Lazy Loading Factory지연 초기화 구조리소스 최적화 및 객체 생명주기 관리
Object Pool Factory재사용 가능한 객체 풀과 연계반복적 객체 생성을 성능 관점에서 최적화
7. 아키텍처 통합레이어드 아키텍처서비스 레이어 분리객체 생성 로직과 비즈니스 로직 분리
플러그인 아키텍처확장점/핫스왑 구조런타임 동적 확장 및 유연성 제공
Hexagonal Architecture외부 인터페이스 분리포트 - 어댑터와 팩토리 결합
마이크로서비스팩토리 기반 서비스 생성서비스별 구현체 구성 시 팩토리 패턴 활용
CQRS명령 - 쿼리 객체 생성을 분리팩토리로 각 역할 객체를 적절히 분리 생성
Event Sourcing이벤트 재구성용 팩토리 구조애그리게이트 재생성 시 활용
CI/CD 통합배포 파이프라인에서 팩토리 적용인프라 리소스/파이프라인 구성 자동화
8. 도메인 주도 설계애그리게이트 팩토리복잡한 도메인 객체 생성 책임 분리도메인 유효성 보장 + 생성 규칙 캡슐화
값 객체 팩토리불변 값 객체 생성생성 시점에서 유효성 보장
9. 성능/보안 최적화성능 최적화팩토리 프로파일링, 객체 재사용성능 병목 파악 및 최적화 가능
보안 강화보안 컨텍스트 팩토리, 암호화 객체 생성정책 기반 객체 생성 및 권한 기반 흐름 설계
10. 리액티브/비동기비동기 팩토리CompletableFuture, RxJava 활용비동기 흐름에서 객체 생성 최적화
스트림 기반 생성Java Stream, Python Generator 활용대량 데이터 기반 객체 생성에 유리

추가 학습 주제

카테고리주제설명
패턴 비교 및 응용Simple Factory vs Factory Method조건 기반 객체 생성 vs 하위 클래스에 생성 책임 위임
Factory Method vs Abstract Factory단일 제품 생성 vs 제품군 생성, 구조와 확장성 비교
Factory vs Builder Pattern생성 절차 캡슐화 vs 객체 조립 과정 단계화
고급 구현 전략Generic Factory Methods제네릭을 이용해 타입 안전성과 유연성 강화
Reflection-based Factory런타임 클래스 로딩을 통한 동적 객체 생성
Annotation-driven Factory어노테이션 기반 메타데이터로 자동 팩토리 로직 생성
Static Factory Method정적 메서드를 이용한 간단하고 효율적인 팩토리 구현
Parameterized Factory매개변수를 통해 다양한 객체를 반환
Registry Factory팩토리 객체를 키 기반으로 등록·관리
Lazy Loading Factory객체 생성을 요청 시점까지 지연시켜 리소스 최적화
Object Pooling with Factory팩토리에서 객체 풀과 연동하여 성능 최적화
아키텍처 통합Factory in Hexagonal Architecture포트 - 어댑터 구조에서 팩토리를 통한 외부 의존성 분리
Microservices Factory Patterns마이크로서비스 환경에서 제품군 객체 생성 전략
CQRS with Factory Pattern명령/쿼리 분리에 따른 생성 책임 구분
Event Sourcing Factory이벤트 기반 애그리게이트 생성을 위한 팩토리 적용
Service Mesh Integration런타임 동적 서비스 생성을 위한 팩토리 적용
Infrastructure as Code FactoryIaC 리소스 생성을 위한 팩토리 구조
Factory in CI/CD Pipelines배포 자동화 파이프라인에서 팩토리를 활용하여 유연한 구성 적용
설계 원칙 및 구조 설계SOLID (OCP, DIP, SRP)객체 생성 책임 분리를 통한 의존성 역전과 확장 가능성 확보
Creator/Product 병렬 계층 구조상속 계층을 나란히 설계하여 구조적 일관성과 확장성 확보
인터페이스 설계Product, Creator 추상화 설계를 통해 구현체 교체 유연성 확보
DI/IoC 결합프레임워크 (Spring/NestJS) 와 통합하여 팩토리 자동 주입 구조 설계
플러그인 구조IDE/게임 엔진 등에서 동적 확장을 위한 팩토리 기반 플러그인 아키텍처
Factory + Strategy제품 생성 + 로직 결정 구조를 조합하여 런타임 유연성 확보
테스트 전략Test Factory Patterns테스트용 객체 생성을 위한 팩토리 설계
Mock 객체 생성테스트 더블 (Mock/Stub) 와 팩토리 조합
통합 테스트 및 변이 테스트팩토리와 생성 객체를 통합하여 테스트 시나리오 구성
성능/보안 최적화Factory Performance Profiling팩토리 메서드 호출 성능 분석
Memory Management팩토리에서 생성된 객체의 생명주기 및 메모리 관리 전략
Secure Factory Implementation객체 생성 시 보안 이슈 (예: 인젝션) 방지 설계
Factory Pattern Security Audit팩토리 코드의 보안 취약점 검토 및 점검
프로그래밍 언어/도구제네릭/정적 타입 언어 기반 구현Java, TypeScript 등에서 타입 안정성 있는 팩토리 구현
함수형 프로그래밍고차 함수 기반의 팩토리 구현 방식 (함수 반환, 클로저 활용 등)
코드 생성기팩토리/제품 클래스를 자동으로 생성하는 도구 활용

용어 정리

카테고리용어설명
핵심 구조 요소Product생성될 객체의 인터페이스 또는 추상 클래스
ConcreteProductProduct 를 구현한 실제 클래스
Creator팩토리 메서드를 선언하는 추상 클래스 또는 인터페이스
ConcreteCreatorCreator 를 상속받아 팩토리 메서드를 오버라이드하고 객체를 생성하는 클래스
Factory Method객체 생성을 하위 클래스에 위임하는 메서드로, 다형성 기반 생성 책임 위임
패턴 확장 요소Registry Factory이름 또는 키 기반으로 등록된 팩토리를 선택하는 방식
Parameterized Factory Method매개변수를 통해 생성할 객체 타입을 결정하는 팩토리 메서드
Static Factory Method정적 메서드로 객체를 생성하는 방식 (생성자 대체)
Reflection-based Factory리플렉션을 이용해 런타임에 클래스를 로드하고 객체 생성
Factory Chain여러 팩토리가 연결되어 복합적인 객체 생성 절차를 처리하는 구조
Aggregate Factory도메인 주도 설계 (DDD) 에서 애그리게이트 루트를 생성하는 전용 팩토리
Functional Factory함수형 인터페이스와 람다식으로 구현한 팩토리 패턴
Template Factory MethodTemplate Method 패턴과 결합된 생성 메서드 구조
Annotation-driven Factory어노테이션 기반 자동 팩토리 구현 방식
구조적 개념Creator HierarchyCreator 클래스 계층 구조
Product HierarchyProduct 클래스 계층 구조
Parallel Class HierarchiesCreator 와 Product 가 병렬 계층 구조를 형성하는 설계
Virtual ConstructorFactory Method 의 별칭, 다형성 기반 생성 방식 강조
설계 원칙 관련OCP (Open/Closed Principle)확장에는 열려 있고 수정에는 닫혀 있는 원칙
DIP (Dependency Inversion Principle)구현이 아닌 추상에 의존하도록 설계
Loose Coupling클래스 간의 결합도를 낮춰 유연한 설계를 유도
Encapsulation객체 생성 로직을 외부에 노출하지 않고 내부로 캡슐화
프로그래밍 기법Lazy Initialization객체가 실제로 필요할 때까지 생성을 지연하는 최적화 기법
Dynamic Loading런타임에 클래스 또는 팩토리를 로드하여 동적으로 객체 생성
Object Pool생성된 객체를 재사용하여 성능을 높이는 풀링 기법
아키텍처 응용Plugin Architecture런타임 시 플러그인을 통해 동적 기능 확장을 허용하는 구조
테스트 관련 용어Test Double테스트 용도로 실제 객체를 대신하는 객체 전체 그룹 (Stub, Mock 등 포함)
Mock Object특정 동작을 검증하기 위해 사용하는 테스트용 객체

참고 및 출처