싱글톤 패턴(Singleton pattern)

hwisaac·2023년 3월 2일
3

리팩토링

목록 보기
6/6

설명

싱글톤 패턴은 객체를 딱 한 번만 생성하도록 보장하는 디자인 패턴입니다. 이 패턴은 전역 변수를 사용하여 객체를 저장하고 다른 객체들이 그 객체를 공유하게 됩니다.

싱글톤 패턴은 주로 다음과 같은 경우에 사용됩니다.

  • 자원을 공유해야 하는 경우
  • 고비용 객체의 생성을 최소화해야 하는 경우
  • 시스템에서 단 하나의 객체만 존재해야 하는 경우

싱글톤 패턴을 구현하는 방법은 여러 가지가 있지만, 대표적인 방법은 다음과 같습니다.

예시

class Singleton {
  private static instance: Singleton;

  private constructor() {
    // private 생성자로 외부에서는 new로 인스턴스를 생성할 수 없습니다.
  }

  static getInstance(): Singleton {
    // 인스턴스가 없을 경우에만 인스턴스를 생성합니다.
    if (!Singleton.instance) {
      Singleton.instance = new Singleton();
    }
    return Singleton.instance;
  }

  public someMethod() {
    // 싱글톤 객체의 메서드
  }
}

// 객체를 생성할 때마다 새로운 인스턴스가 생성되지 않습니다.
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();

console.log(instance1 === instance2); // true

위의 코드에서 Singleton 클래스는 private 생성자를 가지고 있습니다. 이는 외부에서는 인스턴스를 생성할 수 없게끔 합니다.

대신, getInstance 메서드를 호출하여 인스턴스를 반환받을 수 있습니다.

getInstance 메서드는 static 메서드로 정의되어 있습니다.

이를 통해 클래스의 인스턴스를 생성하지 않고도 호출이 가능합니다.

싱글톤 패턴에서는 getInstance 메서드를 통해 인스턴스가 이미 생성되어 있다면 생성된 인스턴스를 반환하고, 없다면 새로운 인스턴스를 생성합니다.

이를 통해 언제 어디서나 같은 인스턴스를 공유해서 사용할 수 있습니다.

위 코드에서는 someMethod라는 메서드를 가지고 있는 간단한 싱글톤 객체를 예시로 사용했지만, 필요한 객체에 따라서 싱글톤 패턴을 적용하여 사용할 수 있습니다.

적용해보기

아래는 React에서 Axios Client를 사용하여 Singleton 패턴을 적용한 예제입니다.

import axios from "axios";

class AxiosClient {
  private static instance: AxiosClient;

  private constructor() {}

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

  public async get<T>(url: string): Promise<T> {
    const response = await axios.get<T>(url);
    return response.data;
  }

  public async post<T, R>(url: string, data: T): Promise<R> {
    const response = await axios.post<R>(url, data);
    return response.data;
  }
}

export default AxiosClient.getInstance();

위 코드에서 AxiosClient 클래스는 Singleton 패턴을 구현하며, getInstance 메서드를 통해 전역적으로 하나의 인스턴스만 생성되도록 합니다.

이를 통해 Axios 인스턴스를 불필요하게 중복 생성하지 않고, 전역적으로 하나의 인스턴스를 공유하여 사용할 수 있습니다. 이렇게 작성된 AxiosClient 객체는 다음과 같이 사용할 수 있습니다.

import axiosClient from './AxiosClient';

const fetchData = async () => {
  const data = await axiosClient.get<MyData>('https://example.com/data');
  console.log(data);
}

fetchData();

장점

애플리케이션 전역에서 인스턴스를 하나만 생성하여 공유할 수 있습니다.

이를 통해 애플리케이션 전체에서 공통적인 리소스나 데이터에 대한 중복 생성 및 관리를 방지할 수 있습니다.

객체 생성 비용과 자원 소모를 최소화할 수 있습니다.

또한 애플리케이션에서 사용되는 여러 개의 인스턴스가 서로 동기화되어야 하는 경우 이를 보장할 수 있습니다.

전역 인스턴스를 사용하면 객체 간의 의존성 관리가 용이해집니다.

예를 들어, 여러 곳에서 사용되는 데이터베이스 연결을 고려해보면, 각 객체가 자신만의 연결 객체를 생성할 경우 연결의 중복이 발생할 수 있습니다.

이러한 문제를 해결하기 위해 싱글톤 패턴을 사용하여 데이터베이스 연결 객체를 하나의 전역 인스턴스로 유지할 수 있습니다.

이렇게 하면 여러 객체에서 데이터베이스 연결 객체에 접근할 때마다 새로운 연결 객체를 생성하는 것이 아니라, 이미 생성된 전역 인스턴스에 접근하여 연결을 공유할 수 있습니다.

특정한 리소스를 공유해야 할 때 유용합니다.

예를 들어 데이터베이스 연결이나 로그 객체 등이 해당됩니다.

이러한 리소스들은 여러 객체에서 공유하여 사용해야 하므로, 싱글톤 패턴을 적용하여 중복 생성을 방지하고 메모리를 절약할 수 있습니다.

주의할 점

상태 변화를 조심스럽게 다루어야 합니다.

전역 상태를 유지하므로 상태 변화를 조심스럽게 다루어야 합니다. 다수의 클라이언트가 해당 인스턴스에 접근하고 수정할 수 있기 때문입니다.

멀티 스레드 환경에서 동시성 문제가 발생할 수 있다.

멀티 스레드 환경에서 여러 스레드가 동시에 인스턴스를 생성하려고 할 때, 싱글톤 인스턴스가 하나 이상 생성될 가능성이 있습니다.

이러한 문제를 방지하기 위해서는 동기화 처리를 해주어야 합니다. 다만, 이 경우 성능이 저하될 수 있으므로, 신중하게 고려해야 합니다.

초기화 과정이 복잡하거나 오래 걸리는 경우에는 애플리케이션의 시작 속도가 느려질 수 있습니다.

싱글톤 객체가 생성될 때 초기화 과정을 거치는 경우가 많습니다.

이러한 경우에는 지연 초기화(lazy initialization) 기법을 적용하거나, 필요한 시점에 초기화하는 방법을 사용할 수 있습니다.

지연 초기화 기법(Lazy Initialization)은 객체가 실제로 사용될 때까지 객체를 초기화하지 않고, 객체가 필요한 시점에 초기화하는 기법입니다.

싱글톤 객체는 객체지향적인 설계에서 문제가 발생할 수 있습니다.

객체지향적인 설계에서는 객체의 상태와 행위를 캡슐화하여 응집도(cohesion)와 결합도(coupling)를 유지해야 합니다.

하지만 싱글톤 객체는 객체의 상태와 행위를 모두 정적(static)으로 구현하기 때문에, 객체지향적인 설계 원칙을 위반할 수 있습니다.

싱글톤 객체가 '정적'으로 구현된다는 것은 해당 객체가 전역에서 유일하게 존재하는 것으로 정적인 특징을 가진다는 것입니다. 이는 객체의 상태와 행위가 변경되지 않고, 항상 일정하게 유지된다는 것을 의미합니다.

따라서, 싱글톤 패턴을 사용할 때는 객체지향적인 설계 원칙을 고려하여 구현하는 것이 중요합니다.

0개의 댓글