CS/๋””์ž์ธ ํŒจํ„ด

[๋””์ž์ธ ํŒจํ„ด] ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด(Singleton Pattern)

1eehyunji 2023. 8. 1. 15:42

ํ•˜๋‚˜์˜ ํด๋ž˜์Šค์— ์˜ค์ง ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค๋งŒ์„ ๊ฐ€์ง€๋Š” ํŒจํ„ด์ด๋‹ค. 

์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ์—์„  ๋‹จ์ˆœํ•˜๊ฒŒ ๋ฆฌํ„ฐ๋Ÿด {} ๋งŒ์œผ๋กœ๋„ ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. 

const obj = {
	a:27
}

const obj2 = {
	a:27
}

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

๋ฆฌํ„ฐ๋Ÿด๋กœ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•˜๊ฒŒ ๋˜๋ฉด ๋‹ค๋ฅธ ์–ด๋–ค ๊ฐ์ฒด์™€๋„ ๊ฐ™์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๊ฐ™์€ ๋‚ด์šฉ์„ ๊ฐ€์ง€๋”๋ผ๋„ obj์™€ obj2๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€์ง„๋‹ค.

ํ•˜์ง€๋งŒ, ์ด๋ ‡๊ฒŒ ๊ตฌํ˜„ํ•˜๋ฉด ๋น„๊ณต๊ฐœ ์ƒํƒœ๋‚˜ ๋น„๊ณต๊ฐœ ํ•จ์ˆ˜๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์„ ๊ตฌ์„ฑํ•œ๋‹ค. 

Class Singleton {
	// constructor(์ƒ์„ฑ์ž)
	// class ๊ฐ์ฒด์˜ ์ดˆ๊ธฐ๊ฐ’ ์„ค์ •
    // class ๋‚ด๋ถ€์—์„œ๋Š” ํ•œ ๊ฐœ๋งŒ ์กด์žฌํ•  ์ˆ˜ ์žˆ๊ณ  2๋ฒˆ ์ด์ƒ ์‚ฌ์šฉํ•˜๋ฉด Syntax Error ๋ฐœ์ƒ
	constructor() {
    	// Singleton ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด, ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
    	if (!Singleton.instance){
        	Singleton.instance = this;
        }
        // ์กด์žฌํ•œ๋‹ค๋ฉด ๋ฐ”๋กœ ์ธ์Šคํ„ด์Šค ๋ฐ˜ํ™˜
        return Singleton.instance;
    }
}

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

์ด๋Ÿฌํ•œ ๋ฐฉ์‹์˜ ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ๋ชจ๋“ˆ์— ๋งŽ์ด ์“ฐ์ธ๋‹ค.

const URL='mongodb://localhost:27017/kundolapp'
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

์ด์™€ ๊ฐ™์ด DB.instance๋ผ๋Š” ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ a,b๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋‹ค.

๋ณดํ†ต ๊ณตํ†ต๋œ ๊ฐ์ฒด๋ฅผ ์—ฌ๋Ÿฌ ๊ฐœ ์ƒ์„ฑํ•ด์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ ๋ชจ๋“ˆ์—์„œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์—ฐ๊ฒฐ์— ๊ด€ํ•œ ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ ๋น„์šฉ์„ ์•„๋‚„ ์ˆ˜ ์žˆ๋‹ค.

 

๋‹จ์ 

TDD(Test Driven Development)๋ฅผ ํ•  ๋•Œ ๊ฑธ๋ฆผ๋Œ์ด ๋  ์ˆ˜ ์žˆ๋‹ค. TDD๋ฅผ ํ•  ๋•Œ, ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ํ•˜๊ฒŒ ๋˜๋Š”๋ฐ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋Š” ํ…Œ์ŠคํŠธ๊ฐ€ ์„œ๋กœ ๋…๋ฆฝ์ ์ด์–ด์•ผ ํ•˜๋ฉฐ ํ…Œ์ŠคํŠธ๋ฅผ ์–ด๋–ค ์ˆœ์„œ๋กœ๋“  ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค. ํ•˜์ง€๋งŒ ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์€ ๋ฏธ๋ฆฌ ์ƒ์„ฑ๋œ ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌํ˜„ํ•˜๋Š” ํŒจํ„ด์ด๋ฏ€๋กœ ๊ฐ ํ…Œ์ŠคํŠธ๋งˆ๋‹ค ๋…๋ฆฝ์ ์ธ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค๊ธฐ๊ฐ€ ์–ด๋ ต๋‹ค.

 

์žฅ์ 

