Unisquads
로그인
홈
이용약관·개인정보처리방침·콘텐츠정책·© 2026 Unisquads
이용약관·개인정보처리방침·콘텐츠정책
© 2026 Unisquads. All rights reserved.

싱글톤 객체 (r1)

이 문서의 과거 버전 (r1)을 보고 있습니다. 수정일: 2026.02.26 12:04

싱글톤 객체

정의

어떤 클래스의 인스턴스가 오직 하나만 생성되도록 보장하고, 이 인스턴스에 대한 전역적인 접근점을 제공하는 디자인 패턴

유형

생성 디자인 패턴

주요 용도

설정 관리

로깅

데이터베이스 연결 풀

캐싱

공유 자원 관리

핵심 특징

단일 인스턴스 보장

전역 접근 제공

지연 초기화 가능

구현 방법

생성자를 private으로 선언

정적 메서드를 통해 인스턴스 반환

상세 정보

장점

메모리 절약

전역 상태 관리 용이

인스턴스 생성 비용 감소

단점

단위 테스트 어려움

전역 상태로 인한 결합도 증가

멀티스레드 환경에서 주의 필요

멀티스레드 환경 대응

이른 초기화

동기화 블록 사용

이중 확인 잠금

정적 내부 클래스 활용

관련 개념

디자인 패턴

전역 변수

정적 클래스

1. 개요

싱글톤 객체는 디자인 패턴 중 생성 패턴에 속하며, 특정 클래스의 인스턴스가 오직 하나만 생성되도록 보장하고, 이 인스턴스에 대한 전역적인 접근점을 제공하는 패턴이다. 이 패턴의 핵심은 시스템 내에서 특정 객체가 단 하나만 존재해야 할 필요가 있을 때 사용되며, 전역 변수를 사용하는 것과 유사한 효과를 내지만 더욱 제어된 방식으로 접근할 수 있다.

주요 용도로는 설정 관리, 로깅, 데이터베이스 연결 풀, 캐싱, 공유 자원 관리 등이 있다. 이러한 요소들은 애플리케이션 전반에 걸쳐 하나의 상태를 유지하거나 자원을 효율적으로 공유해야 하는 경우가 많기 때문에 싱글톤 패턴이 적합하다. 예를 들어, 데이터베이스 연결 풀은 여러 번 생성될 경우 자원 낭비가 심해지므로 싱글톤으로 구현하여 단일 인스턴스만 관리하도록 한다.

구현의 기본 구조는 클래스의 생성자를 private으로 선언하여 외부에서 new 연산자로의 직접적인 인스턴스 생성을 막고, 대신 정적(static) 메서드를 통해 미리 생성해 둔 유일한 인스턴스를 반환하는 방식이다. 이를 통해 어디서든 동일한 인스턴스에 접근할 수 있는 전역 접근점이 마련된다.

싱글톤 패턴은 개념적으로 간단해 보이지만, 멀티스레딩 환경에서의 안전한 초기화, 리플렉션이나 직렬화를 통한 우회, 클래스 로더에 따른 인스턴스 중복 생성 등 다양한 주의사항을 고려해야 하는 복잡성을 내포하고 있다.

2. 특징

2.1. 단일 인스턴스 보장

싱글톤 객체의 가장 핵심적인 특징은 애플리케이션 내에서 특정 클래스의 인스턴스가 오직 하나만 존재하도록 보장하는 것이다. 이는 생성자를 private 접근 제어자로 선언하여 외부에서 new 키워드를 통한 임의의 객체 생성을 원천적으로 차단함으로써 구현된다. 대신, 클래스 내부에 자신의 유일한 인스턴스를 저장하는 정적 변수를 두고, 이 인스턴스를 반환하는 정적 메서드를 통해 객체에 접근할 수 있게 한다.

