이미지 로딩 중...

Singleton Pattern 실무 활용 팁 - 슬라이드 1/6
A

AI Generated

2025. 11. 5. · 5 Views

Singleton Pattern 실무 활용 팁

싱글톤 패턴의 올바른 구현 방법과 실무에서 자주 발생하는 문제들의 해결책을 다룹니다. 초급 개발자가 싱글톤을 효과적으로 활용할 수 있도록 실제 사례와 함께 상세히 설명합니다.


목차

  1. 기본_싱글톤_구현
  2. 클로저를_활용한_싱글톤
  3. ES6_모듈_싱글톤
  4. 프록시를_활용한_지능형_싱글톤
  5. 싱글톤_안티패턴과_대안

1. 기본_싱글톤_구현

시작하며

여러분이 데이터베이스 연결을 관리하거나 앱 설정을 다룰 때, 여러 곳에서 같은 객체를 사용해야 하는 상황을 겪어본 적 있나요? 매번 new 키워드로 새 인스턴스를 만들다 보니 메모리 낭비도 심하고, 데이터 일관성도 깨지는 문제가 발생하죠.

이런 문제는 실제 개발 현장에서 자주 발생합니다. 특히 설정 관리자, 로거, 캐시 매니저 같은 시스템 전역에서 공유되어야 하는 객체들을 다룰 때 더욱 그렇습니다.

여러 개의 인스턴스가 생성되면 상태 불일치, 리소스 낭비, 예측 불가능한 동작 등의 문제가 발생할 수 있습니다. 바로 이럴 때 필요한 것이 싱글톤 패턴입니다.

클래스의 인스턴스를 딱 하나만 생성하고, 어디서든 그 하나의 인스턴스에만 접근할 수 있도록 보장하는 디자인 패턴이죠.

개요

간단히 말해서, 싱글톤 패턴은 클래스의 인스턴스가 오직 하나만 생성되도록 보장하는 디자인 패턴입니다. 데이터베이스 커넥션 풀, 로거, 캐시 매니저처럼 시스템 전체에서 하나의 인스턴스만 필요한 경우에 매우 유용합니다.

예를 들어, 여러 컴포넌트에서 동시에 로그를 남기더라도 하나의 로거 인스턴스만 사용하면 파일 접근 충돌을 방지하고 일관된 로그 형식을 유지할 수 있습니다. 전통적인 방법으로는 전역 변수를 사용하거나 매번 같은 객체를 전달하는 방식을 사용했다면, 싱글톤 패턴을 사용하면 클래스 자체에서 인스턴스 생성을 제어할 수 있습니다.

이는 더 안전하고 캡슐화된 방식입니다. 싱글톤의 핵심 특징은 private 생성자, static 인스턴스 변수, 그리고 public static 접근 메서드입니다.

이러한 특징들이 결합되어 단 하나의 인스턴스만 생성되고 전역 접근점을 제공하면서도 느슨한 결합을 유지할 수 있게 해줍니다.

코드 예제

class DatabaseConnection {
  constructor() {
    // private 생성자 역할 - 외부에서 new 사용 방지
    if (DatabaseConnection.instance) {
      return DatabaseConnection.instance;
    }

    // 실제 초기화 로직
    this.connection = null;
    this.config = { host: 'localhost', port: 3306 };
    this.connect();

    // 인스턴스 저장
    DatabaseConnection.instance = this;
  }

  connect() {
    console.log('Database connected with', this.config);
    this.connection = 'Connected';
  }
}

설명

이것이 하는 일: DatabaseConnection 클래스는 데이터베이스 연결을 관리하는 싱글톤 객체를 생성합니다. 아무리 많이 new DatabaseConnection()을 호출해도 항상 같은 인스턴스를 반환합니다.

첫 번째로, 생성자에서 DatabaseConnection.instance를 확인합니다. 이미 인스턴스가 존재한다면 새로 만들지 않고 기존 인스턴스를 즉시 반환합니다.

