Quick Reference
singleton은 편하게 전역 접근하는 문법이 아니라, 특정 scope에서 instance가 정확히 하나여야 한다는 제약입니다. 하나만 존재해야 하는 output device, session service, app-level settings는 후보가 될 수 있지만, player·enemy·weapon state나 단순한 reference 전달은 singleton 사유가 아닙니다.
| 질문 | singleton에 맞는 답 | 더 나은 대안 |
|---|---|---|
| 누가 생성하는가 | bootstrap이 한 번 만든다 | prefab/scene reference를 직접 전달 |
| 누가 사용할 수 있는가 | 명시한 app·session scope | constructor·serialized field 주입 |
| 언제 사라지는가 | app 종료 또는 scope dispose | scene unload에서 함께 제거 |
| instance가 둘이면 | 중복을 fail·destroy·reuse 중 하나로 고정 | 여러 instance를 허용하는 registry |
| 값이 shared definition인가 | 읽기 전용 config asset | ScriptableObject reference |
public sealed class AudioService : MonoBehaviour
{
public static AudioService Instance { get; private set; }
void Awake()
{
if (Instance != null && Instance != this)
{
Destroy(gameObject);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
}
void OnDestroy()
{
if (Instance == this) Instance = null;
}
}생성과 종료의 계약
DontDestroyOnLoad는 target GameObject와 hierarchy가 scene 전환에서 파괴되지 않게 할 뿐, 어떤 scene이 생성해야 하는지나 duplicate policy를 정하지 않습니다. boot scene 또는 composition root가 AudioService를 만들고, gameplay scene은 존재를 가정하되 생성하지 않는 식으로 책임을 하나로 둡니다. additive scene까지 쓴다면 scene마다 prefab을 두는 방식은 즉시 중복 원인이 됩니다.
Instance getter가 null일 때 자동 생성하는 lazy singleton은 setup 순서와 config 주입 시점을 숨깁니다. 꼭 필요하면 생성 위치, 사용할 prefab/config, first access가 실패할 때의 동작을 정해야 합니다. 일반적으로는 boot에서 ready 상태가 된 뒤에 service를 노출하는 편이 실패를 앞당깁니다.
static state와 Play Mode
Editor에서 domain reload를 꺼 두면 static field와 static event handler가 Play Mode 진입 때 초기화되지 않을 수 있습니다. build에서의 시작 상태와 Editor 반복 실행이 다를 수 있으므로 Instance, static cache, static event를 테스트 시작에 명시적으로 reset하거나 reload 설정을 포함해 검증합니다. OnDestroy에서 Instance == this인지 확인하지 않으면 먼저 파괴된 duplicate가 살아 있는 instance를 null로 덮을 수도 있습니다.
singleton object가 scene object를 field·event·coroutine·async callback으로 잡으면 scene unload 뒤에도 참조가 남습니다. app scope service에는 scene reference를 짧게 전달하고, unsubscribe·cancel·release owner를 scene scope에 둡니다. service가 참조를 오래 보관해야 한다면 scene 종료 신호에서 무효화하는 계약이 필요합니다.
사용을 줄이는 기준
테스트에서 fake audio, fake save, fake clock을 넣어야 한다면 static Instance를 직접 읽는 consumer보다 contract를 받는 consumer가 유리합니다. 읽기 전용 balance나 item definition은 singleton MonoBehaviour보다 ScriptableObject asset reference가 lifecycle이 단순합니다. scene 별 AI, UI panel, player state처럼 복수 instance가 자연스러운 대상은 singleton 대신 owner가 가진 field·collection으로 모델링합니다.
GameManager.Instance가 null이 아니라는 사실은 ready, 설정 완료, 현재 scene과의 호환성을 보장하지 않습니다. singleton을 쓸 때는 instance 수뿐 아니라 생성자, ready 시점, scene 종료, static reset을 함께 확인하세요.
참고 링크
2 sources