본문 바로가기
Artificial Intelligence/60. Python

[PYTHON] 팩토리 패턴을 클래스 메서드로 대체하는 3가지 방법과 실무적 차이점 분석

by Papa Martino V 2026. 3. 29.
728x90

팩토리 패턴(Factory Pattern)
팩토리 패턴 (Factory Pattern)

 

파이썬의 동적 특성을 활용하여 복잡한 객체 생성 로직을 단순화하고, 디자인 패턴을 '파이썬스럽게(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) 관례를 따르는 것이 가장 '파이썬스러운' 해결 방법입니다.

728x90