팩토리 메소드 패턴
- 개발/디자인패턴
- 2025. 12. 22. 00:02
디자인 패턴에는 수많은 패턴들이 존재합니다. 그중에서도 팩토리 패턴은 "객체 생성"이라는 가장 흔하지만, 동시에 가장 위험한 지점을 다룹니다.

팩토리라고 하면 보통 공장의 이미지를 떠올릴 겁니다. 공장은 제품을 만들되, 제품이 어떻게 만들어지는지(공정)는 외부에 노출하지 않죠. 그런데 소프트웨어에서도 비슷한 문제가 생깁니다. 처음엔 new로 객체를 만들면 끝이지만, 시간이 지나면 생성 로직은 복잡해지고, 조건이 늘고, 테스트가 어려워지고, 결국 생성 코드가 여기저기 퍼지면서 변경 비용이 폭발합니다. 팩토리 패턴은 단순히 "객체를 대신 만들어주는 패턴"이 아닙니다. 생성 책임을 분리해서, 변경이 전파되는 범위를 통제하는 설계 전략입니다.
이번 시간에는 "공장처럼 찍어낸다"는 비유를 넘어서, 왜 new가 위험해지는지, 그리고 팩토리가 어떤 결합을 끊어주는지를 깊게 다뤄보겠습니다.
왜 탄생하게 되었을까?
GoF 디자인 패턴에서는 팩토리 패턴을, 객체 생성이 시스템의 변경 지점이 되는 순간 발생하는 변경의 폭발을 막기 위해 탄생한 패턴으로 설명하고 있습니다. 여기서 3가지 포인트에 주목을 해야 합니다.
- 객체 생성
- 변경 지점 (Change Point)
- 폭발 (Change Explosion)
어째서 객체 생성은 변경 지점이 되는가?
이를 이해하기 위해, 먼저 팩토리 패턴을 사용하지 않았을 때 코드는 어떻게 작성되는지 살펴봅시다.
예를 들어, 다음과 같은 코드가 존재한다고 가정해 봅시다.
public class OrderService {
public void order(String paymentType) {
PaymentService paymentService;
if ("KAKAO".equals(paymentType)) {
paymentService = new KakaoPaymentService();
} else if ("NAVER".equals(paymentType)) {
paymentService = new NaverPaymentService();
} else {
throw new IllegalArgumentException("지원하지 않는 결제 수단");
}
paymentService.pay();
}
}
현재 이 코드는 paymentType이라는 정책 값을 기준으로, KAKAO와 NAVER 결제 서비스 객체를 조건 분기를 통해 직접 생성하고 있습니다. 만약, 정책이 추가된다면, 어떻게 해야 할까요? else if를 통해 추가를 해야 할까요? 지금 구조에서는 다음과 같이 생성할 수밖에 없습니다.
} else if ("TOSS".equals(paymentType)) {
paymentService = new TossPaymentService();
}
문제는 else if를 추가하는 행위 그 자체가 아니라, 정책이 추가될 때마다 동일한 수정이 여러 곳에서 반복된다는 점입니다.
이처럼 객체 생성에 대한 정책이 서비스 내부에 흩어져 있으면, 정책 변경 시 코드 수정 범위는 빠르게 확산됩니다.
그렇다면 이 객체 생성 로직을 한 곳으로 모을 수는 없을까요?
이 질문에 대한 해답이 바로 팩토리 패턴입니다.
그러면 다음과 같은 코드를 작성할 수 있습니다.
public class OrderService {
public void order(String paymentType) {
PaymentService paymentService=paymentFactory.create(paymentType);
paymentService.pay();
}
}
이 구조에서는 객체 생성에 대한 정책 판단이 OrderService가 아니라 paymentFactory로 이동하게 됩니다.
즉, 어떤 결제 수단을 사용할지에 대한 결정은 팩토리 내부에서만 관리됩니다.
그 결과, 서비스를 사용하는 쪽의 코드는 객체 생성 정책과 분리되어 훨씬 간결해집니다.
폭발은 어떤 의미일까?
이제 팩토리 패턴이 어떤 방식으로 적용되는지는 살펴보았습니다. 그렇다면 앞에서 언급한 변경의 폭발이란 정확히 무엇을 의미할까요?
사실 이 현상은 이미 앞선 예제에서 확인할 수 있었습니다.
여기서 말하는 변경의 폭발이란, 하나의 정책 변경이 여러 파일과 여러 클래스,
그리고 여러 코드 위치의 수정을 연쇄적으로 요구하는 현상을 의미합니다.
코드를 다시 한번 살펴봅시다.
if ("KAKAO".equals(paymentType)) {
paymentService = new KakaoPaymentService();
} else if ("NAVER".equals(paymentType)) {
paymentService = new NaverPaymentService();
}
이와 같은 코드가 여러 서비스 내부에 흩어져 있다면, 새로운 결제 수단이 추가될 때마다
각 서비스의 동일한 조건 분기와 생성 로직을 반복해서 수정해야 합니다.
팩토리 패턴은 이러한 변경을 완전히 제거하는 것이 아니라, 변경이 발생하는 지점을 하나의 역할로 모아 그 영향 범위를 통제합니다.
여기서 한 가지 오해를 반드시 짚고 넘어가야 합니다. Factory 패턴은 실행 중에 type 값이 바뀌면서
그에 따라 객체를 선택하는 패턴이 아닙니다. 그러한 구조는 여전히 선택 로직이 실행 코드에 남아 있으며,
객체 생성은 여전히 변경 지점으로 존재합니다. Factory 패턴이 전제하는 것은, "어떤 구현을 사용할지"에 대한 결정이
실행 이전의 구성 단계에서 이미 끝나 있다는 점입니다. 실행 중인 코드에는 선택의 흔적조차 남지 않아야 합니다.
그렇다면, 정책을 Factory 객체로 넣으면 모두 Factory패턴인건가?
결론부터 말씀드리자면 정책을 Factory 객체로 옮겼다고 해서 전부 Factory 패턴은 아닙니다.
팩토리 패턴은 위치가 아니라 역할과 구조의 문제입니다.
아래의 코드를 사용한다면, 팩토리 패턴일까요?
class PaymentFactory {
PaymentService create(String type) {
if (type.equals("KAKAO")) return new KakaoPaymentService();
if (type.equals("NAVER")) return new NaverPaymentService();
return null;
}
}
하지만 이 코드를 사용한다고 해서 팩토리 패턴은 아닙니다. 위 코드는 단순히 정책을 모아둔 클래스에 불과합니다.
그러면, 팩토리 패턴의 본질은 과연 어떤것일까요?
객체 생성 코드를 한 곳에 모아두는 것이 아니라 객체를 사용하는 쪽이 구체적인 방식과 타입을 몰라도 되게 만드는 구조입니다.
즉, 핵심은 new의 위치가 아니라 의존성의 방향입니다.
위 코드가 팩토리 패턴이 아닌 이유는 3가지가 존재합니다.
- 호출부가 여전히 정책을 알고 있다.
- String type(KAKAO,NAVER)
- 호출부가 어떤 구현이 존재하는지 알고 있어야 합니다. 즉, 생성 정책이 완전히 추상화되지 않았습니다.
- 확장이 추가가 아니라 수정이다.
- 결제 수단 하나 추가
- PaymentFactory의 if 수정 필수
즉, 이것은 OCP(Open-Closed Principle)위반입니다.
이 구조는 관리하기 쉬운 변경 지점이지 패턴 수준의 확장 구조는 아닙니다.
- 역할이 명확하지 않다.
- Factory라는 이름을 썼을뿐 설계 역할로써 Factory는 아닙니다.
- 보통 이러한 코드를 Simple Factory라고 불립니다.(혹은 Factory-like 구조라 불립니다.)
-> GoF 정식 패턴이 아닙니다.
팩토리 패턴의 본질은 객체 생성 로직을 한 곳에 모아두는 것이 아니라, 객체를 사용하는 코드가 구체적인 생성 방식과 구현 타입으로부터
완전히 분리되도록 구조를 설계하는 데 있습니다.
그래서 어떤것이 Factory 패턴인걸까?
Factory 패턴은 3가지의 구조적 조건을 만족해야 합니다.
Factory 패턴의 판별 기준
Factory 패턴은 단순히 Factory라는 이름의 클래스를 두는 것으로 성립하지 않습니다.
다음 조건을 구조적으로 만족할 때 비로소 Factory 패턴의 영역에 들어갑니다.
1️⃣ 사용하는 쪽은 구체 타입을 전혀 모른다.
- Factory 패턴에서는 사용 코드에 다음이 절대 등장하지 않습니다.
- new KakaoPaymentService()
- "KAKAO", "NAVER" 같은 구현 식별자
- if / switch 기반 생성 분기
사용하는 쪽의 관심사는 오직 하나입니다.
이 역할을 수행하는 객체가 필요하다
즉, 구체 구현과 생성 방식으로부터 완전히 분리되어 있어야 한다.
2️⃣ 생성 책임이 다형성으로 위임된다.
Factory 패턴에서는 객체 선택이 조건문으로 이루어지지 않습니다.
대신 다형성을 통해 생성 책임이 위임됩니다.
대표적인 형태는 다음과 같습니다.
- 서브클래스가 객체 생성을 책임집니다.
- 혹은 구현체가 스스로 생성 규칙을 가집니다.
그 결과,
- if / switch로 객체를 고르는 구조가 아닌
- 타입과 다형성으로 선택 자체가 사라지는 구조가 됩니다.
이 지점이 Simple Factory와 Factory 패턴을 가르는 결정적인 차이입니다.
3️⃣ 확장은 수정이 아니라 추가로 끝난다.
Factory 패턴에서 이상적인 확장 방식은 다음과 같습니다.
- 새로운 구현 클래스 추가
- 기존 비즈니스 코드 수정 없음
이 구조가 가능하다면, 해당 설계는 OCP(Open-Closed Principle)를 구조적으로 만족하고 있으며
Factory 패턴의 영역에 있다고 판단할 수 있습니다.
- 런타임 중에 정책을 선택하는 구조가 아니라, 하나의 정책을 정체성으로 가진
객체 생성 구조를 먼저 확정한 뒤 그 위에서 코드를 작성하도록 만드는 패턴 - 객체를 미리 만들어 두는 패턴이 아니라, 객체를 어떻게 만들 것인지에 대한 정책을 런타임 전에 확정해 두는 패턴
그렇다면, 런타임중에 정책을 바꾸는것은 불가능할까요? 그것은 Factory패턴의 영역이 아닌 Strategy 패턴이라 생각이 듭니다.
Factory 패턴은 객체 생성 기술이 아니라, 객체 생성에 대한 정책을 '정체성'으로 고정하여 변경 전파를 통제하기 위한 설계 전략입니다.
'개발 > 디자인패턴' 카테고리의 다른 글
| 템플릿 메소드 패턴 (0) | 2022.03.19 |
|---|---|
| 퍼사드 패턴 (0) | 2021.12.05 |
| 브릿지 패턴 (0) | 2021.11.25 |
| 전략 패턴 (0) | 2021.11.19 |
| 팩토리 메소드 패턴 vs 추상 팩토리 패턴 (0) | 2021.11.11 |