'android'에 해당되는 글 73건

dagger를 통한 의존성 주입 안드로이드 실전

지난번에는

 

ServiceLocator를 통해 의존성을 주입하는 방법과

 

직접 의존성을 주입하는 방법에 대해서 알아보았다

 

이제 dagger를 통한 의존성을 주입하는 방법에 대해서 학습해보자

 

 

* dagger는 우리가 이전 포스팅에서 작성했던 AppContainer 코드를 자동으로 생성해준다
* 팩토리 클래스를 생성해준다
* 설정에 따라 의존성을 재활용하거나 새로운 인스턴스를 만들어준다
* LoginContainer와 같이 특정 기능을 위한 콘테이너를 생성한다 더이상 필요 없을때 메모리에서 해지하므로 앱의 성능향상이 된다

dagger는 컴파일타임에 마치 직접 만든 코드처럼 어노테이션에 의해 코드를 생성한다


내부적으로 dagger는 그래프를 만드는데(객체를 어떻게 공급하고 만드는지에 대한 일종의 지도라고 생각하자)

 

일종의 이것은 인스턴스를 어떻게 공급할지에 대한 참조를 가지고 있다

 

이 그래프에 존재하는 모든 클래스에 대해서 dagger는 팩토리 클래스를 이용해서 인스턴스를 가져온다

 

 

 

UserRepository의 consturctor 앞에 @Inject 어노테이션을 붙였다

 

이 행위를 통해 우리는 dagger에게

1. 생성자 어노테이션에 쓰인 @Inject 로 UserRepository를 어떻게 생성할수 있는지 알려주었다
2. 어떤 의존성을 가지고 있는지 알려주었다 (UserLocalDataSource와 UserRemoteDataSource)

 

dagger는 이제 UserRepository를 만들수 있다

 

하지만 UserRepository를 생성할때 필요로 하는 의존성을 어떻게 만들어야 하는지에 대해서는 알려주지 않았으므로

 

이것은 알지 못한다


이제 다른 두 클래스(UserLocalDataSource와 UserRemoteDataSource)에도

 

생성자 어노테이션 @Inject를 붙여주면 dagger는 이 것들도 어떻게 생성하는지 알 수 있게 된다

 

 

 

이제 dagger는 완벽하게 UserRepository를 만드는 방법을 알게되었다

 

 


dagger가 container를 생성할수 있도록


인터페이스를 만들고 거기에 @Component어노테이션을 붙여보자


@Component 인터페이스 안에는 함수들을 정의하는데 

 

이 함수들은 필요로 하는 클래스의 인스턴스를 리턴하는것이다

@Component는 dagger에게 이 의존성과 함께 컨테이너를 만들라고 알려준다

 

 

우리는 방금 인터페이스를 만들었는데

 

실제 AppContainer 역할을 하는 구현체는 어디에 있는것일까?

 

 

우리가 프로젝트를 빌드하면 dagger는 방금만들었던 ApplicationGraph 인터페이스의 구현체를 자동으로 생성하는데

 

그 이름은 DaggerApplicationGraph 라는 이름을 갖게된다. 앞에 Dagger라는 접두사가 포함된다


이렇게 생성된 그래프는 UserRepository, UserLocalDatasource,  UserRemoteDataSource 의 관계를 알고 있다

 


그래서 이렇게 함으로서 UserRepository의인스턴스를 얻게 된다

 

 

AppContainer를 직접 만들때는 우리가 직접 팩토리 클래스도 생성해주어야했고

 

의존성을 일일이 직접 코딩해주어야했는데 그럴필요가 없어졌다

 

 


이렇게 만들어진 구현체는 UserRepository의 인스턴스를 우리가 요청할때마다 매번 새롭게 생성한다

 

하지만 매번 생성할필요 없이 같은 인스턴스가 필요할때가 있다

1. ViewModel이나 LoginUserData 처럼 어떤 데이터가 이미 저장되어있어서 사용했던 인스턴스를 다시 받아와야 하거나
2. 객체 생성이 비싼경우(생성에 메모리나 시간을 많이 잡아먹는경우)


위의 예제에서도 UserRepository 의 경우는 매번 생성할필요 없이 같은 인스턴스를 받는것이 더 좋다

dagger에게 매번 다른 인스턴스를 생성하지 말고 기존에 생성한 같은 인스턴스를 돌려달라라고 알려줄수 있다


바로 이때 쓰는것이 scope 어노테이션이다


UserRepository의 유일한 인스턴스를 계속해서 받고 싶다면 ApplicationGraph에게 이걸 알려주면된다

같은 scope 어노테이션을 @Component와 UserRepsoitory에 선언하면된다


이때 이미 만들어져있는 @Singleton이라는 어노테이션이 있으니 이것을 사용하면된다

 

 


이렇게 함으로써 항상 같은 UserRepository 인스턴스를 받을수 있다

 


@Singleton 어노테이션을 사용해도 되고


직접 scope 어노테이션을 만들어서 사용해도 된다

 

 

MyCustomScope라는 어노테이션을 만들었고 

 

이 어노테이션을 @Singleton 어노테이션이 있던 자리에 붙여주었다

 

이 경우도 applicationGraph.repository()를 했을때 같은 UserRepository의 인스턴스를 가져온다

 