이러한 단일 인스턴스 보장 메커니즘은 시스템 전반에 걸쳐 하나의 공유 자원이 필요할 때 매우 유용하다. 예를 들어, 데이터베이스 연결 풀, 애플리케이션의 설정 관리 객체, 캐시 시스템, 로깅 유틸리티 등은 여러 곳에서 동일한 상태와 자원을 공유해야 하며, 불필요한 중복 생성은 메모리 낭비와 데이터 불일치를 초래할 수 있다. 싱글톤 패턴은 이런 문제를 방지한다.

단일 인스턴스 보장은 멀티스레딩 환경에서 특히 주의를 요한다. 여러 스레드가 동시에 인스턴스를 처음 요청할 경우, 경합 조건이 발생하여 의도치 않게 두 개 이상의 인스턴스가 생성될 수 있기 때문이다. 따라서 게으른 초기화 방식을 사용할 때는 동기화 처리나 이중 확인 잠금과 같은 기법을 통해 스레드 안전성을 반드시 확보해야 한다.

2.2. 전역 접근점 제공

싱글톤 패턴의 핵심 특징 중 하나는 애플리케이션 내 어디서나 동일한 인스턴스에 쉽게 접근할 수 있는 전역 접근점을 제공한다는 점이다. 이는 정적 메서드를 통해 구현되며, 일반적으로 getInstance()와 같은 이름의 메서드를 호출하여 유일한 객체를 얻을 수 있다. 이 방식은 전역 변수를 사용하는 것과 유사한 편리함을 주지만, 캡슐화 원칙을 위반하지 않으면서도 객체의 생성과 생명주기를 통제할 수 있는 장점이 있다.

이러한 전역 접근성 덕분에 설정 관리나 로깅과 같이 애플리케이션 전반에서 공통으로 사용되는 기능이나 자원을 관리하는 데 매우 적합하다. 예를 들어, 데이터베이스 연결 풀이나 캐싱 시스템은 하나의 인스턴스가 모든 요청을 처리하도록 설계되는 경우가 많으며, 싱글톤 패턴을 통해 이 인스턴스에 대한 명확하고 일관된 접근 경로를 제공할 수 있다. 이는 코드의 복잡성을 줄이고, 공유 자원에 대한 접근을 중앙에서 관리할 수 있게 한다.

2.3. 지연 초기화

지연 초기화는 싱글톤 객체의 인스턴스를 실제로 필요로 하는 시점까지 생성하지 않고 미루는 기법이다. 이른 초기화 방식과 달리, 애플리케이션 시작 시 곧바로 인스턴스를 생성하지 않기 때문에 불필요한 자원 낭비를 줄일 수 있다. 특히 인스턴스 생성 비용이 크거나, 사용 빈도가 낮은 싱글톤 객체를 다룰 때 유용한 방식이다.

구현은 일반적으로 정적 메서드 내부에서 인스턴스 변수가 null인지 확인하는 조건문으로 시작한다. 처음 호출될 때 인스턴스가 존재하지 않으면 새로 생성하여 정적 필드에 할당하고, 이후 호출에서는 이미 생성된 동일한 인스턴스를 반환한다. 이 기본 형태를 게으른 초기화라고 부른다.

그러나 지연 초기화는 특히 멀티스레딩 환경에서 주의가 필요하다. 여러 스레드가 동시에 인스턴스 생성 여부를 확인하는 조건문에 진입할 경우, 의도치 않게 인스턴스가 여러 개 생성될 수 있는 위험이 있다. 이를 방지하기 위해 동기화된 메서드를 사용하거나, 이중 확인 잠금 패턴, 정적 내부 클래스를 활용하는 초기화 온디맨드 홀더 관용구 등 다양한 기법이 고안되었다.

지연 초기화는 자원 효율성을 높이는 장점이 있지만, 구현 복잡성을 증가시키고, 특히 동기화 처리를 잘못하면 성능 저하를 초래할 수 있다. 따라서 애플리케이션의 요구사항과 실행 환경을 고려하여 이른 초기화와의 장단점을 비교해 선택하는 것이 중요하다.

3. 구현 방법

3.1. 이른 초기화

