paint-brush
Dagger 2를 사용한 종속성 주입: 그것이 무엇이고, 핵심 개념 및 기타~에 의해@dilip2882
412 판독값
412 판독값

Dagger 2를 사용한 종속성 주입: 그것이 무엇이고, 핵심 개념 및 기타

~에 의해 Dilip Patel21m2024/08/28
Read on Terminal Reader

너무 오래; 읽다

종속성 주입(DI)은 제어 역전(IoC)을 구현하는 데 사용되는 디자인 패턴입니다. 객체를 만드는 책임이 코드의 다른 부분으로 이전되는 기술입니다. 이는 느슨한 결합을 촉진하여 코드를 더 모듈화하고 관리하기 쉽게 만듭니다.
featured image - Dagger 2를 사용한 종속성 주입: 그것이 무엇이고, 핵심 개념 및 기타
Dilip Patel HackerNoon profile picture
0-item


의존성 주입 소개

종속성 주입(DI)은 종속성을 만들고 관리하는 제어가 애플리케이션에서 외부 엔터티로 이전되는 제어 역전(IoC)을 구현하는 데 사용되는 디자인 패턴입니다. 이는 보다 모듈화되고 테스트 가능하며 유지 관리 가능한 코드를 만드는 데 도움이 됩니다. 객체를 만드는 책임이 코드의 다른 부분으로 이전되는 기술입니다. 이는 느슨한 결합을 촉진하여 코드를 보다 모듈화하고 관리하기 쉽게 만듭니다.

클래스는 제대로 작동하려면 다른 클래스에 대한 참조가 필요한 경우가 많습니다. 예를 들어, Book 클래스가 필요한 Library 클래스를 생각해 보세요. 이러한 필수 클래스는 종속성이라고 합니다. Library 클래스는 작동하려면 Book 클래스의 인스턴스가 있어야 합니다.

하이퍼스킬.org

클래스가 필요한 객체를 얻는 데는 세 가지 주요 방법이 있습니다.

  1. 자체 구축 : 클래스는 자체 종속성을 생성하고 초기화합니다. 예를 들어, Library 클래스는 Book 클래스의 자체 인스턴스를 생성하고 초기화합니다.
  2. 외부 검색 : 클래스는 외부 소스에서 종속성을 검색합니다. Context getter 및 getSystemService() 와 같은 일부 Android API는 이런 방식으로 작동합니다.
  3. 종속성 주입 : 종속성은 클래스에 제공되며, 생성될 때 또는 종속성을 필요로 하는 메서드를 통해 제공됩니다. 예를 들어, Library 생성자는 Book 인스턴스를 매개변수로 받습니다.

세 번째 옵션은 종속성 주입입니다! DI를 사용하면 클래스 인스턴스가 직접 종속성을 가져오는 대신 클래스의 종속성을 제공합니다.

종속성 주입이 없는 예

DI가 없다면 자체적인 Book 종속성을 생성하는 Library 다음과 같습니다.

 class Library { private Book book = new Book(); void open() { book.read(); } } public class Main { public static void main(String[] args) { Library library = new Library(); library.open(); } }

이것은 Library 클래스가 자체 Book 구성하기 때문에 DI의 예가 아닙니다. 이는 다음과 같은 이유로 문제가 될 수 있습니다.

  • 타이트 커플링 : LibraryBook 타이트 커플링되어 있습니다. Library 의 인스턴스는 한 가지 유형의 Book 사용하므로 하위 클래스나 대체 구현을 사용하기 어렵습니다.
  • 테스트 어려움 : Book 에 대한 강한 의존성으로 인해 테스트가 더 어려워집니다. Library Book 의 실제 인스턴스를 사용하므로 테스트 더블을 사용하여 다른 테스트 케이스에 대해 Book 수정하는 것이 불가능합니다.

종속성 주입을 사용한 예