이것이 싱글톤 패턴의 핵심입니다. JavaScript에서는 생성자가 객체를 반환할 수 있다는 특성을 활용한 것이죠.

두 번째로, 처음 생성될 때만 실제 초기화 로직이 실행됩니다. connection 속성과 config 설정을 초기화하고, connect() 메서드를 호출하여 데이터베이스에 연결합니다.

이 과정은 앱 실행 중 딱 한 번만 일어납니다. 세 번째로, 생성된 인스턴스를 DatabaseConnection.instance에 저장합니다.

이는 클래스의 static 속성처럼 작동하여, 다음에 생성자가 호출될 때 이 저장된 인스턴스를 반환할 수 있게 합니다. 여러분이 이 코드를 사용하면 데이터베이스 연결이 하나만 생성되어 메모리를 절약하고, 모든 모듈에서 동일한 연결을 공유할 수 있습니다.

연결 설정이 변경되어도 한 곳에서만 관리하면 되고, 연결 상태도 일관되게 유지됩니다.

실전 팁

💡 생성자에서 return을 사용하는 것은 JavaScript의 특별한 기능입니다. 다른 언어에서는 static 메서드를 사용해야 합니다

💡 Object.freeze()를 사용하여 싱글톤 인스턴스를 불변으로 만들면 실수로 수정되는 것을 방지할 수 있습니다

💡 싱글톤은 테스트하기 어려울 수 있으므로, reset() 메서드를 추가하여 테스트 환경에서 인스턴스를 초기화할 수 있게 하세요

💡 Node.js에서는 module.exports 자체가 싱글톤처럼 동작하므로, 복잡한 싱글톤 구현 대신 모듈 시스템을 활용하는 것도 좋은 방법입니다


2. 클로저를_활용한_싱글톤

시작하며

여러분이 싱글톤을 구현할 때, 인스턴스 변수가 전역 스코프에 노출되어 누군가 실수로 수정할까 봐 걱정된 적 있나요? DatabaseConnection.instance = null 같은 코드 한 줄이면 싱글톤이 깨져버리죠.

이런 문제는 JavaScript의 동적 특성 때문에 더욱 심각합니다. 누구나 클래스의 static 속성에 접근하여 수정할 수 있고, 이는 예상치 못한 버그의 원인이 됩니다.

특히 대규모 프로젝트에서 여러 개발자가 협업할 때 이런 실수가 발생하기 쉽습니다. 바로 이럴 때 클로저를 활용한 싱글톤 패턴이 빛을 발합니다.

IIFE(즉시 실행 함수 표현식)와 클로저를 사용하면 private 변수를 완벽하게 캡슐화할 수 있습니다.

개요

간단히 말해서, 클로저를 활용한 싱글톤은 외부에서 접근할 수 없는 private 인스턴스를 생성하는 더 안전한 패턴입니다. IIFE로 생성된 클로저 내부에 인스턴스를 숨기면, 외부에서는 오직 공개된 메서드를 통해서만 인스턴스에 접근할 수 있습니다.

예를 들어, 설정 관리자나 인증 토큰 관리자처럼 보안이 중요한 객체를 다룰 때 특히 유용합니다. 기존의 클래스 기반 싱글톤은 instance 속성이 노출되어 있었다면, 클로저 기반 싱글톤은 완전한 정보 은닉을 제공합니다.

이는 객체지향 프로그래밍의 캡슐화 원칙을 더 잘 따르는 방식입니다. 클로저 싱글톤의 핵심은 IIFE, private 변수, 그리고 public 메서드의 조합입니다.

IIFE가 실행되면서 클로저가 생성되고, 내부 변수는 영원히 private으로 유지되면서도 반환된 메서드를 통해 제어된 접근만 허용합니다.

코드 예제