๊ณ ์ •๋œ ๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ์„ ๊ฐ€์ง€๊ณ  ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค์–ด์„œ, ํ•ด๋‹น ์ธ์Šคํ„ด์Šค๋ฅผ ๋‹ค๋ฅธ ๋ชจ๋“ˆ๋“ค์ด ๊ณต์œ ํ•˜๋ฉฐ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•  ๋•Œ ๋“œ๋Š” ๋น„์šฉ์ด ์ค„์–ด๋“ ๋‹ค.

 

์˜์กด์„ฑ ์ฃผ์ž…(Dependency Injection)

์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์€ ๋ชจ๋“ˆ๊ฐ„์˜ ๊ฒฐํ•ฉ์„ ๊ฐ•ํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค. ์ด๋ฅผ ์™„ํ™”ํ•˜๊ธฐ ์œ„ํ•ด '์˜์กด์„ฑ ์ฃผ์ž…'์„ ์ด์šฉํ•œ๋‹ค.

*์˜์กด์„ฑ(์ข…์†์„ฑ) : A๊ฐ€ B์— ์˜์กด์„ฑ์ด ์žˆ๋‹ค๋Š” ๊ฒƒ์€ B์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์— ๋Œ€ํ•ด A๋„ ๋ณ€ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•จ.

์™ผ์ชฝ ๊ทธ๋ฆผ์ฒ˜๋Ÿผ ๋ฉ”์ธ ๋ชจ๋“ˆ์ด ์ง์ ‘ ๋‹ค๋ฅธ ํ•˜์œ„ ๋ชจ๋“ˆ์— ๋Œ€ํ•œ ์˜์กด์„ฑ์„ ์ฃผ๊ธฐ๋ณด๋‹ค๋Š” ์˜ค๋ฅธ์ชฝ ๊ทธ๋ฆผ์ฒ˜๋Ÿผ ์ค‘๊ฐ„์— ์˜์กด์„ฑ ์ฃผ์ž…์ž(Dependency Injection)๊ฐ€ ์ด ๋ถ€๋ถ„์„ ๊ฐ€๋กœ์ฑ„์„œ ๋ฉ”์ธ ๋ชจ๋“ˆ์ด ๊ฐ„์ ‘์ ์œผ๋กœ ์˜์กด์„ฑ์„ ์ฃผ์ž…ํ•˜๋Š” ๋ฐฉ์‹์ด๋‹ค.

์ด๋ฅผ ํ†ตํ•ด ๋ฉ”์ธ ๋ชจ๋“ˆ์€ ํ•˜์œ„ ๋ชจ๋“ˆ์— ๋Œ€ํ•œ ์˜์กด์„ฑ์ด ๋–จ์–ด์ง€๊ฒŒ ๋œ๋‹ค. (๋””์ปคํ”Œ๋ง ๋œ๋‹ค)

 

์˜์กด์„ฑ ์ฃผ์ž…์˜ ์˜ˆ์‹œ ์ฝ”๋“œ๋ฅผ ์•Œ์•„๋ณด์ž

class Weapon {
  public attack() {
    console.log('hit!');
  }
}

class Player {
  private weapon: Weapon;

  constructor() {
    this.weapon = new Weapon();
  }

  public attack() {
    this.weapon.attack();
  }
}

const me = new Player();
me.attack();

์œ„ ์ฝ”๋“œ์—์„œ ๋ณด๋ฉด Player ํด๋ž˜์Šค๋Š” Weapon ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ Weapon ํด๋ž˜์Šค์— ๋Œ€ํ•ด ์˜์กด์„ฑ์„ ๋„๊ณ  ์žˆ๋‹ค๊ณ  ํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ทธ๋Ÿฐ๋ฐ ์ด ์ค‘์— Weapon ํด๋ž˜์Šค๊ฐ€ ์•„๋‹ˆ๋ผ, Weapon ํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›์€ Whip ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด Weapon์„ ์˜์กดํ•˜๋Š” ๋ชจ๋“  ํด๋ž˜์Šค๋ฅผ ์ฐพ์•„ ์ˆ˜์ •ํ•ด์ค˜์•ผ ํ•œ๋‹ค. 

class Weapon {
  public attack() {
    console.log('hit!');
  }
}

class Whip extends Weapon {
  public attack() {
    console.log('whipwhip!');
  }
}

class Player {
  private weapon: Weapon;

  constructor() {
    this.weapon = new Whip();
  }

  public attack() {
    this.weapon.attack();
  }
}

const me = new Player();
me.attack();

์ด๋Ÿฌํ•œ ๊ฒฝ์šฐ, ์˜์กด์„ฑ ์ฃผ์ž…์„ ํ•˜๋ ค๋ฉด ์˜์กดํ•˜๊ณ  ์žˆ๋Š” ์ฝ”๋“œ ๋ถ€๋ถ„์—์„œ ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑ์ž๋กœ ๋ฐ›์•„์˜ค๋ฉด ๋œ๋‹ค.

