추상 팩토리 패턴을 공부하다가.. 너무너무 이해가 안되는거다..
도대체 이게 뭔데!? 를 반복하다가 팩토리 메서드 패턴을 먼저 공부해야 이해가 될것 같아
팩토리 메서드 패턴부터 공부해보려고 한다.
우선 팩토리 메서드 패턴, 왜 필요한 걸까?
객체 지향 프로그래밍을 하면서 굉장히 자주 아래의 코드를 작성하게 된다.
const coffee = new Americano();
굉장히 단순하고 .. 직관적이다. 그런데 이 방식이 점점 문제를 일으키기 시작하는 시점이 있다.
바로 프로그램이 점점 커지고, 제품 종류도 많아지고 제품 생성 방식이 다양해질 때이다.
문제는 어디서 시작되는 것일까?
예를 들어 커피 전문점 프로그램을 만든다고 가정할때
처음엔 아메리카노 하나만 만들면 됐기 떄문에 new Americano() 만 해도 괜찮았다.
그런데 점점 ..
- 라떼, 카푸치노, 바닐라라떼 등 종류가 늘어나게 되고
- 각각 제조하는 방식도 달라지고
- 어느 커피를 만들지 상황마다 달라져야 하고
- 커피 종류를 결정하는 조건문이 곳곳에 등장하게 되고..
이렇게 되면 결과는
- 코드가 중복되고
- 수정하기 어렵고
- 유지 보수가 불편하고
- 새 제품을 추가할 때마다 기존 코드를 뜯어고쳐야하는 상황이 발생된다.
그럼 카페로 계속 예시를 들면서 이야기 해보면
만약 (팩토리 메서드 없이!) 모든 직원들이 커피를 직접 만드는 구조라고 가정해볼때
1. 물 끓이고
2. 커피 원두 갈고
3. 컵에 담고
4. 고객에게 전달 하는 과정으로 커피가 제조되고 전달된다.
그런데 어떤 고객은 아메리카노, 또 어떤 고객은 라떼를 원한다.
그런데 직원마다 커피 레시피를 직접 관리하고 있다면..?
- 누군가는 커피를 너무 진하게 만들고
- 누군가는 우유를 넣지 않고
- 누군가는 시럽을 너무 많이 넣고..
=> 비효율 적이고 품질이 불균형 하다는 단점이 존재한다..
이런 상황에서 커피 머신 ( = 팩토리 메서드 패턴) 이 도입 되는 것이다!
팩토리 메서드 패턴은 예를 들자면 " 커피를 자동으로 뽑아주는 머신 "을 도입하는 것이다!
- 모든 커피는 brew() 라는 동일한 버튼을 누르면 만들어 지고
- 직원은 커피를 어떻게 만드는지 몰라도 원하는 커피를 받을 수 있다.
- 어떤 커피를 만들지는 머신 안에 숨겨져 있다.
그.래.서 이제 새로운 커피가 메뉴에 추가되어도
- 머신 안에 새로운 메뉴를 제조하는 새로운 모듈만 추가하면 되고
- 바깥 코드는 그대로 사용 가능 하다
앞에서 비유를 통해서 팩토리 메서드 패턴에 대해서 이해해 보았다면 이젠 이 패턴이 프로그래밍에서 어떤 역할을 하는지 정리해보자
팩토리 메서드 패턴은 객체 생성 코드 & 사용 코드를 분리해서 유연하고 확장 가능한 구조로 만들기 위해 사용된다.
객체 생성 책임을 자식 클래스에 위임하는 생성 패턴이며 부모 클래스는 어떤 구체 객체를 생성할 지 모른다는 것이고
그 생성 책임을 자식 클래스의 메서드가 떠안고 있다는 의미이다.
아래에서 예시를 통해 실제로 팩토리 메서드 패턴이 어떻게 활용되는지 알아보자
계속 카페 예시를 들었으니 이번에도 카페로 예시를 들어 확인해보려고 한다!
우선 전체 코드는 아래와 같다!
// factory-method-coffee.ts
// 1. 제품 인터페이스
interface Coffee {
prepare(): string;
}
// 2. 구체적인 커피들
class Americano implements Coffee {
prepare(): string {
return "아메리카노를 준비합니다: 에스프레소 + 물";
}
}
class Latte implements Coffee {
prepare(): string {
return "라떼를 준비합니다: 에스프레소 + 우유";
}
}
class VanillaLatte implements Coffee {
prepare(): string {
return "바닐라라떼를 준비합니다: 에스프레소 + 우유 + 바닐라 시럽";
}
}
// 3. 추상 커피 자판기 (Creator)
abstract class CoffeeMachine {
// 공통 로직
public makeCoffee(): string {
const coffee = this.createCoffee(); // 팩토리 메서드 호출
return coffee.prepare();
}
// 팩토리 메서드
protected abstract createCoffee(): Coffee;
}
// 4. 구체적인 커피 자판기들 (Concrete Creators)
class AmericanoMachine extends CoffeeMachine {
protected createCoffee(): Coffee {
return new Americano();
}
}
class LatteMachine extends CoffeeMachine {
protected createCoffee(): Coffee {
return new Latte();
}
}
class VanillaLatteMachine extends CoffeeMachine {
protected createCoffee(): Coffee {
return new VanillaLatte();
}
}
// 5. 클라이언트 코드
function orderCoffee(machine: CoffeeMachine) {
console.log("손님: 커피 하나 주세요!");
console.log(machine.makeCoffee());
}
// 6. 실행
orderCoffee(new AmericanoMachine());
console.log("---");
orderCoffee(new LatteMachine());
console.log("---");
orderCoffee(new VanillaLatteMachine());
과연 결과는?

