맨위로가기

싱글턴 패턴

"오늘의AI위키"는 AI 기술로 일관성 있고 체계적인 최신 지식을 제공하는 혁신 플랫폼입니다.
"오늘의AI위키"의 AI를 통해 더욱 풍부하고 폭넓은 지식 경험을 누리세요.

1. 개요

싱글턴 패턴은 클래스의 인스턴스가 오직 하나만 생성되도록 보장하는 디자인 패턴이다. 파이썬, 자바, C++, C#, PHP, 코틀린, 파스칼, Go, 다트 등 다양한 프로그래밍 언어에서 구현될 수 있으며, 각 언어의 특성에 따라 구현 방식이 다르다. 싱글턴 패턴은 생성자를 private으로 선언하고 정적 메서드를 통해 인스턴스에 접근하는 방식으로 구현되며, 다른 디자인 패턴의 구현에도 사용될 수 있다. 하지만 전역 상태를 도입하여 결합도를 높이고 단위 테스트를 어렵게 만들 수 있다는 비판도 있으며, 멀티턴 패턴은 싱글턴 패턴을 확장한 형태로, 여러 개의 인스턴스를 관리한다.

2. 언어별 구현

싱글턴 패턴은 다양한 프로그래밍 언어에서 구현될 수 있으며, 각 언어의 특성에 맞는 구현 방식이 존재한다.


  • 파이썬 모듈은 그 자체로 싱글턴이다.[1]
  • 자바는 생성자를 `private`으로 선언하여 상속이 불가능함을 지정하기도 한다.
  • C#에서는 private 생성자와 정적 속성을 사용하여 싱글턴을 구현하며, `lock` 키워드를 이용해 스레드 안전성을 확보하거나 정적 초기화자를 사용하여 더 간결하게 구현할 수 있다.[1]
  • C++에서는 클래스의 생성자, 복사 생성자, 복사 대입 연산자를 `private`으로 설정하여 유일성을 보장할 수 있다.[20]


PHP 버전 5.0 이후에는 가시성, 클래스 함수, 클래스 변수 등의 기능을 갖춰 자바와 유사한 사양이 되었으므로, Java와 동일한 원리로 싱글턴 패턴을 구현할 수 있다. PHP 소스 코드는 Java 샘플 코드와 거의 동일하다.[1]

다트(Dart), PHP, 코틀린(Kotlin), 파스칼(Pascal)(델파이), Go 언어의 싱글턴 패턴 구현 예시는 다음과 같다.[1]

언어구현 예시
다트(Dart)
PHP
코틀린(Kotlin)
파스칼(Pascal) (델파이)
Go


2. 1. Java

자바에서는 생성자를 `private`으로 선언하여 외부에서 인스턴스 생성을 막고, 정적 메서드를 통해 유일한 인스턴스에 접근하는 방식으로 싱글턴 패턴을 구현한다.[6] 초기에는 `synchronized` 키워드를 사용하여 여러 스레드에서 동시에 접근해도 하나의 인스턴스만 생성되도록(스레드 안전) 하였으나, 성능 문제로 인해 이중 검사 잠금(double-checked locking) 등의 기법이 고안되었다.[12]

```java

final class Singleton {

private static Singleton instance;

private Singleton() {}

public static synchronized Singleton getInstance() {

if (instance == null) {

instance = new Singleton();

}

return instance;

}

}

```

위 코드는 `synchronized`를 사용하여 스레드 안전성을 확보하는 예시이다.

그러나 이중 검사 잠금은 「아웃 오브 오더 쓰기」를 허용하는 자바 플랫폼의 메모리 모델 때문에 동기화에 실패하는 문제가 있어 현재는 static 필드를 사용하는 것이 권장된다.[12]

```java

final class Singleton {

private static final Singleton instance = new Singleton();

private Singleton() {}

public static Singleton getInstance() {

return Singleton.instance;

}

}

```

이 방식은 동기화 비용이 없고, static 필드 초기화는 클래스가 호출될 때 한 번만 수행되므로 스레드 안전하며, 성능도 우수하다.

하지만 이 경우, `Singleton` 클래스가 로드될 때 초기화가 이루어지므로, `getInstance()`가 처음 호출될 때가 아니다. 이를 개선하기 위해 :en:Initialization-on-demand holder idiom 기법을 사용할 수 있다.

```java

final class Singleton {

private Singleton() {}

private static class SingletonHolder {

private static final Singleton instance = new Singleton();

}

public static Singleton getInstance() {

return SingletonHolder.instance;

}

}

```