DI를 사용하면 Library 의 각 인스턴스가 자체 Book 객체를 구성하는 대신 생성자에서 매개변수로 Book 객체를 받습니다.

 class Library { private Book book; Library(Book book) { this.book = book; } void open() { book.read(); } } public class Main { public static void main(String[] args) { Book book = new Book(); Library library = new Library(book); library.open(); }

주요 기능은 Library 사용합니다. Library Book 에 의존하므로 앱은 Book 의 인스턴스를 생성한 다음 이를 사용하여 Library 의 인스턴스를 구성합니다. 이 DI 기반 접근 방식의 이점은 다음과 같습니다.

  • Library 의 재사용성 : Book 의 다양한 구현을 Library 에 전달할 수 있습니다. 예를 들어, Library 사용하려는 Book 의 새 하위 클래스인 EBook 정의할 수 있습니다. DI를 사용하면 EBook 인스턴스를 Library 에 전달하기만 하면 추가 변경 없이 작동합니다.
  • Library 의 간편한 테스트 : 테스트 더블을 통과하여 다양한 시나리오를 테스트할 수 있습니다.

또 다른 DI 예

NotificationService 클래스가 Notification 클래스에 의존하는 시나리오를 생각해 보세요. DI가 없으면 NotificationService Notification 인스턴스를 직접 생성하므로 다양한 유형의 알림을 사용하거나 다양한 알림 구현으로 서비스를 테스트하기 어렵습니다.

DI를 설명하기 위해 이 예제를 리팩토링해 보겠습니다.

 interface Notification { void send(); } class EmailNotification implements Notification { @Override public void send() { // Send email notification } } class SMSNotification implements Notification { @Override public void send() { // Send SMS notification } } class NotificationService { void sendNotification(Notification notification) { notification.send(); } }

이제 NotificationService 특정 클래스가 아닌 Notification 인터페이스에 의존합니다. 이를 통해 Notification 의 다양한 구현을 상호 교환하여 사용할 수 있습니다. sendNotification 메서드를 통해 사용하려는 구현을 설정할 수 있습니다.

 NotificationService service = new NotificationService(); service.sendNotification(new EmailNotification()); service.sendNotification(new SMSNotification());

안드로이드에서의 종속성 주입 방법

DI에는 세 가지 주요 유형이 있습니다.

  1. 메서드(인터페이스) 주입 : 종속성은 클래스가 인터페이스나 다른 클래스를 통해 액세스할 수 있는 메서드를 통해 전달됩니다. 이전 예제는 메서드 주입을 보여줍니다.
  2. 생성자 주입 : 종속성은 생성자를 통해 클래스에 전달됩니다.
 class NotificationService { private final Notification notification; public NotificationService(Notification notification) { this.notification = notification; } public void sendNotification() { notification.send(); } } public class Main { public static void main(String[] args) { NotificationService service = new NotificationService(new EmailNotification()); service.sendNotification(); } }

3. 필드 주입(또는 세터 주입) : 액티비티 및 프래그먼트와 같은 특정 Android 프레임워크 클래스는 시스템에 의해 인스턴스화되므로 생성자 주입이 불가능합니다. 필드 주입을 사용하면 클래스가 생성된 후 종속성이 인스턴스화됩니다.

 class NotificationService { private Notification notification; public Notification getNotification() { return notification; } public void setNotification(Notification notification) { this.notification = notification; } public void sendNotification() { notification.send(); } } public class Main { public static void main(String[] args) { NotificationService service = new NotificationService(); service.setNotification(new EmailNotification()); service.sendNotification(); } }

4. 메서드 주입 : 종속성은 @Inject 주석을 사용하여 메서드를 통해 제공됩니다.

의존성 주입의 장점

  • 클래스는 더 재사용 가능하고 특정 구현에 덜 의존하게 됩니다. 이는 제어의 역전으로 인해 클래스가 더 이상 종속성을 관리하지 않고 제공된 모든 구성으로 작업하기 때문입니다.
  • 종속성은 API 표면의 일부이며 객체 생성 또는 컴파일 시점에 확인할 수 있어 리팩토링이 더 쉬워집니다.
  • 클래스는 종속성을 관리하지 않으므로 다양한 시나리오를 포괄하기 위해 테스트 중에 서로 다른 구현을 전달할 수 있습니다.

자동화된 종속성 주입

이전 예에서 라이브러리를 사용하지 않고 다른 클래스의 종속성을 수동으로 생성, 제공 및 관리했습니다. 이 접근 방식은 수동 종속성 주입이라고 합니다. 간단한 경우에는 작동하지만 종속성과 클래스 수가 증가함에 따라 번거로워집니다. 수동 종속성 주입에는 몇 가지 단점이 있습니다.

  • 보일러플레이트 코드 : 대규모 애플리케이션의 경우 모든 종속성을 관리하고 올바르게 연결하면 많은 반복 코드가 발생할 수 있습니다. 다중 계층 아키텍처에서 최상위 계층의 객체를 만들려면 그 아래 계층의 모든 종속성을 제공해야 합니다. 예를 들어, 컴퓨터를 빌드하려면 CPU, 마더보드, RAM 및 기타 구성 요소가 필요하고 CPU에는 트랜지스터와 커패시터가 필요할 수 있습니다.
  • 복잡한 종속성 관리 : 앱의 특정 흐름에 대한 지연 초기화나 객체 범위 지정 등의 이유로 종속성을 미리 구축할 수 없는 경우, 메모리에서 종속성의 수명을 관리하기 위한 사용자 지정 컨테이너(또는 종속성 그래프)를 작성하고 유지 관리해야 합니다.

라이브러리는 종속성을 생성하고 제공하여 이 프로세스를 자동화할 수 있습니다. 이러한 라이브러리는 두 가지 범주로 나뉩니다.

  1. 반사 기반 솔루션 : 런타임에 종속성을 연결합니다.
  2. 정적 솔루션 : 컴파일 타임에 종속성을 연결하는 코드를 생성합니다.

Dagger는 Google에서 관리하는 Java, Kotlin 및 Android용 인기 있는 종속성 주입 라이브러리입니다. Dagger는 종속성 그래프를 만들고 관리하여 앱에서 DI를 간소화합니다. 완전히 정적인 컴파일 타임 종속성을 제공하여 Guice와 같은 리플렉션 기반 솔루션과 관련된 많은 개발 및 성능 문제를 해결합니다.

반사 기반 솔루션

이러한 프레임워크는 런타임에 종속성을 연결합니다.

  1. Toothpick : 리플렉션을 사용하여 종속성을 연결하는 런타임 DI 프레임워크입니다. 가볍고 빠르게 설계되어 Android 애플리케이션에 적합합니다.

정적 솔루션

이러한 프레임워크는 컴파일 시간에 종속성을 연결하는 코드를 생성합니다.

  1. Hilt : Dagger 위에 구축된 Hilt는 Dagger 종속성 주입을 Android 애플리케이션에 통합하는 표준 방식을 제공합니다. 사전 정의된 구성 요소와 범위를 제공하여 Dagger의 설정 및 사용을 간소화합니다 .
  2. Koin : Kotlin을 위한 가볍고 간단한 DI 프레임워크. Koin은 DSL을 사용하여 종속성을 정의하며 설정 및 사용이 쉽습니다.
  3. Kodein : 사용하기 쉽고 이해하기 쉬운 Kotlin 기반 DI 프레임워크입니다. 종속성을 관리하기 위한 간단하고 유연한 API를 제공합니다.

종속성 주입의 대안

종속성 주입의 대안은 서비스 로케이터 패턴입니다. 이 디자인 패턴은 또한 클래스를 구체적인 종속성에서 분리하는 데 도움이 됩니다. 종속성을 생성하고 저장하여 필요에 따라 제공하는 서비스 로케이터라는 클래스를 만듭니다.

 object ServiceLocator { fun getProcessor(): Processor = Processor() } class Computer { private val processor = ServiceLocator.getProcessor() fun start() { processor.run() } } fun main(args: Array<String>) { val computer = Computer() computer.start() }

서비스 로케이터 패턴은 종속성이 소비되는 방식에서 종속성 주입과 다릅니다. 서비스 로케이터 패턴을 사용하면 클래스가 필요한 종속성을 요청하지만 종속성 주입을 사용하면 앱이 필요한 객체를 사전에 제공합니다.

Dagger 2는 무엇인가요?

Dagger 2는 Android에서 인기 있는 DI 프레임워크입니다. 컴파일 타임 코드 생성을 사용하며 고성능으로 유명합니다. Dagger 2는 종속성을 처리하는 데 필요한 코드를 생성하여 종속성 주입 프로세스를 간소화하고, 보일러플레이트를 줄이고 효율성을 개선합니다.

Dagger 2는 Android에서 종속성 주입을 위한 주석 기반 라이브러리입니다. 주요 주석과 그 목적은 다음과 같습니다.

  • @Module : 종속성을 제공하는 클래스를 정의하는 데 사용됩니다. 예를 들어, 모듈은 Retrofit에 대한 ApiClient 제공할 수 있습니다.
  • @Provides : 모듈의 메서드에 주석을 달아 종속성을 생성하고 반환하는 방법을 지정합니다.
  • @Inject : 종속성을 요청하는 데 사용됩니다. 필드, 생성자 및 메서드에 적용할 수 있습니다.
  • @Component : @Module@Inject 연결하는 인터페이스입니다. 모든 모듈을 포함하고 애플리케이션의 빌더를 제공합니다.
  • @Singleton : 종속성의 단일 인스턴스가 생성되도록 보장합니다.
  • @Binds : 추상 클래스에서 종속성을 제공하는 데 사용되며, @Provides 와 유사하지만 더 간결합니다.

단검 구성 요소

Dagger는 프로젝트에 대한 종속성 그래프를 생성하여 필요할 때 종속성을 어디에서 얻을지 결정할 수 있습니다. 이를 활성화하려면 인터페이스를 만들고 @Component 로 주석을 달아야 합니다.

@Component 인터페이스 내에서 필요한 클래스의 인스턴스를 반환하는 메서드를 정의합니다(예: BookRepository ). @Component 주석은 Dagger가 노출하는 유형을 충족하는 데 필요한 모든 종속성이 있는 컨테이너를 생성하도록 지시합니다. 이 컨테이너는 Dagger 구성 요소로 알려져 있으며 Dagger가 종속성과 함께 제공하는 방법을 알고 있는 객체 그래프를 포함합니다.


LibraryRepository 와 관련된 예를 살펴보겠습니다.

  1. 생성자에 주석 달기 : LibraryRepository 생성자에 @Inject 주석을 추가하여 Dagger가 LibraryRepository 인스턴스를 생성하는 방법을 알 수 있도록 합니다.
 public class LibraryRepository { private final LocalLibraryDataSource localDataSource; private final RemoteLibraryDataSource remoteDataSource; @Inject public LibraryRepository(LocalLibraryDataSource localDataSource, RemoteLibraryDataSource remoteDataSource) { this.localDataSource = localDataSource; this.remoteDataSource = remoteDataSource; } }

2. 종속성 주석 달기 : 마찬가지로 종속성 생성자( LocalLibraryDataSourceRemoteLibraryDataSource )에 주석을 달아 Dagger가 종속성을 생성하는 방법을 알 수 있도록 합니다.

 public class LocalLibraryDataSource { @Inject public LocalLibraryDataSource() { // Initialization code } } public class RemoteLibraryDataSource { private final LibraryService libraryService; @Inject public RemoteLibraryDataSource(LibraryService libraryService) { this.libraryService = libraryService; } }

3. 구성 요소 정의 : 종속성 그래프를 정의하기 위해 @Component 로 주석이 달린 인터페이스를 만듭니다.

 @Component public interface ApplicationComponent { LibraryRepository getLibraryRepository(); }

프로젝트를 빌드하면 Dagger가 일반적으로 DaggerApplicationComponent 라는 이름의 ApplicationComponent 인터페이스 구현을 생성합니다.

용법

이제 생성된 구성 요소를 사용하여 종속성이 자동으로 주입된 클래스 인스턴스를 얻을 수 있습니다.

 public class MainApplication extends Application { private ApplicationComponent applicationComponent; @Override public void onCreate() { super.onCreate(); applicationComponent = DaggerApplicationComponent.create(); } public ApplicationComponent getApplicationComponent() { return applicationComponent; } }

활동이나 조각에서 LibraryRepository 인스턴스를 검색할 수 있습니다.

 public class MainActivity extends AppCompatActivity { @Inject LibraryRepository libraryRepository; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ((MainApplication) getApplication()).getApplicationComponent().inject(this); // Use the injected libraryRepository } }

Dagger 2의 핵심 개념

1. 모듈
∘ 모듈의 핵심 개념
∘ 컴포넌트에 모듈 포함하기
2. 범위
3. 구성 요소
4. 구성 요소 종속성
5. 런타임 바인딩

1. 모듈

Dagger 2의 모듈은 @Module 로 주석이 달린 클래스로, 구성 요소에 대한 종속성을 제공합니다. @Provides 또는 @Binds 로 주석이 달린 메서드가 포함되어 종속성을 만들고 제공하는 방법을 지정합니다. 모듈은 애플리케이션에 필요한 객체의 생성을 구성하고 관리하는 데 필수적입니다.

모듈의 핵심 개념

  1. @Module Annotation: 이 주석은 클래스를 Dagger 모듈로 정의하는 데 사용됩니다. 모듈 클래스에는 종속성을 제공하는 메서드가 들어 있습니다.
  2. @Provides 주석: 이 주석은 모듈 내의 메서드에서 메서드가 특정 종속성을 제공함을 나타내는 데 사용됩니다. 이러한 메서드는 종속성의 인스턴스를 생성하고 반환하는 역할을 합니다.
  3. @Binds 주석: 이 주석은 추상 클래스에서 구현을 인터페이스에 바인딩하는 데 사용됩니다. @Provides 보다 간결하며 모듈이 추상 클래스일 때 사용됩니다.

모듈의 예

 @Module public class NetworkModule { @Provides @Singleton Retrofit provideRetrofit() { return new Retrofit.Builder() .baseUrl("https://api.example.com") .addConverterFactory(GsonConverterFactory.create()) .build(); } @Provides @Singleton OkHttpClient provideOkHttpClient() { return new OkHttpClient.Builder() .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY)) .build(); } }

이 예에서 NetworkModule @Module 로 주석이 달린 클래스입니다. 여기에는 @Provides 로 주석이 달린 두 개의 메서드가 포함되어 있으며, RetrofitOkHttpClient 의 인스턴스를 생성하고 반환합니다.

@Binds 사용하기

인터페이스와 구현이 있는 경우 @Binds 사용하여 구현을 인터페이스에 바인딩할 수 있습니다. 이는 @Provides 사용하는 것보다 간결합니다.

 public interface ApiService { void fetchData(); } public class ApiServiceImpl implements ApiService { @Override public void fetchData() { // Implementation } } @Module public abstract class ApiModule { @Binds abstract ApiService bindApiService(ApiServiceImpl apiServiceImpl); }

이 예에서 ApiModule @Module 로 주석이 달린 추상 클래스입니다. bindApiService 메서드는 @Binds 로 주석이 달려 있어 ApiServiceImpl ApiService 에 바인딩합니다.

모듈은 제공하는 기능에 따라 구성할 수 있습니다. 예를 들어, 네트워크 작업, 데이터베이스 작업 및 UI 관련 종속성에 대해 별도의 모듈을 가질 수 있습니다.

예:

  • NetworkModule : Retrofit , OkHttpClient 와 같은 네트워크 관련 종속성을 제공합니다.
  • DatabaseModule : RoomDatabase 와 같은 데이터베이스 관련 종속성을 제공합니다.
  • UIModule : ViewModelPresenter 와 같은 UI 관련 종속성을 제공합니다.

구성 요소에 모듈 포함

모듈은 필요한 클래스에 종속성을 제공하기 위해 구성 요소에 포함됩니다. 다음은 이를 설정하는 방법입니다.

ApplicationComponent.java:

 @Singleton @Component(modules = {NetworkModule.class, DatabaseModule.class}) public interface ApplicationComponent { void inject(MyApplication application); }

이 예에서 ApplicationComponent 애플리케이션에 대한 종속성을 제공하기 위해 NetworkModuleDatabaseModule 포함합니다.

2. 범위

Dagger 2의 스코프는 종속성의 수명 주기를 정의하는 주석입니다. 이는 종속성의 단일 인스턴스가 지정된 스코프 내에서 생성되고 공유되도록 보장합니다. 이는 메모리를 효율적으로 관리하고 적절한 경우 종속성을 재사용하는 데 도움이 됩니다.

  • 싱글톤 범위 : 애플리케이션 수명 주기 전체에 걸쳐 종속성의 단일 인스턴스를 보장합니다.
  • 활동 범위 : 활동 수명 주기 내에서 종속성의 단일 인스턴스를 보장합니다.
  • 조각 범위 : 조각의 수명 주기 내에서 종속성의 단일 인스턴스를 보장합니다.

1. 싱글톤 범위

정의 : @Singleton 범위는 종속성의 단일 인스턴스가 생성되어 전체 애플리케이션 수명 주기 동안 공유되도록 보장합니다.

이 범위는 일반적으로 네트워크 클라이언트, 데이터베이스 인스턴스 또는 공유 기본 설정과 같이 전체 애플리케이션에서 공유해야 하는 종속성에 사용됩니다.

예:

 @Singleton @Component(modules = {NetworkModule.class, DatabaseModule.class}) public interface ApplicationComponent { void inject(MyApplication application); }

이 예제에서 @Singleton 주석은 NetworkModuleDatabaseModule 이 제공하는 RetrofitDatabase 인스턴스가 싱글톤이고 전체 애플리케이션에서 공유되도록 보장합니다.

2. 활동 범위

정의 : @ActivityScope (사용자 정의 범위)는 종속성의 단일 인스턴스가 활동 수명 주기 내에서 생성되고 공유되도록 합니다.

이 범위는 활동에 특화되어 활동이 다시 생성될 때마다 다시 생성되어야 하는 종속성(예: 프리젠터 또는 뷰 모델)에 유용합니다.

:

 @Scope @Retention(RetentionPolicy.RUNTIME) public @interface ActivityScope { } @ActivityScope @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); }

이 예제에서 @ActivityScope 주석은 ActivityModule 에서 제공하는 종속성이 활동의 수명 주기로 범위가 지정되도록 합니다.

3. 조각 범위

정의 : @FragmentScope (또 다른 사용자 정의 범위)는 종속성의 단일 인스턴스가 생성되어 프래그먼트의 수명 주기 내에서 공유되도록 합니다.

사용 사례: 이 범위는 프래그먼트에 특정한 종속성에 유용하며, 프래그먼트가 재생성될 때마다 다시 생성되어야 합니다. 여기에는 프래그먼트별 프리젠터나 뷰 모델이 포함됩니다.

:

 @Scope @Retention(RetentionPolicy.RUNTIME) public @interface FragmentScope { } @FragmentScope @Component(dependencies = ActivityComponent.class, modules = FragmentModule.class) public interface FragmentComponent { void inject(MyFragment myFragment); }

이 예제에서 @FragmentScope 주석은 FragmentModule 이 제공하는 종속성이 프래그먼트의 수명 주기로 범위가 지정되도록 합니다.

3. 구성 요소

구성 요소 종속성은 한 구성 요소가 다른 구성 요소에 종속되도록 하여 종속성을 재사용할 수 있도록 합니다. 구성 요소 종속성에는 두 가지 주요 유형이 있습니다.

  • 애플리케이션 구성 요소 : 전체 애플리케이션에 필요한 종속성을 제공합니다.
  • 활동 구성 요소 : 특정 활동 내에 필요한 종속성을 제공합니다.

1. 응용 프로그램 구성 요소

정의 : 애플리케이션 구성 요소는 전체 애플리케이션에서 필요한 종속성을 제공합니다. 일반적으로 @Singleton 으로 범위가 지정되어 종속성이 애플리케이션 전체에서 공유되도록 합니다.

이 구성 요소는 네트워크 클라이언트, 데이터베이스 인스턴스 또는 공유 기본 설정 등 전역적으로 사용할 수 있어야 하는 종속성에 사용됩니다.

:

 @Singleton @Component(modules = {NetworkModule.class, DatabaseModule.class}) public interface ApplicationComponent { void inject(MyApplication application); }

이 예제에서 ApplicationComponent 전체 애플리케이션에서 공유되는 RetrofitDatabase 인스턴스를 제공하는 역할을 합니다.

2. 활동 구성요소

정의 : Activity Component는 특정 활동 내에서 필요한 종속성을 제공합니다. 일반적으로 @ActivityScope 와 같은 사용자 지정 범위로 범위가 지정되어 활동이 재생성될 때마다 종속성이 재생성되도록 합니다.

이 구성 요소는 프리젠터나 뷰 모델과 같은 활동에 특정한 종속성에 사용됩니다.

:

 @ActivityScope @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); }

이 예에서 ActivityComponent ApplicationComponent 에 종속되며 MainActivity 에 대한 특정 종속성을 제공합니다.

4. 구성 요소 종속성

구성 요소 종속성은 한 구성 요소가 다른 구성 요소에 종속되도록 하여 종속성을 재사용할 수 있도록 합니다. 구성 요소 종속성에는 두 가지 주요 유형이 있습니다.

  • 하위 구성 요소 : 하위 구성 요소는 다른 구성 요소의 자식이며 부모의 종속성에 액세스할 수 있습니다.
  • 종속성 속성 : 이를 통해 구성 요소는 하위 구성 요소가 되지 않고도 다른 구성 요소에 종속될 수 있습니다.

1. 하위 구성 요소:

하위 구성 요소는 다른 구성 요소의 자식이며 부모의 종속성에 액세스할 수 있습니다. 하위 구성 요소는 부모 구성 요소 내에서 정의되며 해당 범위를 상속할 수 있습니다.

:

 @ActivityScope @Subcomponent(modules = ActivityModule.class) public interface ActivitySubcomponent { void inject(MainActivity mainActivity); }

이 예에서 ActivitySubcomponent 부모 구성 요소의 하위 구성 요소이며 해당 종속성에 액세스할 수 있습니다.

2. 종속성 속성

이를 통해 구성 요소는 하위 구성 요소가 되지 않고도 다른 구성 요소에 종속될 수 있습니다. 종속 구성 요소는 부모 구성 요소가 제공하는 종속성에 액세스할 수 있습니다.

:

 @ActivityScope @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); }

