퉁탕퉁탕 만들어보자

Dagger2 가이드 번역(1) 본문

Computer/Android

Dagger2 가이드 번역(1)

호숀티 2022. 9. 5. 01:35
반응형

우선 Dagger를 알아야 Hilt 도 볼 듯하여, 사전 스터디를 약간 하는 겸.. 해서 공식사이트 번역을 했다.

 

<이 글은 아래 링크의 번역입니다. 오역, 임의번역, 생략 있습니다.>

https://dagger.dev/dev-guide/

 

Dagger

The best classes in any application are the ones that do stuff: the BarcodeDecoder, the KoopaPhysicsEngine, and the AudioStreamer. These classes have dependencies; perhaps a BarcodeCameraFinder, DefaultPhysicsEngine, and an HttpStreamer. To contrast, the w

dagger.dev

 

모든 어플리케이션에 있어서 최고의 클래스는, 자기 일을 하는 Class들 입니다. 예를 들면,  BarcodeDecoder, the KoopaPhysicsEngine, and the AudioStreamer 같은 클래스들이죠.

이 클래스들은 Dependency 를 갖고있습니다. 아마도 다음과 같은 클래스들일것입니다. BarcodeCameraFinder, DefaultPhysicsEngine, HttpStreamer.

 

대조적으로, 어플리케이션에 있어서 가장 최악의 클리스들은 별로 하는 일도 없으면서 공간을 많이 차지하는 녀석들입니다.

BarcodeDecoderFactory, the CameraServiceLoader, and the MutableContextWrapper와 같은 클래스들이죠.

이 클래스들은 흥미로운 것들을 대충 묶어놓은 박스 테이프 같은 클래스들입니다.

 

Dagger은 dependency injection 디자인 패턴을 구현하는 --Factory 클래스들의 대체제 입니다. boilerplate 를 작성하는 부담없이 말이죠. 이렇게 해서 당신이 중요한 class 들에 집중할 수 있도록 도와줍니다. dependency를 선언하고, 이를 충족시키는 방법을 선언하고, 당신의 앱을 출시하세요!

 

스탠다드 javax.inject 어노테이션(JSR 330) 을 기반으로 하여, 각각의 클래스들을 쉽게 테스트 할 수 있습니다. RpcCreditCardService를 FakeCreditCardService로 교체하기 위해 많은 boilerplate 가 필요하지 않습니다.

 

의존성 주입은 테스트만을 위한 것이 아닙니다. 재사용 가능하고, 교체 가능한 모듈 또한 쉽게 만들 수 있습니다. 모든 앱에서 동일한 AuthenticationModule을 공유할 수 있습니다. 또한 개발 중에는 DevLoggingModule을 실행하고 프로덕션에서는 ProdLoggingModule을 실행하여 각 상황에서 올바른 동작을 얻을 수 있습니다.

 

왜 Dagger2가 특별한가?

Dependency injection 프레임워크들은 구성 및 주입(configuring & injecting) 을 위해서 아-주 다양한 API 들과 함께 수년간 존재해 왔습니다. 그런데, 바퀴를 재 발명하는 이유는 무엇일까요? (why reinvent the wheel?) 

Dagger 2는 자동 생성되는 코드로 full stack 을 구현하는 최초의 제품입니다.

가이드하는 원칙은, Dependency Injection이 가능한 한 간단하고, 추적 가능하며, 성능이 좋은지 확인하기 위해 사용자가 손으로 작성한 코드를 모방하는 코드를 생성하는 것입니다. 

더 많은 background 를 원하신다면, 다음의 링크를 확인하세요.  -> this talk (slides) by Gregory Kick.

Dagger 사용하기

우리는 coffee maker 를 만들면서 dependency injection 을 시연해보겠습니다.

compile과 실행이 가능한 전체 sample 코드는 다음 링크에 있습니다.  -> Dagger's coffee example.

Dependencies 선언하기

Dagger는 appliction 클래스의 인스턴스를 Construct 하고 그들의 종속성을 충족시킵니다. 

 javax.inject.Inject 주석을 사용하여, 관심 있는 생성자와 필드를 식별할 수 있습니다.

 