이 방식은 `getInstance()` 호출에 의해 `SingletonHolder` 클래스가 로드될 때 `Singleton` 클래스가 초기화된다.

안드로이드 환경에서는 클래스 언로드에 주의해야 한다.[13] 참조되지 않는 클래스는 가비지 컬렉션에 의해 회수되어 JVM에서 언로드될 수 있으며, 이로 인해 static 필드도 무효화된다.[14][15] 따라서 안드로이드에서는 애플리케이션의 수명과 동일한 static 필드를 사용하려면, `Application` 클래스를 상속하여 싱글턴을 구현할 수 있다.[16]

2. 2. C++

C++에서는 클래스의 생성자, 복사 생성자, 복사 대입 연산자를 `private`으로 설정하여 유일성을 보장할 수 있다.[20] 소멸자를 `private`으로 설정하면 파생 클래스의 스택 상에서 객체 생성을 금지할 수 있다.

정적 지역 변수를 사용한 구현 예시는 다음과 같다.

```cpp

class Singleton {

private:

Singleton() {} // 생성자를 private으로 둔다.

Singleton(const Singleton&); // 복사 생성자도 private으로 두고 정의하지 않는다.

Singleton& operator=(const Singleton&); // 복사 대입 연산자도 private으로 두고 정의하지 않는다.

~Singleton() {} // 소멸자를 private으로 둔다.

public:

static Singleton& getInstance() {

static Singleton inst; // private 생성자를 호출한다.

return inst;

}

const char* getString() const {

return "Hello world!";

}

};

// 사용 예시.

int main() {

std::cout << Singleton::getInstance().getString() << std::endl;

}

```

정적 지역 변수를 사용하여 구현한 경우, 싱글턴 인스턴스를 삭제하는 시점을 명시적으로 제어할 수 없다는 점에 주의해야 한다. 또한, 인스턴스가 생성되는 것은 처음 `getInstance()` 함수를 호출하는 시점이지만, C++11 이전 규격에서는 정적 지역 변수의 초기화가 스레드 안전성이 보장되지 않으므로, 여러 스레드에서 동시에 첫 접근이 발생하면 정의되지 않은 동작을 일으킬 수 있다. 정적 지역 변수가 아닌, 포인터 타입의 정적 멤버 변수를 사용하여 구현하는 경우에도, ''double-checked locking'' 등의 기법을 통한 스레드 안전성이 필요하다.

C++11 표준에서는 컴파일러가 생성하는 함수에 대한 default/delete 지정을 통해 생성자/소멸자 외에는 `private`으로 둘 필요가 없어졌다. 또한, `final` 키워드를 사용하여 클래스를 꾸밈으로써, 파생 클래스의 정의를 금지할 수 있다. 게다가, 정적 지역 변수의 초기화는 자동으로 상호 배타적으로 제어되어, 스레드 안전성이 확보된다.

```cpp

class Singleton final {

private:

Singleton() = default; // 생성자를 private으로 둔다.

~Singleton() = default; // 소멸자를 private으로 둔다.

public:

Singleton(const Singleton&) = delete; // 복사 생성자를 delete 지정.

Singleton& operator=(const Singleton&) = delete; // 복사 대입 연산자도 delete 지정.

Singleton(Singleton&&) = delete; // 이동 생성자를 delete 지정.

Singleton& operator=(Singleton&&) = delete; // 이동 대입 연산자도 delete 지정.

...

};

2. 3. C#

C#에서는 private 생성자와 정적 속성을 사용하여 싱글턴을 구현한다. `lock` 키워드를 사용하여 스레드 안전성을 확보하거나, 정적 초기화자를 사용하여 더 간결하게 구현할 수 있다.[1]

첫 번째 예시는 스레드 안전성을 보장하는 싱글턴 구현 방법이다.

```csharp

public sealed class Singleton

{

private static volatile Singleton? _instance;

private static readonly object _lock = new object();

private Singleton() { }

public static Singleton Instance

{

get

{

if (_instance != null)

{

return _instance;

}

lock (_lock)

{

if (_instance == null)

_instance = new Singleton();

}

return _instance;

}

}

}

```

두 번째 예시는 정적 초기화자를 사용한 더 간결한 구현 방법이다.

```csharp

public static class Singleton

{

public static MyOtherClass Instance { get; } = new MyOtherClass();

}