const ConfigManager = (function() {
  // private 인스턴스 - 외부에서 접근 불가
  let instance = null;

  // private 생성자 함수
  function createInstance() {
    const config = {
      apiUrl: 'https://api.example.com',
      timeout: 5000,
      retryCount: 3
    };

    return {
      get: (key) => config[key],
      set: (key, value) => { config[key] = value; },
      getAll: () => ({...config})  // 복사본 반환
    };
  }

  // public 메서드만 노출
  return {
    getInstance: function() {
      if (!instance) {
        instance = createInstance();
      }
      return instance;
    }
  };
})();

설명

이것이 하는 일: ConfigManager는 IIFE를 사용하여 설정 관리 싱글톤을 생성합니다. 클로저를 통해 인스턴스와 설정 데이터를 완벽하게 보호하면서도 필요한 기능만 외부에 노출합니다.

첫 번째로, IIFE가 실행되면서 클로저가 생성됩니다. 이 클로저 내부의 instance 변수와 createInstance 함수는 외부에서 절대 직접 접근할 수 없습니다.

이것이 JavaScript에서 true private을 구현하는 전통적인 방법입니다. 두 번째로, getInstance() 메서드가 호출될 때 instance가 null이면 createInstance()를 호출하여 새 인스턴스를 생성합니다.

createInstance 함수는 설정 객체를 private으로 유지하면서도, get/set/getAll 메서드를 통해 제어된 접근을 제공합니다. 세 번째로, 설정 데이터 자체도 클로저 내부에 숨겨져 있습니다.

getAll() 메서드는 스프레드 연산자를 사용하여 복사본을 반환하므로, 원본 설정 객체가 외부에서 수정되는 것을 방지합니다. 이는 불변성을 유지하는 좋은 패턴입니다.

마지막으로, IIFE는 객체 하나만 반환합니다. 이 객체에는 getInstance 메서드만 있으므로, 외부에서는 이 메서드를 통해서만 싱글톤에 접근할 수 있습니다.

ConfigManager.instance = null 같은 코드로 싱글톤을 깨뜨릴 수 없게 됩니다. 여러분이 이 코드를 사용하면 완벽한 캡슐화와 정보 은닉을 달성할 수 있습니다.

설정 데이터의 무결성이 보장되고, 의도하지 않은 수정으로부터 안전하며, 명확한 인터페이스를 통해서만 상호작용이 가능합니다.

실전 팁

💡 IIFE 패턴은 ES6 모듈이 없던 시절의 모듈 패턴이기도 합니다. 현대적인 프로젝트에서는 ES6 모듈과 함께 사용하면 더욱 강력합니다

💡 클로저는 메모리에 계속 유지되므로, 대용량 데이터를 다루는 싱글톤에서는 메모리 사용량을 주의깊게 모니터링하세요

💡 개발 환경에서는 디버깅을 위해 _debug() 메서드를 추가하여 내부 상태를 확인할 수 있게 하는 것도 좋습니다

💡 TypeScript를 사용한다면 private 키워드와 static 메서드를 활용하여 더 깔끔하게 구현할 수 있습니다


3. ES6_모듈_싱글톤

시작하며

여러분이 복잡한 싱글톤 패턴을 구현하다가 "왜 이렇게 복잡하게 해야 하지?"라고 생각해본 적 있나요? 특히 Node.js나 모던 JavaScript 환경에서 개발할 때, 굳이 클래스나 클로저를 사용해야 할까 의문이 들 때가 있죠.

사실 ES6 모듈 시스템 자체가 싱글톤의 특성을 가지고 있다는 걸 아시나요? 모듈은 처음 import될 때 한 번만 실행되고, 그 이후에는 캐시된 결과를 반환합니다.

이는 싱글톤 패턴이 해결하려는 문제를 자연스럽게 해결해줍니다. 바로 이런 ES6 모듈의 특성을 활용하면, 별도의 패턴 구현 없이도 간단하고 깔끔한 싱글톤을 만들 수 있습니다.

특히 설정 객체, 유틸리티 함수 모음, 상태 관리자 등을 구현할 때 매우 유용합니다.

