프로그래밍/Unity

[Unity] Reflex

musiklo 2023. 12. 4. 22:26
❗ Reflex는 매우 빠르고, 최소한이지만 완전한 Unity용 종속성 주입 프레임워크 입니다.
 

GitHub - gustavopsantos/Reflex: Minimal dependency injection framework for Unity

Minimal dependency injection framework for Unity. Contribute to gustavopsantos/Reflex development by creating an account on GitHub.

github.com

🎯 Reflex 도입 목적 및 목표


저는 수년 동안 Zenject/Extenject를 사용했지만 CodeBase가 너무 컸고, 공개된 API가 너무 많아서 초보자의 학습 곡선이 가파르게 느껴졌고, 성능도 그다지 좋다고 느끼지 않았습니다. 그래서 Reflex를 디자인하게 되었습니다. - Reflex 개발자 gustavopsantos

위 글처럼 기존 유니티에서 사용하던 IoC Container 들이 초보자들이 배우기에 어려웠으며, 성능이 뛰어나지도 않아 본인이 해당 IoC Container 보다 뛰어난 성능, 쉬운 학습 곡선을 가진 프레임워크를 개발해야겠다 라는 생각에 탄생한 IoC Container가 Reflex입니다.

다른 IoC Container들에 비해 Reflex는 아래와 같은 장점을 가집니다.

  • 빠름 : VContainer보다 3배 빠르며, Zenject보다 7배 빠릅니다.
  • GC 친화적 : VContainer보다 2배 적은 할당, Zenject보다 9배 적은 할당.

❓ 유니티에서 Reflex를 사용하는 이유


Reflex를 사용하면 클래스를 세분화된 책임으로 분리하며 이 과정에서 느슨한 결합을 유지할 수 있게됩니다.(단일 책임의 원칙이 잘 적용될 수 있다고 합니다.) 또한 Reflex는 확장이 가능하며 유연한 방식으로 코드를 쉽게 작성, 재사용, 리팩터링 및 테스트 할 수 있도록 구성할 수 있습니다.

간단하게 아래와 같은 이점이 있습니다 😺

  • 클래스간 결합도 감소
  • 코드 재사용성 증가
  • 코드 유지보수 용이
  • 단위 테스트 용이

 

⛹️‍♂️ Reflex 시작하기


  1. 우선 Reflex를 설치합니다. (설치 되어 있지 않다면, 좌측 링크를 통해 설치해주세요)
  2. ProjectInstaller.cs 아래와 같이 생성
using Reflex.Core;
using UnityEngine;

public class ProjectInstaller : MonoBehaviour, IInstaller
{
    public void InstallBindings(ContainerDescriptor descriptor)
    {
        descriptor.AddSingleton("Hello");
    }
}

3. 유니티 프로젝트 창에서 Asset/Resources 디렉터리를 생성합니다.
4. 방금 생성된 디렉터리를 선택 후 Create → Reflex → Project Scope를 생성합니다.
5. 이후 Project Scope에 ProjectInstaller 컴포넌트를 추가해줍니다.

6. 이후 Greet 씬을 생성합니다. 이후 Build Settings에서 씬을 추가해줍니다.

7. 이후 Greeter.cs 를 생성한 뒤 아래 코드를 입력해줍니다.

using UnityEngine;
using Reflex.Core;
using System.Collections.Generic;

public class Greeter : IStartable // IStartable will force it to be constructed on container build
{
    public Greeter(IEnumerable<string> strings)
    {
        Debug.Log(string.Join(" ", strings));
    }

    public void Start()
    {
    }
}

8. Greet 씬 내에서 Scene Scope라는 빈 오브젝트를 생성하고 Scene Scope 스크립트와 GreetInstaller 스크립트를 생성하여 코드를 작성한 뒤 스크립트를 넣어줍니다.

using Reflex.Core;
using UnityEngine;

public class GreetInstaller : MonoBehaviour, IInstaller
{
    public void InstallBindings(ContainerDescriptor descriptor)
    {
        descriptor.AddSingleton("World");
        descriptor.AddSingleton(typeof(Greeter), typeof(IStartable)); // IStartable will force it to be constructed on container build
    }
}

9. 이후 Boot 라는 씬을 생성한 뒤 빌드 세팅에도 추가해줍니다.

10. 이후 Loader 스크립트를 생성합니다.

using Reflex.Core;
using UnityEngine;
using UnityEngine.SceneManagement;

public class Loader : MonoBehaviour
{
    private void Start()
    {
	// If you are loading scenes without addressables
	var scene = SceneManager.LoadScene("Greet", new LoadSceneParameters(LoadSceneMode.Single));
	ReflexSceneManager.PreInstallScene(scene, descriptor => descriptor.AddSingleton("beautiful"));

	// If you are loading scenes with addressables
	Addressables.LoadSceneAsync("Greet", activateOnLoad: false).Completed += handle =>
	{
		ReflexSceneManager.PreInstallScene(handle.Result.Scene, descriptor => descriptor.AddSingleton("beautiful"));
		handle.Result.ActivateAsync();
	};
    }
}

11. 이후 Boot씬의 빈 게임 오브젝트에 Loader 스크립트를 할당한 뒤 씬을 실행하면 아래와 같이 결과가 출력됩니다.