디자인패턴 코드와 정리

topics 701 디자인패턴
types 이론 실습

싱글톤 패턴

하나의 클래스에 하나의 인스턴스, 데이터베이스 연결 모듈에 많이 사용

코드와 함께 설명

기본 싱글톤 생성 방식
//원리 constructor는 암묵적으로 this를 반환.
class SingletonA{
     constructor( ) {
         if(!Singleton.instance){
             Singleton.instance = this
         }
         return Singleton.instance
     }
     getInstance(){
         return this.instance
     }
}

class SingletonB {
    static instance
    constructor() {
        if (instance) return instance;
        instance = this;
  }
}

const singleton1 = new Singleton();
const singleton2 = new Singleton();

const a1 = new Singleton();
const a2 = new Singleton();
console.log(a1===a2) // true
db에서 사용 예

node.js에서 mysql과 mongodb의 라이브러리에서 Connect할때 때 이러한 패턴을 실제로 사용한다.

const URL = 'dburl';
const createConnection = url => ({"url":url});

class DB{
    constructor(url){
        if(!DB.instance){
            DB.instance = createConnection(url);
        }
        return DB.instance;
    }
    connect(){
        return this.instance
    }
}

const a = new DB(URL);
const b = new DB(URL);

console.log(a===b);//true

장점

  • 인스턴트 생성 비용이 줄고 자원을 공유함.
  • 데이터베이스 또는 로그 연결 객체와 같이 애플리케이션 전체에서 공통으로 사용되는 리소스에 대해 일관된 접근 방식 제공 가능
  • 애플리케이션 전역에서 객체에 접근할 수 있으므로, 별도의 파라미터 전달이나 의존성 주입과 같은 과정이 필요없다.

단점

  • 의존성이 커짐(=모듈간의 결합을 강하게). -> 단위 테스트에 걸림돌
  • 멀티스레드 환경처럼 다수의 클라이언트가 동시에 접근할 때 발생하는 객체의 상태 변화에 유의해야 함. 이런 환경에서 싱글턴 객체의 동기화가 보장되지 않는다면, 이에 의존하는 코드에 다양한 오류를 발생시킬 수 있음.
    https://seongjin.me/woowacourse-three-design-patterns-in-javascript/

=> 해결 방법! 의존성주입 의존성 주입

팩토리패턴

객체 생성부분을 떼어내 추상화한 패턴
상위 클래스에서 중요한 뼈대를 결정하고 하위클래스에서 객체생성에 대한 구체적인 내용을 결정

장점

  • 상위클래스에서 인스턴스 생성 방식에 알 필요가없음 -> 유연성 Up
  • 유지 보수성이 증가

코드와 함께 설명

https://aljjabaegi.tistory.com/617

팩토리 패턴 이전코드
const Carfactory = function({name,price}){
    this.name = name;
    this.price = price;
    this.getInfo = function(){
        return this.name+"의 가격은 "+this.price+" 입니다.";
    }
}
const car1 = new Carfactory({name: "아반떼", price: "1,570 ~ 2,453만원"});
const car2 = new Carfactory({name: "쏘나타", price: "2,386 ~ 3,367만원"});
console.log(car1, car2);
팩토리 패턴
class Car{
    constructor(info){
        this.name = info.name;
        this.price = info.price;
    }
    getInfo(){
        return this.name+"의 가격은 "+this.price+" 입니다.";
    }
    static factory(name){
        if(name === "Avante"){
           return new Avante();
        }else if(name === "Sonata"){
           return new Sonata();
        }
    }
}
class Sonata extends Car{
    constructor(){
        super({name: "쏘나타", price: "2,386 ~ 3,367만원"});
    }
}
class Avante extends Car{
    constructor(){
        super({name: "아반테", price: "1,570 ~ 2,453만원"});
    }
}
const avante = Car.factory("Avante");
const sonata = Car.factory("Sonata");
console.log(avante, sonata);

전략 패턴(== 정책 패턴)

객체 지향 디자인 패턴 중 하나로 동일한 문제를 해결하기 위한 여러 알고리즘(전략)을 정의하고, 각각을 캡슐화하여 상호 교환 가능하도록 만드는 패턴

방법

  1. 전략 추상화
    • 공통적으로 수행하느 전략을 추상화하여 추상클래스/인터페이스로 정의한다.
    • 되도록이면 하나의 역할만 수행하는 것이 좋다.
  2. 전략 구현
    • 추상화된 전략을 상속받아 구체적인 전략을 수립한다.
  3. 전략 등록(컨텍스트)
    • 전략을 등록하고 실행하는 메서드를 정의한다.
  4. 전략에 맞게 사용

특징

  • OOP의 집합체다.(SOLID 등..)
  • oauth나 payment에 주로 사용
  • 동작이 런테임에 실시간으로 교체되어야할때 사용
  • 장점 : 기능 확장에 용이
  • 단점 : 클래스 개수 너무 많아질 수 있음.