개요

간단히 말해서, ES6 모듈을 사용한 싱글톤은 JavaScript 모듈 시스템의 캐싱 메커니즘을 활용하여 자연스럽게 싱글톤을 구현하는 방법입니다. 모듈을 여러 파일에서 import해도 실제 모듈 코드는 단 한 번만 실행되고, 동일한 인스턴스가 공유됩니다.

예를 들어, API 클라이언트나 이벤트 버스 같은 전역 서비스를 구현할 때 복잡한 싱글톤 패턴 없이도 같은 효과를 얻을 수 있습니다. 전통적인 싱글톤 패턴은 인스턴스 생성을 제어하는 로직이 필요했다면, ES6 모듈 방식은 그런 보일러플레이트 코드가 전혀 필요 없습니다.

그저 일반 객체나 클래스 인스턴스를 export하면 됩니다. ES6 모듈 싱글톤의 장점은 단순함, 가독성, 그리고 트리 셰이킹 지원입니다.

번들러가 사용하지 않는 코드를 자동으로 제거할 수 있고, 순환 의존성도 더 쉽게 감지할 수 있습니다.

코드 예제

// logger.js - 싱글톤 모듈
class Logger {
  constructor() {
    this.logs = [];
    this.logLevel = 'info';
    console.log('Logger initialized'); // 한 번만 출력됨
  }

  log(level, message) {
    const timestamp = new Date().toISOString();
    const logEntry = { timestamp, level, message };
    this.logs.push(logEntry);
    console.log(`[${level}] ${message}`);
  }

  info(message) { this.log('info', message); }
  error(message) { this.log('error', message); }
  getLogs() { return [...this.logs]; }
}

// 인스턴스를 생성하여 export
export default new Logger();

설명

이것이 하는 일: Logger 클래스의 인스턴스를 생성하여 export합니다. 이 모듈을 import하는 모든 파일은 동일한 Logger 인스턴스를 공유하게 됩니다.

첫 번째로, Logger 클래스를 정의하고 즉시 인스턴스를 생성합니다. 생성자의 console.log는 앱 실행 중 단 한 번만 출력되는데, 이는 모듈이 처음 import될 때만 실행된다는 것을 증명합니다.

이후의 import는 모두 캐시된 결과를 받게 됩니다. 두 번째로, Logger 인스턴스는 내부 상태(logs 배열)를 유지합니다.

어느 파일에서 로그를 추가하든, 모든 파일이 같은 logs 배열을 공유합니다. 이는 전역 로그 수집기로서 완벽하게 작동합니다.

세 번째로, getLogs() 메서드는 스프레드 연산자로 배열의 복사본을 반환합니다. 이렇게 하면 외부에서 원본 logs 배열을 직접 수정할 수 없어 데이터 무결성이 보장됩니다.

네 번째로, 이 패턴의 진짜 장점은 사용하는 쪽의 코드입니다. import logger from './logger.js'만 하면 되고, getInstance() 같은 메서드를 호출할 필요가 없습니다.

일반 객체처럼 자연스럽게 사용할 수 있죠. 여러분이 이 코드를 사용하면 복잡한 싱글톤 로직 없이도 전역 상태를 안전하게 관리할 수 있습니다.

코드가 간결해지고, 의도가 명확해지며, 모듈 번들러의 최적화 혜택도 받을 수 있습니다.

실전 팁

💡 개발 중에는 hot module replacement(HMR)가 모듈을 다시 로드할 수 있으므로, 상태가 초기화될 수 있다는 점을 인지하세요

💡 순환 의존성을 피하기 위해 싱글톤 모듈은 다른 모듈에 대한 의존성을 최소화하는 것이 좋습니다

💡 테스트를 위해 reset() 메서드를 추가하되, process.env.NODE_ENV === 'test'일 때만 동작하도록 조건을 걸어두세요

💡 TypeScript를 사용한다면 as const assertion을 활용하여 싱글톤 객체의 타입을 더 엄격하게 관리할 수 있습니다