이른 초기화는 싱글톤 객체를 구현하는 가장 기본적이고 간단한 방법 중 하나이다. 이 방법은 클래스가 로딩되는 시점에 정적 필드로 싱글톤 인스턴스를 미리 생성해 놓는 것을 특징으로 한다. 클래스 로더에 의해 클래스가 초기화될 때 정적 변수가 할당되므로, 인스턴스 생성 시점이 프로그램 실행 초기로 고정된다.

구현은 매우 직관적이다. 생성자를 private으로 선언하여 외부에서의 new 연산자 사용을 차단하고, 대신 클래스 내부에 정적 필드를 선언하여 그곳에 인스턴스를 생성해 저장한다. 이 인스턴스에 접근하기 위한 정적 메서드인 getInstance()는 단순히 이 미리 생성된 필드를 반환하기만 하면 된다. 이 방식은 지연 초기화를 사용하지 않으므로, 인스턴스가 실제로 사용되지 않더라도 항상 메모리를 점유하게 된다는 단점이 있다.

이른 초기화의 가장 큰 장점은 멀티스레딩 환경에서도 완벽하게 안전하다는 점이다. 인스턴스 생성이 클래스 로딩 단계에서 한 번만 이루어지기 때문에, 여러 스레드가 동시에 getInstance() 메서드를 호출하더라도 항상 동일한 인스턴스를 얻을 수 있다. 별도의 동기화 처리나 잠금 메커니즘이 필요 없어 성능상의 이점이 있다. 따라서 인스턴스 생성 비용이 크지 않고, 애플리케이션 실행 중 반드시 사용될 것이 확실한 경우에 적합한 구현 방식이다.

3.2. 게으른 초기화

게으른 초기화는 싱글톤 객체의 인스턴스를 실제로 필요로 하는 시점까지 생성하지 않고 미루는 방식이다. 이른 초기화와 달리, 애플리케이션 시작 시에 인스턴스가 바로 생성되지 않는다. 대신, 인스턴스를 반환하는 정적 메서드가 처음 호출될 때 인스턴스를 생성하고, 이후에는 이미 생성된 동일한 인스턴스를 반환한다.

이 방식의 주요 장점은 시스템 자원을 효율적으로 사용할 수 있다는 점이다. 인스턴스 생성 비용이 크거나, 사용 빈도가 낮은 싱글톤 객체의 경우, 애플리케이션 구동 시간을 단축시킬 수 있다. 또한, 인스턴스가 반드시 필요하지 않은 상황에서는 아예 생성되지 않을 수도 있어 메모리 사용 측면에서 유리하다.

그러나 게으른 초기화를 구현할 때는 멀티스레딩 환경에서 주의가 필요하다. 여러 스레드가 동시에 인스턴스 생성 메서드에 접근하면, 경합 조건이 발생하여 인스턴스가 두 번 이상 생성될 위험이 있다. 이를 방지하기 위해 동기화 처리나 이중 확인 잠금과 같은 기법이 사용된다.

따라서 게으른 초기화는 자원 사용 최적화가 중요한 경우에 적합하지만, 멀티스레딩 문제를 해결하기 위한 추가적인 고려사항이 따른다.

3.3. 동기화 처리

싱글톤 객체를 멀티스레딩 환경에서 사용할 때 가장 주의해야 할 점은 동시에 여러 스레드가 인스턴스 생성 메서드를 호출하여 여러 개의 인스턴스가 만들어지는 것을 방지하는 것이다. 이를 위해 동기화 처리가 필수적이다.

가장 기본적인 방법은 인스턴스를 반환하는 정적 메서드에 synchronized 키워드를 적용하는 것이다. 이렇게 하면 해당 메서드에 한 번에 하나의 스레드만 접근할 수 있게 되어 인스턴스가 중복 생성되는 문제를 해결할 수 있다. 그러나 이 방법은 메서드 호출 시마다 락을 획득해야 하므로 성능 저하가 발생할 수 있다는 단점이 있다.