이 예에서 ActivityComponent ApplicationComponent 에 종속되며 해당 종속성에 액세스할 수 있습니다.

5. 런타임 바인딩

Dagger 2의 런타임 바인딩은 필요한 컨텍스트에 따라 런타임에 생성되고 관리되는 종속성 제공을 의미합니다.

  • 애플리케이션 컨텍스트 : 애플리케이션만큼 오랫동안 유지되어야 하는 종속성에 사용됩니다.
  • 활동 컨텍스트 : 활동만큼 오랫동안 유지되어야 하는 종속성에 사용됩니다.

1. 애플리케이션 컨텍스트

정의 : 애플리케이션 컨텍스트는 전체 애플리케이션의 수명 주기에 연결된 컨텍스트입니다. 애플리케이션 자체만큼 오래 지속되어야 하는 종속성에 사용됩니다.

전체 애플리케이션에서 공유되고 각 활동이나 조각에 대해 다시 만들 필요가 없는 종속성. 네트워크 클라이언트, 데이터베이스 인스턴스, 공유 선호도가 그 예입니다.

:

 @Module public class AppModule { private final Application application; public AppModule(Application application) { this.application = application; } @Provides @Singleton Application provideApplication() { return application; } @Provides @Singleton Context provideApplicationContext() { return application.getApplicationContext(); } }