💡 동적 import()를 사용하면 싱글톤의 초기화 시점을 제어할 수 있어, 무거운 초기화 작업이 있을 때 유용합니다


4. 프록시를_활용한_지능형_싱글톤

시작하며

여러분이 싱글톤 객체의 사용을 추적하고 싶거나, 특정 조건에서만 인스턴스를 생성하고 싶을 때가 있나요? 예를 들어, 실제로 사용되기 전까지는 무거운 리소스를 로드하고 싶지 않은 경우가 있죠.

일반적인 싱글톤 패턴은 즉시 또는 첫 호출 시 인스턴스를 생성합니다. 하지만 때로는 더 똑똑한 동작이 필요합니다.

접근 패턴을 분석하거나, 권한을 체크하거나, 자동으로 캐시를 관리하는 등의 추가 기능이 필요한 경우가 있습니다. 바로 이럴 때 ES6 Proxy를 활용한 지능형 싱글톤이 해답이 될 수 있습니다.

Proxy를 사용하면 객체 접근을 가로채고, 원하는 로직을 추가할 수 있습니다.

개요

간단히 말해서, Proxy를 활용한 싱글톤은 객체 접근을 가로채어 추가적인 기능을 제공하는 스마트한 싱글톤 패턴입니다. Lazy initialization, 접근 로깅, 자동 직렬화, 권한 체크 등 다양한 횡단 관심사를 싱글톤에 투명하게 추가할 수 있습니다.

예를 들어, 대용량 데이터를 다루는 캐시 매니저에서 메모리 사용량을 모니터링하면서 자동으로 오래된 데이터를 정리하는 기능을 추가할 수 있습니다. 기존 싱글톤은 단순히 하나의 인스턴스만 보장했다면, Proxy 싱글톤은 인스턴스의 동작을 런타임에 수정하고 확장할 수 있습니다.

이는 AOP(Aspect-Oriented Programming) 개념을 JavaScript에 적용한 것입니다. Proxy 싱글톤의 강력함은 투명성, 유연성, 그리고 관심사의 분리에 있습니다.

원본 객체는 변경하지 않으면서도 다양한 기능을 레이어처럼 추가할 수 있습니다.

코드 예제

const SmartCache = (function() {
  let instance = null;
  let accessCount = {};

  class Cache {
    constructor() {
      this.data = new Map();
      this.maxSize = 100;
    }
    set(key, value) {
      if (this.data.size >= this.maxSize) {
        const firstKey = this.data.keys().next().value;
        this.data.delete(firstKey);  // LRU 제거
      }
      this.data.set(key, value);
    }
    get(key) { return this.data.get(key); }
  }

  return new Proxy({}, {
    get(target, prop) {
      if (!instance) {
        console.log('늦은 초기화: Cache 생성');
        instance = new Cache();
      }
      // 접근 횟수 추적
      accessCount[prop] = (accessCount[prop] || 0) + 1;

      if (prop === 'stats') {
        return () => ({ accessCount, size: instance.data.size });
      }
      return typeof instance[prop] === 'function'
        ? instance[prop].bind(instance)
        : instance[prop];
    }
  });
})();

설명

이것이 하는 일: SmartCache는 Proxy를 사용하여 지능형 캐시 싱글톤을 구현합니다. 실제로 사용되기 전까지 인스턴스를 생성하지 않고, 모든 메서드 호출을 추적하며, LRU 정책으로 메모리를 관리합니다.

첫 번째로, Proxy의 get 트랩이 모든 속성 접근을 가로챕니다. 처음 접근할 때만 Cache 인스턴스를 생성하는 지연 초기화(lazy initialization)가 구현되어 있습니다.

이는 앱 시작 시간을 단축하고, 사용하지 않는 리소스의 로딩을 방지합니다. 두 번째로, 모든 속성 접근이 accessCount 객체에 기록됩니다.