이렇게 주문이 들어간 순서대로 커피가 나오는 것을 확인할 수 있다.
그럼 코드를 살펴보며 팩토리 메서드 패턴의 구조를 파악해보자!
1. 제품 인터페이스 : Coffee
interface Coffee {
prepare(): string;
}
모든 커피는 prepare() 라는 메서드를 가진다.
어떤 커피든 만들어 지는 방식은 다르지만 이 인터페이스는 공통으로 사용된다.
2. 다양한 커피들
class Americano implements Coffee {
prepare(): string {
return "아메리카노를 준비합니다: 에스프레소 + 물";
}
}
class Latte implements Coffee {
prepare(): string {
return "라떼를 준비합니다: 에스프레소 + 우유";
}
}
class VanillaLatte implements Coffee {
prepare(): string {
return "바닐라라떼를 준비합니다: 에스프레소 + 우유 + 바닐라 시럽";
}
}
각 커피는 Coffee 인터페이스를 구현하고 prepare()는 실제 제조 로직을 나타낸다.
3. 추상 커피 자판기 ( 팩토리 메서드 정의 )
abstract class CoffeeMachine {
// 템플릿 메서드: 외부에서는 이 메서드만 사용
public makeCoffee(): string {
const coffee = this.createCoffee(); // 팩토리 메서드 호출!
return coffee.prepare();
}
// 팩토리 메서드: 어떤 커피를 만들지는 자식이 정의
protected abstract createCoffee(): Coffee;
}
makeCoffee() 는 외부에 노출된 공통 로직이다.
createCoffee() 는 자식이 어떤 커피를 만들지 결정한다. ( 팩토리 메서드를 호출 하는 곳이다 )
4. 구체적인 자판기들
class AmericanoMachine extends CoffeeMachine {
protected createCoffee(): Coffee {
return new Americano();
}
}
class LatteMachine extends CoffeeMachine {
protected createCoffee(): Coffee {
return new Latte();
}
}
class VanillaLatteMachine extends CoffeeMachine {
protected createCoffee(): Coffee {
return new VanillaLatte();
}
}
각 머신은 어떤 커피를 만들지를 정하고 다른 자판기로 바꾸기만 하면 다른 커피가 나오게 된다.
5. 클라이언트 코드
function orderCoffee(machine: CoffeeMachine) {
console.log("손님: 커피 하나 주세요!");
console.log(machine.makeCoffee());
}
따라서 클라이언트 에서는 생성 로직을 몰라도 사용이 가능하다.
예시를 통해 다시한번 팩토리 메서드 패턴의 정의를 살펴보면
CoffeeMachine은 부모 클래스이다. 이 클래스는 createCoffee()가 어떤 구체 클래스를 생성하는지 모른다.
단지 "나는 Coffee 타입의 무언가를 받을거야" 라고 약속할뿐이다.
즉, new Americano() / new Latte() 같은 구체 생성자는 이 안에 없다..!
자식 클래스인 AmericanoMachine는 abstract createCoffee()라는 추상 메서드를 선언하고
실제로 new Americano() 와 같은 구체 생성은 자식 클래스에서만 이루어 지는 구조이며
이렇게 객체 생성 책임을 자식 클래스에 위임한 다는 것을 확인할 수 있다.
만약 팩토리 메서드를 사용하지 않았다면...?
function makeCoffee(type: string) {
if (type === "americano") {
return new Americano();
} else if (type === "latte") {
return new Latte();
} else if (type === "vanillaLatte") {
return new VanillaLatte();
}
// ...이후 계속 추가됨
}
이렇게 조건문이 계속 늘어나게 되고 새로운 커피를 추가할 때마다 makeCoffee()를 고쳐야 한다. 이는 OCP 위반 으로 이어지게 된다. ㅠㅠ ( OCP : 개방 - 폐쇄 원칙 - 기존 코드를 건드리지 않고 확장할 수 있어야함 )
그런데 팩토리 메서드에서는?
class HazelnutLatteMachine extends CoffeeMachine {
protected createCoffee(): Coffee {
return new HazelnutLatte();
}
}
기존 CoffeeMachine 구조는 단 1줄도 고치지 않아도 새로운 커피가 생기면 그에 맞는 새로운 자식 클래스만 만들면 되고
클라이언트는 그냥 orderCoffee(new HazelnutLatteMachine())만 호출하면 된다!
팩토리 메서드 패턴의 구조를 UML로 표현해보자면 아래와 같다.