@Singleton 이라는 이름을 가진 어노테이션은 단지 이름만 Singleton일뿐 어떤한 특정을 가지고 있지는 않다

 

@Singleton이든 @MyCustomScope든 이름만 다를뿐 역할은 동일하며

다만 Component와 원하는 클래스의 scope를 같은것을 사용함으로서 같은 생명주기를 가지고 있다고 알려주는 것이다

 

 

안드로이드 프로젝트에서 dagger를 사용해보자

 

간단한 gradle 설정으로 dagger를 이용할수 있다

 

이 예제는 코틀린의 예제이며 kapt가 필요하다

 

 

아까전의 예제와 같은 예제를 이용해볼텐데

application 클래스에서 dagger graph를 생성할것인데 여기에 생성하면 앱이 동작하는 동안에 메모리에 상주되기 때문이다

이름은 AppliationGraph라고 해도 상관없고 ApplicationComponent라고 해도 상관없다

 

 

 

안드로이드프로젝트에서 dagger를 사용할때 참고해야할것은

 

액티비티나 프래그먼트 클래스는 우리가 직접 인스턴스를 생성하는것이 아니라


안드로이드 시스템이 생성하기 때문에 dagger가 직접 액티비티나 프래그먼트를 생성해줄수 없다


그렇기 때문에 우리는 클래스의 생성자에 @Inject를 써서 생성자 주입방법을 통해서 클래스를 생성할수 없고

 

필드 인젝션을 사용해야한다

 

 

 

 

 

 


여기서 외부로 노출된 inject 함수는 

LoginActivity가 인젝션을 요구한다고 알려준다

만약 만드는 앱의 LoginActivity와 RegistrationActivity가 모두 인젝션을 필요로 한다면

 

각각의 액티비티를 인자로 받는 함수를 만들면된다

 

 



하지만 이 두개의 액티비티를 포함하는 어떤 제네릭을 만들어서 사용해서는 안된다(dagger는 이것을 이해하지 못한다)


액티비티에 의존성 주입을 하기 위해서


application 클래스에 선언한 appComponent 를 가져와서 inject() 메소드를 호출하면된다


액티비티를 사용한다면 액티비티의 onCreate() 메소드에서 super.onCreate()가 호출되기 전에 inject를 하자


super.onCreate()는 액티비티가 프래그먼트를 복원하는 구간인데

 

이것이 실행되기 전에 원하는 값을 주입받아야 하기때문이다

만약 프래그먼트안에서 inject를 해야한다면 onAttach() 메소드에서 실행하면된다


마찬가지로 super.onAttach() 전에 호출하자

 

 



방금 예제의 경우에는 UserRemoteDataSource에서 retrofit이라는 네트워크라이브러리를 사용했다


즉 UserRemoteDataSource는 LoginRetrofitService에 의존적이라는 것이다

 

그렇다면 여기서도 마찬가지로 LoginRetrofitService라는 인스턴스를 만드는 방법을 dagger에게 알려줘야한다


그런데 LoginRetrofitService라는 클래스는 우리가 여지껏 인스턴스를 만드는 방법과 다르다


Retrofit.Builder()를 통해서 인스턴스를 만들기 때문이다


생성자에 @Inject 어노테이션을 사용하는것 말고도 dagger에게 어떻게 인스턴스를 생성할지 알려주는 방법이 있다


바로 @Provides 어노테이션이다

 

 

@Provides 라는 어노테이션을 갖고 있는

 

메소드에서 해당 인스턴스를 어떻게 만들면되는지를 알려주면된다

 

관례상 메소드 이름은 provide라는 접두어를 붙여서 짓는다

 

중요한건 리턴타입이다

 

 

 

이러한 방식으로 OkHttpClient나 Gson 같은 것들도 생성자 주입방식을 사용하지 않고도 의존성을 공급할수 있게 된다

 

방금 위의 예제에서는 provideLoginRetrofitService() 메소드가 아무런 파라메터를 가지고 있지 않지만

OkHttpClient를 파라메터로 넘겨줄수도 있을것이다

 

 

물론 이렇게 하는 경우라면 provideLoginRetrofitService()가 의존적인 OkHttpClient를 주입할수 있는 방법도

dagger에게 @Provides 어노테이션을 사용해서 알려주면된다

 

우리가 방금 모듈을 만들긴 했지만 이 모듈은 지금 그 어느것에도 연결이 되어있지 않다

 

ApplicationComponent가 NetworkModule을 사용할수 있도록 dagger에게 알려주어야한다

 

 

이렇게 해서 ApplicationComponent는 NetworkModule을 알게 되었다

 

 


여기까지 완성한 것으로 이런 전체적인 흐름이 완성된다

 

 

 

ApplicationComponent 가 알고 있는 모든 클래스들은 팩토리패턴 형태의 메소드를 통해 인스턴스가 생성된다

UserRepository의 경우는 필요할때마다 매번 생성하지 않아도 되니깐 

 

@Singleton 어노테이션을 붙여서 동일한 인스턴스를 반복해서 사용할수 있다 

물론 LoginRetrofitService도 마찬가지이다.

 

 

| 1 | 2 | 3 | 4 | ··· | 73 |