이를 통해 어떤 메서드가 얼마나 자주 호출되는지 추적할 수 있고, 성능 분석이나 최적화에 활용할 수 있습니다. 이런 메트릭은 프로덕션 환경에서 매우 유용합니다.

세 번째로, Cache 클래스 자체는 LRU(Least Recently Used) 캐시를 구현합니다. maxSize를 초과하면 가장 오래된 항목을 자동으로 제거하여 메모리 사용량을 제한합니다.

이는 메모리 누수를 방지하는 중요한 패턴입니다. 네 번째로, 특별한 'stats' 속성을 통해 런타임 통계를 제공합니다.

이는 원본 Cache 클래스에는 없는 기능이지만, Proxy를 통해 투명하게 추가되었습니다. 디버깅과 모니터링에 매우 유용합니다.

마지막으로, 메서드를 반환할 때 bind를 사용하여 this 컨텍스트를 보장합니다. 이는 메서드를 변수에 할당하거나 콜백으로 전달할 때도 올바르게 동작하도록 합니다.

여러분이 이 코드를 사용하면 고성능 캐시 시스템을 구축할 수 있습니다. 자동 메모리 관리, 사용 패턴 분석, 지연 로딩 등 엔터프라이즈급 기능을 간단하게 구현할 수 있습니다.

실전 팁

💡 Proxy는 성능 오버헤드가 있으므로, 자주 호출되는 hot path에서는 주의해서 사용하세요

💡 개발 환경에서만 Proxy를 적용하고, 프로덕션에서는 일반 객체를 사용하는 조건부 로직을 추가할 수 있습니다

💡 Reflect API와 함께 사용하면 더 안전하고 표준적인 Proxy 구현이 가능합니다

💡 WeakMap을 사용하여 private 데이터를 저장하면 메모리 누수를 방지하면서도 캡슐화를 유지할 수 있습니다


5. 싱글톤_안티패턴과_대안

시작하며

여러분이 싱글톤을 남용하다가 테스트가 어려워지고, 코드가 강하게 결합되어 수정이 힘들어진 경험이 있나요? "싱글톤은 전역 변수를 객체지향으로 포장한 것뿐이다"라는 비판을 들어보신 적이 있을 겁니다.

실제로 싱글톤 패턴은 많은 문제를 야기할 수 있습니다. 단위 테스트가 어려워지고, 의존성이 숨겨지며, 병렬 처리 환경에서 문제가 발생할 수 있습니다.

특히 마이크로서비스나 서버리스 환경에서는 싱글톤의 전제 조건 자체가 성립하지 않을 수 있죠. 그렇다면 싱글톤 없이 어떻게 전역 상태를 관리하고 리소스를 공유할까요?

의존성 주입, 팩토리 패턴, 서비스 로케이터 등 다양한 대안이 있습니다.

개요

간단히 말해서, 싱글톤의 문제점을 인식하고 적절한 대안을 선택하는 것이 현대적인 소프트웨어 설계의 핵심입니다. 싱글톤은 테스트 격리를 방해하고, 숨은 의존성을 만들며, 확장성을 제한합니다.

예를 들어, 데이터베이스 연결을 싱글톤으로 만들면 테스트용 mock 데이터베이스로 교체하기 어렵고, 여러 데이터베이스를 사용해야 할 때 문제가 됩니다. 전통적인 싱글톤 대신 의존성 주입(DI)을 사용하면, 필요한 의존성을 명시적으로 전달받아 테스트와 확장이 쉬워집니다.

생성자나 메서드 파라미터로 의존성을 받으면 코드의 의도가 명확해지고 결합도가 낮아집니다. 현대적인 접근 방법은 IoC 컨테이너, 서비스 레지스트리, 또는 컨텍스트 API를 사용하는 것입니다.

이들은 싱글톤의 이점(인스턴스 재사용)은 유지하면서도 단점(강한 결합, 테스트 어려움)을 해결합니다.

코드 예제