위 UML 구조로 각 요소를 설명하자면
(1) CoffeeMachine (추상 클래스)
- makeCoffee() 템플릿 메서드
- createCoffee() 팩토리 메서드 (abstract) 자식이 구현함.
(2) 자식 클래스들
- AmericanoMachine, LatteMachine, VanillaLatteMachine
- createCoffee() 를 오버라이드해서 구체 객체 생성 책임을 맡음
(3) Coffee (제품 인터페이스)
- prepare() 메서드 정의
- 어떤 커피든 prepare() 메서드를 구현해야함
(4) 구체 제품
- Americano, Latte, VanillaLatte
- Coffee 인터페이스를 구현하고 실제 동작정의
Reference
https://refactoring.guru/ko/design-patterns/factory-method
팩토리 메서드 패턴
/ 디자인 패턴들 / 생성 패턴 팩토리 메서드 패턴 다음 이름으로도 불립니다: 가상 생성자, Factory Method 의도 팩토리 메서드는 부모 클래스에서 객체들을 생성할 수 있는 인터페이스를 제공하지
refactoring.guru
https://patterns-dev-kr.github.io/design-patterns/factory-pattern/
Factory 패턴
📜 원문: patterns.dev - factory pattern 팩토리 패턴을 사용하면 함수를 호출하는 것으로 객체를 만들어낼 수 있다. new 키워드를 사용하는 대신 함수 호출의 결과로 객체를 만들 수 있는 것이다. 앱에
patterns-dev-kr.github.io
'공부 > 디자인 패턴' 카테고리의 다른 글
| [디자인 패턴] MVC 와 MVVM 패턴 이해하기 (0) | 2026.01.29 |
|---|---|
| 디자인 패턴 - 추상 팩토리 패턴(Abstract Factory) (0) | 2025.06.28 |
| 디자인 패턴 - 싱글톤 (0) | 2025.06.27 |