소프트웨어 개발에서 팩토리 패턴은 객체 생성 로직을 캡슐화하여 코드의 유연성과 유지보수성을 높이는 대표적인 디자인 패턴입니다. JavaScript에서는 언어의 유연한 특성을 활용하여 심플 팩토리, 팩토리 메서드, 추상 팩토리라는 세 가지 형태로 주로 구현됩니다.
1. 심플 팩토리 패턴 (Simple Factory)
심플 팩토리 패턴은 하나의 팩토리 객체가 입력된 인자에 따라 적절한 클래스의 인스턴스를 생성하여 반환하는 방식입니다. 주로 비슷한 구조를 가진 객체들을 한곳에서 관리하며 생성할 때 사용됩니다.
// 제품군 정의
const EmailProvider = function() {
this.name = 'Email';
};
EmailProvider.prototype.send = function(msg) {
console.log(`[이메일] 발송: ${msg}`);
};
const SMSProvider = function() {
this.name = 'SMS';
};
SMSProvider.prototype.send = function(msg) {
console.log(`[문자] 발송: ${msg}`);
};
const PushProvider = function() {
this.name = 'Push';
};
PushProvider.prototype.send = function(msg) {
console.log(`[푸시 알림] 발송: ${msg}`);
};
// 심플 팩토리 함수
const MessageFactory = function(type) {
let provider;
switch (type) {
case 'email':
provider = new EmailProvider();
break;
case 'sms':
provider = new SMSProvider();
break;
case 'push':
provider = new PushProvider();
break;
default:
throw new Error('지원하지 않는 타입입니다.');
}
return provider;
};
// 사용 예시
const messenger = MessageFactory('sms');
messenger.send('안녕하세요, 팩토리 패턴 테스트입니다.');
// 출력: [문자] 발송: 안녕하세요, 팩토리 패턴 테스트입니다.
심플 팩토리는 객체 생성 로직을 분리하여 클라이언트 코드가 구체적인 클래스를 알 필요 없게 만듭니다. 하지만 새로운 타입이 추가될 때마다 팩토리 함수 내부의 조건문을 수정해야 한다는 단점이 있습니다.
2. 팩토리 메서드 패턴 (Factory Method)
팩토리 메서드 패턴은 객체 생성을 직접 하지 않고, 이를 서브클래스나 프로토타입에 위임하는 방식입니다. JavaScript에서는 팩토리 함수의 프로토타입에 실제 생성될 객체들의 생성자를 등록하여 동적으로 인스턴스를 생성할 수 있습니다.
// 팩토리 메서드 구현
const DevFactory = function(type, task) {
// 안전한 생성자 호출 처리
if (this instanceof DevFactory) {
return new this[type](task);
} else {
return new DevFactory(type, task);
}
};
// 팩토리 프로토타입에 제품군 등록
DevFactory.prototype = {
Frontend: function(task) {
console.log(`프론트엔드 개발자가 "${task}" 작업을 수행합니다.`);
},
Backend: function(task) {
console.log(`백엔드 개발자가 "${task}" 작업을 수행합니다.`);
},
Design: function(task) {
console.log(`디자이너가 "${task}" 작업을 수행합니다.`);
}
};
// 사용 예시
const tasks = [
{ type: 'Frontend', task: 'React 컴포넌트 개발' },
{ type: 'Backend', task: 'API 서버 구축' },
{ type: 'Design', task: 'UI 시안 제작' }
];
tasks.forEach(item => {
DevFactory(item.type, item.task);
});
이 방식은 핵심 로직을 건드리지 않고도 프로토타입을 확장함으로써 새로운 객체 타입을 유연하게 추가할 수 있다는 장점이 있습니다.
3. 추상 팩토리 패턴 (Abstract Factory)
추상 팩토리 패턴은 구체적인 클래스에 의존하지 않고 서로 연관되거나 의존적인 객체들의 조합을 만들기 위한 인터페이스를 제공합니다. JavaScript에는 추상 클래스 키워드가 없으므로, 메서드 호출 시 에러를 던지는 방식으로 추상 메서드를 흉내 냅니다.
// 추상 팩토리 유틸리티
const AbstractFactory = function(subType, superType) {
if (typeof AbstractFactory[superType] === 'function') {
function Temp() {}
Temp.prototype = new AbstractFactory[superType]();
subType.constructor = subType;
subType.prototype = new Temp();
} else {
throw new Error('존재하지 않는 추상 클래스입니다.');
}
};
// 추상 클래스 정의: 가전제품
AbstractFactory.Appliance = function() {
this.type = 'Appliance';
};
AbstractFactory.Appliance.prototype = {
turnOn: function() { throw new Error('추상 메서드는 직접 호출할 수 없습니다.'); },
getPrice: function() { throw new Error('추상 메서드는 직접 호출할 수 없습니다.'); }
};
// 구체적인 서브클래스: 냉장고
const Refrigerator = function(price) {
this.price = price;
};
// 상속 처리
AbstractFactory(Refrigerator, 'Appliance');
// 추상 메서드 구현
Refrigerator.prototype.turnOn = function() {
console.log('냉장고 전원을 켭니다. 냉각 시스템 가동.');
};
Refrigerator.prototype.getPrice = function() {
console.log(`현재 가격: ${this.price}원`);
};
// 사용 예시
const myFridge = new Refrigerator(1500000);
myFridge.turnOn(); // 냉장고 전원을 켭니다. 냉각 시스템 가동.
myFridge.getPrice(); // 현재 가격: 1500000원
추상 팩토리는 대규모 시스템에서 객체들 사이의 일관성이 중요할 때 유용하며, 명확한 인터페이스 가이드를 제공하여 코드의 규격을 맞추는 데 효과적입니다.