```

세 번째 예시는 유니티 엔진에서 `MonoBehaviour`를 상속받아 싱글턴을 구현하는 방법이다.

```csharp

class Singleton : MonoBehaviour

{

public static Singleton Instance { get; private set; }

private void Awake()

{

if (Instance != null && Instance != this)

{

Destroy(this.gameObject);

}

else

{

Instance = this;

}

}

}

2. 4. Python

파이썬의 모듈은 그 자체로 싱글턴이다.[1] 파이썬에서는 모듈 수준에서 변수와 함수를 정의하여 싱글턴 패턴처럼 사용할 수 있다.

다음은 `__new__` 메서드를 재정의하여 클래스 수준에서 싱글턴을 구현하는 예시이다.

```python

class Singleton:

__instance = None

def __new__(cls, *args):

if cls.__instance is None:

cls.__instance = object.__new__(cls, *args)

return cls.__instance

2. 5. 기타 언어

PHP 버전 5.0 이후에는 가시성, 클래스 함수, 클래스 변수 등의 기능을 갖춰 자바와 더 유사한 사양이 되었으므로, Java에서의 구현 예와 동일한 원리로 싱글턴 패턴을 구현할 수 있다. PHP 소스 코드는 Java의 샘플 코드와 거의 동일하다.[1]

다트(Dart), PHP, 코틀린(Kotlin), 파스칼(Pascal)(델파이), Go 언어의 싱글턴 패턴 구현 예시는 다음과 같다.[1]

언어구현 예시
다트(Dart)
PHP
코틀린(Kotlin)
파스칼(Pascal) (델파이)
Go


3. 활용

싱글턴은 전역 네임스페이스를 오염시키지 않기 때문에 전역 변수보다 선호되는 경우가 많다. 또한, 많은 언어에서 전역 변수는 항상 리소스를 소비하는 반면, 싱글턴은 지연 평가 할당 및 초기화를 허용한다.[1][3]

싱글턴 패턴은 추상 팩토리 패턴, 팩토리 메서드 패턴, 빌더 패턴프로토타입 패턴과 같은 다른 디자인 패턴의 기반으로도 사용될 수 있다. 파사드 패턴 객체도 하나의 파사드 객체만 필요하기 때문에 종종 싱글턴으로 구현된다.

로그 파일은 싱글턴의 일반적인 실제 사용 사례이다. 메시지를 기록하려는 모든 객체가 균일한 접근 지점을 필요로 하고 개념적으로 단일 소스에 기록하기 때문이다.[4]

4. 비판

싱글턴은 종종 불필요하게 애플리케이션에 전역 상태를 도입하는 안티 패턴으로 간주되기도 한다. 이는 다른 객체들이 싱글턴에 의존하게 만들 수 있으며, 실제로 의존성이 존재하는지 확인하기 위해 구현 세부 사항을 분석해야 한다.[7] 이러한 증가된 결합도는 단위 테스트에 어려움을 야기할 수 있다.[8] 결과적으로, 이는 싱글턴을 사용하는 모든 추상화에 제약을 가하며, 여러 인스턴스의 동시 사용을 방지하는 등의 문제가 발생한다.[8][9][10]

싱글턴은 또한 정상적인 기능을 수행하는 것과 함께 자체적인 고유성을 강제하기 때문에 단일 책임 원칙을 위반한다.[8] 싱글턴 패턴은 취급 방식에 따라 전역 변수처럼 작동시킬 수도 있다. 예를 들어, Java에는 전역 변수가 없다고 하지만, 이 싱글턴 패턴으로 싱글턴 클래스를 생성함으로써 코드 어디에서든 동일한 인스턴스에 접근할 수 있다. 이는 전역 변수 그 자체이며, 따라서 싱글턴 패턴은 전역 변수와 마찬가지로 문제를 일으킬 위험성을 내포하고 있다. 단, 패키지 (네임스페이스)를 지정하여 인스턴스에 접근 가능한 코드 범위를 제한함으로써 이러한 문제를 회피하거나 완화할 수 있다.

또한, 코드를 잘 활용하면 인스턴스 개수 제한을 설정하는 것도 가능하다. 예를 들어, 샘플 코드의 getInstance()를 여러 번 호출했을 때, 10번째까지는 매번 새로운 인스턴스를 생성하지만, 11번째 이후에는 이전 인스턴스를 재사용하는 것과 같은 기법이다. 이는 단순한 전역 변수로는 실현할 수 없는 장점이다.

5. 멀티턴 패턴

Multiton 패턴 클래스 다이어그램

멀티턴 패턴(Multiton pattern)은 싱글턴 패턴을 확장한 디자인 패턴으로, 연관 배열을 이용하여 여러 개의 인스턴스를 보관한다. 즉, 키-값 쌍을 이용하여 여러 개의 인스턴스를 관리하는 방식이다.[21]

멀티턴 패턴은 싱글턴 패턴과 마찬가지로 단위 테스트를 어렵게 만들고, 가비지 컬렉션이 있는 언어에서는 객체에 대한 강한 참조로 인해 메모리 누수가 발생할 수 있다는 단점이 있다.[21]

다음은 멀티턴 패턴의 Java 코드 예시이다.

```java

