08
29
Zenject 깃허브 링크
https://github.com/modesttree/Zenject

 

 

Zenject

Zenject는 Unity 3D를 대상으로 제작된 DI(종속성 주입) 프레임워크. 오픈소스이다.

C#클래스뿐만 아니라 MonoBehaviour 클래스의 초기화를 지원한다.

 

다른 대표적인 DI 프레임 워크로는 Strangeioc, uFrame등이 있으나, 둘 다 마지막 업데이트가 5년 전으로 지원 중단되어서 메인 프레임워크로 가져가기는 무리가 있다. Zenject는 최근까지도 지원 중인 프레임 워크.

특히 'Signals event system' 라는 항목이 있는데 UniRX 같은 건가 싶어서 기대됨..

 

Extenject

에셋스토어에 zenject를 검색해보니 Extenject라고 검색되는데, 원제작자가 팀에서 나와서 zenject라는 이름을 못쓰게되서 Extenject라는 이름으로 배포하기 시작? 복잡한 문제가 있는 듯 한데,

그냥 Zenject == Extenject라고 생각하면 된다.

 

Extenject Dependency Injection IOC | 유틸리티 도구 | Unity Asset Store

Use the Extenject Dependency Injection IOC from Mathijs Bakker on your next project. Find this utility tool & more on the Unity Asset Store.

assetstore.unity.com

 

 

Why Zenject?

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

 

 

 

설치

일반적으로 깃허브 페이지에서 릴리즈 된 패키지를 설치하면 된다.

에셋스토어(위쪽 링크)에서 다운로드도 가능하지만, 에셋스토어 검증기간이 일주일이기 때문에 최신 버전이 아닐 수도 있다고 함. 

 

 

기초 튜토리얼

공식 튜토리얼은 아니지만 Zenject 깃허브 readme에서 추천할 정도.

 

 

의존성 주입(DI : Dependency Injection)이란 무엇인가?

하나의 클래스가 다른 클래스와 서로 상호 작용해야 하는 경우가 있다.

이 경우 다른 클래스에 대해 레퍼런스가 필요한데, 간단하게 생성자를 호출하여 종속성을 생성해 볼 수 있다.

public class Foo
{
    ISomeService _service;

    public Foo()
    {
        _service = new SomeService();
    }

    public void DoSomething()
    {
        _service.PerformTask();
       … 
    }
}

뭐, 작은 프로젝트라면 충분히 잘 작동하겠지만, 프로젝트가 커져갈수록 Foo클래스는 SomeService와 결합도가 커지면서 구현이 바뀔 경우 Foo클래스로 다시 돌아가서 변경해야 된다.

 

그러다 Foo가 특정 구현의 세부사항에 의존해서는 안 되는 걸 깨닫고 추상 인터페이스를 사용해 개선해보자는 결론에 이르게 된다. 

public class Foo
{
    ISomeService _service;

    public Foo(ISomeService service)
    {
        _service = service;
    }

    public void DoSomething()
    {
        _service.PerformTask();
        ...
    }
}

좀 더 개선되었다. 하지만 Foo를 생성할 때 Foo의 추가적인 의존을 주입해줘야 하는 문제가 생긴다.

public class Bar
{
    public void DoSomething()
    {
        var foo = new Foo(new SomeService());
        foo.DoSomething();
        ...
    }
}

그리고 Bar 또한 구현에 신경 쓸 필요가 없기 때문에 종속성을 다시 푸시한다.

public class Bar
{
    ISomeService _service;

    public Bar(ISomeService service)
    {
        _service = service;
    }

    public void DoSomething()
    {
        var foo = new Foo(_service);
        foo.DoSomething();
        ...
    }
}

따라서 특정 클래스가 어떤 특정 구현을 사용할 것인지 정하는 것이 꽤나 유용하다는 걸 알게 되었다.

어플리케이션이 시작되기 전에 모든 종속성이 충족되어야 하며 이를 composition root 라고 부른다. 일반적으로 다음과 같다.

var service = new SomeService();
var foo = new Foo(service);
var bar = new Bar(service);
var qux = new Qux(bar);

.. etc.

Zenject는 이러한 종속성을 생성하고 전달하는 프로세스를 자동화하기 때문에 위 코드에서와 같이 직접 의존성을 주입할 필요가 없다.

 

 

오해

DI를 처음 접하는 사람들이 자주 하는 실수는 '모든 클래스에서 인터페이스를 추출하고 클래스를 직접 사용하는 대신 모든 곳에서 인터페이스를 사용한다는 것'이다. 하지만 대부분의 경우 애플리케이션의 다양한 책임에는 이를 구현하는 단일 특정 클래스가 있으니, 불필요한 오버헤드가 추가될 뿐이다.

클래스에 둘 이상의 구현이 있거나 미래에 여러 구현을 가지려는 경우에만 인터페이스를 만들도록 하자.

 

 

 

COMMENT