
파이썬의 동적 특성을 활용하여 복잡한 객체 생성 로직을 단순화하고, 디자인 패턴을 '파이썬스럽게(Pythonic)' 재해석하는 가이드입니다.
1. 개요: 왜 팩토리 패턴인가, 그리고 왜 클래스 메서드인가?
객체 지향 프로그래밍(OOP)에서 팩토리 패턴(Factory Pattern)은 객체 생성 로직을 별도의 클래스나 메서드로 분리하여 결합도를 낮추는 핵심 디자인 패턴입니다. 하지만 Java나 C++ 같은 정적 타입 언어에서 사용되는 엄격한 추상 팩토리 구조를 파이썬에 그대로 이식하는 것은 때로 과도한 엔지니어링(Over-engineering)이 될 수 있습니다. 파이썬은 클래스 자체가 일급 객체이며, @classmethod를 통해 다중 생성자(Multiple Constructors) 효과를 낼 수 있는 유연함을 제공합니다. 본 글에서는 전통적인 팩토리 클래스와 파이썬의 클래스 메서드를 활용한 구현 방식을 심층 비교하고, 실무에서 즉시 사용 가능한 7가지 이상의 예제를 통해 최적의 아키텍처를 제안합니다.
2. 팩토리 패턴 vs 클래스 메서드: 주요 차이점 비교
전통적인 방식과 파이썬 특화 방식의 핵심 차이를 아래 표로 정리하였습니다.
| 비교 항목 | 전통적 팩토리 패턴 (Factory Class) | 파이썬 클래스 메서드 (Alternative Constructor) |
|---|---|---|
| 정의 위치 | 별도의 팩토리 클래스 생성 | 대상 클래스 내부에 정의 |
| 객체 생성 목적 | 다양한 서브클래스 중 하나를 선택하여 생성 | 동일 클래스의 다양한 입력 형식을 처리 |
| 코드 복잡도 | 높음 (인터페이스, 구체 클래스 필요) | 낮음 (데코레이터 활용으로 직관적) |
| 결합도 | 클라이언트와 구체 클래스 간의 분리 탁월 | 도메인 모델 내에 생성 로직이 포함됨 |
| 확장성 | 새로운 제품군 추가 시 유리 | 기존 클래스의 인터페이스 확장에 유리 |
3. 실무 적용을 위한 7가지 구현 Sample Examples
Example 1: 데이터 형식에 따른 다중 생성자 구현
JSON, Dict 등 다양한 입력 데이터로부터 객체를 생성할 때 팩토리 클래스 대신 사용합니다.
class User:
def __init__(self, name, email):
self.name = name
self.email = email
@classmethod
def from_json(cls, data):
# JSON 데이터를 파싱하여 인스턴스 반환
import json
payload = json.loads(data)
return cls(payload['name'], payload['email'])
@classmethod
def from_dict(cls, data_dict):
return cls(data_dict.get('username'), data_dict.get('user_email'))
# 사용 예시
user1 = User.from_json('{"name": "Kim", "email": "kim@example.com"}')
Example 2: 상속 구조에서의 유연한 인스턴스화
cls 키워드를 사용하면 자식 클래스에서 호출하더라도 해당 자식의 인스턴스를 올바르게 생성합니다.
class Logger:
@classmethod
def create(cls):
return cls()
class FileLogger(Logger):
pass
# FileLogger 인스턴스가 반환됨
logger = FileLogger.create()
Example 3: 복잡한 초기화 로직 캡슐화
데이터베이스 연결이나 설정 로직이 필요한 경우입니다.
import sqlite3
class DatabaseConnector:
def __init__(self, connection):
self.conn = connection
@classmethod
def connect_local(cls, db_name):
conn = sqlite3.connect(f"{db_name}.db")
return cls(conn)
Example 4: 싱글톤 패턴과의 결합
오직 하나의 인스턴스만 생성하도록 보장하는 팩토리 로직입니다.
class AppConfig:
_instance = None
def __init__(self):
self.settings = {}
@classmethod
def get_instance(cls):
if cls._instance is None:
cls._instance = cls()
return cls._instance
Example 5: 유효성 검사를 포함한 팩토리 메서드
객체 생성 전 비즈니스 로직을 검증합니다.
class Subscription:
def __init__(self, plan):
self.plan = plan
@classmethod
def create_premium(cls, user_points):
if user_points < 1000:
raise ValueError("포인트가 부족합니다.")
return cls(plan="Premium")
Example 6: 플러그인 시스템 구현 (Registry Factory)
클래스 메서드를 등록소(Registry)와 결합하여 동적으로 객체를 선택합니다.
class Processor:
_registry = {}
@classmethod
def register(cls, name):
def wrapper(subclass):
cls._registry[name] = subclass
return subclass
return wrapper
@classmethod
def get_processor(cls, name):
return cls._registry[name]()
@Processor.register("image")
class ImageProcessor(Processor): pass
Example 7: 불변 객체(Immutable)를 위한 팩토리
데이터 클래스와 함께 사용하여 객체 생성을 엄격히 관리합니다.
from dataclasses import dataclass
@dataclass(frozen=True)
class Point:
x: int
y: int
@classmethod
def origin(cls):
return cls(0, 0)
4. 결론 및 해결 전략
팩토리 패턴을 무조건 클래스 메서드로 대체해야 하는 것은 아닙니다. 하지만 다음과 같은 기준을 가질 때 코드의 가독성이 극대화됩니다.
- 클래스 메서드를 선택할 때: 입력 데이터의 형태가 다양할 뿐, 생성되는 객체의 타입이 명확할 때.
- 팩토리 클래스를 선택할 때: 조건에 따라 완전히 다른 클래스 계층 구조의 인스턴스를 반환해야 하거나, 생성 로직이 도메인 모델과 분리되어야 할 만큼 비대할 때.
파이썬에서는 @classmethod가 제공하는 명명된 생성자(Named Constructor) 관례를 따르는 것이 가장 '파이썬스러운' 해결 방법입니다.
'Artificial Intelligence > 60. Python' 카테고리의 다른 글
| [PYTHON] 파이썬 싱글톤(Singleton) 패턴을 구현하는 세련된 7가지 방법과 차이 해결 (0) | 2026.03.29 |
|---|---|
| [PYTHON] 의존성 주입(Dependency Injection)을 구현하는 독보적인 7가지 방법과 실무적 해결책 (0) | 2026.03.29 |
| [PYTHON] 믹스인(Mixin) 설계 시 상속 구조 문제를 해결하는 3가지 방법과 실무적 차이점 (0) | 2026.03.29 |
| [PYTHON] 상속보다 합성을 선택해야 하는 5가지 상황과 구조적 차이 해결 방법 (0) | 2026.03.29 |
| [PYTHON] TDD 적용 시 코드 구조 설계를 최적화하는 3가지 방법과 실무적 차이점 분석 (0) | 2026.03.29 |