CS/Algorithm

📌 디자인 패턴 (Design Patterns) – TypeScript로 상세 정리

nkm 2025. 2. 2. 21:01
728x90
반응형

📌 디자인 패턴 (Design Patterns) – TypeScript로 상세 정리

디자인 패턴은 자주 발생하는 소프트웨어 설계 문제를 해결하기 위한 일반적인 해결책이다.
유지보수성과 확장성이 뛰어난 코드를 작성하는 데 도움을 준다.


1. 디자인 패턴 개요

디자인 패턴은 3가지 주요 유형으로 분류된다.

패턴 유형 설명 대표 패턴

생성(Creational) 패턴 객체 생성 방법을 최적화 Singleton, Factory, Builder
구조(Structural) 패턴 클래스나 객체의 구조를 조정하여 효율적인 설계 제공 Adapter, Decorator, Facade
행동(Behavioral) 패턴 객체 간의 상호작용을 최적화 Observer, Strategy, Command

2. 생성(Creational) 패턴

객체를 효율적으로 생성하고 관리하는 패턴.
인스턴스를 직접 생성하는 대신, 유연하게 객체를 생성할 수 있도록 한다.


2.1 Singleton 패턴

"한 클래스의 인스턴스가 하나만 존재하도록 제한하는 패턴"

  • 전역 상태 관리에 유용 (예: 설정 정보, 데이터베이스 연결)

Singleton 패턴 예제

class Singleton {
  private static instance: Singleton;

  private constructor() {} // 외부에서 인스턴스 생성을 방지

  static getInstance(): Singleton {
    if (!Singleton.instance) {
      Singleton.instance = new Singleton();
    }
    return Singleton.instance;
  }

  showMessage(): void {
    console.log("🟢 Singleton 인스턴스입니다!");
  }
}

// 사용법
const obj1 = Singleton.getInstance();
const obj2 = Singleton.getInstance();

console.log(obj1 === obj2); // ✅ true (같은 인스턴스)
obj1.showMessage(); // 🟢 Singleton 인스턴스입니다!

📌 활용 예시

  • 데이터베이스 연결 객체
  • 애플리케이션 설정 객체

2.2 Factory 패턴

"객체 생성을 서브클래스에서 수행하도록 위임하여 코드의 유연성을 높이는 패턴"

  • 클라이언트 코드가 직접 객체를 생성하지 않고, 팩토리 메서드를 통해 생성.

Factory 패턴 예제

// 공통 인터페이스
interface Animal {
  makeSound(): void;
}

// 구체적인 클래스
class Dog implements Animal {
  makeSound() {
    console.log("🐶 멍멍!");
  }
}

class Cat implements Animal {
  makeSound() {
    console.log("🐱 야옹!");
  }
}

// 팩토리 클래스
class AnimalFactory {
  static createAnimal(type: string): Animal {
    if (type === "dog") {
      return new Dog();
    } else {
      return new Cat();
    }
  }
}

// 사용법
const myPet = AnimalFactory.createAnimal("dog");
myPet.makeSound(); // 🐶 멍멍!

📌 활용 예시

  • 데이터베이스 연결 객체 생성
  • UI 컴포넌트 동적 생성

2.3 Builder 패턴

"복잡한 객체 생성을 단계적으로 수행하는 패턴"

  • 생성자의 인자가 많을 때, 가독성과 유지보수성을 높인다.

Builder 패턴 예제

class Car {
  constructor(
    public brand: string,
    public model: string,
    public color?: string
  ) {}
}

class CarBuilder {
  private brand!: string;
  private model!: string;
  private color?: string;

  setBrand(brand: string): CarBuilder {
    this.brand = brand;
    return this;
  }

  setModel(model: string): CarBuilder {
    this.model = model;
    return this;
  }

  setColor(color: string): CarBuilder {
    this.color = color;
    return this;
  }

  build(): Car {
    return new Car(this.brand, this.model, this.color);
  }
}

// 사용법
const myCar = new CarBuilder().setBrand("Tesla").setModel("Model S").setColor("Red").build();
console.log(myCar);

📌 활용 예시

  • 데이터베이스 쿼리 생성
  • 복잡한 UI 컴포넌트 생성

3. 구조(Structural) 패턴

"클래스나 객체의 구조를 조정하여 효율적인 설계를 제공하는 패턴"


3.1 Adapter 패턴

"서로 다른 인터페이스를 가진 클래스를 연결하는 패턴"

  • 기존 클래스를 수정하지 않고 다른 인터페이스와 호환 가능.

Adapter 패턴 예제

class LegacyPrinter {
  printMessage(): void {
    console.log("🖨️ 레거시 프린터 출력 중...");
  }
}

// 새로운 인터페이스
interface NewPrinter {
  print(): void;
}

// 어댑터 클래스
class PrinterAdapter implements NewPrinter {
  constructor(private legacyPrinter: LegacyPrinter) {}

  print(): void {
    this.legacyPrinter.printMessage();
  }
}

// 사용법
const oldPrinter = new LegacyPrinter();
const printer = new PrinterAdapter(oldPrinter);
printer.print(); // 🖨️ 레거시 프린터 출력 중...

📌 활용 예시

  • 레거시 코드와 새로운 코드의 호환성 유지
  • API 통합 및 변환

3.2 Decorator 패턴

"기능을 동적으로 추가할 수 있도록 하는 패턴"

  • 상속 없이 객체의 기능을 확장할 수 있다.

Decorator 패턴 예제

interface Coffee {
  getDescription(): string;
  cost(): number;
}

class BasicCoffee implements Coffee {
  getDescription(): string {
    return "기본 커피";
  }
  cost(): number {
    return 3000;
  }
}

class MilkDecorator implements Coffee {
  constructor(private coffee: Coffee) {}

  getDescription(): string {
    return this.coffee.getDescription() + ", 우유 추가";
  }

  cost(): number {
    return this.coffee.cost() + 500;
  }
}

// 사용법
const myCoffee = new MilkDecorator(new BasicCoffee());
console.log(myCoffee.getDescription()); // 기본 커피, 우유 추가
console.log(myCoffee.cost()); // 3500원

📌 활용 예시

  • UI 컴포넌트 기능 추가
  • 미들웨어 패턴 (예: Express.js)

4. 행동(Behavioral) 패턴

"객체 간의 상호작용을 최적화하는 패턴"


4.1 Observer 패턴

"한 객체의 상태 변화가 다른 객체들에게 자동으로 전달되도록 하는 패턴"

  • 이벤트 리스너 같은 역할을 한다.

Observer 패턴 예제

interface Observer {
  update(message: string): void;
}

class User implements Observer {
  constructor(private name: string) {}

  update(message: string): void {
    console.log(`${this.name}님, 알림: ${message}`);
  }
}

class NewsChannel {
  private observers: Observer[] = [];

  subscribe(observer: Observer): void {
    this.observers.push(observer);
  }

  notify(message: string): void {
    this.observers.forEach(observer => observer.update(message));
  }
}

// 사용법
const user1 = new User("Alice");
const user2 = new User("Bob");

const news = new NewsChannel();
news.subscribe(user1);
news.subscribe(user2);

news.notify("새 뉴스가 도착했습니다!");

📌 활용 예시

  • 이벤트 시스템 (예: DOM 이벤트 리스너)
  • Pub-Sub 시스템

🚀 결론

디자인 패턴을 적용하면 유지보수성과 확장성이 뛰어난 코드 작성 가능!
생성, 구조, 행동 패턴을 이해하고 실무에 활용하면 코드 품질 향상!

🔥 다음 단계 → 의존성 주입(DI) 자세히 설명! 🚀

728x90
반응형