public class FooMultiton {

private static final Map instances = new HashMap();

private FooMultiton() {

// 명시적인 구현 없음

}

public static synchronized FooMultiton getInstance(Object key) {

// "키별" 싱글턴

FooMultiton instance = instances.get(key);

if (instance == null) {

// 지연 로딩으로 인스턴스 생성

instance = new FooMultiton();

// 맵에 추가

instances.put(key, instance);

}

return instance;

}

// 다른 필드와 메서드 ...

}

참조

[1] 서적 Design Patterns: Elements of Reusable Object-Oriented Software https://archive.org/[...] Addison Wesley
[2] 웹사이트 The Singleton design pattern - Problem, Solution, and Applicability http://w3sdesign.com[...] 2017-08-16
[3] 뉴스 What Is a Singleton? https://betterprogra[...] 2021-08-28
[4] 뉴스 Use your singletons wisely https://www.ibm.com/[...] IBM 2021-08-28
[5] 서적 More Effective C++ Addison Wesley
[6] 서적 Head First Design Patterns O'Reilly Media, Inc 2004-10
[7] 웹사이트 Why Singletons Are Controversial https://code.google.[...] 2021-08-28
[8] 뉴스 Why Singletons are Evil https://docs.microso[...] Microsoft 2021-08-28
[9] 문서 Singletons considered stupid http://steve.yegge.g[...] 2004-09
[10] 간행물 Global State and Singletons http://googletesting[...] 2008-11-21
[11] 서적 オブジェクト指向における再利用のためのデザインパターン [[ソフトバンクパブリッシング]]
[12] 웹사이트 double-checked lockingとSingletonパターン - この破綻したプログラミング・イディオムを多角的に検討する | IBM https://web.archive.[...]
[13] 웹사이트 クラスローダーとJ2EEパッケージング戦略を理解する: 第2回「クラスローダーを理解する - シングルトンがシングルトンでなくなる日」| IBM https://web.archive.[...]
[14] 웹사이트 JNI tips | Android NDK | Android Developers https://developer.an[...]
[15] 웹사이트 JNI に関するヒント | Android NDK | Android Developers https://developer.an[...]
[16] 웹사이트 Application | Android Developers https://developer.an[...]
[17] 웹사이트 Class Unloading in Layered Java Applications - Gunnar Morling https://www.morling.[...]
[18] 웹사이트 Chapter 12. Execution §12.7. Unloading of Classes and Interfaces | Oracle Java SE 15 > Java SE Specifications > Java Language Specification https://docs.oracle.[...]
[19] 웹사이트 Java のクラスアンロード (Class Unloading) http://www.nminoru.j[...]
[20] 웹사이트 ブロックスコープを持つstatic変数初期化のスレッドセーフ化 - cpprefjp C++日本語リファレンス https://cpprefjp.git[...]
[21] 웹사이트 Google Testing Blog: Clean Code Talks - Global State and Singletons https://testing.goog[...]



본 사이트는 AI가 위키백과와 뉴스 기사,정부 간행물,학술 논문등을 바탕으로 정보를 가공하여 제공하는 백과사전형 서비스입니다.
모든 문서는 AI에 의해 자동 생성되며, CC BY-SA 4.0 라이선스에 따라 이용할 수 있습니다.
하지만, 위키백과나 뉴스 기사 자체에 오류, 부정확한 정보, 또는 가짜 뉴스가 포함될 수 있으며, AI는 이러한 내용을 완벽하게 걸러내지 못할 수 있습니다.
따라서 제공되는 정보에 일부 오류나 편향이 있을 수 있으므로, 중요한 정보는 반드시 다른 출처를 통해 교차 검증하시기 바랍니다.

문의하기 : help@durumis.com