diff --git "a/jihyeon/03.\353\215\260\354\275\224\353\240\210\354\235\264\355\204\260_\355\214\250\355\204\264.md" "b/jihyeon/03.\353\215\260\354\275\224\353\240\210\354\235\264\355\204\260_\355\214\250\355\204\264.md" new file mode 100644 index 0000000..d37349e --- /dev/null +++ "b/jihyeon/03.\353\215\260\354\275\224\353\240\210\354\235\264\355\204\260_\355\214\250\355\204\264.md" @@ -0,0 +1,811 @@ +# 데코레이터 패턴 완벽 가이드: 객체에 동적 기능 부여하기 + +> 상속 없이 객체에 새로운 기능을 추가할 수 있는 방법 + +## 문제 상황 + +### 커피숍 주문 시스템 예시 + +커피숍 주문 시스템을 개발한다고 가정한다. + +``` +기본 음료: 에스프레소, 다크로스트, 하우스블렌드 +추가 옵션: 모카, 휘핑, 두유, 우유 +``` + +### 상속으로 접근할 경우 + +``` +Espresso +├── EspressoWithMocha +├── EspressoWithWhip +├── EspressoWithMochaAndWhip +├── EspressoWithMochaAndWhipAndSoy +└── ... (조합 폭발) +``` + +5개 음료와 4개 토핑의 조합은 수십 개의 클래스를 필요로 한다. + +### 문제점 + +- 토핑 가격 변경 시 모든 관련 서브클래스 수정 필요 +- 새로운 토핑 추가 시 클래스 수 기하급수적 증가 +- 토핑 적용 순서 추적 불가 + +## 데코레이터 패턴이란 + +### 정의 + +> 객체에 동적으로 추가 책임을 부여하는 패턴. 서브클래싱보다 유연하게 기능을 확장할 수 있다. + +### 핵심 개념 + +객체를 감싸는(wrapping) 방식으로 기능을 추가한다. 선물 상자에 포장지를 덧씌우는 것과 유사하다. + +- 상자: 기본 객체 +- 포장지: 첫 번째 데코레이터 +- 리본: 두 번째 데코레이터 +- 카드: 세 번째 데코레이터 + +## 실생활 비유 + +### 피자 주문 + +``` +기본: 얇은 도우 피자 (10,000원) + ↓ ++ 치즈 토핑 (+2,000원) + ↓ ++ 올리브 토핑 (+1,000원) + ↓ +최종: 얇은 도우 + 치즈 + 올리브 (13,000원) +``` + +### 의상 착용 + +``` +사람 (기본) + ↓ +속옷 착용 (데코레이터 1) + ↓ +티셔츠 착용 (데코레이터 2) + ↓ +재킷 착용 (데코레이터 3) + ↓ +코트 착용 (데코레이터 4) +``` + +## 패턴 구조 + +### 구성 요소 + +``` +┌─────────────────────────────────────┐ +│ Component (추상) │ +│ + operation() │ +└──────────────┬──────────────────────┘ + │ + ┌───────┴───────┐ + │ │ +┌──────▼──────┐ ┌─────▼────────────┐ +│ Concrete │ │ Decorator │ +│ Component │ │ - component │ +│ │ │ + operation() │ +└─────────────┘ └────────┬─────────┘ + │ + ┌──────┴──────┐ + │ │ + ┌──────▼──────┐ ┌────▼──────┐ + │ ConcreteA │ │ ConcreteB │ + │ Decorator │ │ Decorator │ + └─────────────┘ └───────────┘ +``` + +### 역할 설명 + +| 역할 | 설명 | 예시 | +| ----------------- | ------------------------------ | ------------------- | +| Component | 추상 클래스 또는 인터페이스 | Beverage | +| ConcreteComponent | 기본 구현체 | DarkRoast, Espresso | +| Decorator | Component를 감싸는 추상 클래스 | CondimentDecorator | +| ConcreteDecorator | 실제 기능 추가 구현 | Mocha, Whip, Soy | + +## 코드 구현 + +### 1단계: Component 정의 + +```typescript +// Beverage.ts - 모든 음료의 기반 클래스 +export abstract class Beverage { + protected description: string = "Unknown Beverage"; + + getDescription(): string { + return this.description; + } + + abstract cost(): number; +} +``` + +### 2단계: ConcreteComponent 구현 + +```typescript +// DarkRoast.ts - 기본 다크로스트 커피 +export class DarkRoast extends Beverage { + constructor() { + super(); + this.description = "Dark Roast Coffee"; + } + + cost(): number { + return 0.99; + } +} + +// Espresso.ts - 에스프레소 +export class Espresso extends Beverage { + constructor() { + super(); + this.description = "Espresso"; + } + + cost(): number { + return 1.99; + } +} + +// HouseBlend.ts - 하우스 블렌드 +export class HouseBlend extends Beverage { + constructor() { + super(); + this.description = "House Blend Coffee"; + } + + cost(): number { + return 0.89; + } +} +``` + +### 3단계: Decorator 정의 + +```typescript +// CondimentDecorator.ts - 모든 토핑의 기반 클래스 +export abstract class CondimentDecorator extends Beverage { + protected beverage!: Beverage; + + abstract getDescription(): string; +} +``` + +### 4단계: ConcreteDecorator 구현 + +```typescript +// Mocha.ts - 모카 토핑 +export class Mocha extends CondimentDecorator { + constructor(beverage: Beverage) { + super(); + this.beverage = beverage; + } + + getDescription(): string { + return this.beverage.getDescription() + ", Mocha"; + } + + cost(): number { + return 0.2 + this.beverage.cost(); + } +} + +// Whip.ts - 휘핑 토핑 +export class Whip extends CondimentDecorator { + constructor(beverage: Beverage) { + super(); + this.beverage = beverage; + } + + getDescription(): string { + return this.beverage.getDescription() + ", Whip"; + } + + cost(): number { + return 0.1 + this.beverage.cost(); + } +} + +// Soy.ts - 두유 토핑 +export class Soy extends CondimentDecorator { + constructor(beverage: Beverage) { + super(); + this.beverage = beverage; + } + + getDescription(): string { + return this.beverage.getDescription() + ", Soy"; + } + + cost(): number { + return 0.15 + this.beverage.cost(); + } +} +``` + +### 5단계: 클라이언트 코드 + +```typescript +// StarbuzzCoffee.ts - 주문 시스템 +export class StarbuzzCoffee { + static main(): void { + console.log("=== Starbuzz Coffee Orders ===\n"); + + // 주문 1: 에스프레소 (기본) + let beverage1 = new Espresso(); + console.log(`${beverage1.getDescription()}`); + console.log(`$${beverage1.cost().toFixed(2)}\n`); + + // 주문 2: 다크로스트 + 모카 2샷 + 휘핑 + let beverage2 = new DarkRoast(); // $0.99 + beverage2 = new Mocha(beverage2); // +$0.20 + beverage2 = new Mocha(beverage2); // +$0.20 + beverage2 = new Whip(beverage2); // +$0.10 + // 총: $1.49 + + console.log(`${beverage2.getDescription()}`); + console.log(`$${beverage2.cost().toFixed(2)}\n`); + + // 주문 3: 하우스블렌드 + 두유 + 모카 + 휘핑 + let beverage3 = new HouseBlend(); // $0.89 + beverage3 = new Soy(beverage3); // +$0.15 + beverage3 = new Mocha(beverage3); // +$0.20 + beverage3 = new Whip(beverage3); // +$0.10 + // 총: $1.34 + + console.log(`${beverage3.getDescription()}`); + console.log(`$${beverage3.cost().toFixed(2)}\n`); + } +} + +StarbuzzCoffee.main(); +``` + +### 실행 결과 + +``` +=== Starbuzz Coffee Orders === + +Espresso +$1.99 + +Dark Roast Coffee, Mocha, Mocha, Whip +$1.49 + +House Blend Coffee, Soy, Mocha, Whip +$1.34 +``` + +### 동작 원리 분석 + +```typescript +// 주문 2의 동작 과정 +let beverage2 = new DarkRoast(); +// beverage2.cost() → 0.99 + +beverage2 = new Mocha(beverage2); +// beverage2.cost() → 0.20 + DarkRoast.cost() = 1.19 + +beverage2 = new Mocha(beverage2); +// beverage2.cost() → 0.20 + Mocha(DarkRoast).cost() = 1.39 + +beverage2 = new Whip(beverage2); +// beverage2.cost() → 0.10 + Mocha(Mocha(DarkRoast)).cost() = 1.49 +``` + +--- + +## 장단점 + +### 장점 + +| 장점 | 설명 | +| ------------------- | ------------------------------------------- | +| 유연한 확장 | 상속 없이 런타임에 기능 추가 및 제거 가능 | +| 조합의 자유로움 | 데코레이터들을 자유롭게 조합 가능 | +| 단일 책임 원칙 준수 | 각 데코레이터는 하나의 책임만 가짐 | +| 개방-폐쇄 원칙 준수 | 기존 코드 수정 없이 확장 가능 | +| 클래스 폭발 방지 | 5음료 × 4토핑 = 9클래스 (상속 시 20개 이상) | + +### 단점 + +| 단점 | 설명 | +| ---------------- | --------------------------------------- | +| 작은 클래스 다수 | 각 기능마다 별도 클래스 필요 | +| 복잡성 증가 | 데코레이터 스택 구조 이해 필요 | +| 디버깅 난이도 | 여러 레이어의 데코레이터 추적 필요 | +| 타입 정보 손실 | 구체 컴포넌트의 타입 확인 불가 | +| 객체 식별 문제 | 래핑된 객체는 원본과 다른 객체로 식별됨 | + +## 실무 활용 사례 + +### 1. Java I/O 스트림 + +```java +InputStream is = new BufferedInputStream( + new FileInputStream("file.txt") + ); +``` + +### 2. TypeScript 메서드 데코레이터 + +```typescript +function Log(target: any, key: string, descriptor: PropertyDescriptor) { + const original = descriptor.value; + + descriptor.value = async function (...args: any[]) { + console.log(`Calling ${key}`, args); + const result = await original.apply(this, args); + console.log(`Result:`, result); + return result; + }; + + return descriptor; +} + +class UserService { + @Log + async getUser(id: number) { + return fetch(`/users/${id}`); + } +} +``` + +### 3. NestJS 데코레이터 + +```typescript +@Controller("users") +@UseGuards(AuthGuard) +@UseInterceptors(LoggingInterceptor) +export class UsersController { + @Get() + @Roles("admin") + findAll() { + return this.usersService.findAll(); + } +} +``` + +### 4. Express 미들웨어 + +```typescript +app.use(cors()); +app.use(helmet()); +app.use(morgan("dev")); +app.use(express.json()); +``` + +## 프론트엔드 활용 예시 + +### 1. React HOC (Higher-Order Component) + +React의 HOC는 데코레이터 패턴의 대표적인 프론트엔드 구현이다. + +```tsx +// 기본 컴포넌트 +const UserProfile = ({ user }: { user: User }) =>
(Component: React.ComponentType
) {
+ return function AuthenticatedComponent(props: P) {
+ const { user, isLoading } = useAuth();
+
+ if (isLoading) return
(Component: React.ComponentType
) {
+ return function LoggedComponent(props: P) {
+ useEffect(() => {
+ console.log(`${Component.displayName} mounted`);
+ }, []);
+
+ return
( + Component: React.ComponentType
, +) { + return class ErrorBoundary extends React.Component
{
+ state = { hasError: false };
+
+ static getDerivedStateFromError() {
+ return { hasError: true };
+ }
+
+ render() {
+ if (this.state.hasError) {
+ return
( + Component: React.ComponentType
,
+ requiredRole: string,
+) {
+ return function PermissionCheckedComponent(props: P) {
+ const { user } = useAuth();
+
+ if (!user?.roles.includes(requiredRole)) {
+ return
(
+ Component: React.ComponentType<
+ P & { data: any; loading: boolean; error: Error | null }
+ >,
+ fetchData: () => Promise ,
+ ) {
+ const [data, setData] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState (
+ Component: React.ComponentType ,
+ styles: CSSProperties,
+) {
+ const StyledComponent = styled(Component) `
+ ${Object.entries(styles)
+ .map(([key, value]) => `${key}: ${value};`)
+ .join("\n")}
+ `;
+
+ return StyledComponent;
+}
+
+// 테마 데코레이터
+function withTheme (Component: React.ComponentType ) {
+ return function ThemedComponent(props: P) {
+ const theme = useTheme();
+ return (Component: React.ComponentType ) {
+ return function DarkModeAware(props: P) {
+ const isDark = useMediaQuery("(prefers-color-scheme: dark)");
+ return (
+ Component: React.ComponentType ,
+ areEqual?: (prevProps: P, nextProps: P) => boolean,
+) {
+ return React.memo(Component, areEqual);
+}
+
+// 지연 로딩 데코레이터
+function withLazyLoading (
+ importFn: () => Promise<{ default: React.ComponentType }>,
+) {
+ const LazyComponent = React.lazy(importFn);
+
+ return function LazyWrapper(props: P) {
+ return (
+ (
+ Component: React.ComponentType ,
+ validators: Array<(value: string) => string | null>,
+) {
+ return function ValidatedInput(props: P) {
+ const [error, setError] = useState (
+ Component: React.ComponentType ,
+ eventName: string,
+) {
+ return function TrackedComponent(props: P) {
+ useEffect(() => {
+ analytics.track(`${eventName}_viewed`);
+ }, []);
+
+ const handleClick = (e: React.MouseEvent) => {
+ analytics.track(`${eventName}_clicked`);
+ props.onClick?.(e);
+ };
+
+ return (
+ ComponentA: React.ComponentType ,
+ ComponentB: React.ComponentType ,
+ testId: string,
+) {
+ return function ABTestWrapper(props: P) {
+ const variant = useABTestVariant(testId);
+ return variant === "A" ? (
+ {product.price}원 (
+ Component: React.ComponentType ,
+ decorators: Array<(c: React.ComponentType ) => React.ComponentType >,
+) {
+ return decorators.reduce((acc, decorator) => decorator(acc), Component);
+}
+
+// 사용
+const FinalProductCard = useDecoratedComponent(ProductCard, [
+ withMemo,
+ withDarkMode,
+ (c) => withTracking(c, "product_card"),
+ withErrorBoundary,
+]);
+```
+
+## 상속과 데코레이터 비교
+
+### 상속 방식
+
+```typescript
+// 클래스 폭발 발생
+class Beverage {
+ cost() {
+ return 1.0;
+ }
+}
+
+class BeverageWithMocha extends Beverage {
+ cost() {
+ return 1.2;
+ }
+}
+
+class BeverageWithMochaAndWhip extends BeverageWithMocha {
+ cost() {
+ return 1.3;
+ }
+}
+```
+
+### 데코레이터 방식
+
+```typescript
+// 조합 가능
+let beverage = new Beverage();
+beverage = new Mocha(beverage);
+beverage = new Whip(beverage);
+```
+
+### 비교표
+
+| 기준 | 상속 | 데코레이터 |
+| --------- | ------------------ | ------------- |
+| 확장 시점 | 컴파일 타임 (정적) | 런타임 (동적) |
+| 클래스 수 | 많음 (조합 폭발) | 적음 |
+| 유연성 | 낮음 | 높음 |
+| 복잡성 | 낮음 | 중간 |
+| 기능 제거 | 어려움 | 쉬움 |
+
+## 정리
+
+### 핵심 요약
+
+```
+데코레이터 패턴 = 객체 감싸기 + 기능 위임
+
+적용 상황:
+- 상속으로 클래스 폭발 발생
+- 동적으로 기능 추가/제거 필요
+- 다양한 조합 필요
+
+목적:
+- 유연한 확장
+- SRP/OCP 준수
+- 클래스 수 최소화
+```
+
+### 사용 권장 상황
+
+- 피자 토핑, 커피 옵션 같은 조합 가능한 기능
+- 로깅, 캐싱, 압축 같은 횡단 관심사
+- 상속이 클래스 폭발을 일으킬 때
+- 런타임에 동적으로 기능 변경이 필요할 때
+
+### 프론트엔드 적용 포인트
+
+| 영역 | 데코레이터 적용 |
+| --------- | -------------------------------------------- |
+| 인증/인가 | withAuth, withPermission |
+| 데이터 | withDataFetching, withCache |
+| UI | withTheme, withDarkMode, withStyled |
+| 성능 | withMemo, withLazyLoading |
+| 품질 | withErrorBoundary, withLogging, withTracking |
+| 폼 | withValidation |
+
+### 관련 패턴
+
+- 어댑터 패턴: 인터페이스 변환
+- 컴포지트 패턴: 객체 트리 구조
+- 체인 오브 리스판서빌리티: 요청 처리 체인
+- 프록시 패턴: 접근 제어
diff --git "a/jihyeon/04.\355\214\251\355\206\240\353\246\254_\355\214\250\355\204\264.md" "b/jihyeon/04.\355\214\251\355\206\240\353\246\254_\355\214\250\355\204\264.md"
new file mode 100644
index 0000000..b51f9ae
--- /dev/null
+++ "b/jihyeon/04.\355\214\251\355\206\240\353\246\254_\355\214\250\355\204\264.md"
@@ -0,0 +1,703 @@
+# 팩토리 패턴 (Factory Pattern)
+
+## 1. 왜 팩토리 패턴이 필요한가?
+
+### 문제 상황
+
+피자 가게에서 여러 종류의 피자를 만든다고 가정한다.
+
+```typescript
+// 팩토리 패턴 없이 - 문제가 많은 코드
+class PizzaStore {
+ orderPizza(type: string): Pizza {
+ let pizza: Pizza;
+
+ // 타입별로 직접 객체 생성
+ if (type === "cheese") {
+ pizza = new CheesePizza();
+ } else if (type === "pepperoni") {
+ pizza = new PepperoniPizza();
+ } else if (type === "clam") {
+ pizza = new ClamPizza();
+ } else if (type === "veggie") {
+ pizza = new VeggiePizza();
+ }
+
+ pizza.prepare();
+ pizza.bake();
+ pizza.cut();
+ pizza.box();
+
+ return pizza;
+ }
+}
+```
+
+### 이 코드의 문제점
+
+| 문제 | 설명 |
+| -------------------- | -------------------------------------------------------------------- |
+| 🔴 **OCP 위반** | 새로운 피자 타입을 추가할 때마다 `orderPizza()` 메서드를 수정해야 함 |
+| 🔴 **높은 결합도** | PizzaStore가 모든 구체적인 Pizza 클래스에 의존 |
+| 🔴 **중복 코드** | 다른 가게에서도 같은 분기문이 반복됨 |
+| 🔴 **테스트 어려움** | PizzaStore를 테스트할 때 모든 Pizza 구현체가 필요 |
+
+### 팩토리 패턴의 해결책
+
+> **"객체 생성(new)을 직접 하지 말고, 누군가에게 맡겨라"**
+
+---
+
+## 2. Simple Factory (간단 팩토리)
+
+### 개념
+
+객체 생성 로직을 별도의 클래스로 분리하는 가장 단순한 형태
+
+> **Note**: 정식 패턴은 아니지만, 널리 사용되는 관용구
+
+### 구조
+
+```
+┌─────────────────┐ ┌──────────────────────┐
+│ PizzaStore │────────>│ SimplePizzaFactory │
+│─────────────────│ │──────────────────────│
+│ - factory │ │ + createPizza(type) │
+│ + orderPizza() │ └──────────────────────┘
+└─────────────────┘ │
+ ↓
+ ┌───────────────────────┐
+ │ Pizza (interface) │
+ └───────────────────────┘
+ ↑
+ ┌─────────────────────┼─────────────────────┐
+ │ │ │
+ CheesePizza PepperoniPizza VeggiePizza
+```
+
+### 실제 코드
+
+#### SimplePizzaFactory.ts
+
+```typescript
+// 📁 factory/pizzas/SimplePizzaFactory.ts
+export class SimplePizzaFactory {
+ createPizza(type: string): Pizza {
+ let pizza = null;
+
+ if (type.equals("cheese")) {
+ pizza = new CheesePizza();
+ } else if (type.equals("pepperoni")) {
+ pizza = new PepperoniPizza();
+ } else if (type.equals("clam")) {
+ pizza = new ClamPizza();
+ } else if (type.equals("veggie")) {
+ pizza = new VeggiePizza();
+ }
+
+ return pizza;
+ }
+}
+```
+
+#### PizzaStore.ts
+
+```typescript
+// 📁 factory/pizzas/PizzaStore.ts
+export class PizzaStore {
+ factory: SimplePizzaFactory;
+
+ constructor(factory: SimplePizzaFactory) {
+ this.factory = factory;
+ }
+
+ orderPizza(type: string): Pizza {
+ const pizza = this.factory.createPizza(type); // 위임!
+
+ pizza.prepare();
+ pizza.bake();
+ pizza.cut();
+ pizza.box();
+
+ return pizza;
+ }
+}
+```
+
+### 사용 예시
+
+```typescript
+const factory = new SimplePizzaFactory();
+const store = new PizzaStore(factory);
+const pizza = store.orderPizza("cheese");
+```
+
+### 장단점
+
+| 장점 | 단점 |
+| --------------------------------------------------- | ------------------------------------------------- |
+| 생성 로직이 한 곳에 집중 | 새로운 피자 타입 추가 시 팩토리 코드 수정 필요 |
+| PizzaStore는 구체적인 Pizza 타입을 몰라도 됨 | 확장에는 열려있지 않음 (OCP 완벽하게 지키지 못함) |
+| 재사용 가능 (다른 가게에서도 같은 팩토리 사용 가능) | |
+
+### 언제 사용할까?
+
+- 생성 로직이 단순하고 자주 변하지 않을 때
+- 한 가지 타입의 객체만 생성할 때
+- 빠르게 리팩토링하고 싶을 때
+
+---
+
+## 3. Factory Method (팩토리 메서드)
+
+### 개념
+
+객체 생성을 **서브클래스에게 위임**하는 패턴
+
+- 부모 클래스는 "무엇을 할지" 정의 (템플릿)
+- 자식 클래스가 "어떻게 만들지" 결정 (구현)
+
+### 구조
+
+```
+ PizzaStore (abstract)
+ ├── orderPizza() // 공통 로직 (변하지 않음)
+ └── createPizza() // 추상 메서드 (서브클래스가 구현)
+ ↑
+ ───────┴───────
+ ↑ ↑
+ NYPizzaStore ChicagoPizzaStore
+ createPizza() createPizza()
+ ↓ ↓
+ NYStyle... ChicagoStyle...
+```
+
+### 실제 코드
+
+#### PizzaStore.ts (추상 클래스)
+
+```typescript
+// 📁 factory/pizzafm/PizzaStore.ts
+export abstract class PizzaStore {
+ // ⭐ 팩토리 메서드 - 서브클래스가 구현해야 함
+ abstract createPizza(item: string): Pizza;
+
+ // 공통 로직은 부모가 관리 (템플릿 메서드)
+ orderPizza(type: string): Pizza {
+ const pizza = this.createPizza(type); // 서브클래스에게 위임!
+
+ console.log("--- Making a " + pizza.getName() + " ---");
+ pizza.prepare();
+ pizza.bake();
+ pizza.cut();
+ pizza.box();
+
+ return pizza;
+ }
+}
+```
+
+#### NYPizzaStore.ts (구체적인 서브클래스)
+
+```typescript
+// 📁 factory/pizzafm/NYPizzaStore.ts
+export class NYPizzaStore extends PizzaStore {
+ createPizza(item: string): Pizza {
+ if (item.equals("cheese")) {
+ return new NYStyleCheesePizza(); // 뉴욕 스타일!
+ } else if (item.equals("veggie")) {
+ return new NYStyleVeggiePizza();
+ } else if (item.equals("clam")) {
+ return new NYStyleClamPizza();
+ } else if (item.equals("pepperoni")) {
+ return new NYStylePepperoniPizza();
+ }
+ return null;
+ }
+}
+```
+
+#### ChicagoPizzaStore.ts
+
+```typescript
+// 📁 factory/pizzafm/ChicagoPizzaStore.ts
+export class ChicagoPizzaStore extends PizzaStore {
+ createPizza(item: string): Pizza {
+ if (item.equals("cheese")) {
+ return new ChicagoStyleCheesePizza(); // 시카고 스타일!
+ } else if (item.equals("veggie")) {
+ return new ChicagoStyleVeggiePizza();
+ }
+ // ... 다른 피자들
+ return null;
+ }
+}
+```
+
+### 사용 예시
+
+```typescript
+const nyStore = new NYPizzaStore();
+const chicagoStore = new ChicagoPizzaStore();
+
+const pizza1 = nyStore.orderPizza("cheese"); // 뉴욕 스타일 치즈 피자
+const pizza2 = chicagoStore.orderPizza("cheese"); // 시카고 스타일 치즈 피자
+```
+
+### 실행 흐름
+
+```
+1. nyStore.orderPizza("cheese") 호출
+ ↓
+2. PizzaStore.orderPizza() 실행
+ ↓
+3. this.createPizza("cheese") 호출 (다형성!)
+ ↓
+4. NYPizzaStore.createPizza() 실행
+ ↓
+5. NYStyleCheesePizza 반환
+ ↓
+6. prepare() → bake() → cut() → box() 실행
+```
+
+### 장단점
+
+| 장점 | 단점 |
+| ----------------------------------------------------------------- | ---------------------------- |
+| **OCP 완벽 준수**: 새로운 피자 스타일 추가 시 기존 코드 수정 없음 | 클래스 수 증가 |
+| **확장성**: 새로운 가게 추가가 서브클래스 생성만으로 가능 | 상속 구조로 인한 유연성 부족 |
+| **공통 로직 보호**: `orderPizza()`는 한 곳에서 관리 | |
+
+### 언제 사용할까?
+
+- 라이브러리/프레임워크에서 확장 포인트를 제공할 때
+- 클라이언트가 구체적인 타입을 알 필요 없을 때
+- 템플릿 메서드 패턴과 함께 사용할 때
+
+---
+
+## 4. Abstract Factory (추상 팩토리)
+
+### 개념
+
+**연관된 객체들의 군(family)**을 생성하기 위한 인터페이스를 제공
+
+- 구체적인 클래스는 지정하지 않음
+- 제품군 간의 일관성 보장
+
+### 구조
+
+```
+ PizzaIngredientFactory (interface)
+ │
+ ┌───────────────┼───────────────┐
+ │ │ │
+ createDough() createSauce() createCheese() ...
+ │
+ ↓
+┌───────────────────────┐
+│ NYPizzaIngredient │
+│ Factory │
+├───────────────────────┤
+│ createDough() │──> ThinCrustDough
+│ createSauce() │──> MarinaraSauce
+│ createCheese() │──> ReggianoCheese
+│ createClam() │──> FreshClams
+└───────────────────────┘
+
+┌───────────────────────┐
+│ ChicagoPizza │
+│ IngredientFactory │
+├───────────────────────┤
+│ createDough() │──> ThickCrustDough
+│ createSauce() │──> PlumTomatoSauce
+│ createCheese() │──> MozzarellaCheese
+│ createClam() │──> FrozenClams
+└───────────────────────┘
+```
+
+### 실제 코드
+
+#### PizzaIngredientFactory.ts (인터페이스)
+
+```typescript
+// 📁 factory/pizzaaf/PizzaIngredientFactory.ts
+export interface PizzaIngredientFactory {
+ createDough(): Dough;
+ createSauce(): Sauce;
+ createCheese(): Cheese;
+ createVeggies(): Veggies[];
+ createPepperoni(): Pepperoni;
+ createClam(): Clams;
+}
+```
+
+#### NYPizzaIngredientFactory.ts
+
+```typescript
+// 📁 factory/pizzaaf/NYPizzaIngredientFactory.ts
+export class NYPizzaIngredientFactory implements PizzaIngredientFactory {
+ createDough(): Dough {
+ return new ThinCrustDough(); // 얇은 도우
+ }
+
+ createSauce(): Sauce {
+ return new MarinaraSauce(); // 마리나라 소스
+ }
+
+ createCheese(): Cheese {
+ return new ReggianoCheese(); // 레지아노 치즈
+ }
+
+ createVeggies(): Veggies[] {
+ return [new Garlic(), new Onion(), new Mushroom()];
+ }
+
+ createPepperoni(): Pepperoni {
+ return new SlicedPepperoni();
+ }
+
+ createClam(): Clams {
+ return new FreshClams(); // 신선한 조개 (뉴욕 특산!)
+ }
+}
+```
+
+#### ChicagoPizzaIngredientFactory.ts
+
+```typescript
+// 📁 factory/pizzaaf/ChicagoPizzaIngredientFactory.ts
+export class ChicagoPizzaIngredientFactory implements PizzaIngredientFactory {
+ createDough(): Dough {
+ return new ThickCrustDough(); // 두꺼운 도우
+ }
+
+ createSauce(): Sauce {
+ return new PlumTomatoSauce(); // 플럼 토마토 소스
+ }
+
+ createCheese(): Cheese {
+ return new MozzarellaCheese(); // 모짜렐라 치즈
+ }
+
+ createVeggies(): Veggies[] {
+ return [new BlackOlives(), new Spinach(), new Eggplant()];
+ }
+
+ createPepperoni(): Pepperoni {
+ return new SlicedPepperoni();
+ }
+
+ createClam(): Clams {
+ return new FrozenClams(); // 냉동 조개 (내륙이니까!)
+ }
+}
+```
+
+#### CheesePizza.ts (팩토리 사용)
+
+```typescript
+// 📁 factory/pizzaaf/CheesePizza.ts
+export class CheesePizza extends Pizza {
+ ingredientFactory: PizzaIngredientFactory;
+
+ constructor(ingredientFactory: PizzaIngredientFactory) {
+ super();
+ this.ingredientFactory = ingredientFactory;
+ }
+
+ prepare(): void {
+ console.log("Preparing " + this.name);
+
+ // 팩토리에서 재료를 가져옴 - 어떤 지역인지는 모름!
+ this.dough = this.ingredientFactory.createDough();
+ this.sauce = this.ingredientFactory.createSauce();
+ this.cheese = this.ingredientFactory.createCheese();
+ }
+}
+```
+
+### 사용 예시
+
+```typescript
+// 뉴욕 스타일
+const nyFactory = new NYPizzaIngredientFactory();
+const nyStore = new NYPizzaStore(nyFactory);
+const nyCheese = nyStore.orderPizza("cheese");
+// → ThinCrustDough + MarinaraSauce + ReggianoCheese + FreshClams
+
+// 시카고 스타일
+const chicagoFactory = new ChicagoPizzaIngredientFactory();
+const chicagoStore = new ChicagoPizzaStore(chicagoFactory);
+const chicagoCheese = chicagoStore.orderPizza("cheese");
+// → ThickCrustDough + PlumTomatoSauce + MozzarellaCheese + FrozenClams
+```
+
+### 제품군 비교
+
+| 재료 | 뉴욕 스타일 | 시카고 스타일 |
+| ---- | ----------------------- | ------------------------------ |
+| 도우 | ThinCrustDough (얇은) | ThickCrustDough (두꺼운) |
+| 소스 | MarinaraSauce | PlumTomatoSauce |
+| 치즈 | ReggianoCheese | MozzarellaCheese |
+| 조개 | FreshClams (신선한) | FrozenClams (냉동) |
+| 야채 | Garlic, Onion, Mushroom | BlackOlives, Spinach, Eggplant |
+
+### 장단점
+
+| 장점 | 단점 |
+| ---------------------------------------------------------- | ---------------------------------------------- |
+| **제품군 일관성**: 뉴욕 스타일이면 모든 재료가 뉴욕 스타일 | 새로운 제품 타입 추가 시 모든 팩토리 수정 필요 |
+| **클라이언트 독립**: 피자는 어떤 팩토리인지 몰라도 됨 | 복잡한 구조 |
+| **쉬운 교체**: 팩토리만 바꾸면 전혀 다른 제품군으로 전환 | |
+
+### 언제 사용할까?
+
+- 연관된 객체들이 함께 사용되어야 할 때
+- 제품군 전체를 교체해야 할 때 (테마, 플랫폼 등)
+- 구체적인 클래스에 의존하지 않고 인터페이스로 작업하고 싶을 때
+
+---
+
+## 5. 세 가지 패턴 비교
+
+### 비교 테이블
+
+| 특징 | Simple Factory | Factory Method | Abstract Factory |
+| ------------- | ---------------- | ---------------------- | ------------------ |
+| **목적** | 생성 로직 캡슐화 | 서브클래스에 생성 위임 | 제품군 생성 |
+| **확장 방법** | 팩토리 수정 | 서브클래스 추가 | 새로운 팩토리 추가 |
+| **OCP 준수** | ⚠️ 부분 | 완벽 | 완벽 |
+| **복잡도** | 낮음 | 중간 | 높음 |
+| **클래스 수** | 적음 | 중간 | 많음 |
+| **사용 시기** | 단순한 생성 | 다양한 변형 | 연관된 제품군 |
+
+### 선택 가이드
+
+```
+ ┌─────────────────────┐
+ │ 객체 생성이 필요한가? │
+ └──────────┬──────────┘
+ │
+ ┌──────────↓──────────┐
+ │ 생성 로직이 복잡한가? │
+ └──────────┬──────────┘
+ │
+ ┌────────────────┼────────────────┐
+ │ │ │
+ 아니오 예 모르겠음
+ │ │ │
+ ↓ ↓ ↓
+ 그냥 new 사용 ┌───────────┐ Simple Factory
+ │ 제품군이 │
+ │ 필요한가? │
+ └─────┬─────┘
+ │
+ ┌────────┼────────┐
+ │ │
+ 아니오 예
+ │ │
+ ↓ ↓
+ Factory Method Abstract Factory
+```
+
+### 코드 비교
+
+```typescript
+// Simple Factory
+const factory = new SimplePizzaFactory();
+const pizza = factory.createPizza("cheese");
+
+// Factory Method
+const store = new NYPizzaStore();
+const pizza = store.orderPizza("cheese");
+
+// Abstract Factory
+const ingredientFactory = new NYPizzaIngredientFactory();
+const pizza = new CheesePizza(ingredientFactory);
+pizza.prepare();
+```
+
+---
+
+## 6. 실무 예시
+
+### 6.1 UI 컴포넌트 팩토리 (Abstract Factory)
+
+```typescript
+// 제품 인터페이스
+interface Button {
+ render(): void;
+}
+interface TextInput {
+ render(): void;
+}
+
+// 추상 팩토리
+interface UIComponentFactory {
+ createButton(): Button;
+ createTextInput(): TextInput;
+}
+
+// Material Design 팩토리
+class MaterialFactory implements UIComponentFactory {
+ createButton(): Button {
+ return new MaterialButton();
+ }
+ createTextInput(): TextInput {
+ return new MaterialTextInput();
+ }
+}
+
+// Ant Design 팩토리
+class AntFactory implements UIComponentFactory {
+ createButton(): Button {
+ return new AntButton();
+ }
+ createTextInput(): TextInput {
+ return new AntTextInput();
+ }
+}
+
+// 사용
+function createForm(factory: UIComponentFactory) {
+ const submitButton = factory.createButton();
+ const nameInput = factory.createTextInput();
+ // 일관된 스타일의 컴포넌트들
+}
+```
+
+### 6.2 데이터베이스 연결 팩토리 (Factory Method)
+
+```typescript
+// 추상 클래스
+abstract class DatabaseConnection {
+ abstract createConnection(): Connection;
+
+ async query(sql: string) {
+ const conn = this.createConnection();
+ return conn.execute(sql);
+ }
+}
+
+// MySQL
+class MySQLConnection extends DatabaseConnection {
+ createConnection(): Connection {
+ return new MySQLClient({
+ host: "localhost",
+ user: "root",
+ password: "password",
+ });
+ }
+}
+
+// PostgreSQL
+class PostgreSQLConnection extends DatabaseConnection {
+ createConnection(): Connection {
+ return new PostgreSQLClient({
+ host: "localhost",
+ user: "postgres",
+ password: "password",
+ });
+ }
+}
+
+// 사용
+const db =
+ process.env.DB === "mysql"
+ ? new MySQLConnection()
+ : new PostgreSQLConnection();
+await db.query("SELECT * FROM users");
+```
+
+### 6.3 로깅 팩토리 (Simple Factory)
+
+```typescript
+class LoggerFactory {
+ static createLogger(type: string): Logger {
+ switch (type) {
+ case "console":
+ return new ConsoleLogger();
+ case "file":
+ return new FileLogger("/var/log/app.log");
+ case "remote":
+ return new RemoteLogger("https://logs.example.com");
+ default:
+ throw new Error(`Unknown logger type: ${type}`);
+ }
+ }
+}
+
+// 사용
+const logger = LoggerFactory.createLogger(process.env.LOG_TYPE);
+logger.info("Application started");
+```
+
+### 6.4 테스트에서의 팩토리
+
+```typescript
+// 테스트용 Mock 팩토리
+class MockUserFactory implements UserFactory {
+ create(id: string): User {
+ return {
+ id,
+ name: "Test User",
+ email: "test@example.com",
+ createdAt: new Date("2024-01-01"),
+ };
+ }
+}
+
+// 실제 팩토리
+class RealUserFactory implements UserFactory {
+ async create(id: string): Promise
+ {data.map((user) => (
+
+ );
+};
+
+const EnhancedUserList = withDataFetching(UserList, () =>
+ fetch("/api/users").then((r) => r.json()),
+);
+```
+
+### 4. 스타일링 데코레이터
+
+```tsx
+// 스타일 데코레이터 (styled-components 패턴)
+function withStyled
+
{product.name}
+