// 나쁜 예: 싱글톤 직접 사용
class OrderService {
  createOrder(data) {
    // 숨은 의존성 - 테스트 어려움
    DatabaseConnection.getInstance().save(data);
    Logger.getInstance().info('Order created');
  }
}

// 좋은 예: 의존성 주입
class BetterOrderService {
  constructor(database, logger) {
    // 명시적 의존성 - 테스트 쉬움
    this.database = database;
    this.logger = logger;
  }

  createOrder(data) {
    this.database.save(data);
    this.logger.info('Order created');
  }
}

// 서비스 컨테이너/팩토리
class ServiceContainer {
  constructor() {
    this.services = new Map();
    this.singletons = new Map();
  }

  register(name, factory, options = {}) {
    this.services.set(name, { factory, ...options });
  }

  get(name) {
    const service = this.services.get(name);
    if (service.singleton) {
      if (!this.singletons.has(name)) {
        this.singletons.set(name, service.factory(this));
      }
      return this.singletons.get(name);
    }
    return service.factory(this);
  }
}

설명

이것이 하는 일: 코드는 싱글톤의 문제점과 해결책을 보여줍니다. 숨은 의존성을 명시적으로 만들고, 서비스 컨테이너를 통해 유연한 인스턴스 관리를 구현합니다.

첫 번째로, 나쁜 예시의 OrderService는 싱글톤을 직접 참조합니다. 이 코드를 테스트하려면 전역 싱글톤을 모킹해야 하고, 다른 데이터베이스나 로거를 사용하려면 코드를 수정해야 합니다.

이는 개방-폐쇄 원칙을 위반합니다. 두 번째로, BetterOrderService는 생성자를 통해 의존성을 주입받습니다.

테스트할 때는 mock 객체를 전달하고, 프로덕션에서는 실제 객체를 전달할 수 있습니다. 코드가 더 이상 특정 구현에 의존하지 않고 인터페이스에 의존합니다.

세 번째로, ServiceContainer는 싱글톤과 DI의 장점을 결합합니다. register 메서드로 서비스를 등록하고, singleton 옵션으로 인스턴스 재사용을 제어합니다.

이는 Spring의 IoC 컨테이너나 Angular의 DI 시스템과 유사한 패턴입니다. 네 번째로, 서비스 컨테이너의 get 메서드는 지능적으로 동작합니다.

singleton 플래그가 true면 인스턴스를 캐시하고, false면 매번 새 인스턴스를 생성합니다. 이렇게 하면 상황에 따라 적절한 스코프를 선택할 수 있습니다.

마지막으로, factory 함수는 컨테이너 자체를 받아 다른 서비스를 주입받을 수 있습니다. 이는 서비스 간 의존성을 자동으로 해결하는 강력한 패턴입니다.

여러분이 이 코드를 사용하면 테스트 가능하고, 확장 가능하며, 유지보수가 쉬운 코드를 작성할 수 있습니다. 싱글톤의 편리함은 유지하면서도 그 제약에서 벗어날 수 있습니다.

실전 팁

💡 싱글톤을 사용하기 전에 "정말 전역적으로 하나만 있어야 하는가?"를 자문해보세요. 대부분의 경우 그렇지 않습니다

💡 테스트 코드를 먼저 작성하면(TDD) 자연스럽게 의존성 주입을 사용하게 됩니다

💡 TypeScript를 사용한다면 인터페이스를 정의하여 의존성을 추상화하고, 구현체를 쉽게 교체할 수 있게 하세요

💡 함수형 프로그래밍의 순수 함수와 불변성을 활용하면 싱글톤 없이도 예측 가능한 코드를 작성할 수 있습니다

💡 React의 Context API나 Vue의 provide/inject처럼 프레임워크 제공 DI 메커니즘을 활용하는 것도 좋은 방법입니다


#JavaScript#DesignPattern#Singleton#OOP#BestPractices

댓글 (0)

댓글을 작성하려면 로그인이 필요합니다.