오늘은 StageScene 구조를 정리하다가 “각 스테이지 정보를 어떻게 불러오고 관리할까?”에 대해 고민했다.
Scene 안에는 StageManager 오브젝트가 있고, 그 하위에는 여러 개의 Stage 프리팹이 들어 있다.

Stage를 선택하면 그 스테이지로 진입하고, 진행 중에는 나머지 스테이지가 간섭하지 않게 해야 한다.
즉, “어떤 스테이지를 선택했는가”에 따라 그 스테이지만 활성화하고 나머지는 비활성화하는 구조가 필요했다.
처음엔 크게 세 가지 방향을 생각했다.
Instantiate 방식
→ 프리팹을 Resources에서 불러와서 즉시 생성
→ 초기화는 쉽지만 로딩 비용이 발생
활성화/비활성화 방식
→ 씬 안에 미리 모든 스테이지를 넣고, 선택된 스테이지만 SetActive(true)
→ 빠르고 간단하지만, 메모리 상에 모든 스테이지가 상주
데이터 기반 로드 (JSON / ScriptableObject)
→ 스테이지 정보만 저장하고 필요할 때 불러오는 방식
→ 구조는 깔끔하지만, 지금은 오버엔지니어링에 가까움
결국 지금은 스테이지 수가 많지 않으니까, 두 번째 방법(활성화/비활성화)을 택했다.
로딩 속도가 빠르고, 구조도 단순하기 때문이다.
StageManager는 전체 스테이지를 관리하고, StageController는 각 스테이지 내부 로직을 담당한다.
StageManager는 게임의 진행 상태에 따라 각 StageController의 메서드를 호출한다.
public enum GameState
{
None,
Ready,
Play,
Dead,
Stop,
}
public void Init(GameManager gameManager)
{
_gameManager = gameManager;
var stages = GetComponentsInChildren<StageController>(true); // 비활성화 포함
_stages.Clear();
foreach (var stage in stages)
{
int id = stage.StageId;
if (_stages.ContainsKey(id))
{
Debug.Log($"중복된 ID: {id}");
continue;
}
_stages[id] = stage;
stage.gameObject.SetActive(false);
}
}
public void SelectStage(int id)
{
_currentStage = _stages[id];
_currentStage.gameObject.SetActive(true);
StartStage();
}
이 구조의 핵심은 “모든 Stage를 Dictionary로 캐싱하고, 필요할 때만 활성화한다”는 점이다.
또한 GameState를 따로 분리했기 때문에 UI나 ScoreManager에서도 손쉽게 게임 상태를 참조할 수 있다.
나중에는 여기에 이벤트 시스템(OnGameStateChanged)을 추가해서 UI가 자동으로 반응하도록 확장할 수도 있을 것 같다.
이번 구조는 ‘모든 걸 새로 불러오기’보다 ‘이미 있는 걸 효율적으로 관리하기’에 초점을 맞췄다.
지금은 스테이지가 6개뿐이지만, 나중에 규모가 커지면 Addressable이나 JSON 로드 방식으로 자연스럽게 확장할 수 있는 구조이기도 하다.
튜터님께 들은 바로는, 실제 프로젝트에서는 대부분 외부 리소스에서 스테이지를 로드하는 방식을 쓴다고 한다.
예를 들어 JSON에 프리팹 이름만 저장해두고 그 이름을 기준으로 로딩하거나, ScriptableObject를 통해 데이터를 직렬화해 관리하는 식이다.
하지만 지금처럼 소규모 프로젝트에서는 복잡한 설계보다는 “돌아가는 구조”가 더 중요하다.
Dictionary 기반 구조만으로도 충분히 유연하게 관리할 수 있었고, 무엇보다 개발 속도가 빨랐다.
다만 나중에 시간이 생기면 외부 리소스 로드나 ScriptableObject 기반으로 이 구조를 확장해보는 것도 좋은 도전이 될 것 같다.