@Inject로, Dagger가 클래스의 인스턴스를 생성할 때 사용할 생성자를 표시합니다.

새 인스턴스가 요청되면 Dagger는 필수 매개변수 값을 얻고 이 생성자를 호출합니다.

class Thermosiphon implements Pump {
  private final Heater heater;

  @Inject
  Thermosiphon(Heater heater) {
    this.heater = heater;
  }

  ...
}

 

Dagger는 필드를 직접 inject 할 수 있습니다.

아래 예제에서는 Heater 필드에 대한 Heater 인스턴스와 Pump 필드에 대한 Pump 인스턴스를 얻습니다.

class CoffeeMaker {
  @Inject Heater heater;
  @Inject Pump pump;

  ...
}

 

클래스에 @Inject 필드가 있지만 @Inject 생성자가 없는 경우, Dagger는 요청된 경우 해당 필드를 삽입하지만 새 인스턴스를 생성하지는 않습니다. @Inject 주석과 함께 argument 가 없는 생성자를 추가하여 Dagger가 인스턴스도 생성할 수 있다는 뜻입니다.

Dagger는 method injection 도 지원하지만 일반적으로 생성자 또는 필드 injection이 주로 많이 사용됩니다.
@Inject 주석이 없는 클래스는 Dagger로 생성할 수 없다는것을 기억하세요.

Dependencies 충족하기

기본적으로 Dagger는 위에서 설명한 대로 요청된 type의 인스턴스를 construct 하여 종속성을 충족합니다.

만일 CoffeeMaker를 요청한다면, Dagger는 new CoffeeMaker()를 호출하고 주입 가능한 필드를 설정하여 하나를 얻습니다.

하지만 @Inject는 모든 곳에서 작동하지 않습니다.

  • Interfaces 는 construct 될 수 없습니다.
  • Third-party classe에는 어노테이션을 달 수 없습니다.
  • Configurable한 객체는 반드시 configured되어야 합니다!

@Inject가 충분하지 않거나 제대로 되지 않은 경우, @Provides 어노테이션이 붙은 메서드를 사용하여 종속성을 충족합니다.

메서드의 return type 은 충족하는 dependency를 정의합니다.

 

예) Heater 가 필요할 때마다 providerHeater()가 호출됩니다.

@Provides static Heater provideHeater() {
  return new ElectricHeater();
}

 

@Provides 메서드가 자기 자체적으로 종속성을 가질 수도 있습니다. 

예를 들어, ElectricHeater에 @Inject constructor 가 있다면, 위의 메서드를 다음과 같이 작성할 수 있습니다. (<- 여기 이해 못함)

@Provides static Heater provideHeater(ElectricHeater heater) {
  return heater;
}

이러한 방법으로 Dagger는 ElectricHeater 인스턴스화를 처리하고, @Provides 메서드는 Heater type에 별칭(alias)을 지정하는 데만 사용됩니다.

이러한 특별한 경우에는 @Binds 메서드를 사용하여 별칭(alias)을 정의하여 더 단순화할 수 있습니다. @Provides와 달리, @Binds 메서드는 abstract이고 구현부가 없습니다.

@Binds Heater bindHeater(ElectricHeater impl);

Note: @Binds를 사용하는 것은 별칭(alias)을 정의하는 데 선호되는 방법입니다. 왜냐하면 Bind 를 사용해서 컴파일 타임에는 모듈을 사용하고 런타임에는 모듈을 로드하는 것을 피할 수 있습니다.

 

마지막으로 모든 @Provides 메서드는 `모듈`에 속해야 합니다. 이들은  @Module 주석이 있는 클래스일 뿐입니다.

@Module
interface HeaterModule {
  @Binds Heater bindHeater(ElectricHeater impl);
}

컨벤션에 따라 @Provides 메서드는 'provide' 프리픽스로 이름이 지정되고 @Binds 메서드는 'bind' 프리픽스로 이름이 지정되며 모듈 클래스는 Module 접미사로 이름이 지정됩니다. 

Graph 만들기

