" Park 기술 블로그 "
# 카테고리
# 도구
[Android 안드로이드] Hilt의 @Binds Annotation-Processor 만들기 - HiltBinder
2022-06-16 00:50:00

Android 프로젝트에서 DI를 사용한다고 하면 많은 사람들이 Hilt를 사용하는 것 같다. (가끔 Dagger만 그대로 사용할 수도..?)

필자 또한 그 사람 중에 하나였다. Dagger나 Hilt를 올바르게 사용해본 사람이라면 대부분 겪는 귀찮음불편함이 있을 것이라고 생각한다. 바로 @Binds를 사용하는 부분이다. 간단하게 이야기 하자면 Dagger Hilt에서 직접 만든 Class를 의존성 주입 받기 위해서 아래와 같이 코드를 작성한다.

class TestUseCaseImpl @Inject constructor(
    private val testRepository: Repository
) : TestUseCase {
    ... 
}

그리고 위와 같은 TestUseCase를 사용하고 싶은 부분에서는 아래와 같이 코드를 작성한다.

@HiltViewModel
class TestViewModel @Inject constructor(
    private val testUseCase: TestUseCase
) : ViewModel() {
    ...
}

하지만 이렇게만 작성하면 빌드 실패한다. 왜냐하면 TestUseCaseImplTestUseCase로 형변환 해주는 부분이 없기 때문에 ViewModel에서 TestUseCase를 갖고 올 수 없다는 에러와 함께 빌드를 실패한다. 다음과 같이 코드를 작성하면 TestUseCaseImpl에서 TestUseCase로 형변환이 되고 의존성 주입을 받을 수 있다.

@Module
@InstallIn(SingletonComponent::class)
abstract class UseCaseModule {
 
    @Binds
    abstract fun bindTestUseCase(useCase: TestUseCaseImpl): TestUseCase
}

이제 빌드하면 ViewModel에서 정상적으로 TestUseCase를 받아서 사용할 수 있게된다. 하지만 여기서 생각해볼 수 있는 것은 위의 @Binds 함수다. 위와 같이 구현체 클래스를 interface로 변환해서 사용하는 형태가 프로젝트 안에 많이 존재할 텐데, 이러한 경우 전부 @Binds 함수를 만들어야 한다는 것이다. 그러면 이제 RepositoryModule, MapperModule 등등 늘어날 것이다. 이러한 문제점을 해소하고 싶었고, 평소 관심 있었으나 쉽게 도전하지 않았던 annotation-processor 만드는 법에 대해서 공부도 할 겸해서, annotation-processor를 통해 @Binds 함수들을 전부 알아서 만들도록 구현했다.

 

먼저 사용법은 간단하다. 아래의 코드와 같이 @HiltBinds를 붙이기만 하면 알아서 형변환 해주는 @Binds를 annotation-processor가 만들어준다.

interface TestUseCase {
 
    ...
 
}
 
 
@HiltBinds
class TestUseCaseImpl @Inject constructor(
    private val testRepository: Repository
) : TestUseCase {
    ...
}
 
// 아래의 코드를 Annotation-Processor가 만들어 줌.
@Module
@InstallIn(SingletonComponent.class)
abstract class TestUseCaseImpl_BindsModule {
  @Binds
  public abstract TestUseCase bindTestUseCaseImpl(TestUseCaseImpl target);
}

사실 KAPT보다 KSP가 몇 배 더 빠르고 좋다고해서 바로 KSP를 통해 kotlin 코드를 만들도록 구현해볼까 하다가, 공부하는 단계다 보니 java 코드를 만드는 방법 먼저 공부해보고 그다음에 KSP도 공부해보려고 한다. 대충 중요한 재료(?)는 다음과 같다. JavaPoet, AutoService, AbstractProcessor, hilt-core 이렇게가 중요한 재료이자 키워드라고 보면 좋을 것 같다.

그리고 솔직히 하나의 클래스 안에 다 작성하는 방법이 있긴한데, 성격상 그렇게 하긴 싫었다. 그래서 핵심 core 부분, module을 만들어 내는 generator 부분, 각종 Ext 부분 이렇게 구성했다.

 

지금은 Hilt에 대한 Annotation-Processor를 만들어 봤는데, 나중에는 활용할 수 있는 부분이 많을 것 같다.

(단, 너무 많이 사용하면 빌드 타임을 늘리는 단점이 있으니 적당히 활용해야 한다.)

요즘 ServerDriven을 통해 UI를 그려내는 프레임워크 개발 작업 중에 있는데, 이러한 부분에서도 활용할 수 있을 것 같다. Retrofit에 ConverterFactory를 잘 활용하면 API 응답값 json에서 특정 String의 값에 따라서 배열 내의 Object들을 각기 다른 Class로 파싱할 수 있다. 이러한 Class 타입으로 파싱하기 위해서 미리 파싱될 VO Class를 ConverterFactory에 특정 String값과 1대1 매칭으로 설정해주어야 하는데, 이러한 부분에서도 annotation-processor를 활용할 수도 있을 것 같다. VO Class를 직접 ConverterFactory에 설정하는 것이 아니라, annotation-processor와 Dagger Hilt에서 지원하는 map-multibinding을 잘 활용하면 VO Class를 작성하고 특정 어노테이션을 붙이면 알아서 ConverterFactory에 설정될 수 있도록 구현할 수 있을 것 같다.

 

Example 프로젝트와 함께 자세한 구현 프로젝트는 여기에서 확인할 수 있다.

 

 

0
# 댓글 + 새 댓글 작성
# 새 댓글 작성
댓글 암호는 댓글 삭제 시 필요합니다.
# 님에게 답변 작성
대댓글 암호는 댓글 삭제 시 필요합니다.
# 님의 댓글 삭제
댓글 작성 시 입력했던 암호를 입력해주세요.
아직 댓글이 없습니다