개발을 진행하다 보면 프로그램 전반에서 공유되는 전역 객체를 만들어야 할 때가 있다.
이때 static으로 할지, Singleton으로 할지 고민하는 경우가 많지만
실제로는 명확한 기준 없이 느낌에 따라 결정하는 경우가 많다.
개인적으로 프로그래밍에서 근거 있는 선택은 매우 중요하다고 생각한다..
그래서 이번 기회에 static과 Singleton의 차이를 정리하고
언제 어떤 상황에서 선택해야 하는지에 대해 비유와 예제를 통해 명확히 해보려 한다.
| 항목 | static | Singleton |
|---|---|---|
| 정의 | 클래스 로딩 시 메모리에 올라가는 정적 자원 | 애플리케이션에서 단 하나의 인스턴스를 보장하는 전역 객체 |
| 메모리 관리 | JVM 종료 전까지 존재 (GC 대상 아님) | GC 대상이 될 수 있음 |
| 상태 관리 | 상태를 가지면 위험 (모든 스레드가 공유) | 상태를 가지되, 스레드별 제어 가능 (예: ThreadLocal) |
| 스레드 안전성 | 직접 제어 어려움 | 동기화 또는 ThreadLocal로 제어 가능 |
| 제어 가능성 | 불변에 가깝고 초기화 이후 제어 어려움 | 라이프사이클, 초기화, 해제 등 명시적으로 제어 가능 |
| 주요 사용 예 | 상수, 유틸성 로직 (Math, Collections, 포맷터 등) | DB 커넥션 풀, 세션 관리자, 설정값 캐시 등 상태가 있는 전역 객체 |
| 개념 | 자동차 공장 비유 |
|---|---|
| static | 공장 한가운데 있는 공용 공구상자 모든 작업자가 공유하며, 상태가 없거나 공유해도 괜찮은 계산기·자 모음 등 |
| Singleton | 공장 전체를 통제하는 중앙 통제기(제어 장치) 상태를 가지며, 각 작업자(스레드)가 안전하게 접근해야 하는 구성요소 (예: 세션 관리자, 설정 캐시 등) |
static은 무상태인 전역 도구Singleton은 상태를 가지되, 제어 가능한 전역 객체✔️ 무상태이면 static,
✔️ 상태와 제어가 필요하면 Singleton
public class UserSessionManager {
private static final UserSessionManager INSTANCE = new UserSessionManager();
// ThreadLocal을 이용한 사용자 세션 분리
private final ThreadLocal<String> currentUser = new ThreadLocal<>();
private UserSessionManager() {}
public static UserSessionManager getInstance() {
return INSTANCE;
}
public void login(String username) {
currentUser.set(username);
}
public String getCurrentUser() {
return currentUser.get();
}
public void logout() {
currentUser.remove();
}
}
currentUser)를 가지고 있음ThreadLocal을 사용함Singleton으로 구현하면:static 필드에 상태를 저장하면 모든 스레드가 같은 값을 공유하게 됨static은 초기화, 재설정, 수명 관리가 불가능해 유연성이 낮음| 항목 | static | Singleton |
|---|---|---|
| 객체 필요 여부 | ❌ 인스턴스 없이 바로 사용 가능 | ✅ 단 하나의 인스턴스를 통해 사용 |
| 상태 보유 | ❌ 지양됨 (전역 공유 위험) | ✅ 가능 (ThreadLocal 등으로 안전하게 분리) |
| 대표 사용 사례 | Math, Utils, 상수 클래스 등 | 설정 캐시, 세션 관리자, DB 커넥션 풀 등 |
static과 Singleton은 모두 전역 자원처럼 보이지만,
상태 관리와 라이프사이클 제어 가능성에서 큰 차이가 있다.
정리 기준은 단순하다다:
- 계산만 하는 유틸 도구 → static
- 상태를 가지며 제어가 필요한 전역 객체 → Singleton