์˜์กด์„ฑ ์ฃผ์ž… ์˜ˆ์‹œ

Player์—์„œ ์ƒ์„ฑ์ž๋กœ ์˜์กดํ•˜๊ณ ์ž ํ•˜๋Š” Weapon ํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›๋Š” ํด๋ž˜์Šค๋ฅผ ๋„˜๊ฒจ์ค€๋‹ค.

const me = new Player(new Whip());

๊ทธ๋Ÿฌ๋ฉด ๊ธฐ์กด์˜ ์˜์กด์„ฑ์„ ๊ฐ€์ง€๋Š” Player ํด๋ž˜์Šค๋ฅผ ์ˆ˜์ •ํ•  ํ•„์š”์—†์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋œ๋‹ค. 

 

์˜์กด์„ฑ ์ฃผ์ž…์˜ ์žฅ์ 

  • ๋ชจ๋“ˆ๋“ค์„ ์‰ฝ๊ฒŒ ๊ต์ฒดํ•  ์ˆ˜ ์žˆ๋Š” ๊ตฌ์กฐ๊ฐ€ ๋˜์–ด ํ…Œ์ŠคํŒ…ํ•˜๊ธฐ ์‰ฝ๊ณ  ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜(์—…๋ฐ์ดํŠธ)ํ•˜๊ธฐ ์ˆ˜์›”ํ•˜๋‹ค. 
  • ๊ตฌํ˜„ํ•  ๋•Œ ์ถ”์ƒํ™” ๊ณ„์ธต์„ ๋„ฃ๊ณ  ์ด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌํ˜„ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์˜์กด์„ฑ ๋ฐฉํ–ฅ์ด ์ผ๊ด€๋˜๊ณ  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‰ฝ๊ฒŒ ์ถ”๋ก ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋ชจ๋“ˆ ๊ฐ„์˜ ๊ด€๊ณ„๊ฐ€ ๋” ๋ช…ํ™•ํ•ด์ง„๋‹ค.

์˜์กด์„ฑ ์ฃผ์ž…์˜ ๋‹จ์ 

  • ๋ชจ๋“ˆ๋“ค์ด ๋”์šฑ ๋ถ„๋ฆฌ๋˜๋ฏ€๋กœ ํด๋ž˜์Šค ์ˆ˜๊ฐ€ ๋Š˜์–ด๋‚˜ ๋ณต์žก์„ฑ์ด ์ฆ๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์•ฝ๊ฐ„์˜ ๋Ÿฐํƒ€์ž„ ํŒจ๋„ํ‹ฐ๊ฐ€ ์ƒ๊ธฐ๊ธฐ๋„ ํ•œ๋‹ค.

์˜์กด์„ฑ ์ฃผ์ž… ์›์น™

  • ๊ณ ์ˆ˜์ค€ ๋ชจ๋“ˆ์€ ์ €์ˆ˜์ค€ ๋ชจ๋“ˆ์— ์˜์กดํ•ด์„œ๋Š” ์•ˆ๋˜๋ฉฐ, ๋‘˜ ๋‹ค ์ถ”์ƒํ™”์— ์˜์กดํ•ด์•ผ ํ•œ๋‹ค.
  • ์ถ”์ƒํ™”๋Š” ์„ธ๋ถ€ ์‚ฌํ•ญ์— ์˜์กดํ•˜์ง€ ์•Š์•„์•ผ ํ•œ๋‹ค.
  • ์ฆ‰, ์ƒ์œ„ ์ˆ˜์ค€ ๋ชจ๋“ˆ์€ ํ•˜์œ„ ์ˆ˜์ค€ ๋ชจ๋“ˆ ๋Œ€์‹ , ์ถ”์ƒํ™”๋œ ์ธํ„ฐํŽ˜์ด์Šค๋‚˜ ์ถ”์ƒ ํด๋ž˜์Šค์™€ ๊ฐ™์€ ์ถ”์ƒํ™”๋œ ๊ฐœ๋…์— ์˜์กดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๊ตฌ์ฒด์ ์ธ ๊ตฌํ˜„ ์„ธ๋ถ€ ์‚ฌํ•ญ์— ์ง์ ‘ ์˜์กดํ•˜์ง€ ์•Š๊ณ , ์ด๋ฅผ ์ถ”์ƒํ™”์— ์œ„์ž„ํ•จ์œผ๋กœ์จ ์‹œ์Šคํ…œ์˜ ์œ ์—ฐ์„ฑ๊ณผ ํ™•์žฅ์„ฑ์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š”๋ฐ ๋„์›€์ด ๋œ๋‹ค.