One_KWS

Zenject 본문

Unity

Zenject

One-Kim 2023. 3. 14. 13:07

Zenject 소개

Zenject 란 ?

Zenject는 Unity를 위한 DI(Dependency Injection : 의존성 주입) 프레임워크이다.

 

Github

https://github.com/modesttree/Zenject

Asset Store

https://assetstore.unity.com/packages/tools/utilities/extenject-dependency-injection-ioc-157735

의존성 주입(Dependency Injection : DI)

의존성 주입이란 객체 간의 의존성을 클래스 외부에서 주입하는 것을 말한다. 인터페이스를 사이에 둠으로서 클래스 레벨에서는 의존관계가 고정되지 않도록 하고 런타임시에 동적으로 주입하여 유연성을 확보하고 결합도를 낮출수 있게 해준다.

의존성 주입의 장점

  • 단위 테스트가 쉬워진다.
  • 코드의 재활용성이 높아딘다.
  • 객체 간의 의존성을 줄일 수 있다.
  • 객체 간의 결합도를 낮춰 유연한 코드를 작성할 수 있다.

의존성 문제

아래와 같은 코드가 있다고 하자.

public class Barista {
	private CoffeeRecipe coffeeRecipe;
	
	public Barista(){
		coffeeRecipe = new CoffeeRecipe();
	}

	public void MakeCoffee(){
		coffeeRecipe.Make();
	}
}

public class CoffeeRecipe {
	public void Make(){
		Debug.Log("Make Coffee");
	}
}

만약 위의 코드에서 커피 레시피가 바뀐다면 바리스타는 레시피를 수정해야 한다. 또는이처럼 커피 레시피의 변화가 바리스타에게 영향을 미칠때 의존한다고 한다.

위 코드는 인터페이스를 이용하여 개선할 수 있다.

public class Barista {
	private ICoffeeRecipe coffeeRecipe;
	
	public Barista(){
		coffeeRecipe = new AmericanoRecipe();
	}
}
interface ICoffeeRecipe{
	void Make();
}

public class EspressoRecipe : ICoffeeRecipe {
	public void Make(){
		Debug.Log("Make Espresso");
	}
}

public class AmericanoRecipe : ICoffeeRecipe {
	public void Make(){
		Debug.Log("Make Americano");
	}
}

public class LatteRecipe : ICoffeeRecipe {
	public void Make(){
		Debug.Log("Make Latte");
	}
}

위 코드처럼 인터페이스를 이용하여 추상화하면 두 클래스 의 결합을 느슨하게 할 수 있고 더 유연한 코드를 작성할 수 있다.

의존성 주입이 필요한 이유

위 코드에서는 Barista 클래스에서 직접 Recipe를 정했지만 Cafe라는 외부 클래스에서 Barista가 어떤 Recipe를 가질지 정해서 주입시킬 수도 있다.

public class Barista {
	private ICoffeeRecipe coffeeRecipe;
	
	public Barista(ICoffeeRecipe coffeeRecipe){
		this.coffeeRecipe = coffeeRecipe;
	}
}

public class Cafe : MonoBehaviour{
	private void Start(){
		var barista = new Barista(new AmericanoRecipe());
		barista.MakeCoffe();
	}
}

위 코드처럼 한 클래스(Barista)에서 어떤 클래스(CoffeeRecipe)에 의존할 지를 외부에서 주입 시키는 것을 **의존성 주입(Dependency Injection)**이라고 한다.

Zenject을 이용한 의존성 주입

Zenject를 이용한 의존성 주입에는 여러가지 방법들이 있다.

Injection

Constructor Injection

public class Foo {
    IBar _bar;

    [Inject]
    public Foo(IBar bar) {
        _bar = bar;
    }
}

Field Injection

public class Foo {
    [Inject]
    IBar _bar;
}

Field Injection은 생성자가 호출된 직후에 수행된다. [Inject] 어트리뷰트가 붙은 모든 필드는 컨테이너에서 조회되고 값이 지정된다. 이러한 필드는 private이나 public 둘 다 가능하다.

Property Injection

public class Foo {
    [Inject]
    public IBar Bar {
        get;
        private set;
    }
}

프로퍼티 주입은 필드 주입과 동일하게 동작한다. 필드와 마찬가지로, 이 경우 set은 public 또는 private일 수 있다.

Method Injection

public class Foo {
    IBar _bar;
    Qux _qux;

    [Inject]
    public void Init(IBar bar, Qux qux) {
        _bar = bar;
        _qux = qux;
    }
}

메소드 주입은 생성자 주입과 비슷하게 동작한다.

Binding

Zenject에서 종속성 매핑은 컨테이너라고 하는 것에 바인딩을 추가함으로써 이루어진다. 그런 다음 컨테이너는 주어진 객체에 대한 모든 종속성을 재귀적으로 해결함으로써 응용 프로그램의 모든 객체 인스턴스를 생성하는 방법을 알고 있어야 한다

컨테이너가 특정 유형의 인스턴스를 생성하도록 요청을 받으면 C# 리플렉션을 사용하여 생성자 인수 목록과 [Inject] 애트리뷰트로 표시된 모든 필드 또는 속성을 찾는다. 그런 다음 생성자를 호출하고 새 인스턴스를 만드는 데 사용하는 필수 종속성을 각각 해결하려고 시도한다.

public class Foo {
    IBar _bar;

    public Foo(IBar bar) {
        _bar = bar;
    }
}

위와 같은 클래스가 있다고 할 때 종속성 주입은 아래와 같이 표현할 수 있다.

Container.Bind<Foo>().AsSingle();
Container.Bind<IBar>().To<Bar>().AsSingle();

예제

SceneContext 생성

Hierachy에서 우클릭 - Zenject - Scene Context를 클릭하여 SceneContext를 생성한다.

Mono Installer 스크립트 생성

Project - Create - Zenject - Mono Installer를 클릭하여 MonoInstaller 스크립트를 생성한다.

public class CafeInstaller : MonoInstaller {
    public override void InstallBindings() {
        Container.Bind<ICoffeeRecipe>().FromInstance(new AmericanoRecipe()).NonLazy();
    }
}

Scene Context에 MonoInstaller 연결

GameObject를 생성하여 위에서 작성한 스크립트를 연결한 후 Scene Context의 Mono Instasllers에 연결해준다.

스크립트 작성

Barista 스크립트를 생성하여 아래와 같이 작성하고 GameObject를 생성하여 붙여준다.

public class Barista : MonoBehaviour {
		[Inject]
    private ICoffeeRecipe coffeeRecipe;
    public ICoffeeRecipe CoffeeRecipe {
        get => coffeeRecipe;
        set => coffeeRecipe = value;
    }

    private void Start() {
        MakeCoffee();
    }

    private void MakeCoffee(){
				coffeeRecipe.Make();
		}
	}

실행결과

따로 Barista의 CoffeeRecipe에 할당해 주지 않아도 Zenject가 자동으로 Coffee Recipe를 지정해준다.

 

'Unity' 카테고리의 다른 글

Addressable System #5 - Build layout report  (0) 2023.03.24
Unity - Editor.Log  (0) 2023.03.24
UniRx  (0) 2023.03.14
ScriptableObject 사용하기  (0) 2023.03.08
Unity 인앱 결제 (In-App Purchasing) 구현하기  (0) 2023.03.01