### **Hello World** VContainer를 애플리케이션에 통합하는 기본적인 방법은 다음과 같습니다: - **`LifetimeScope` 컴포넌트 생성:** 씬(Scene)에 `LifetimeScope`를 상속받는 컴포넌트를 생성합니다. 이 컴포넌트는 하나의 컨테이너와 하나의 스코프(Scope)를 가집니다. - **의존성 등록 (Composition Root):** `LifetimeScope`의 서브클래스에서 C# 코드를 사용하여 의존성들을 등록합니다. 이 부분이 **컴포지션 루트(Composition Root)** 역할을 합니다. - **컨테이너 자동 빌드 및 디스패치:** 씬이 재생될 때, `LifetimeScope`는 자동으로 컨테이너를 빌드하고 이를 VContainer 자체의 `PlayerLoopSystem`에 디스패치합니다. **참고 (NOTE):** - 일반적으로 "스코프(Scope)"는 게임 도중에 반복적으로 생성되고 파괴됩니다. `LifetimeScope`는 이러한 특성을 가정하며, 부모-자식 관계를 가질 수 있습니다. --- **1. 다른 클래스에 의존하는 클래스 작성 (Write a class that depends on other classes)** "Hello world"를 예시로 들어보겠습니다. `HelloWorldService.cs` C# ``` namespace MyGame { public class HelloWorldService { public void Hello() { UnityEngine.Debug.Log("Hello world"); } } } ``` 이 클래스는 단순히 "Hello world" 메시지를 Unity 콘솔에 출력하는 역할을 합니다. --- **2. 컴포지션 루트 정의 (Define composition root)** 이제 위에서 작성한 `HelloWorldService` 클래스를 자동으로 연결(auto-wiring)할 수 있도록 설정을 작성해봅시다. - Project 탭의 폴더 내에서 마우스 오른쪽 버튼을 클릭하여 `Create -> C# Script`를 선택합니다. - 스크립트 이름을 `GameLifetimeScope.cs`로 지정합니다. **참고 (NOTE):** - VContainer는 `*LifetimeScope`로 끝나는 C# 스크립트에 대해 자동으로 템플릿을 생성합니다. `GameLifetimeScope.cs` 파일에 다음 코드를 추가하여 `HelloWorldService`를 컨테이너에 등록합니다. C# ``` using VContainer; using VContainer.Unity; // 이 using 문이 문서에 누락되어 있지만, LifetimeScope 사용을 위해 필요합니다. namespace MyGame { public class GameLifetimeScope : LifetimeScope { protected override void Configure(IContainerBuilder builder) { builder.Register<HelloWorldService>(Lifetime.Singleton); } } } ``` **설명:** - `GameLifetimeScope`는 `LifetimeScope`를 상속받습니다. - `Configure` 메서드는 컨테이너를 빌드할 때 호출되며, 여기서 의존성을 등록합니다. - `builder.Register<HelloWorldService>(Lifetime.Singleton);`는 `HelloWorldService` 클래스를 컨테이너에 싱글톤(Singleton)으로 등록합니다. 즉, 이 스코프 내에서 `HelloWorldService`의 인스턴스는 단 하나만 생성되고 재사용됩니다. **참고 (NOTE):** - VContainer는 항상 진입점(entry point)을 요구합니다. VContainer의 `PlayerLoop`에 참여하는 클래스를 정의해야 합니다. --- **3. 엔트리 포인트(Entry Point) 클래스 작성** `GamePresenter.cs` C# ``` using VContainer.Unity; // IStartable을 위해 필요 using UnityEngine; // Debug.Log 사용을 위해 필요 namespace MyGame { public class GamePresenter : IStartable // 또는 ITickable (문서에 두 가지 옵션이 제시됨) { readonly HelloWorldService helloWorldService; public GamePresenter(HelloWorldService helloWorldService) // 생성자 주입 { this.helloWorldService = helloWorldService; } void IStartable.Start() // IStartable 구현 { helloWorldService.Hello(); } // ITickable을 구현하는 경우: // void ITickable.Tick() // { // helloWorldService.Hello(); // 매 프레임마다 호출됨 // } } } ``` **설명:** - `GamePresenter`는 `HelloWorldService`에 의존합니다. 생성자를 통해 `helloWorldService`를 주입받습니다. - `IStartable` 인터페이스를 구현하여 `Start()` 메서드를 가집니다. VContainer는 `GameLifetimeScope`가 시작될 때 `GamePresenter`의 `Start()` 메서드를 자동으로 호출합니다. 이 메서드에서 `helloWorldService.Hello()`를 호출하여 "Hello world" 메시지를 출력합니다. --- **4. Scene 설정** - 새로운 Unity Scene을 생성하고 `Main Camera`와 `Directional Light`를 제거합니다. - 빈 GameObject를 생성하고 이름을 `GameLifetimeScope`로 지정합니다. - 이 `GameLifetimeScope` GameObject에 2단계에서 생성한 `GameLifetimeScope.cs` 스크립트를 컴포넌트로 추가합니다. **5. 빌드 및 실행 (Build and Run)** - Unity 에디터에서 Play 버튼을 누르면, 콘솔에 "Hello world" 메시지가 출력되는 것을 확인할 수 있습니다. --- **6. View Component 분리 및 상호작용 (Separating View Component)** DI의 이점을 강조하기 위해 UI(`HelloScreen`)와 제어 로직(`GamePresenter`)을 분리하는 예시를 추가합니다. - `HelloScreen.cs` (UI View 컴포넌트) C# ``` using UnityEngine; using UnityEngine.UI; namespace MyGame { public class HelloScreen : MonoBehaviour { public Button HelloButton; // 인스펙터에서 버튼을 할당할 수 있도록 public 필드로 선언 } } ``` - 이 클래스는 단순히 UI 요소(`HelloButton`)를 참조하는 `MonoBehaviour`입니다. 직접적인 로직은 포함하지 않습니다. - `GamePresenter.cs` (제어 로직) C# ``` using VContainer.Unity; using UnityEngine; // Debug.Log 사용을 위해 추가 using UnityEngine.UI; // Button 사용을 위해 추가 namespace MyGame { public class GamePresenter : IStartable { readonly HelloWorldService helloWorldService; readonly HelloScreen helloScreen; // HelloScreen 의존성 추가 public GamePresenter( HelloWorldService helloWorldService, HelloScreen helloScreen) // 생성자 주입 { this.helloWorldService = helloWorldService; this.helloScreen = helloScreen; } void IStartable.Start() { // HelloButton이 클릭될 때 HelloWorldService의 Hello 메서드 호출 helloScreen.HelloButton.onClick.AddListener(() => helloWorldService.Hello()); } } } ``` - `GamePresenter`는 이제 `HelloScreen`에도 의존합니다. - `Start()` 메서드에서 `HelloScreen`의 `HelloButton`에 `onClick` 리스너를 추가하여 버튼 클릭 시 `HelloWorldService.Hello()`가 호출되도록 합니다. - `GameLifetimeScope.cs` 업데이트 C# ``` using VContainer; using VContainer.Unity; using UnityEngine; // SerializeField를 위해 필요 namespace MyGame { public class GameLifetimeScope : LifetimeScope { [SerializeField] // Unity 에디터에서 HelloScreen을 할당할 수 있도록 함 HelloScreen helloScreen; protected override void Configure(IContainerBuilder builder) { builder.RegisterEntryPoint<GamePresenter>(); builder.Register<HelloWorldService>(Lifetime.Singleton); builder.RegisterComponent(helloScreen); // HelloScreen 인스턴스 등록 } } } ``` - `HelloScreen` 인스턴스를 `GameLifetimeScope`의 `SerializeField`로 노출하고, `builder.RegisterComponent(helloScreen);`를 사용하여 컨테이너에 등록합니다. `RegisterComponent`는 `MonoBehaviour` 인스턴스를 등록하는 데 사용됩니다. **이렇게 하면 다음의 역할 분리가 성공적으로 이루어집니다:** - **`GamePresenter`:** 오직 제어 흐름(Control Flow)만 책임집니다. - **`HelloWorldService`:** 언제 어디서든 호출될 수 있는 기능(functionality)만 책임집니다. - **`HelloScreen`:** 오직 뷰(View)만 책임집니다. **Scene에서의 설정:** 1. 새로운 UI `Button`을 씬에 추가합니다. 2. 빈 GameObject를 생성하고 이름을 `HelloScreen`으로 지정합니다. 3. `HelloScreen` GameObject에 `HelloScreen.cs` 스크립트를 컴포넌트로 추가합니다. 4. `HelloScreen` 컴포넌트의 인스펙터에서 `HelloButton` 필드에 씬에 추가한 `Button` UI를 드래그하여 할당합니다. 5. `GameLifetimeScope` GameObject를 선택하고 `GameLifetimeScope` 컴포넌트의 인스펙터에서 `Hello Screen` 필드에 씬의 `HelloScreen` GameObject를 드래그하여 할당합니다. 이제 Unity 에디터에서 Play 버튼을 누르고 UI 버튼을 클릭하면 "Hello world" 메시지가 콘솔에 출력될 것입니다. 이는 DI를 통해 뷰와 로직이 효과적으로 분리되었음을 보여주는 예시입니다. --- 이전 : [[2.1 Installation]] | 다음 : [[3.1 Constructor Injection]]