Node.js

싱글톤 패턴 (Singleton 패턴)

Jerry_K 2024. 12. 16. 09:05

nest 공부를 하다가, Singleton 패턴에 대해 나왔다.

singleton패턴에서는 하나의 객체만 만든다고 하는데 도저히 이해가 되지 않았다. 

 

하다보니 singleton패턴  몇 가지 의문이 들었었다.

(하나의 객체만 만들면 클라이언트끼리 특정 데이터에 접근하려 할 때 충돌이 발생하지는 않을까 등등)

 

그래서 Singel 패턴에 대해서 좀 알아 보았다. 


Singleton 패턴 

  • 싱글톤은 클래스의 인스턴스를 하나만 생성하도록 보장
    • 프로그램 전체에서 하나의 객체만 생성
    • 새로운 객체를 생성하지 않고 이미 존재하는 객체 재사용
  • 주로 글로벌 상태를 관리 (전역 접근 가능)
    • 하나의 클라이언트가 싱글톤 객체 속성을 수정하면 , 변경사항은 다른 모른 클라이언트에도 즉시 반영
  • 리소스 낭비를 방지
  • 생성자 (private)로 외부 생성 제한

싱글톤 패턴을 사용하면 자원 관리, 일관된 접근, 메모리 절약 등의 이점을 가져갈 수 있다.

핵심원리는 클래스 외부에서 객체 생성을 제한하고, 클래스 내부에서 객체를 관리한다. 

생성된 객체는 전역적으로 접근이 가능한 것이다. 

 

확실히 공유 자원 관리, 읽기 중심의 작업과 같은 경우에는 좋을 것 같다.


하지만 여기에 좀 찜찜한 기분이 든다. 

분명 하나의 객체만 생성하는 점은 충분한 이점이 있어보이지만,

예상치 못한 동작, 데이터 충돌 또는 클라이언트가 객체 단독 제어가 안돼 발생한 문제의 원인을 찾기가 어렵다.

 

위와 같은 문제를 아래와 같은 방법으로 해결한다

 

1. 클라이언트별 독립적인 상태 관리

class Singleton {
  private static instance: Singleton;
  private clientData: Map<string, any> = new Map();

  private constructor() {}

  public static getInstance(): Singleton {
    if (!Singleton.instance) {
      Singleton.instance = new Singleton();
    }
    return Singleton.instance;
  }

  public setClientData(clientId: string, data: any): void {
    this.clientData.set(clientId, data);
  }

  public getClientData(clientId: string): any {
    return this.clientData.get(clientId);
  }
}

// 사용 예시
const singleton = Singleton.getInstance();
singleton.setClientData("client1", { name: "Alice" });
singleton.setClientData("client2", { name: "Bob" });

console.log(singleton.getClientData("client1")); // { name: "Alice" }
console.log(singleton.getClientData("client2")); // { name: "Bob" }

 

 

2. Immutable 객체 사용

class Singleton {
  private static instance: Singleton;
  private data: { [key: string]: any } = {};

  private constructor() {}

  public static getInstance(): Singleton {
    if (!Singleton.instance) {
      Singleton.instance = new Singleton();
    }
    return Singleton.instance;
  }

  public getData(): { [key: string]: any } {
    return { ...this.data }; // 복사본 반환
  }

  public setData(key: string, value: any): void {
    this.data[key] = value; // 내부 상태는 수정 가능
  }
}

// 사용 예시
const singleton = Singleton.getInstance();
const clientData = singleton.getData(); // 복사본
clientData["name"] = "Bob"; // 복사본 수정, 원본에는 영향 없음

console.log(singleton.getData()); // 원본 상태는 그대로 유지
  • 상태를 변경하지 않고, 새로운 객체를 반환하여 충돌 방지
  • 데이터 변경 시 항상 복사본 만들어 제공

 

3. 읽기/쓰기 분리

class Singleton {
  private static instance: Singleton;
  private data: { [key: string]: any } = {};

  private constructor() {}

  public static getInstance(): Singleton {
    if (!Singleton.instance) {
      Singleton.instance = new Singleton();
    }
    return Singleton.instance;
  }

  public readData(key: string): any {
    return this.data[key]; // 읽기
  }

  public writeData(key: string, value: any): void {
    if (this.canWrite(key, value)) {
      this.data[key] = value; // 조건에 따라 쓰기
    }
  }

  private canWrite(key: string, value: any): boolean {
    // 쓰기 조건 (예: 중복 검사)
    return true;
  }
}
  • 읽기 작업은 자유롭게 허용
  • 쓰기 작업에서 제한 

 

(참고)

private constructor() {}
  • 이 부분이 싱글톤에 핵심
  • private 접근 제한자로 설정하여 클래스 외부에서 새로운 인스턴스 생성 제한

 

'Node.js' 카테고리의 다른 글

Default Export 와 Named Export  (0) 2024.12.12
Java script에서 Promise 란 ? (+@ Async / Await)  (0) 2024.12.12