VContainer 는 스코프의 Awake 안에서 컨테이너를 세운다
Esc

    VContainer 는 스코프의 Awake 안에서 컨테이너를 세운다

    2026년 9월 30일 수정–UnityDI라이브러리
    builder.Register<IGreeter, ConsoleGreeter>(Lifetime.Singleton);
    builder.RegisterEntryPoint<GameEntryPoint>();   // Start() 는 언제 불릴까

    등록 두 줄을 적고 실행하면 로그가 찍힙니다. 그 사이에 무엇이 지나가는지 모르면 IStartable.Start() 를 유니티의 Start 와 같은 것으로 읽고, 주입이 끝나기 전의 값을 씁니다.

    VContainer 는 씬에 놓인 스코프(모노비헤이비어)의 Awake 안에서 등록을 모으고 컨테이너를 빌드하고, 진입점은 다음 프레임에 플레이어 루프에서 부릅니다. 기준 판본은 1.19.0 입니다.

    등장인물은 스코프, 빌더, Resolver 셋이다

    이름 정체 역할
    LifetimeScope 모노비헤이비어 씬에 놓는 진입점. 무엇을 등록할지 선언하고 컨테이너를 소유합니다
    IContainerBuilder 순수 C# Configure() 에 넘어오는 등록 수집기
    IObjectResolver 순수 C# 빌드 결과물. 실제로 객체를 만들어 주는 Resolver

    스코프가 모노비헤이비어라서 씬과 함께 죽으면서 자기가 들고 있던 컨테이너를 해제합니다.

    최소 예제는 등록 두 줄이다

    진입점은 모노비헤이비어가 아니라 순수 C# 클래스입니다.

    public class GameEntryPoint : IStartable
    {
        readonly IGreeter greeter;
        public GameEntryPoint(IGreeter greeter) => this.greeter = greeter;   // 생성자 주입
        public void Start() => greeter.Greet("VContainer");
    }
    
    public class GameLifetimeScope : LifetimeScope
    {
        protected override void Configure(IContainerBuilder builder)
        {
            builder.Register<IGreeter, ConsoleGreeter>(Lifetime.Singleton);
            builder.RegisterEntryPoint<GameEntryPoint>();
        }
    }

    빈 게임오브젝트에 GameLifetimeScope 를 붙이고 실행하면 콘솔에 로그가 찍힙니다.

    진입점과 모노비헤이비어는 이 순서로 불린다

    스코프의 Awake 는 실행 순서가 -5000 으로 박혀 있어 다른 스크립트보다 먼저 돕니다. 그 안에서 Configure 로 등록을 모으고, 빌드하면서 순환 참조를 잡고, 진입점 디스패처를 플레이어 루프에 꽂습니다. 그 뒤로는 진입점과 모노비헤이비어 콜백이 번갈아 불립니다.

    공식 문서(Entry point)는 진입점마다 “MonoBehaviour.Start() 와 거의 같은 때” 처럼 적습니다. 앞인지 뒤인지는 소스(PlayerLoopHelper)가 정합니다. 디스패처는 유니티가 모노비헤이비어 콜백을 부르는 단계 바로 앞에 진입점을, 그 그룹 맨 끝에 Post 를 꽂습니다.

    모노비헤이비어의 Start 에서 준비한 값을 읽어야 하면 IPostStartable 을, 모든 Update 가 끝난 값을 읽어야 하면 IPostTickable 을 씁니다.

    주의할 점

    1. IStartable.Start() 를 유니티의 Start 로 읽지 않는다

    안 지키면 생기는 문제. 진입점은 게임오브젝트에 붙어 있지 않아서 유니티가 부르는 것이 아닙니다. 스코프가 빌드를 끝낸 뒤 디스패처가 플레이어 루프에 꽂은 자리에서 불립니다. 모노비헤이비어의 Start 와 같은 순서, 같은 조건으로 돈다고 가정하면 순서가 어긋납니다.

    이렇게 쓴다. 위 흐름도를 기준으로 고릅니다. 모노비헤이비어의 Start 뒤여야 하면 IPostStartable, Awake 안에서 곧바로 끝내야 하면 IInitializable 입니다.

    2. 진단은 개발 중에만 켠다

    안 지키면 생기는 문제. 설정 자산의 Enable Diagnostics 를 켜면 등록마다 스택 추적을 뜨고, Resolve 마다 진단 정보를 선형으로 훑고, 만든 인스턴스를 붙들고 있습니다. Singleton Resolve 1회가 Mono 에서 약 36배(0.13 µs → 4.6 µs), IL2CPP 에서 약 11배(0.27 µs → 2.8 µs) 느려지고 Resolve 마다 약 500 B 를 할당했습니다. Transient 는 Resolve 를 거듭할수록 느려져 10,000번이면 Mono 약 19배, IL2CPP 약 31배였습니다. 켠 채로 누수를 재면 진단이 쥔 참조 때문에 결과가 왜곡되고, Resolve 시간은 밀리초 정수라 1밀리초 아래는 0으로 찍힙니다.

    이렇게 쓴다. 진단 창(Window > VContainer Diagnostics)은 등록과 계층을 확인할 때만 켜고, 릴리스 전과 측정할 때는 설정 자산에서 끕니다.

    정리

    VContainer 는 스코프의 Awake 안에서 컨테이너를 세우고, 진입점은 다음 프레임 플레이어 루프에서 부릅니다.

    • 스코프(모노비헤이비어), 빌더(수집), Resolver(생성) 셋이 등장인물이고, 스코프가 씬과 함께 죽으며 컨테이너를 해제합니다.
    • Awake(순서 -5000) → Configure → 빌드 → Initialize 까지가 스코프 Awake 안이고, 다른 스크립트 Awake 뒤 첫 프레임에 IStartable.Start() → Start() → PostStart() 순입니다.
    • 매 프레임은 Tick() → Update() → PostTick() 처럼 진입점이 모노비헤이비어 콜백 앞에, Post 가 뒤에 옵니다.
    • 진단은 Resolve 를 수십 배 느리게 하고 인스턴스를 붙듭니다. 개발 중에만 켭니다.