Engineering

디자인 패턴, 싱글톤(Singleton pattern)

소프트웨어를 개발하다 보면 특정 객체가 애플리케이션 전체에서 단 하나만 존재해야 하는 경우가 있습니다. 대표적으로 데이터베이스 연결 객체, 설정Configuration 관리 객체, 로그Logger 객체, 캐시Cache 관리자 등이 있습니다.

M
Mason
Software Engineer · · 4분

소프트웨어를 개발하다 보면 특정 객체가 애플리케이션 전체에서 단 하나만 존재해야 하는 경우가 있습니다. 대표적으로 데이터베이스 연결 객체, 설정(Configuration) 관리 객체, 로그(Logger) 객체, 캐시(Cache) 관리자 등이 있습니다. 이러한 객체가 여러 개 생성되면 자원 낭비가 발생하거나 데이터의 일관성이 깨질 수 있는데, 이를 해결하기 위해 사용하는 디자인 패턴이 바로 싱글톤(Singleton) 패턴입니다.

싱글톤 패턴은 클래스의 인스턴스가 오직 하나만 생성되도록 보장하고, 어디서든 해당 인스턴스에 접근할 수 있도록 전역 접근점을 제공하는 생성(Creational) 패턴입니다. 객체 생성 과정을 클래스 내부에서 직접 제어하기 때문에 외부에서는 새로운 객체를 생성할 수 없으며, 항상 동일한 인스턴스를 사용하게 됩니다.

예를 들어 웹 애플리케이션에서 데이터베이스 연결 객체를 매번 새로 생성한다면 불필요한 연결이 계속 발생하게 됩니다. 하지만 싱글톤 패턴을 적용하면 하나의 연결 객체를 재사용할 수 있어 메모리 사용량을 줄이고 성능을 향상시킬 수 있습니다.

TypeScript로 구현한 가장 기본적인 싱글톤 패턴은 다음과 같습니다.

class Singleton {
  private static instance: Singleton;
 
  private constructor() {}
 
  public static getInstance(): Singleton {
    if (!Singleton.instance) {
      Singleton.instance = new Singleton();
    }
 
    return Singleton.instance;
  }
 
  public printMessage() {
    console.log('Singleton Instance');
  }
}

위 코드에서 constructor를 private으로 선언했기 때문에 외부에서는 new Singleton()을 사용할 수 없습니다. 대신 getInstance() 메서드를 통해 객체를 가져오게 되며, 최초 한 번만 생성되고 이후에는 동일한 인스턴스를 반환합니다.

실제로 사용해 보면 항상 같은 객체가 반환되는 것을 확인할 수 있습니다.

const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
 
console.log(instance1 === instance2);

실행 결과는 다음과 같습니다.

true

두 변수는 서로 다른 객체가 아니라 동일한 객체를 참조하고 있기 때문에 true가 출력됩니다. 이것이 싱글톤 패턴의 핵심입니다.

싱글톤 패턴의 장점은 명확합니다. 먼저 객체를 한 번만 생성하기 때문에 메모리 사용량을 줄일 수 있습니다. 또한 공통 자원을 중앙에서 관리할 수 있어 상태를 일관성 있게 유지할 수 있습니다. 그리고 전역 접근이 가능하기 때문에 여러 곳에서 동일한 객체를 공유하기 쉽습니다.

반면 단점도 존재합니다. 전역 상태를 가지게 되므로 객체 간 결합도가 높아질 수 있으며, 테스트 환경에서는 객체 상태가 공유되어 단위 테스트가 어려워질 수 있습니다. 또한 지나치게 사용하면 코드 의존성이 증가하여 유지보수가 어려워질 수 있습니다.

class AppConfig {
  private static instance: AppConfig;
 
  private config = {
    apiUrl: 'https://api.example.com',
    version: '1.0.0',
  };
 
  private constructor() {}
 
  static getInstance() {
    if (!this.instance) {
      this.instance = new AppConfig();
    }
 
    return this.instance;
  }
 
  getConfig() {
    return this.config;
  }
}

위와 같은 설정 관리 객체는 애플리케이션 전체에서 동일한 설정값을 사용해야 하기 때문에 싱글톤 패턴을 적용하기 좋은 예시입니다.

최근에는 프레임워크 자체에서 싱글톤 개념을 제공하는 경우도 많습니다. 예를 들어 NestJS의 Provider는 기본적으로 Singleton Scope를 사용하며, Spring Framework의 Bean 역시 기본 Scope가 Singleton입니다. 따라서 현대적인 서버 애플리케이션을 개발할 때는 의식하지 않아도 싱글톤 패턴을 활용하는 경우가 매우 많습니다.

싱글톤 패턴은 가장 단순하면서도 널리 사용되는 디자인 패턴 중 하나입니다. 하지만 무조건 적용하기보다는 정말 하나의 인스턴스만 존재해야 하는 경우에 사용하는 것이 중요합니다. 적절하게 활용한다면 메모리 사용을 줄이고 자원 관리의 효율성을 높일 수 있지만, 남용할 경우 오히려 유지보수성과 테스트 용이성을 떨어뜨릴 수 있기 때문입니다. 결국 싱글톤 패턴은 "객체를 하나만 생성해야 하는 명확한 이유가 있을 때 사용하는 패턴"이라고 이해하는 것이 가장 중요합니다.

이 글이 어떠셨나요? 이모지로 반응을 남겨주세요
M
Mason
Software Engineer · masonlab

코드와 비즈니스, 그 사이에서 배운 것들을 기록하고 공유합니다.

댓글 0

아직 댓글이 없어요. 첫 댓글을 남겨보세요.

이런 글은 어때요?