이 예에서 AppModule 애플리케이션 컨텍스트를 싱글톤 종속성으로 제공합니다. provideApplicationContext 메서드는 제공된 컨텍스트가 애플리케이션의 수명 주기에 연결되도록 합니다.

2. 활동 컨텍스트

정의 : 활동 컨텍스트는 특정 활동의 수명 주기에 연결된 컨텍스트입니다. 활동 자체만큼 오래 지속되어야 하는 종속성에 사용됩니다.

활동에 특화되어 활동이 재생성될 때마다 재생성되어야 하는 종속성. 예를 들어 뷰 모델, 프리젠터, UI 관련 종속성이 있습니다.

:

 @Module public class ActivityModule { private final Activity activity; public ActivityModule(Activity activity) { this.activity = activity; } @Provides @ActivityScope Activity provideActivity() { return activity; } @Provides @ActivityScope Context provideActivityContext() { return activity; } }

이 예에서 ActivityModule 활동 컨텍스트를 범위가 지정된 종속성으로 제공합니다. provideActivityContext 메서드는 제공된 컨텍스트가 활동의 수명 주기에 연결되도록 합니다.

컴포넌트에서 런타임 바인딩 사용

이러한 런타임 바인딩을 사용하려면 구성 요소에 해당 모듈을 포함해야 합니다.