성능 문제를 개선하기 위해 등장한 방법이 이중 확인 잠금이다. 이 패턴은 먼저 인스턴스가 생성되었는지 확인하고, 생성되지 않았을 때만 동기화 블록으로 진입하여 다시 한번 확인한 후 인스턴스를 생성한다. 이를 통해 최초 생성 시점을 제외하고는 불필요한 동기화 오버헤드를 피할 수 있다. 하지만 자바 메모리 모델 상에서 완전히 안전하게 동작하도록 구현하려면 volatile 키워드를 함께 사용해야 한다는 점에 유의해야 한다.

3.4. 이중 확인 잠금

이중 확인 잠금은 게으른 초기화 방식의 싱글톤 객체를 멀티스레딩 환경에서 안전하게 구현하기 위한 기법이다. 이 방법은 인스턴스가 아직 생성되지 않았을 때만 동기화 블록을 사용하여 성능 저하를 최소화한다. 기본적인 동기화 처리 방식은 인스턴스를 반환하는 정적 메서드 전체에 잠금을 걸기 때문에, 인스턴스가 이미 생성된 후에도 불필요한 동기화 오버헤드가 발생하는 단점이 있었다.

이중 확인 잠금의 구현은 두 번의 인스턴스 널 체크를 기반으로 한다. 첫 번째 체크는 동기화 블록 외부에서 수행되어 인스턴스가 이미 존재하면 바로 반환한다. 만약 인스턴스가 없다면, 동기화 블록 내부로 진입하여 두 번째 널 체크를 한 후 인스턴스를 생성한다. 이렇게 함으로써 최초 생성 시점을 제외하고는 대부분의 스레드가 동기화 블록을 거치지 않고 인스턴스에 접근할 수 있어 효율적이다.

그러나 이 구현 방식은 자바와 같은 언어에서 메모리 모델의 특성으로 인해 정상적으로 동작하지 않을 수 있는 문제가 있었다. 컴파일러의 명령어 재배치 최적화 때문에, 생성 중인 객체의 참조가 인스턴스 변수에 할당되는 시점이 객체의 생성자 내부 초기화가 완료되기 전에 발생할 수 있다. 이로 인해 다른 스레드가 부분적으로만 초기화된 객체를 참조할 위험이 존재했다.

이 문제를 해결하기 위해 인스턴스 변수에 volatile 키워드를 사용한다. volatile 키워드는 해당 변수에 대한 읽기와 쓰기 작업이 메인 메모리에서 직접 이루어지도록 보장하고, 컴파일러 및 CPU의 재배치 최적화를 방지한다. 이를 통해 한 스레드에서 인스턴스 생성을 완료한 후, 다른 스레드가 완전히 초기화된 인스턴스를 안전하게 읽을 수 있게 된다.

3.5. 정적 내부 클래스

정적 내부 클래스 방식은 싱글톤 패턴을 구현하는 방법 중 하나로, 게으른 초기화의 장점과 멀티스레딩 환경에서의 안전성을 모두 확보한 방법으로 평가받는다. 이 방식은 싱글톤 인스턴스를 정적 내부 클래스라는 별도의 클래스에 보관한다. 외부 클래스가 로드될 때는 내부 클래스가 로드되지 않아 지연 초기화가 가능하며, 내부 클래스가 로드될 때 JVM의 클래스 로더 메커니즘에 의해 정적 필드의 초기화가 단 한 번만 수행되도록 보장받는다. 이는 동기화 키워드를 사용하지 않고도 스레드 안전성을 자연스럽게 얻을 수 있는 장점이 있다.

구현 방식은 외부 클래스에 private 생성자를 두고, 내부에 private static으로 선언된 내부 클래스를 정의한다. 이 내부 클래스는 외부 클래스의 인스턴스를 private static final 상수로 가지고 있다. 외부 클래스는 공개된 public static 메서드를 제공하여, 이 내부 클래스의 상수를 반환함으로써 싱글톤 인스턴스를 획득하게 한다. 이 방법은 이른 초기화 방식의 단순함과 게으른 초기화 방식의 효율성을 결합한 것으로, 자바에서 권장되는 구현 방법 중 하나이다.

3.6. 열거형

