Studio 모달 완료

This commit is contained in:
logonkhi
2025-12-16 19:35:43 +09:00
parent 0df2f0d8da
commit b5fdf7faeb
49 changed files with 10777 additions and 876 deletions

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine;
namespace UVC.Core
@@ -18,7 +19,7 @@ namespace UVC.Core
/// <para><b>[ 역할 ]</b></para>
/// <list type="bullet">
/// <item><description>Injector 인스턴스 생성 및 관리</description></item>
/// <item><description>App 라이프사이클 서비스 등록</description></item>
/// <item><description>App 라이프사이클 서비스 등록 (동기/비동기)</description></item>
/// <item><description>씬 로드 시 자동 의존성 주입 (선택적)</description></item>
/// <item><description>서비스 해결을 위한 편의 메서드 제공</description></item>
/// </list>
@@ -30,31 +31,41 @@ namespace UVC.Core
/// <item><description>Inspector에서 App Prefab 목록 설정 (선택)</description></item>
/// </list>
///
/// <para><b>[ 사용 방법 - 상속 ]</b></para>
/// <para><b>[ 사용 방법 - 동기 등록 ]</b></para>
/// <code>
/// public class MyAppContext : InjectorAppContext
/// {
/// [SerializeField] private UIManager uiManagerPrefab;
///
/// protected override void RegisterServices()
/// {
/// base.RegisterServices();
///
/// // Type A: 순수 C# 클래스
/// Injector.Register&lt;ILogService, ConsoleLogger&gt;(ServiceLifetime.App);
///
/// // Type B: MonoBehaviour 동적 생성
/// Injector.Register&lt;IAudioManager, AudioManager&gt;(ServiceLifetime.App);
///
/// // Type C: Prefab 기반
/// Injector.RegisterPrefab&lt;IUIManager&gt;(uiManagerPrefab.gameObject, ServiceLifetime.App);
///
/// // Type D: Singleton 연동
/// Injector.RegisterSingleton&lt;SettingsManager&gt;();
/// }
/// }
/// </code>
///
/// <para><b>[ 사용 방법 - 비동기 등록 ]</b></para>
/// <code>
/// public class MyAppContext : InjectorAppContext
/// {
/// protected override async UniTask RegisterServicesAsync()
/// {
/// // 비동기 로드가 필요한 서비스
/// var setting = new Setting();
/// var library = new Library();
///
/// await UniTask.WhenAll(
/// setting.LoadAsync(),
/// library.LoadAllParallelAsync()
/// );
///
/// Injector.RegisterInstance&lt;Setting&gt;(setting);
/// Injector.RegisterInstance&lt;Library&gt;(library);
/// }
/// }
/// </code>
///
/// <para><b>[ 서비스 접근 방법 ]</b></para>
/// <code>
/// // 방법 1: [Inject] 어트리뷰트 (권장)
@@ -69,8 +80,20 @@ namespace UVC.Core
///
/// <para><b>[ 이벤트 ]</b></para>
/// <list type="bullet">
/// <item><description>OnInjectorInitialized: Injector 생성 후 발생</description></item>
/// <item><description>OnServicesRegistered: 서비스 등록 완료 후 발생</description></item>
/// <item><description>OnInjectorInitialized: Injector 생성 및 모든 서비스 등록 완료 후 발생</description></item>
/// <item><description>OnServicesRegistered: 동기 서비스 등록 완료 후 발생</description></item>
/// <item><description>OnServicesRegisteredAsync: 비동기 서비스 등록 완료 후 발생</description></item>
/// </list>
///
/// <para><b>[ 초기화 순서 ]</b></para>
/// <list type="number">
/// <item><description>Injector 인스턴스 생성</description></item>
/// <item><description>RegisterServices() 호출 (동기)</description></item>
/// <item><description>OnServicesRegistered 이벤트 발생</description></item>
/// <item><description>RegisterServicesAsync() 호출 (비동기) - await 대기</description></item>
/// <item><description>OnServicesRegisteredAsync 이벤트 발생</description></item>
/// <item><description>IsInitialized = true</description></item>
/// <item><description>OnInjectorInitialized 이벤트 발생</description></item>
/// </list>
/// </remarks>
/// <seealso cref="Injector"/>
@@ -108,7 +131,7 @@ namespace UVC.Core
/// 초기화 완료 여부
/// </summary>
/// <remarks>
/// <para>Init() 완료 후 true가 됩니다.</para>
/// <para>동기 및 비동기 서비스 등록이 모두 완료 후 true가 됩니다.</para>
/// <para>InjectorSceneContext는 이 값이 true가 될 때까지 대기합니다.</para>
/// </remarks>
public bool IsInitialized { get; private set; }
@@ -121,19 +144,27 @@ namespace UVC.Core
/// Injector 초기화 완료 시 발생
/// </summary>
/// <remarks>
/// <para>Injector 인스턴스가 생성되고 서비스 등록이 완료된 직후 발생합니다.</para>
/// <para>동기 및 비동기 서비스 등록이 모두 완료된 직후 발생합니다.</para>
/// </remarks>
public event System.Action OnInjectorInitialized;
/// <summary>
/// 서비스 등록 완료 시 발생
/// 동기 서비스 등록 완료 시 발생
/// </summary>
/// <remarks>
/// <para>RegisterServices() 메서드 완료 후 발생합니다.</para>
/// <para>이 이벤트 이후부터 등록된 서비스들을 Resolve할 수 있습니다.</para>
/// <para>비동기 등록 전에 발생합니다.</para>
/// </remarks>
public event System.Action OnServicesRegistered;
/// <summary>
/// 비동기 서비스 등록 완료 시 발생
/// </summary>
/// <remarks>
/// <para>RegisterServicesAsync() 메서드 완료 후 발생합니다.</para>
/// </remarks>
public event System.Action OnServicesRegisteredAsync;
#endregion
#region Lifecycle
@@ -142,12 +173,24 @@ namespace UVC.Core
{
base.Init();
// 비동기 초기화 시작
InitializeAsync().Forget();
}
/// <summary>
/// 비동기 초기화 수행
/// </summary>
private async UniTaskVoid InitializeAsync()
{
// Injector 인스턴스 생성
Injector = new Injector();
// 서비스 등록
// 동기 서비스 등록
RegisterServices();
// 비동기 서비스 등록
await RegisterServicesAsync();
// 씬 로드 이벤트 구독
if (autoInjectOnSceneLoad)
{
@@ -175,11 +218,12 @@ namespace UVC.Core
#region Service Registration
/// <summary>
/// 서비스 등록을 수행합니다. 자식 클래스에서 오버라이드하여 추가 서비스를 등록하세요.
/// 동기 서비스 등록을 수행합니다. 자식 클래스에서 오버라이드하여 추가 서비스를 등록하세요.
/// </summary>
/// <remarks>
/// <para><b>[ 호출 시점 ]</b></para>
/// <para>Init() 메서드에서 Injector 인스턴스 생성 직후 호출됩니다.</para>
/// <para>InitializeAsync()에서 Injector 인스턴스 생성 직후 호출됩니다.</para>
/// <para>RegisterServicesAsync() 이전에 호출됩니다.</para>
///
/// <para><b>[ 오버라이드 시 주의사항 ]</b></para>
/// <para>base.RegisterServices()를 먼저 호출하여 Inspector에서 할당한 Prefab들이 등록되도록 하세요.</para>
@@ -190,13 +234,8 @@ namespace UVC.Core
/// {
/// base.RegisterServices(); // Inspector Prefab 등록
///
/// // Type A: 순수 C# 클래스
/// // 동기 등록이 적합한 서비스들
/// Injector.Register&lt;ILogService, ConsoleLogger&gt;(ServiceLifetime.App);
///
/// // Type B: MonoBehaviour 동적 생성
/// Injector.Register&lt;IAudioManager, AudioManager&gt;(ServiceLifetime.App);
///
/// // Type D: Singleton 연동
/// Injector.RegisterSingleton&lt;SettingsManager&gt;();
/// }
/// </code>
@@ -218,6 +257,45 @@ namespace UVC.Core
OnServicesRegistered?.Invoke();
}
/// <summary>
/// 비동기 서비스 등록을 수행합니다. 자식 클래스에서 오버라이드하여 비동기 로드가 필요한 서비스를 등록하세요.
/// </summary>
/// <remarks>
/// <para><b>[ 호출 시점 ]</b></para>
/// <para>RegisterServices() 완료 후 호출됩니다.</para>
/// <para>이 메서드가 완료될 때까지 IsInitialized는 false입니다.</para>
///
/// <para><b>[ 사용 예시 ]</b></para>
/// <code>
/// protected override async UniTask RegisterServicesAsync()
/// {
/// // JSON 설정 파일 비동기 로드
/// var setting = new Setting();
/// var library = new Library();
///
/// // 병렬 로드로 성능 최적화
/// await UniTask.WhenAll(
/// setting.LoadAsync(),
/// library.LoadAllParallelAsync()
/// );
///
/// // 로드 완료 후 등록
/// Injector.RegisterInstance&lt;Setting&gt;(setting);
/// Injector.RegisterInstance&lt;Library&gt;(library);
/// }
/// </code>
///
/// <para><b>[ 주의사항 ]</b></para>
/// <para>base.RegisterServicesAsync()를 호출할 필요는 없습니다 (기본 구현은 비어있음).</para>
/// <para>이 메서드 완료 전까지 InjectorSceneContext는 대기합니다.</para>
/// </remarks>
/// <returns>비동기 작업</returns>
protected virtual UniTask RegisterServicesAsync()
{
OnServicesRegisteredAsync?.Invoke();
return UniTask.CompletedTask;
}
/// <summary>
/// Prefab을 해당 타입으로 등록합니다.
/// </summary>
@@ -318,6 +396,22 @@ namespace UVC.Core
return Injector?.TryResolve<T>();
}
/// <summary>
/// 초기화가 완료될 때까지 대기합니다.
/// </summary>
/// <returns>초기화 완료 대기 태스크</returns>
/// <remarks>
/// <para>비동기 서비스 등록이 완료될 때까지 대기해야 할 때 사용합니다.</para>
/// <code>
/// await InjectorAppContext.Instance.WaitForInitializationAsync();
/// var setting = InjectorAppContext.Instance.Get&lt;Setting&gt;();
/// </code>
/// </remarks>
public async UniTask WaitForInitializationAsync()
{
await UniTask.WaitUntil(() => IsInitialized);
}
#endregion
}
}

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic;
using UnityEngine;
using Cysharp.Threading.Tasks;
namespace UVC.Core
{
@@ -149,6 +150,15 @@ namespace UVC.Core
/// </remarks>
public event System.Action OnSceneInjectionCompleted;
/// <summary>
/// Scene 서비스 비동기 등록 완료 시 발생
/// </summary>
/// <remarks>
/// <para>RegisterSceneServicesAsync() 메서드 완료 후 발생합니다.</para>
/// <para>비동기로 로드가 필요한 Scene 서비스들의 등록이 완료된 상태를 나타냅니다.</para>
/// </remarks>
public event System.Action OnSceneServicesRegisteredAsync;
#endregion
#region Lifecycle
@@ -157,44 +167,44 @@ namespace UVC.Core
/// Unity Awake - InjectorAppContext 준비 확인 후 초기화
/// </summary>
protected virtual void Awake()
{
InitializeAsync().Forget();
}
/// <summary>
/// 비동기 초기화 수행 - AppContext 대기 후 서비스 등록 및 의존성 주입
/// </summary>
/// <remarks>
/// <para><b>[ 실행 순서 ]</b></para>
/// <list type="number">
/// <item><description>InjectorAppContext 초기화 대기</description></item>
/// <item><description>동기 서비스 등록 (RegisterSceneServices)</description></item>
/// <item><description>비동기 서비스 등록 (RegisterSceneServicesAsync) - await</description></item>
/// <item><description>자동 의존성 주입 (옵션)</description></item>
/// <item><description>IsInitialized = true</description></item>
/// </list>
/// </remarks>
private async UniTaskVoid InitializeAsync()
{
// InjectorAppContext가 준비될 때까지 대기
if (InjectorAppContext.Instance == null || !InjectorAppContext.Instance.IsInitialized)
{
Debug.LogWarning("[InjectorSceneContext] InjectorAppContext is not initialized. Waiting...");
StartCoroutine(WaitForAppContext());
return;
await UniTask.WaitUntil(() => InjectorAppContext.Instance != null && InjectorAppContext.Instance.IsInitialized);
}
Initialize();
}
/// <summary>
/// InjectorAppContext가 준비될 때까지 대기하는 코루틴
/// </summary>
private System.Collections.IEnumerator WaitForAppContext()
{
while (InjectorAppContext.Instance == null || !InjectorAppContext.Instance.IsInitialized)
{
yield return null;
}
Initialize();
}
/// <summary>
/// 실제 초기화 수행 - 서비스 등록 및 의존성 주입
/// </summary>
private void Initialize()
{
if (Injector == null)
{
Debug.LogError("[InjectorSceneContext] Injector is null. Make sure InjectorAppContext exists in the scene.");
return;
}
// Scene 서비스 등록
// Scene 서비스 등록 (동기)
RegisterSceneServices();
// Scene 서비스 등록 (비동기) - 완료될 때까지 대기
await RegisterSceneServicesAsync();
// 자동 의존성 주입
if (autoInjectSceneObjects)
{
@@ -204,6 +214,32 @@ namespace UVC.Core
IsInitialized = true;
}
/// <summary>
/// 초기화가 완료될 때까지 대기합니다.
/// </summary>
/// <returns>초기화 완료 대기 UniTask</returns>
/// <remarks>
/// <para>다른 컴포넌트에서 SceneContext 초기화 완료를 기다릴 때 사용합니다.</para>
/// <para><b>[ 사용 예시 ]</b></para>
/// <code>
/// public class MyComponent : MonoBehaviour
/// {
/// private async void Start()
/// {
/// var sceneContext = FindObjectOfType&lt;InjectorSceneContext&gt;();
/// await sceneContext.WaitForInitializationAsync();
///
/// // 이제 모든 Scene 서비스가 준비됨
/// var service = sceneContext.Injector.Resolve&lt;IMyService&gt;();
/// }
/// }
/// </code>
/// </remarks>
public async UniTask WaitForInitializationAsync()
{
await UniTask.WaitUntil(() => IsInitialized);
}
/// <summary>
/// Unity OnDestroy - Scene 서비스 정리
/// </summary>
@@ -266,6 +302,48 @@ namespace UVC.Core
OnSceneServicesRegistered?.Invoke();
}
/// <summary>
/// Scene 서비스 비동기 등록을 수행합니다. 자식 클래스에서 오버라이드하여 비동기 서비스를 등록하세요.
/// </summary>
/// <returns>비동기 등록 완료 UniTask</returns>
/// <remarks>
/// <para><b>[ 호출 시점 ]</b></para>
/// <para>InitializeAsync()에서 RegisterSceneServices() 완료 후 호출됩니다.</para>
/// <para>이 메서드는 await되므로 완료될 때까지 IsInitialized가 false로 유지됩니다.</para>
///
/// <para><b>[ 사용 시나리오 ]</b></para>
/// <list type="bullet">
/// <item><description>씬별 설정 파일 비동기 로드</description></item>
/// <item><description>씬별 리소스 비동기 로드</description></item>
/// <item><description>씬별 네트워크 초기화</description></item>
/// </list>
///
/// <para><b>[ 사용 예시 ]</b></para>
/// <code>
/// public class BattleSceneContext : InjectorSceneContext
/// {
/// protected override async UniTask RegisterSceneServicesAsync()
/// {
/// await base.RegisterSceneServicesAsync();
///
/// // 씬별 설정 비동기 로드
/// var battleConfig = new BattleConfig();
/// await battleConfig.LoadAsync();
/// Injector.RegisterInstance&lt;BattleConfig&gt;(battleConfig, ServiceLifetime.Scene);
///
/// // 씬별 리소스 비동기 로드
/// var enemyData = await Resources.LoadAsync&lt;EnemyDatabase&gt;("EnemyData");
/// Injector.RegisterInstance&lt;EnemyDatabase&gt;(enemyData as EnemyDatabase, ServiceLifetime.Scene);
/// }
/// }
/// </code>
/// </remarks>
protected virtual UniTask RegisterSceneServicesAsync()
{
OnSceneServicesRegisteredAsync?.Invoke();
return UniTask.CompletedTask;
}
/// <summary>
/// Prefab을 해당 타입으로 등록합니다. (리플렉션 사용)
/// </summary>