디자인패턴 코드와 정리
| 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);
전략 패턴(== 정책 패턴)
객체 지향 디자인 패턴 중 하나로 동일한 문제를 해결하기 위한 여러 알고리즘(전략)을 정의하고, 각각을 캡슐화하여 상호 교환 가능하도록 만드는 패턴
방법
- 전략 추상화
- 공통적으로 수행하느 전략을 추상화하여 추상클래스/인터페이스로 정의한다.
- 되도록이면 하나의 역할만 수행하는 것이 좋다.
- 전략 구현
- 추상화된 전략을 상속받아 구체적인 전략을 수립한다.
- 전략 등록(컨텍스트)
- 전략을 등록하고 실행하는 메서드를 정의한다.
- 전략에 맞게 사용
특징
- 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) => {
// ...
});
옵저버 패턴
객체의 상태변화를 관찰하다가 상태변화가 있을때 메서드를 통해서 옵저버 목록에 있는 옵저버들에게 변화를 알려줌
- 주로 이벤트 기반 시스탬과 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!!");
코드 시각화
#### 프록시 객체
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
프록시
- 보안
- 익명화
- 접근제어
- 캐싱
- 로드밸런싱
포워드 프록시와 리버스 프록시는 두 가지 서로 다른 프록시 서버 유형입니다. 각각의 주요 특징은 다음과 같습니다:
포워드 프록시(Forward Proxy):
- 클라이언트가 인터넷에 직접 접근하는 것을 막고, 클라이언트의 요청을 대신 받아서 외부 서버로 전달합니다.
- 클라이언트가 외부 리소스에 접근할 때 프록시 서버를 경유하여 보안, 로깅, 필터링, 캐싱 등의 기능을 수행합니다.
- 주로 회사나 조직의 내부 네트워크에서 직원들의 인터넷 접근을 제어하고 모니터링하는 데 사용됩니다.
리버스 프록시(Reverse Proxy):
- 외부에서 내부로 들어오는 클라이언트의 요청을 받아들이고 이를 내부의 서버로 전달합니다.
- 클라이언트가 웹 서버에 접근할 때, 리버스 프록시 서버가 요청을 받아들이고 요청을 처리할 적절한 내부 서버(예: 웹 서버, 애플리케이션 서버)로 라우팅합니다.
- 보안, 부하 분산, 성능 향상 등을 위해 사용됩니다. 외부 요청에 대한 내부 시스템의 보호와 안전성을 높이며, 여러 서버 사이의 부하를 균형있게 분산시키는 역할을 합니다.
두 유형의 프록시 서버는 각각의 목적에 따라 구성되며, 네트워크 보안 및 성능 최적화를 위해 중요한 역할을 수행합니다.
https://www.cloudflare.com/ko-kr/learning/cdn/glossary/reverse-proxy/