디자인 패턴 - 싱글톤 패턴

kyle·2023년 8월 7일
0

CS

목록 보기
2/3

디자인 패턴이란 ?

프로그램을 설계할 때 발생했던 문제점들을 객체 간의 상호 관계등을 이용하여 해결할 수 있도록 하나의 '규약' 형태로 만들어 놓은 것을 의미합니다.
즉, 프로그램을 설계할 때 참고하면 큰 도움이 되겠죠. 규약 형태로 되어있으니 따라하기만 하면 되니까요. 물론, 장단점이 있습니다. 그건 아래에서 설명하겠습니다.

싱글톤 패턴이란 ?

싱글톤 패턴(Singleton Pattern)은 '하나의 클래스'에 오직 '하나의 인스턴스'만 가지는 패턴.

하나의 클래스를 기반으로 여러 개의 인스턴스를 만들 수 있지만, 그렇게 하지않고 '하나의 클래스'를 기반으로 '단 하나의 인스턴스'를 만들어 이를 기반으로 로직을 만드는 데 쓰이며, 보통 데이터베이스 연결 모듈에 많이 사용합니다.

장점: 인스턴스를 생성하고 생성한 인스턴스를 여러 모듈에서 사용할 수 있기 때문에 인스턴스 생성에 대한 비용을 절감할 수 있다는 것이 장점
단점: 인스턴스를 여러군데 가져다가 사용해야하기때문에 의존성이 높아진다는 단점이 존재함. (결합도 높아짐)

자바스크립트에서 {}나 new Object로 객체를 생성하게 되면, 생성된 객체는 서로 다르기 때문에 이 자체로도 싱글톤 패턴이라 부를 수 있습니다.

const obj = {
	a: 27
};

const obj2 = {
	a: 29
};

console.log(obj === obj2) // false

하지만, 실제로는 다른 코드를 작성합니다.

class Singleton {
	constructor(){
		if(!Singleton.instance){
        	Singleton.instance = this;
        }
      	return Singleton.instance
    }
  	
  	getInstance(){
      return Singleton.instance;
    }
}

const a = new Singleton();
const b = new Singleton();
console.log(a === b) // true;

앞의 코드는 Singleton.instance라는 하나의 인스턴스를 가지는 클래스를 구현한 코드입니다. 이를 통해 a와 b는 서로 같은 인스턴스를 가집니다.

다음은 데이터베이스 연결 모듈입니다. 싱글톤 패턴 중 많이 쓰이는 케이스입니다.

const URL = 'db://localhost:xxxxx/test';
const createConnection = url => ({url});
class DB {
	constructor(){
    	if(!DB.instance){
        	DB.instance = this;
        }
     	return DB.instance;
    }
  
  	connect(){
    	return DB.instance;
    }
}

const a = new DB(url);
const b = new DB(url);
console.log(a === b); //true

이렇게 DB.instance라는 하나의 인스턴스를 기반으로 a,b를 생성하는 것을 확인할 수 있습니다.
연결에 대한 인스턴스 생성 비용을 아낄 수 있습니다.

싱글톤 패턴의 단점?

싱글톤 패턴은 TDD(Test Driven Development)를 진행할 때 걸림돌이 됩니다.
TDD를 할 때 주로 단위테스트를 진행하는데, 단위테스트는 테스트가 서로 독립적이어야하며, 테스트를 어떤 순서로든 진행할 수 있어야합니다.
하지만, 싱글톤 패턴은 미리 생성된 하나의 인스턴스를 기반으로 구현하는 패턴이므로 각 테스트마다 '독립적인' 인스턴스를 만들기가 어렵습니다.

의존성 주입

또한, 싱글톤 패턴은 사용하기가 쉽고 굉장히 실용적이지만, 모듈 간의 결합을 강하게 만들 수 있다는 단점이 있습니다.
모듈 간의 결합이 강하다는 것은, A와 B,C,D라는 각 모듈이 존재한다고 가정할 때, A와 B,C,D는 서로 상호 관계에 있다고 이해하면 됩니다. A가 바뀌면, B,C,D도 바뀌어야합니다.
A에 싱글톤 패턴으로 생성한 인스턴스가 있고, B,C,D에서 이 인스턴스를 받아와 사용하는 프로그램이 있다고 가정할 때, A가 없다면 B,C,D는 동작하지않습니다.
단순한 구조로 이루어진 프로그램이라면 큰 상관은 없겠지만, 규모가 커진다면 이는 유지보수가 어려워지는 길이 됩니다.

이를 해결하기 위해선 의존성을 주입해야합니다. 이를 DI(Dependency Injection)라고 부릅니다. 의존성을 주입하면 결합의 정도를 약하게 만들어 줄 수 있습니다. 참고로 의존성이란, 종속성이라고도 부르며 A가 B에 의존성이 있다는 것은 B가 변경되었을 때 A도 변경되어야한다는 것을 의미합니다.

위 그림처럼 메인모듈이 직접 다른 하위 모듈에 대한 의존성을 주기보다는 중간에 의존성 주입자가 이 부분을 가로채 메인 모듈이 간접적으로 의존성을 주입하는 방식입니다.

이를 통해 메인 모듈(상위 모듈)은 하위 모듈에 대한 의존성이 떨어지게 됩니다. 참고로 이를 디커플링이 된다고도 합니다. 모듈 간 결합도를 낮추는 행위죠.

장점
1. 모듈들을 쉽게 교체할 수 있는 구조가 됩니다.
2. 테스팅, 마이그레이션이 수월합니다.
3. 구현할 때 추상화 레이어를 넣고 이를 기반으로 구현체를 넣어주기 때문에 애플리케이션 의존성 방향이 일관됨.
4. 애플리케이션을 쉽게 추론할 수 있음
5. 모듈 간의 관계들이 조금 더 명확해집니다.

단점
1. 모듈의 분리로 인한 복잡성 증가
2. 약간의 런타임 패널티 발생

의존성 주입 원칙

의존성 주입은 "상위 모듈은 하위 모듈에서 어떠한 것도 가져오지않아야한다. 또한, 둘 다 추상화에 의존해야하며, 추상화는 세부사항에 의존하지않아야한다." 라는 의존성 주입 원칙을 지켜주면서 만들어야합니다.

다음 포스팅에서는 팩토리 패턴에 대해 설명하는 시간을 갖도록 하겠습니다.
의존성 주입에 관해 자세한 내용은 추후에 포스팅하도록 하겠습니다.
감사합니다.

profile
DX를 사랑하는 개발자

2개의 댓글

comment-user-thumbnail
2023년 8월 7일

즐겁게 읽었습니다. 유용한 정보 감사합니다.

1개의 답글