싱글톤 패턴(Singleton Pattern)
클래스의 인스턴스가 오직 하나만 생성되도록 보장하는 디자인 패턴이다.
싱글톤 패턴의 등장

요청을 할 때 마다 객체를 새로 생성되고 처리가 완료되면 소멸된다.
메모리 낭비가 아주 심하다.
싱글톤 패턴 적용

객체가 한번만 생성되어 리소스를 절약할 수 있다.
Java 코드 예시
public interface Singleton {
void showMessage();
}
public class SingletonImpl implements Singleton {
private static SingletonImpl instance;
// private 생성자를 통해 외부에서 객체 생성을 방지
private SingletonImpl() {}
// public으로 설정하여 인스턴스가 필요하면
// getInstance 메서드를 통해 인스턴스에 접근하도록 만듦
public static SingletonImpl getInstance() {
// 인스턴스가 없을 때만 생성
if (instance == null) {
instance = new SingletonImpl();
}
return instance;
}
@Override
public void showMessage() {
System.out.println(instance.toString());
}
}
public class MainApp {
public static void main(String[] args) {
// 첫 번째 싱글톤 인스턴스 요청, 구현클래스.getInstance();
Singleton instance1 = SingletonImpl.getInstance();
instance1.showMessage(); // 인스턴스 주소값 출력
// 두 번째 싱글톤 인스턴스 요청, 구현클래스.getInstance();
Singleton instance2 = SingletonImpl.getInstance();
instance2.showMessage(); // 인스턴스 주소값 출력
// 다른 구현체로 바꾸려면 DIP, OCP 위반
Singleton instance3 = SingletonImplV2.getInstance();
instance3.showMessage();
}
}

Spring이 Bean을 등록하는 방법은 기본적으로 싱글톤 이다. 하지만, 요청할 때 마다 새로운 객체를 생성해서 반환하는 기능도 제공한다.
싱글톤 패턴의 주의점
객체의 인스터스를 하나만 생성하여 공유하는 싱글톤 패턴의 객체는 상태를 유지(statrful)하면 안 된다.
public class StatefulSingleton {
private static StatefulSingleton instance;
// 상태를 나타내는 필드
private int value;
// private 생성자
private StatefulSingleton() {}
// 싱글톤 인스턴스를 반환하는 메서드
public static StatefulSingleton getInstance() {
if (instance == null) {
instance = new StatefulSingleton();
}
return instance;
}
// 상태 변경 메서드
public void setValue(int value) {
this.value = value;
}
// 상태를 반환하는 메서드
public int getValue() {
return this.value;
}
}
public class MainApp {
public static void main(String[] args) {
// 클라이언트 1: 싱글톤 인스턴스를 가져와서 상태를 설정
StatefulSingleton client1 = StatefulSingleton.getInstance();
client1.setValue(42);
System.out.println("클라이언트 1이 설정한 값: " + client1.getValue());
// 클라이언트 2: 동일한 싱글톤 인스턴스를 사용해 상태를 변경
StatefulSingleton client2 = StatefulSingleton.getInstance();
client2.setValue(100);
System.out.println("클라이언트 2가 설정한 값: " + client2.getValue());
// 클라이언트 1이 다시 값을 확인
System.out.println("클라이언트 1이 다시 확인한 값: " + client1.getValue());
}
}
클라이언트 1이 설정한 값: 42
클라이언트 2가 설정한 값: 100
클라이언트 1이 다시 확인한 값: 100value 필드는 공유되는 필드인데, 특정 클라이언트가 값을 변경한다.