싱글턴
은 클래스에 인스턴스가 하나만 있도록 하면서 이 인스턴스에 대한 전역 접근(엑세스) 지점을 제공하는 생성 디자인 패턴입니다. 이렇게 정의로만 설명하면 말이 어려워 보일 수 있지만 실제 사용 사례들을 들어보면 그렇게 어려운 개념이 아니라는 것을 알 수 있습니다.Car
라는 클래스를 이용하여 서로에게 영향을 미치지 않은 독립적인 상태
를 갖는 여러 객체를 생성하였습니다. 하지만 만약 우리가 객체가 서로 독립적인 상태
가 아닌 공유 리소스
를 갖는 여러 객체를 생성한다고 할 경우는 어떻게 해야 할까요? 또 다른 예시로 자세히 설명해보겠습니다.country
의 값은 뭐가 나올까요? 정답은 undefined
입니다! 왜냐하면 db2 에서는 DB에 대한 인스턴스가 새로 생성되었기 때문에 서로 공유된 자원이 아닌 독립된 상태를 지니기 때문에 db1에서 설정한 sunub
에 대한 값은 공유가 되는 것이 아니기 때문에 db1 에서만 사용이 가능합니다.싱글턴
패턴입니다!!static
을 이용하여 new
연산자를 이용하여 새로운 인스턴스를 생성하지 않고 인스턴스의 수를 하나로 제한하여 공용 리소스를 사용할 수 있습니다. 하지만 위의 방법에는 진정한 싱글턴이 아니라는 문제가 존재합니다. 이 문제는 constructor 값이 private 값으로 설정되어 있지 않기 때문에 강제로 싱글턴 패턴을 이용하게끔 만들 수는 없다는 문제가 있습니다. 여기서 우리는 typescript
의 위대함을 또 다시 느낄 수 있습니다. javascript
에서는 이 문제를 해결하기 위해 다음과 같은 과정을 거쳐야 합니다.typescrip
t가 등장한다면 어떻게 될까요?class Car {
constructor(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
this.speed = 0;
}
accelerate(amount) {
this.speed += amount;
}
brake(amount) {
this.speed = Math.max(0, this.speed - amount);
}
}
const car1 = new Car("KIA", "K7", 2022);
const car2 = new Car("HYNDAI", "Sonata", 2023);
function singleton<Value>(name: string, value: () => Value): Value {
const yolo = global as any;
yolo.__singleton ??= {};
yolo.__singleton[name] ??= value();
return yolo.__singleton[name];
}
declare global {
interface Global {
__singleton?: Map<string, unknown>;
}
}
interface Global {
__singleton?: Map<string, unknown>;
}
export function singleton<Value>(name: string, value: () => Value): Value {
const thusly = globalThis as Global;
thusly.__singleton ??= new Map();
if (!thusly.__singleton.has(name)) {
thusly.__singleton.set(name, value());
}
return thusly.__singleton.get(name) as Value;
}
class DB {
constructor() {
this.table = {};
}
set(name, value) {
this.table[name] = value;
}
get(name) {
return this.table[name];
}
}
const db1 = new DB();
db1.set("sunub", { country: "Korea" });
const db2 = new DB();
const { country } = db2.get("sunub");
class Singleton {
static #instance = null;
constructor() {}
getInstance() {
if (Singleton.#instance == null) {
Singleton.#instance = new Singleton();
}
return Singleton.#instance;
}
}
const Singleton = (() => {
const constructorKey = Symbol("constructor");
return class {
static #instance = null;
constructor(key) {
if (key !== constructorKey) {
throw new Error("Private constructor, use getInstance() method.");
}
}
static getInstance() {
if (!Singleton.#instance) {
Singleton.#instance = new Singleton(constructorKey);
}
return Singleton.#instance;
}
};
})();
class Singleton {
private static instance: Singleton | null = null;
private constructor() {}
public static getInstance(): Singleton {
if (Singleton.instance === null) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
}