열거형을 이용한 싱글톤 객체 구현은 자바에서 권장되는 방법 중 하나이다. 이 방식은 이른 초기화나 게으른 초기화와 같은 다른 구현 방법들이 가질 수 있는 문제점들을 근본적으로 해결한다.

열거형 싱글톤의 가장 큰 장점은 리플렉션을 통한 공격으로부터 안전하다는 점이다. 일반 클래스의 생성자를 private으로 선언하더라도 리플렉션 API를 사용하면 강제로 새로운 인스턴스를 생성할 수 있지만, 열거형은 JVM 차원에서 싱글톤을 보장하므로 이러한 위협이 없다. 또한 직렬화와 역직렬화 과정에서도 별도의 조치 없이 단일 인스턴스가 유지된다. 직렬화 가능 클래스로 선언하고 readResolve() 메서드를 구현할 필요가 없으며, JVM이 열거형 상수의 유일성을 보장한다.

구현은 매우 간결하다. 하나의 열거형 상수를 정의하고, 필요한 인스턴스 변수와 메서드를 추가하면 된다. 이 방식은 게으른 초기화와 유사하게 클래스 로딩 시점이 아닌 열거형 상수가 처음 참조될 때 초기화되지만, 동기화에 대한 별도의 고려가 필요 없다. 멀티스레딩 환경에서도 완전히 안전하며, 코드의 가독성과 간결성이 뛰어나다. 따라서 Joshua Bloch는 그의 저서에서 열거형을 싱글톤 구현을 위한 최선의 방법으로 소개하기도 했다.

4. 장단점

4.1. 장점

싱글톤 객체를 사용하는 주요 장점은 메모리 사용의 효율성과 전역 변수의 단점을 보완하는 데 있다. 인스턴스를 하나만 생성하고 재사용함으로써, 특히 자원 소모가 큰 객체(예: 데이터베이스 연결 풀, 캐시 시스템)를 반복적으로 생성하는 데 드는 비용을 절감할 수 있다. 이는 시스템의 전체적인 성능 향상으로 이어진다.

또한, 싱글톤은 전역 상태를 관리하는 안전한 방법을 제공한다. 전역 변수를 사용하면 네임스페이스가 오염되고 의도치 않은 수정이 발생할 위험이 있지만, 싱글톤은 캡슐화를 통해 인스턴스 생성과 접근을 통제한다. 이를 통해 설정 정보나 공유 자원과 같은 전역적으로 접근해야 하는 데이터를 일관된 방식으로 관리할 수 있다.

마지막으로, 싱글톤 패턴은 코드의 유연성을 높인다. 필요에 따라 게으른 초기화를 적용하여 애플리케이션 시작 시 부담을 줄일 수 있으며, 향후 인스턴스 생성 방식을 변경하거나 하위 클래스를 사용하도록 확장하는 것도 비교적 용이하다. 이는 디자인 패턴이 추구하는 변경에 대한 유연한 대응과도 일치한다.

4.2. 단점

싱글톤 패턴은 전역 상태를 유지한다는 점에서 본질적으로 모듈성을 저해할 수 있다. 의존성 주입 없이 싱글톤 객체에 직접 의존하는 코드는 단위 테스트를 어렵게 만든다. 테스트 시 싱글톤 인스턴스의 상태가 각 테스트 케이스 간에 공유되거나, 특정 테스트를 위해 인스턴스의 행동을 변경하기가 복잡해질 수 있기 때문이다. 이는 테스트의 격리 원칙을 위반한다.

이 패턴은 객체 지향 설계 원칙 중 개방-폐쇄 원칙과 단일 책임 원칙을 위반할 가능성이 높다. 싱글톤 클래스는 인스턴스 생성을 관리하는 책임과 본래의 비즈니스 로직이라는 두 가지 책임을 동시에 지게 되기 쉽다. 또한, 전역 접근점을 제공함으로써 애플리케이션의 여러 부분이 이 객체에 강하게 결합되어, 시스템의 유연성과 확장성을 떨어뜨린다.

