AGENT SKILL
29-singleton
暂无简介
- License 许可
- MIT
- 最近更新
- 2026-04-12
- Stars
- ★ 0
- Forks
- 0
03 SKILL.md 正文
29-singleton
Status: ACTIVE AppliesTo: v10 Type: Component Specification
0. 목표
- 개발자가 실수 없이 싱글톤을 사용하도록 script-created AutoSingleton을 제공한다.
- 필요 시 CompoSingleton으로 씬/프리팹 배치 책임(component)을 명시한다.
- 필요 시
Singleton.Create또는 **Singleton.CreateFromResources**로 Boot source 싱글톤을 명시 생성한다. - 모든 싱글톤은 **단일 저장소(SingletonRegistry)**를 통해 통합 관리한다.
1. 제공 타입 (2종)
AutoSingleton<T> (기본)
T.Instance 접근 시:
- Registry 조회
- 없으면 새 GameObject 생성 + AddComponent
Awake()(base 내부 고정)에서 Registry 등록 + DontDestroyOnLoad 적용 +onInitAwake()훅 호출OnDestroy()(base 내부 고정)에서onDestroy()훅 호출 후 Registry 해제. 하위 클래스는onDestroy()를 override한다.
원칙: AutoSingleton은 script code가 Instance 또는 프레임워크 생성 경로를 통해 만드는 패턴만 지원한다.
씬/프리팹에 미리 부착해서 사용하는 것은 금지다.
Shutdown 억제: 에디터 종료/플레이 종료/앱 종료 중(IsShuttingDown == true)에는 자동 생성이 억제되며 Instance는 null을 반환한다. Shutdown 방어가 필요하면:
AutoSingleton<T>.IsShuttingDown으로 사전 체크Singleton.TryGet<T>(out var t)또는T.TryGet(out var t)로 안전 조회 (자동 생성 없음)
CompoSingleton<T> (선택)
- 씬/프리팹에 컴포넌트로 붙여서 사용한다.
- 런타임
AddComponent로 생성하는 것은 패턴 위반이다. Awake()(base 내부 고정)에서 Registry에 등록하고, 하위 클래스 초기화는onInitAwake()에서 수행한다.OnDestroy()(base 내부 고정)에서onDestroy()훅 호출 후 Registry 해제. 하위 클래스 정리는onDestroy()에서 수행한다.- 우선순위 최고: CompoSingleton이 등록되면 같은 타입의 Auto/Boot 인스턴스를 대체한다(Adopt).
Auto vs Compo vs Create vs CreateFromResources 선택 기준
| Auto | Compo | Create | CreateFromResources | |
|---|---|---|---|---|
| 생성 방식 | script code가 Instance로 new GameObject + AddComponent | 씬/프리팹에 미리 직접 배치 | new GameObject + AddComponent (명시 호출) | Resources.Load<GameObject> + Instantiate |
| 우선순위 | Auto(0) 최저 | Compo(2) 최고 | Boot(1) 중간 | Boot(1) 중간 |
| SerializeField | 불가 (빈 GO 생성) | 가능 (씬/프리팹에 설정) | 불가 (빈 GO 생성) | 가능 (프리팹에 미리 설정) |
| 자동 생성 | O (Instance가 script-create) |
X (씬/프리팹 사전 배치 필요) | X (명시적 호출 필요) | X (명시적 호출 필요) |
| Base 상속 | 불가 | 불가 | 가능 | 가능 |
| 용도 | 설정 불필요한 script-created 싱글톤 | 씬/프리팹 배치가 필요한 싱글톤 | 런타임에서 명시 부트 생성 | 프리팹 설정값이 필요한 부트 싱글톤 |
2. 우선순위 규칙 (Hard Rule)
같은 타입 T에 대해 Compo > Boot > Auto가 항상 승리한다.
- CompoSingleton이 늦게 로드되어도, 기존 AutoSingleton/Boot 인스턴스를 **대체(Adopt)**해야 한다.
- 동일 우선순위끼리 중복은 **즉시 실패(예외)**로 처리한다.
3. Adopt 정책 (Hard Rule)
Registry에 Auto/Boot가 등록된 상태에서 Compo가 등록되면:
- CompoSingleton을 "정본"으로 등록
- 기존 AutoSingleton/Boot 인스턴스는 **제거(파괴)**하여 중복을 해소
- 이 상황은 실수 가능성이 높으므로 Error 로그를 남긴다 (단, 앱이 계속 진행 가능해야 함)
4. 접근 API (권장)
| API | 동작 |
|---|---|
Singleton.Get<T>() |
없으면 예외 (Fail-fast) |
Singleton.TryGet<T>(out T) |
없으면 false |
Singleton.IsShuttingDown |
Shutdown 구간 여부. BaseApplication.IsApplicationQuitting || !Application.isPlaying에 위임. Create* / CreateFromResources* 생성 억제 기준 |
Singleton.Create<T>() |
빈 GameObject 생성 + AddComponent + Registry 등록(Boot). key=T |
Singleton.Create<TBase,TSelf>() |
빈 GameObject 생성 + AddComponent + Registry 등록(Boot). key=TBase |
Singleton.CreateFromResources<T>(path) |
Resources에서 프리팹 로드 + Registry 등록(Boot). key=T |
Singleton.CreateFromResources<TBase,TSelf>(path) |
Resources에서 프리팹 로드 + Registry 등록(Boot). key=TBase |
T.Instance |
AutoSingleton/CompoSingleton이 제공하는 편의. Auto 계열은 script-create, Compo 계열은 기등록 인스턴스 조회. Shutdown 중 null 반환은 Auto 계열만 해당 |
AutoSingleton<T>.IsShuttingDown |
Shutdown 구간 여부 (Singleton.IsShuttingDown에 위임) |
4.1 Boot source 의미
SingletonSource.Boot는 Registry 우선순위 이름이다.- 이것이 프레임워크가 Bootstrap prefab 생성이나
BootProc()호출을 자동 처리한다는 뜻은 아니다. - Bootstrap 생성/
BootProc()호출은 app/contents layer가 명시적으로 수행한다.
5. 금지 (Hard Rule)
- 기존 싱글톤 시스템(SceneSingleton/MonoSingleton 등)을 새로 사용/추가하지 않는다.
- Registry의 Adopt/Destroy는 구현 정책으로 존재한다. 호출자는 이 동작에 의존해 중복 생성 흐름을 설계하지 않는다.
- Registry를 우회하는 static instance 보관을 금지한다 (모든 인스턴스는 Registry가 SSOT).
6. 파일 위치 (SSOT)
com.devian.foundation/Runtime/Unity/Singletons/
├── SingletonSource.cs # enum
├── SingletonRegistry.cs # SSOT 저장소
├── Singleton.cs # 정적 파사드
├── AutoSingleton.cs # AutoSingleton<T>
└── CompoSingleton.cs # CompoSingleton<T>
7. SingletonSource enum
public enum SingletonSource
{
Auto = 0, // 자동 생성 (최저 우선순위)
Boot = 1, // Resources 로드 등록 (중간 우선순위)
Compo = 2, // 씬/프리팹 컴포넌트 (최고 우선순위)
}
8. SingletonRegistry 규약
저장소 구조
private static readonly Dictionary<Type, Entry> _entries;
private readonly struct Entry
{
public readonly object Instance;
public readonly SingletonSource Source;
public readonly string DebugSource;
}
등록 규칙 (Hard Rule)
| 기존 | 신규 | 결과 |
|---|---|---|
| Auto | Boot | Adopt (기존 파괴 + Warning 로그) |
| Auto | Compo | Adopt (기존 파괴 + Error 로그) |
| Boot | Compo | Adopt (기존 파괴 + Error 로그) |
| Compo | Compo | 즉시 예외 (중복 금지) |
| Boot | Boot | 즉시 예외 (중복 금지) |
| Auto | Auto | 즉시 예외 (중복 금지) |
| Compo | Boot/Auto | 무시 (Compo 우선) |
| Boot | Auto | 무시 (Boot 우선) |
파괴 정책
- old 인스턴스가
Component면Object.Destroy(comp)로 컴포넌트만 제거 (GameObject 전체 파괴 금지) - old 인스턴스가
UnityEngine.Object이지만 Component가 아니면Object.Destroy(old)로 제거 - 순수 C# 객체면 참조만 교체 (로그 남김)
9. Create / CreateFromResources 규칙
Create 규칙
생성 방식:
- 빈
GameObject생성 →AddComponent<T>()또는AddComponent<TSelf>() - 이름은
"[TypeName]"형식
등록 소스: SingletonSource.Boot (중간 우선순위)
적합한 경우:
AutoSingleton자동 생성이 아니라 부트 시점에 명시 생성하고 싶을 때- 프리팹/
SerializeField설정이 필요 없을 때
Shutdown 억제:
Singleton.IsShuttingDown == true이면 생성하지 않고null반환 (경고 로그)
CreateFromResources 규칙
경로: 호출자가 string resourcePath로 직접 지정한다.
로드 방식:
Resources.Load<GameObject>(resourcePath)→GetComponent<T>()또는GetComponent<TSelf>()- GameObject로 로드하는 이유: IL2CPP에서 리플렉션 없이 안전하게 동작
- 프리팹 루트에 싱글톤 컴포넌트가 반드시 붙어있어야 함
등록 소스: SingletonSource.Boot (중간 우선순위)
Shutdown 억제:
Singleton.IsShuttingDown == true이면 생성하지 않고null반환 (경고 로그)
10. 사용 예시
기본 사용 (AutoSingleton — 1-param)
public class GameManager : AutoSingleton<GameManager>
{
public void StartGame() { ... }
}
// 어디서든 접근 - script code가 필요 시 생성
GameManager.Instance.StartGame();
씬 배치가 필요한 경우 (CompoSingleton — 1-param)
public class UIRoot : CompoSingleton<UIRoot>
{
[SerializeField] private Canvas _mainCanvas;
}
// UIRoot는 scene/prefab에 미리 부착해야 한다.
// runtime AddComponent로 생성하지 않는다.
명시 생성 (Singleton.Create)
// 부트스트랩 등에서 1회 명시 생성 (빈 GameObject + AddComponent)
Singleton.Create<GameRuntimeManager>();
// Base key로 등록하면서 구체 타입 생성 (시스템/컨텐츠 분리)
Singleton.Create<BaseTelemetrySystem, GameTelemetrySystem>();
Base 상속 + Resources 기반 싱글톤 (Singleton.CreateFromResources)
// ── 시스템 레이어 (framework) ──
public abstract class BaseAudioSystem : MonoBehaviour
{
public abstract void PlayBGM(string trackName);
}
// ── 컨텐츠 레이어 (game) ──
// 프리팹 배치: Assets/Resources/Singletons/GameAudioSystem.prefab
public sealed class GameAudioSystem : BaseAudioSystem
{
[SerializeField] private AudioSource _bgmSource;
public override void PlayBGM(string trackName) { ... }
}
// 초기화 (Bootstrap 등에서 1회 호출)
Singleton.CreateFromResources<BaseAudioSystem, GameAudioSystem>("Singletons/GameAudioSystem");
// 접근 (Registry key = BaseAudioSystem)
Singleton.Get<BaseAudioSystem>() // OK (반환 타입: BaseAudioSystem)
규칙 요약
AutoSingleton: scene/prefab에 미리 붙이지 않는다. script code가 만든다.CompoSingleton: scene/prefab에 미리 붙인다. runtime AddComponent로 만들지 않는다.Singleton.Create*: Boot 소스로 명시 생성할 때만 사용한다.
11. Reference
- Parent:
skills/devian/10-module/03-ssot/SKILL.md - Index:
skills/devian-unity/20-common-package/00-overview/SKILL.md