응용 프로그램 구성 요소 :

 @Singleton @Component(modules = {AppModule.class, NetworkModule.class}) public interface ApplicationComponent { void inject(MyApplication application); Context getApplicationContext(); }

활동 구성요소 :

 @ActivityScope @Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent { void inject(MainActivity mainActivity); Context getActivityContext(); }

컨텍스트 주입

구성 요소와 모듈을 설정한 후 필요에 따라 컨텍스트를 클래스에 주입할 수 있습니다.

활동의 예 :

 public class MainActivity extends AppCompatActivity { @Inject Context activityContext; @Inject Context applicationContext; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ApplicationComponent appComponent = ((MyApplication) getApplication()).getApplicationComponent(); ActivityComponent activityComponent = DaggerActivityComponent.builder() .applicationComponent(appComponent) .activityModule(new ActivityModule(this)) .build(); activityComponent.inject(this); // Use the injected contexts Log.d("MainActivity", "Activity Context: " + activityContext); Log.d("MainActivity", "Application Context: " + applicationContext); } }

이 예에서 MainActivity 종속성 주입을 통해 활동 컨텍스트와 애플리케이션 컨텍스트를 모두 수신합니다. 이를 통해 활동은 종속성의 특정 요구 사항에 따라 적절한 컨텍스트를 사용할 수 있습니다.

예: Android 애플리케이션에서 Dagger 2 사용

Dagger 2 설정

프로젝트에서 Dagger 2를 사용하려면 build.gradle 파일에 다음 종속성을 추가해야 합니다.

 dependencies { implementation 'com.google.dagger:dagger:2.x' annotationProcessor 'com.google.dagger:dagger-compiler:2.x' }

2.x Dagger 2의 최신 버전으로 교체합니다.

1단계: 모듈 정의

종속성을 제공하는 모듈을 만듭니다. 예를 들어, Retrofit 인스턴스를 제공하는 NetworkModule :

 @Module public class NetworkModule { @Provides @Singleton Retrofit provideRetrofit() { return new Retrofit.Builder() .baseUrl("https://api.example.com") .addConverterFactory(GsonConverterFactory.create()) .build(); } }

2단계: 구성 요소 정의

모듈과 종속성이 필요한 클래스를 연결하는 구성 요소를 만듭니다.

 @Singleton @Component(modules = {NetworkModule.class}) public interface ApplicationComponent { void inject(MyApplication application); }

3단계: 종속성 주입

구성 요소를 사용하여 클래스에 종속성을 주입합니다. 예를 들어, Application 클래스에서:

 public class MyApplication extends Application { private ApplicationComponent applicationComponent; @Override public void onCreate() { super.onCreate(); applicationComponent = DaggerApplicationComponent.builder() .networkModule(new NetworkModule()) .build(); applicationComponent.inject(this); } public ApplicationComponent getApplicationComponent() { return applicationComponent; } }

4단계: 주입된 종속성 사용

이제 클래스에서 주입된 종속성을 사용할 수 있습니다. 예를 들어, Activity 에서:

 public class MainActivity extends AppCompatActivity { @Inject Retrofit retrofit; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ((MyApplication) getApplication()).getApplicationComponent().inject(this); // Use the injected Retrofit instance // ... } }

결론

이 주제를 요약해 보겠습니다.

  • DI의 주요 목적은 결합을 느슨하게 하여 종속성을 관리하기 쉽게 만드는 것입니다.
  • DI를 사용하면 코드의 유연성을 높이고 테스트 프로세스를 간소화할 수 있습니다.
  • DI는 시나리오에 따라 다양한 구현이 필요한 복잡한 주제입니다.
  • 다양한 언어의 DI에는 작업 방식에 영향을 줄 수 있는 특성이 있습니다.
  • Dagger 2는 종속성을 만들고 제공하는 프로세스를 자동화하여 보일러플레이트 코드를 줄이고 유지 관리성을 향상시킵니다.
  • Dagger 2는 컴파일 시점 안전성을 제공하여 애플리케이션이 실행되기 전에 모든 종속성이 충족되는지 확인합니다.
  • Dagger 2는 컴파일 시점에 코드를 생성하므로 리플렉션 기반 솔루션과 관련된 성능 오버헤드를 방지합니다.