멀티스레딩 환경에서 게으른 초기화를 구현할 경우, 적절한 동기화 처리가 이루어지지 않으면 여러 인스턴스가 생성될 수 있는 위험이 있다. 반면, 과도한 동기화는 성능 저하를 초래한다. 또한, 분산 시스템이나 클래스 로더가 여러 개인 환경(예: 일부 애플리케이션 서버)에서는 싱글톤의 단일성 보장이 깨질 수 있다.

마지막으로, 싱글톤 객체의 생명주기는 애플리케이션의 생명주기와 동일하므로, 사용이 끝난 후 명시적으로 해제하기 어렵다. 이는 특히 자원(예: 데이터베이스 연결, 파일 핸들)을 보유한 싱글톤의 경우 문제가 될 수 있으며, 메모리 누수의 원인이 될 수도 있다.

5. 사용 사례

싱글톤 객체는 애플리케이션 전역에서 단일 인스턴스가 공유되어야 하는 다양한 관리자 클래스나 공유 자원에 널리 적용된다. 대표적인 사용 사례로는 애플리케이션의 설정이나 환경 변수를 중앙에서 관리하는 설정 관리자가 있다. 이는 데이터베이스 연결 정보, API 키, 시스템 경로 등 일관된 구성을 보장해야 할 때 유용하다. 또한, 로깅 시스템에서도 싱글톤이 자주 사용되며, 모든 모듈에서 동일한 로그 파일이나 출력 스트림에 기록할 수 있게 해준다.

자원을 효율적으로 관리해야 하는 경우에도 싱글톤 패턴이 적합하다. 데이터베이스 연결 풀은 데이터베이스에 대한 연결을 생성하고 재사용하는 데 사용되며, 불필요한 연결 생성을 방지하고 성능을 최적화한다. 마찬가지로, 캐싱 메커니즘을 구현할 때 싱글톤 객체를 사용하면 메모리 내 캐시 데이터의 일관성을 유지할 수 있다.

하드웨어 접근이나 파일 시스템과 같은 공유 자원을 제어할 때도 싱글톤이 활용된다. 예를 들어, 프린터 스풀러나 특정 하드웨어 장치 드라이버는 시스템 전체에서 하나의 인스턴스만이 자원에 접근하도록 관리해야 한다. 이는 자원에 대한 충돌이나 상태 불일치를 방지하는 데 도움이 된다.

6. 주의사항

6.1. 직렬화와 싱글톤

직렬화는 객체를 바이트 스트림으로 변환하여 파일에 저장하거나 네트워크를 통해 전송할 수 있게 하는 메커니즘이다. 싱글톤 객체를 직렬화 가능한 클래스로 선언하고(예: Java의 Serializable 인터페이스 구현) 해당 객체를 직렬화한 후 다시 역직렬화하면, 싱글톤의 핵심 원칙인 단일 인스턴스 보장이 깨질 수 있다. 역직렬화 과정은 기본적으로 새로운 객체를 생성하는 방식으로 동작하기 때문이다.

이 문제를 해결하기 위해서는 싱글톤 클래스에 readResolve 메서드를 명시적으로 구현해야 한다. 이 메서드는 역직렬화 과정에서 객체가 생성된 후 호출되며, 여기서 기존의 싱글톤 인스턴스를 반환하도록 함으로써 새로운 인스턴스 생성을 방지하고 싱글톤의 무결성을 유지한다. 이는 직렬화와 역직렬화를 사용하는 분산 시스템이나 객체 상태를 지속적으로 저장해야 하는 애플리케이션에서 특히 중요하다.

또한, 모든 인스턴스 필드를 transient로 선언하는 것도 고려할 수 있다. 이는 해당 필드들이 직렬화 대상에서 제외됨을 의미하며, 불필요한 상태 복제를 방지하고 싱글톤의 본질적인 역할에 집중하는 데 도움이 될 수 있다.

6.2. 리플렉션 공격