@Inject, @Provides-annotated 클래스는 종속성으로 연결된 object들의 그래프를 형성합니다. 

이 graph의 root들의 집합은 argument가 없고 원하는 유형을 return하는 메서드가 있는 인터페이스로 정의됩니다. 이러한 인터페이스에 @Component 주석을 적고 module type을 modules parameter에 전달합니다.

애플리케이션의 main method나 Android  Application과 같은 코드가 호출되었을 때 이렇게 만들어진 root 집합을 통해서 해당 그래프에 액세스합니다.

@Component(modules = DripCoffeeModule.class)
interface CoffeeShop {
  CoffeeMaker maker();
}

구현체는 component 인터페이스 이름에 'Dagger'접사가 붙은 이름입니다.(CoffeeShop -> DaggerCoffeeShop)

이 구현체에서 builder() 메서드를 호출하여 인스턴스를 얻고-> 반환된 빌더를 사용하여 종속성을 설정하고-> 새 인스턴스를 build()합니다.

CoffeeShop coffeeShop = DaggerCoffeeShop.builder()
    .dripCoffeeModule(new DripCoffeeModule())
    .build();

참고: @Component가 최상위 유형이 아닌 경우에 생성된 component의 이름은 다음과 같습니다. 아래의 예를 참고하세요

예)

class Foo {
  static class Bar {
    @Component
    interface BazComponent {}
  }
}

위 클래스는 DaggerFoo_Bar_BazComponent 라는 이름의 컴포넌트를 생성합니다.

 

액세스 가능한 기본 생성자가 있는 모듈은 설정되지 않은 경우 빌더가 자동으로 인스턴스를 생성하므로 생략할 수 있습니다.(무엇을 생략?)

 

그리고 @Provides 메소드가 전부 static 모듈인 경우, 구현에 인스턴스가 전혀 필요하지 않습니다.

사용자가 dependency 인스턴스를 생성하지 않고 모든 종속성을 구성할 수 있는 경우, 새 인스턴스를 가져오는 데 사용할 수 있는 create() 메서드도 있습니다. (빌더는 필요없음)

CoffeeShop coffeeShop = DaggerCoffeeShop.create();

이제 우리의 CoffeeApp은 Dagger가 생성한 CoffeeShop 구현을 사용하여 완전히 주입된 CoffeeMaker를 얻을 수 있습니다.

public class CoffeeApp {
  public static void main(String[] args) {
    CoffeeShop coffeeShop = DaggerCoffeeShop.create();
    coffeeShop.maker().brew();
  }
}

이제 그래프가 구성되고 진입점이 주입되었으므로 커피 메이커 앱을 실행합니다. 재미있네요(?)

$ java -cp ... coffee.CoffeeApp
~ ~ ~ heating ~ ~ ~
=> => pumping => =>
 [_]P coffee! [_]P

Bindings in the graph

위의 예는 일반적인 바인딩을 사용하여 component 를 구성하는 방법을 보여주지만 그래프에 바인딩을 제공하는데에는 더 다양한 메커니즘이 있습니다. 다음 항목들은 종속성으로 사용할 수 있으며 잘 구성된 component 를 생성하는 데 사용할 수 있습니다.

  • Those declared by @Provides methods within a @Module referenced directly by @Component.modules or transitively via @Module.includes
  • Any type with an @Inject constructor that is unscoped or has a @Scope annotation that matches one of the component’s scopes
  • The component provision methods of the component dependencies
  • The component itself
  • Unqualified builders for any included subcomponent
  • Provider or Lazy wrappers for any of the above bindings
  • A Provider of a Lazy of any of the above bindings (e.g., Provider<Lazy<CoffeeMaker>>)
  • A MembersInjector for any type
Copyright 2012 The Dagger Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
728x90
반응형

'Computer > Android' 카테고리의 다른 글

Leak Canary Android 33 에서 Leak 발생  (0) 2023.04.15
Dagger2 가이드 번역(2)  (0) 2022.09.05
Android checkstyle 적용하기  (0) 2022.09.02
Elevation이 안 먹을때  (0) 2022.07.29
앱 권한 요청하기  (0) 2022.07.29