Abstract Factory 패턴: 제품군을 통째로 일관되게 생성하는 법
Abstract Factory 패턴의 구조와 에러 처리, Java 구현 예제, Builder·Factory Method와의 선택 기준을 실무 관점에서 정리한다.
2026-08-13 · 최초 발행 2025-10-14
라이트 테마 버튼에 다크 테마 체크박스가 섞여 렌더링된다면, 제품군 사이의 호환성을 지켜주는 장치가 없다는 뜻이다. Abstract Factory 패턴은 서로 호환되는 제품군을 일관되게 생성하기 위한 객체 생성 패턴이다. UI 테마, 스토리지 드라이버, 프로토콜 스택처럼 제품군 개념이 존재하는 영역에서 구현 상세를 캡슐화하고 런타임에 교체할 수 있게 한다. 생성 책임을 팩토리 인터페이스로 옮겨 클라이언트의 결합도를 낮추고 일관성을 보장하는 것이 핵심이다.
제품군을 통째로 바꾼다는 것
목적은 관련성 있는 객체의 집합(제품군)을 구체 클래스에 의존하지 않고 생성하기 위한 인터페이스를 제공하는 것이다. AbstractFactory가 AbstractProduct들의 생성 연산을 정의하고, ConcreteFactory가 일관된 ConcreteProduct 조합을 생성한다. 참여자는 AbstractFactory·ConcreteFactory, AbstractProductA/B 등과 그 구현체인 ConcreteProductA1/A2·ConcreteProductB1/B2 등, 그리고 오직 추상 타입에만 의존하고 ConcreteFactory 주입으로 제품군을 교체하는 Client로 구성된다.
구성 흐름과 에러 처리
클라이언트가 제품군을 선택(설정·환경·주입)하면, AbstractFactory 인터페이스 기반으로 생성을 요청하고 ConcreteFactory가 일관된 제품군 인스턴스를 생성해, 호환성이 보장된 제품군 객체 세트를 반환한다.
미지원 제품군을 요청하면 UnsupportedFactory 예외를 던지고 기본 팩토리로 폴백하도록 구성한다. 불일치 조합을 막기 위해 단일 ConcreteFactory에서만 제품을 생성하도록 허용하고 교차 생성은 정책적으로 금지한다. 팩토리를 싱글톤으로 둔다면 불변 설계와 상태 없는 구현으로 스레드 안정성을 확보한다.
일관성과 결합도, 교체 가능성
같은 팩토리에서 생성된 객체끼리는 상호 호환성이 보장된다. UI 테마나 플랫폼별 API 차이를 캡슐화해 런타임에 서로 다른 제품군이 뒤섞이는 것(hybrid)을 막는다. 클라이언트는 추상 타입에만 의존하므로 DIP를 준수하고, 테스트 시에는 MockFactory를 주입해 격리 테스트를 할 수 있다. 환경 설정이나 플러그인 방식으로 ConcreteFactory를 교체할 수 있어 확장성이 좋고, 새 제품군을 추가해도 기존 클라이언트 변경은 최소화된다. 다만 제품 수 곱하기 제품군 수만큼 클래스가 늘어나는 트레이드오프가 있어, 단일 제품만 확장하는 경우에는 과도한 구조가 될 수 있다.
코드로 보는 라이트·다크 테마
전제조건은 JDK 17 이상, 표준 콘솔 실행 환경이다. 목적은 라이트·다크 테마(UI 제품군)를 일관되게 생성하는 것이다.
// Java 17+
// 1) Abstract Products
interface Button { String render(); }
interface Checkbox { String render(); }
// 2) Concrete Products
class LightButton implements Button { public String render() { return "Light Button"; } }
class DarkButton implements Button { public String render() { return "Dark Button"; } }
class LightCheckbox implements Checkbox { public String render() { return "Light Checkbox"; } }
class DarkCheckbox implements Checkbox { public String render() { return "Dark Checkbox"; } }
// 3) Abstract Factory
interface UIFactory {
Button createButton();
Checkbox createCheckbox();
}
// 4) Concrete Factories
class LightFactory implements UIFactory {
public Button createButton() { return new LightButton(); }
public Checkbox createCheckbox() { return new LightCheckbox(); }
}
class DarkFactory implements UIFactory {
public Button createButton() { return new DarkButton(); }
public Checkbox createCheckbox() { return new DarkCheckbox(); }
}
// 5) Client
class Screen {
private final Button button;
private final Checkbox checkbox;
Screen(UIFactory factory) {
this.button = factory.createButton();
this.checkbox = factory.createCheckbox();
}
public void draw() {
System.out.println(button.render() + " + " + checkbox.render());
}
}
// 6) Runtime selection + error handling
public class Main {
enum Theme { LIGHT, DARK }
static UIFactory factoryOf(Theme theme) {
return switch (theme) {
case LIGHT -> new LightFactory();
case DARK -> new DarkFactory();
};
}
public static void main(String[] args) {
// Input from env/args/config; default LIGHT
Theme theme = (args.length > 0 && args[0].equalsIgnoreCase("dark")) ? Theme.DARK : Theme.LIGHT;
UIFactory factory = factoryOf(theme);
new Screen(factory).draw();
}
}
java Main dark를 실행하면 "Dark Button + Dark Checkbox"가 출력되어 제품군 일관성이 지켜졌음을 확인할 수 있다.
Abstract Factory가 필요한 실무 상황
- UI 테마·플랫폼 독립 위젯: 데스크톱·웹·모바일 위젯 세트를 일관되게 생성하고, 접근성·로케일별 제품군을 분리해 규정 준수를 쉽게 한다.
- 데이터 액세스 드라이버: RDBMS·NoSQL 드라이버 세트와 커넥션·쿼리·트랜잭션 객체군을 한꺼번에 교체하고, 샤딩이나 리드레플리카 정책에 따라 팩토리를 전환한다.
- 클라우드 프로바이더 어댑터: AWS·Azure·GCP의 스토리지·큐·시크릿 제품군을 일관되게 추상화하고, 멀티 클라우드 전략에서 배포 환경별로 팩토리를 주입한다.
- 프로토콜 스택·IoT 디바이스: BLE·Zigbee·MQTT 제품군을 생성해 센서·게이트웨이 호환을 관리하고, 인증·암호화 구성을 제품군화해 보안 설정의 일관성을 유지한다.
Builder·Factory Method와 비교하면
| 항목/패턴 | Abstract Factory | Factory Method | Builder |
|---|---|---|---|
| 성능 | 상(간접 호출 1회) | 상(간접 호출 1회) | 중(조립 단계 다수) |
| 확장성 | 상(제품군 추가 용이) | 중(단일 제품 확장에 적합) | 중(복잡 객체 변형에 적합) |
| 일관성 | 상(제품군 호환성 보장) | 중(조합 일관성은 별도 관리) | 중(구성 규칙에 의존) |
| 안정성 | 상(런타임 교체 안전) | 중(서브클래싱 복잡성) | 중(조립 오류 가능) |
| 운영 편의 | 중(클래스 증가 관리 필요) | 상(구조 단순) | 중(설정 관리 부담) |
제품 "군"의 일관성이 핵심이면 Abstract Factory, 단일 제품 생성 다양화는 Factory Method, 복잡한 객체 조립은 Builder가 적합하다.
도입 절차와 베스트 프랙티스
설계 단계에서는 제품군 경계와 제품 인터페이스를 정하고, 교체 축(테마·플랫폼·벤더)을 식별해 버전 전략을 세운다. 구현 단계에서는 AbstractFactory·AbstractProduct를 정의하고 ConcreteFactory·Products를 구현하며, 환경 변수·플러그인·DI 컨테이너로 팩토리 선택 로더를 만든다. 운영 단계에서는 구성 파일이나 피처 플래그로 팩토리 전환 절차를 두고, 선택된 팩토리와 오류율 같은 관측성 지표를 대시보드화한다.
DI 컨테이너를 활용한다면 바인딩은 컴포지션 루트에서만 하고 테스트 모듈에서 재바인딩한다. 팩토리는 무상태·불변으로 두어 스레드 안정성을 확보하고, 캐싱은 제품 객체 수준에서 수행한다. 제품군 버저닝은 Factory v1/v2를 공존시켜 점진적으로 이행하되 교차 의존은 금지한다. 초기 클래스 폭증은 코드 생성기나 템플릿, 표준화된 폴더 구조로 관리할 필요가 있다.
Abstract Factory 도입 효과
사전 검증된 공장 조합을 쓰면 이종 조합 결함(호환성 불일치)이 50% 이상 감소한다고 가정한다. 신규 제품군을 추가할 때 클라이언트 수정은 0~1개 파일 수준(팩토리 바인딩만 변경)에 그친다. 팩토리 모킹으로 UI·드라이버 교체 테스트 시간을 30% 절감할 수 있고, 배포 환경별로 제품군을 스위칭하면 구성 드리프트가 줄고 롤백이 단순해져 운영 안정성이 오른다.