리플렉션은 자바와 같은 프로그래밍 언어에서 런타임에 클래스의 정보를 분석하고 조작할 수 있는 기능을 제공한다. 이 기능을 악용하면 싱글톤 패턴의 근본적인 원칙인 '단일 인스턴스 보장'을 깨뜨릴 수 있다. 비록 생성자가 private으로 선언되어 있어 외부에서 new 키워드로 직접 인스턴스를 생성하는 것을 막고 있지만, 리플렉션의 setAccessible(true) 메서드를 사용하면 접근 제어자를 무시하고 강제로 생성자에 접근하여 새로운 인스턴스를 생성할 수 있기 때문이다.

이러한 리플렉션 공격을 방어하기 위한 일반적인 방법은 두 가지가 있다. 첫 번째는 이른 초기화 방식을 사용하는 것이다. 클래스가 로딩되는 시점에 정적 필드로 인스턴스를 미리 생성해두면, 리플렉션으로 새로운 인스턴스를 생성하더라도 이미 생성된 정적 인스턴스를 사용하도록 코드를 작성할 수 있다. 그러나 이 방법은 완벽한 방어가 되지 않을 수 있다.

보다 강력한 두 번째 방어책은 열거형을 이용해 싱글톤을 구현하는 것이다. 열거형은 JVM 수준에서 하나의 인스턴스만 존재함을 보장하며, 리플렉션을 통한 생성자 호출 자체를 허용하지 않는다. 따라서 조슈아 블로크가 제안한 바와 같이, 리플렉션 공격으로부터 안전한 싱글톤을 구현하는 가장 이상적인 방법은 열거형을 사용하는 것이다.

6.3. 클래스 로더 문제

싱글톤 패턴은 하나의 클래스 로더가 관리하는 JVM 내에서는 단일 인스턴스를 보장한다. 그러나 웹 애플리케이션이나 엔터프라이즈 애플리케이션과 같이 여러 개의 클래스 로더를 사용하는 환경에서는 각기 다른 클래스 로더가 동일한 싱글톤 클래스를 로드할 수 있다. 이 경우, 클래스 로더마다 별도의 클래스 객체를 생성하게 되어 싱글톤 클래스 자체가 여러 번 로드되고, 결과적으로 각 클래스 로더 영역에 서로 다른 인스턴스가 생성되는 문제가 발생한다. 이는 싱글톤의 기본 원칙인 '전역적으로 유일한 인스턴스'를 훼손한다.

이 문제를 해결하기 위해서는 클래스 로더의 계층 구조를 이해하고 싱글톤 클래스를 로드하는 주체를 통일해야 한다. 일반적으로 부트스트랩 클래스 로더나 공통된 상위 클래스 로더에 의해 로드되도록 하는 것이 안전하다. 또는 열거형을 이용한 싱글톤 구현은 JVM이 열거형 상수의 생성을 보장하고 직렬화를 처리하기 때문에, 여러 클래스 로더 환경에서도 비교적 안전한 싱글톤을 제공하는 방법으로 권장된다[1].

6.4. 멀티스레딩 환경

멀티스레딩 환경에서 싱글톤 객체를 구현할 때는 여러 스레드가 동시에 인스턴스를 생성하려고 시도하는 경쟁 조건이 발생할 수 있다. 이로 인해 의도와 달리 인스턴스가 두 개 이상 생성되는 문제가 생길 수 있으며, 이는 싱글톤 패턴의 근본적인 원칙을 위반한다.

이러한 문제를 해결하기 위해 다양한 동기화 기법이 사용된다. 가장 기본적인 방법은 인스턴스를 반환하는 정적 메서드 전체에 synchronized 키워드를 적용하여 한 번에 하나의 스레드만 메서드에 접근하도록 보장하는 것이다. 그러나 이 방법은 메서드 호출마다 락 오버헤드가 발생하여 성능 저하를 초래할 수 있다.

보다 효율적인 방법으로는 이중 확인 잠금이 널리 사용된다. 이 방식은 먼저 인스턴스가 생성되었는지 확인하고, 생성되지 않았을 경우에만 동기화 블록으로 진입하여 다시 한번 확인한 후 인스턴스를 생성한다. 이를 통해 최초 생성 시점을 제외하고는 락을 사용하지 않아 성능을 개선할 수 있다. 또한 정적 내부 클래스를 이용한 홀더 패턴이나, 열거형을 사용하는 방법은 JVM의 클래스 초기화 메커니즘을 활용하여 스레드 안전성을 보장하면서도 간결한 구현이 가능하다.

