Zenject 배우기 3-1) 바인딩

 

Zenject에서 바인딩이라고 부르는 'Register a new mapping'에 대해 배워보자.

 

종속성 맵핑

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

 

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

 

종속성 맵핑 예제

예를 들어 다음과 같은 클래스가 주어졌다면,

public class Foo
{
    IBar _bar;

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

다음과 같이 작성하여 이 클래스에 종속성을 연결할 수 있다.

Container.Bind<Foo>().AsSingle();

위의 코드는 모든 Foo 유형의 클래스가 종속성을 요구할 때 동일한 인스턴스를 사용해야 한다고 알려준다.

또한 이 인스턴스는 자동으로 생성된다.

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

위의 코드는 IBar 인터페이스가 요청되면 Bar유형의 동일한 인스턴스를 제공한다.

 

 

종속성 맵핑 전체 코드

다음은 종속성 맵핑의 전체 코드이다. 대부분의 경우 일부만 사용하고 정의되지 않은 것들은 기본값으로 설정된다.

Container.Bind<ContractType>()
    .WithId(Identifier)
    .To<ResultType>()
    .FromConstructionMethod()
    .AsScope()
    .WithArguments(Arguments)
    .OnInstantiated(InstantiatedCallback)
    .When(Condition)
    .(Copy|Move)Into(All|Direct)SubContainers()
    .NonLazy()
    .IfNotBound();

 

ContractType

- 주입되는 필드/매개 변수의 유형

 

ResultType

- 바인딩할 유형. 기본값은 ContractType

- ContractType과 같거나 ContractType의 파생 클래스여야 한다.

 

Identifier

- 바인딩을 고유하게 식별하는 데 사용할 값. 동일한 유형을 가진 여러 바인딩을 구현해야 하는 경우 사용.

public class Bar1
{
    [Inject(Id = "foo")]
    IFoo _foo;
}

 

ConstructionMethod

- ResultType의 인스턴스를 생성/검색하는 방법.

- 세부 구현은 링크 참조.

- 기본값은 FromNew()

- FromGetter, FromMethod, FromResolve, FromComponentInNewPrefab, FromSubContainerResolve, FromInstance 등

 

Scope

- 여러 주입에서 재사용되는 빈도(혹은 재사용 여부)를 결정함.

- 기본값은 AsTransient

- 모든 바인딩에 기본값이 있는 것은 아니라서 제공되지 않으면 예외가 발생한다. 하지만 검색을 사용하는 바인딩의 경우에는 범위를 명시적으로 설정할 필요가 없다.

a. AsTransient : 전혀 재사용하지 않음. ContractType이 요청될 때마다 구성 메서드를 다시 실행합니다.

b. AsCached : ContractType이 요청될 때마다 동일한 ResultType 인스턴스를 재사용함. 

c. AsSingle : ResultType에 바인딩이 이미 있다면 예외가 발생하는 거 빼고 AsCached와 같다. ResultType이 컨테이너 내에서 고유한지 확인하기만 한다. 주어진 컨테이너에 하나의 인스턴스만 있음을 보장한다. 즉, 하위 컨테이너에서 동일한 바인딩과 함께 AsSingle을 사용하면 두 번째 인스턴스가 생성될 수 있다.

- 대부분의 경우 AsSingle을 많이 사용하게 된다.

 

Arguments

- ResultType의 새 인스턴스를 생성할 때 사용할 인자들이다.

- 타입 인수에 대한 바인딩을 따로 만드는 것 대신 사용해볼 수 있다.

Container.BindInstance(arg).WhenInjectedInto<ResultType>()

 

InstantiatedCallback

- 인스턴스화 한 뒤에 추가 호출되는 콜백.

- 특히나, 서드파티 라이브러리의 객체를 초기화할 때 특정 메서드의 추가 호출이 필요한 경우 유용하게 사용된다.

Container.Bind<Foo>().AsSingle().OnInstantiated<Foo>(OnFooInstantiated);

void OnFooInstantiated(InjectContext context, Foo foo)
{
    foo.Qux = "asdf";
}

물론, 람다식으로 사용도 가능하다.

Container.Bind<Foo>().AsSingle().OnInstantiated<Foo>((ctx, foo) => foo.Bar = "qux");

 

Condition

- LINQ의 .when 절과 비슷한 역할을 한다.

- 세부 구현은 링크 참조.

Container.Bind<IFoo>().To<Foo1>().AsSingle().WhenInjectedInto<Bar1>();
Container.Bind<IFoo>().To<Foo>().AsSingle().When(context => context.ObjectType == typeof(Bar));

 

(Copy|Move)Into(All|Direct)SubContainers

- 굉장히 특수한 경우에 사용하니, 있다는 정도만 알고 있으면 된다.

- 하위 컨테이너에서 바인딩을 자동으로 상속시킬 수 있다. 예를 들면, Foo 클래스의 고유한 인스턴스가 각 컨테이너와 하위 컨테이너에서 자동으로 세팅되도록 하려면 아래 코드를 사용하여 바인딩할 수 있다. 

Container.Bind<Foo>().AsSingle().CopyIntoAllSubContainers()

각 하위 컨테이너에서 Container.Bind<Foo>().AsSingle()을 설정한 것과 같은 결과가 된다.

혹은, Foo 클래스를 현재의 컨테이너에서는 필요 없지만 하위에서는 사용하고 싶다면 아래의 코드를 사용하자.

Container.Bind<Foo>().AsSingle().MoveIntoAllSubContainers()

혹은, Foo클래스를 직계 자식 컨테이너에서만 사용하고 싶다면 아래의 코드를 사용하자.

Container.Bind<Foo>().AsSingle().MoveIntoDirectSubContainers()

 

NonLazy

- 일반적으로, ResultType은 바인딩이 처음으로 사용될 때 인스턴스화 된다. (지연 생성)

- NonLazy를 사용하면 시작할 때 바로 생성한다.

 

IfNotBound

- 이미 바인딩된 게 있다면, 이 바인딩은 무시한다.

 

 

 

댓글

Designed by JB FACTORY