안드로이드 애플리케이션을 개발하다 보면 프로젝트가 완전히 혼란에 빠지는 것을 막기 위해 클래스를 더 체계적으로 정리해야 할 필요성을 느껴본 적이 있을 것입니다. 바로 이럴 때 의존성 주입(Dependency Injection) 이라는 디자인 패턴이 유용하게 사용됩니다. 처음에는 다소 복잡해 보일 수 있지만, 본질적으로는 각 클래스가 필요한 객체를 직접 생성하지 않고도 필요한 객체를 제공받을 수 있도록 하는 방식입니다.
Hilt는 Dagger의 복잡성에서 우리를 구해내기 위해 등장했으며, 의존성 삽입을 표준화하는 최상위 계층을 제공합니다 . Jetpack과의 통합을 통해 Hilt는 컨테이너와 생명주기를 자동으로 관리하므로, 마치 이사하듯 객체를 이리저리 옮기는 대신 비즈니스 로직에 집중할 수 있습니다.
초기 환경 구성
Hilt를 시작하려면 먼저 Gradle 환경을 준비해야 합니다. 프로젝트 수준 설정 파일에 hilt-android-gradle-plugin을 추가하는 것이 필수적입니다 . 그런 다음 애플리케이션의 모듈 파일에서 플러그인을 적용하고 해당 종속성을 추가해야 하며, 컴파일러(kapt 또는 KSP 사용 여부와 관계없이)가 올바르게 구성되었는지 확인해야 합니다.
한 가지 중요한 점은 모든 것이 원활하게 작동하려면 프로젝트가 Java 17 (이전 버전의 경우 Java 8)을 사용하도록 구성되어야 한다는 것입니다. Hilt와 Jetpack Compose가 완벽하게 연동되려면 Java 17이 필수 조건이기 때문입니다. 플러그인을 활성화했을 때 프로그램이 컴파일되지 않는다면 Kotlin과 KSP 버전이 정확히 일치하는지 다시 한번 확인하십시오. 아주 작은 버전 차이로도 전체 컴파일 과정이 중단될 수 있습니다.
힐트의 핵심: 응용 수업
이 라이브러리를 활용하려는 모든 앱은 사용자 정의 Application 클래스를 가지고 있어야 합니다. 단순히 클래스를 생성하는 것만으로는 충분하지 않으며, `@HiltAndroidApp` 어노테이션을 추가해야 합니다 . 이 어노테이션은 Hilt의 코드 생성 기능을 활성화하여 애플리케이션의 루트 컨테이너를 생성합니다.
이 최상위 구성 요소는 앱의 전체 수명 주기와 연결되고 다른 모든 구성 요소가 이 수준에서 정의된 종속성에 접근할 수 있는 기반이 되기 때문에 매우 중요합니다. 물론, AndroidManifest.xml 파일에 name 속성을 사용하여 이 클래스를 등록하는 것을 잊지 마세요. 그렇지 않으면 Hilt는 전혀 인식되지 않습니다.
안드로이드 컴포넌트에 종속성 주입하기
애플리케이션 개발이 완료되면 액티비티, 프래그먼트, 뷰 또는 서비스에 종속성을 주입할 수 있습니다. 이를 위해 `@AndroidEntryPoint` 어노테이션을 사용합니다 . 이 어노테이션은 Hilt에게 해당 클래스가 진입점이며, 이를 위한 특정 컴포넌트를 생성해야 함을 알려줍니다.
액티비티 내에서 특정 의존성을 얻고자 할 때, `@Inject` 어노테이션을 통해 필드 주입을 사용합니다 . 이때 중요한 점은 이러한 필드는 `private`으로 선언할 수 없다는 것 입니다. `private` 접근 제한자를 사용하면 컴파일러가 즉시 오류를 발생시킵니다. Jetpack Compose의 경우, 루트 액티비티에 어노테이션을 추가하는 것만으로 컴포저블 함수 내에서 ViewModel에 직접 접근할 수 있습니다.
연계성을 정의하기 위한 전략
Hilt는 우리가 요청하는 객체를 생성하는 방법을 알아야 합니다. 가장 직접적인 방법은 생성자 주입으로 , 클래스 생성자 바로 앞에 `@Inject` 어노테이션을 붙이는 것입니다. 이렇게 하면 Hilt가 매개변수를 분석하여 각 의존성을 어떻게 제공해야 하는지 결정할 수 있습니다.
하지만 Retrofit이나 OkHttpClient 같은 외부 라이브러리를 사용할 때처럼 클래스를 완전히 제어할 수 없는 경우가 있습니다. 바로 이럴 때 Hilt Modules가 유용합니다 . 모듈은 `@Module` 어노테이션이 붙은 클래스이며, 해당 종속성을 어떤 컴포넌트에 포함시킬지 지정하기 위해 `@InstallIn` 어노테이션을 반드시 포함해야 합니다.
- @Provides 사용법: 이 방식은 클래스가 타사 클래스이거나 복잡한 생성 패턴이 필요한 경우에 사용됩니다. 여기서는 필요한 인스턴스를 반환하는 함수를 정의합니다.
- @Binds를 사용하는 경우: 이는 인터페이스를 구체적인 구현에 연결하려는 경우에 가장 적합한 옵션입니다. 이 함수는 Hilt에게 "누군가 이 인터페이스를 요청하면 이 구현을 제공하라"고 지시하는 추상 함수로 정의됩니다.
범위 및 수명주기 관리
기본적으로 Hilt는 종속성이 요청될 때마다 새 인스턴스를 생성합니다. 하지만 때로는 앱이나 화면의 수명 주기 동안 객체가 동일하게 유지되어야 할 때가 있습니다. 이를 위해 컴포넌트 스코프를 사용합니다.
클래스에 `@Singleton` 어노테이션을 붙이고 싱글턴 컴포넌트에 구현하면 전역적으로 하나의 인스턴스만 존재하게 됩니다. 하지만 객체가 액티비티가 실행되는 동안에만 유지되도록 하려면 액티비티 컴포넌트 내에서 `@ ActivityScoped` 어노테이션을 사용합니다 . 객체를 메모리에 너무 오래 유지하면 애플리케이션 성능에 부정적인 영향을 미칠 수 있으므로 스코프를 과도하게 사용하지 않는 것이 중요합니다.
특별한 경우 및 한정 사항
때로는 동일한 인터페이스의 서로 다른 두 가지 구현체를 주입해야 할 때가 있습니다. 컴파일러에 과부하를 주지 않기 위해 한정자를 사용합니다 . 한정자는 특정 바인딩에 레이블을 지정하는 데 사용되는 사용자 지정 어노테이션으로, 예를 들어 인증용 HTTP 클라이언트와 일반 요청용 클라이언트를 구분할 수 있게 해줍니다.
또한 Hilt는 @ApplicationContext 및 @ActivityContext와 같은 미리 정의된 한정자를 통해 작업을 간소화하여 모듈을 수동으로 구성하지 않고도 Android 컨텍스트를 얻을 수 있도록 합니다. 마지막으로 Hilt에서 기본적으로 지원하지 않는 클래스(예: ContentProvider)의 경우 @EntryPoint 어노테이션을 사용하여 Hilt 그래프에서 종속성을 추출하는 수동 브리지를 만들 수 있습니다. 더 많은 사용자가 이 정보를 알 수 있도록 공유해 주세요.