7. 관련 디자인 패턴

싱글톤 객체는 특정 클래스의 인스턴스를 하나만 생성하고 전역적으로 접근할 수 있도록 하는 생성 디자인 패턴이다. 이 패턴은 팩토리 메서드 패턴이나 빌더 패턴과 같이 객체 생성 방식을 제어하는 다른 생성 패턴들과 유사한 목적을 공유한다. 특히, 팩토리 메서드 패턴은 객체 생성을 서브클래스에 위임하는 반면, 싱글톤은 생성 자체를 철저히 통제하여 단일 인스턴스를 보장한다는 점에서 차이가 있다.

싱글톤 패턴은 종종 전략 패턴이나 퍼사드 패턴과 같은 구조 디자인 패턴과 함께 사용된다. 예를 들어, 전략 패턴에서 여러 알고리즘을 캡슐화할 때, 특정 알고리즘의 인스턴스를 싱글톤으로 관리하여 불필요한 객체 생성을 방지하고 자원을 효율적으로 공유할 수 있다. 또한, 퍼사드 패턴이 복잡한 서브시스템에 대한 단순화된 인터페이스를 제공할 때, 그 퍼사드 객체 자체를 싱글톤으로 구현하여 시스템 전역에서 일관된 접근점을 제공하는 경우도 있다.

한편, 싱글톤 패턴은 의존성 주입 프레임워크의 등장과 함께 그 사용에 대해 재고될 필요가 있다. 의존성 주입은 객체 간의 결합도를 낮추고 테스트 용이성을 높이는 데 초점을 맞춘다. 싱글톤이 전역 상태를 만들어내어 은닉화를 위반하고 단위 테스트를 어렵게 만들 수 있다는 점에서, 의존성 주입 컨테이너를 통해 애플리케이션 수명 주기 동안 단일 인스턴스로 관리되는 "싱글톤 스코프"의 빈을 사용하는 방식이 대안으로 제시된다. 이는 싱글톤의 장점은 유지하되, 객체 생성과 관리를 프레임워크에 위임하여 유연성을 확보하는 접근법이다.

8. 여담

싱글톤 객체는 디자인 패턴 중에서도 가장 널리 알려지고 기본적인 패턴 중 하나이다. 그만큼 많은 서적과 강의에서 초반에 소개되며, 객체지향 프로그래밍을 배우는 과정에서 거의 필수적으로 접하게 된다. 이 패턴의 개념 자체는 간단해 보이지만, 멀티스레드 환경에서 안전하게 구현하는 방법은 생각보다 다양하고 미묘한 차이가 있어 깊이 파고들면 흥미로운 주제가 된다.

싱글톤 패턴은 전역 상태를 만든다는 점에서 때로는 안티패턴으로 간주되기도 한다. 이는 의존성 주입이나 테스트 주도 개발 같은 현대적인 소프트웨어 설계 철학과 충돌할 수 있기 때문이다. 특히 단위 테스트를 작성할 때 싱글톤 인스턴스가 테스트 간 상태를 공유하면 테스트의 독립성을 해칠 수 있어 주의가 필요하다.

이러한 논란에도 불구하고, 싱글톤은 로거, 설정 관리자, 데이터베이스 연결 풀 같이 애플리케이션 전반에 걸쳐 정말로 하나의 인스턴스만 존재해야 하는 명확한 경우에는 여전히 유효한 해결책이다. 자바에서는 열거형을 이용한 구현이 리플렉션과 직렬화 문제까지 안전하게 해결한다는 점에서 조슈아 블로크에 의해 권장되기도 했다. 결국 중요한 것은 패턴을 맹목적으로 사용하기보다, 상황에 맞게 적절한 도구로 선택하는 것이다.

리비전 정보

버전r1
수정일2026.02.26 12:04
편집자unisquads
편집 요약AI 자동 생성