예제

// 카카오 로그인 전략 등록
passport.use(new KakaoStrategy({ clientID, callbackURL }, async (accessToken, refreshToken, profile, done) => {
    // ...
});
// 네이버 로그인 전략 등록
passport.use(new NaverStrategy({ clientID, clientSecret, callbackURL }, async (accessToken, refreshToken, profile, done) => {
    // ...
});

참고 링크 :
https://inpa.tistory.com/entry/GOF-%F0%9F%92%A0-%EC%A0%84%EB%9E%B5Strategy-%ED%8C%A8%ED%84%B4-%EC%A0%9C%EB%8C%80%EB%A1%9C-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EC%9E%90

옵저버 패턴

객체의 상태변화를 관찰하다가 상태변화가 있을때 메서드를 통해서 옵저버 목록에 있는 옵저버들에게 변화를 알려줌

스크린샷 2023-11-25 오후 7.13.54.png
  • 주로 이벤트 기반 시스탬과 MVC 패턴에서 사용

예제

코드
interface Subject{
  register(obj:Observer):void;
  unregister(obj:Observer):void;
  notifyObservers():void;
  getUpdate(obj:Observer):string;
}
interface Observer{
  update():void;
}

class Topic implements Subject{
  private observers:Observer[] = [];
  private message:string = "";

  register(obj:Observer):void{
    this.observers.push(obj);
  }
  
  unregister(obj:Observer):void{
    this.observers.splice(this.observers.indexOf(obj), 1);
  }
  notifyObservers():void{
    this.observers.forEach((observer:Observer) => observer.update());
  }
  getUpdate(obj:Observer):string|null{
    if(this.observers.includes(obj)){
      return this.message;
    }
    return "can't accept to message";
  }
  postMesssage(msg:string):void{
    console.log("msg sended to topic :"+ msg);
    this.message = msg;
    this.notifyObservers();
  } 
}

class TopicSubscriber implements Observer{
  private name:string;
  private topic:Subject;

  constructor(name:string, topic:Subject){
    this.name = name;
    this.topic = topic;
  }

  update():void{
    let msg:string = topic.getUpdate(this);
    console.log(this.name + ":: got message >> " + msg)
  }
}

let topic = new Topic();
let a =  new TopicSubscriber("a", topic);
let b =  new TopicSubscriber("b", topic);
topic.register(a);
topic.register(b);
topic.postMesssage("amumu is op champion!!");
코드 시각화
Pasted image 20231129141201.png #### 프록시 객체 https://velog.io/@esthevely/JS-%EC%9E%90%EB%B0%94%EC%8A%A4%ED%81%AC%EB%A6%BD%ED%8A%B8%EC%97%90%EC%84%9C-Proxy-%EA%B0%9D%EC%B2%B4-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0

프록시

  • 보안
  • 익명화
  • 접근제어
  • 캐싱
  • 로드밸런싱

포워드 프록시와 리버스 프록시는 두 가지 서로 다른 프록시 서버 유형입니다. 각각의 주요 특징은 다음과 같습니다:

  1. 포워드 프록시(Forward Proxy):

    • 클라이언트가 인터넷에 직접 접근하는 것을 막고, 클라이언트의 요청을 대신 받아서 외부 서버로 전달합니다.
    • 클라이언트가 외부 리소스에 접근할 때 프록시 서버를 경유하여 보안, 로깅, 필터링, 캐싱 등의 기능을 수행합니다.
    • 주로 회사나 조직의 내부 네트워크에서 직원들의 인터넷 접근을 제어하고 모니터링하는 데 사용됩니다.
  2. 리버스 프록시(Reverse Proxy):

    • 외부에서 내부로 들어오는 클라이언트의 요청을 받아들이고 이를 내부의 서버로 전달합니다.
    • 클라이언트가 웹 서버에 접근할 때, 리버스 프록시 서버가 요청을 받아들이고 요청을 처리할 적절한 내부 서버(예: 웹 서버, 애플리케이션 서버)로 라우팅합니다.
    • 보안, 부하 분산, 성능 향상 등을 위해 사용됩니다. 외부 요청에 대한 내부 시스템의 보호와 안전성을 높이며, 여러 서버 사이의 부하를 균형있게 분산시키는 역할을 합니다.

두 유형의 프록시 서버는 각각의 목적에 따라 구성되며, 네트워크 보안 및 성능 최적화를 위해 중요한 역할을 수행합니다.

https://www.cloudflare.com/ko-kr/learning/cdn/glossary/reverse-proxy/

https://inpa.tistory.com/entry/NETWORK-%F0%9F%93%A1-Reverse-Proxy-Forward-Proxy-%EC%A0%95%EC%9D%98-%EC%B0%A8%EC%9D%B4-%EC%A0%95%EB%A6%AC