이동식 저장소

[Hilt] 1. 의존성 주입이란? 본문

Primary/Android

[Hilt] 1. 의존성 주입이란?

해스끼 2021. 4. 29. 10:37

이 글은 Google의 (Dependency injection in Android | Android Developers) 공식 문서를 참고하여 작성되었습니다.


의존성 주입(Dependency Injection, DI)는 프로그래밍에서 널리 사용되는 테크닉이다. DI를 적용하면 더 좋은 아키텍쳐를 만드는 데 도움이 된다. 좋은 아키텍쳐란 다음과 같다.

  • 코드의 재사용성이 높음
  • 리팩토링이 쉬움
  • 코드를 테스트하기 쉬움

그래서 DI가 뭡니까?

객체 지향적 설계에서 하나의 클래스가 다른 클래스를 참조하는 일은 흔하다. 예를 들어 Car 클래스의 정의에는 Engine 클래스가 포함될 수 있다. 이러한 필요 관계를 의존성이라고 말한다. 이 경우에는 CarEngine에 의존한다고 말한다.

클래스가 의존하는 객체를 얻는 방법은 세 가지가 있다.

  1. 클래스가 직접 객체를 만든다. 위의 예시로 따지자면 Car 클래스의 생성자에서 자체적으로 Engine 객체를 만드는 것이다.
  2. 어딘가에서 가져온다. Android에서 Context.getSystemService()로 가져오는 방식 등이 해당된다.
  3. 매개변수로 받는다. 위의 예시로 따지자면 EngineCar 클래스의 생성자 매개변수로 넘기거나, set 메소드를 이용하여 객체를 얻을 수 있다.

3번 방법이 바로 DI이다. 외부로부터 의존성(dependency) 있는 객체를 제공받는(injection) 개념이다.

1번 방법을 코드로 구현해 보면 다음과 같다.

class Car {
    private val engine = Engine()

    fun start() {
        engine.start()
    }
}

fun main(args: Array) {
    val car = Car()
    car.start()
}

Car 클래스에서 Engine 객체를 자체적으로 만들고 있다. 이런 방법은 문제가 있을 수 있다.

  • CarEngine이 강하게 결합되었다. Car는 오직 Engine 타입만을 사용할 수 있으며, ElectricEngine이나 GasEngine을 사용할 수 없다. 다른 엔진을 사용하고 싶다면 해당 엔진을 사용하는 클래스를 새로 정의해야 한다. 아니면 Car를 전부 리팩토링하거나.
  • Car를 테스트하기 어렵다. CarEngine에 강하게 결합되었기 때문에 Car를 테스트하려면 Engine까지 함께 테스트해야 하는 문제가 생긴다. 이게 무슨 뜻인지 나중에 알게 될 것이다.

DI를 적용한 구현은 다음과 같다.

class Car(private val engine: Engine) {
    fun start() {
        engine.start()
    }
}

fun main(args: Array) {
    val engine = Engine()
    val car = Car(engine)
    car.start()
}

Car의 생성자 매개변수로 Engine을 받고 있다. 이렇게 하면 뭐가 좋을까?

  • Car의 재사용성이 높아진다. 이제 CarEngine의 다른 구현체(ElectricEngine, GasEngine, DieselEngine 등)를 전달하여 사용할 수 있다. 인터페이스만 잘 구현해 놓으면 되는 것이다!
  • Car를 테스트하기 쉬워진다. 테스트용 Engine을 여러 개 구현하여 다양한 상황에서 Car를 테스트할 수 있다.

우와!

위의 예시에서는 Engine 객체를 코드로 직접 생성하여 넘겨주었다. 당장은 괜찮지만, 프로젝트가 커지면 Engine 객체를 만들기가 어려워질 수도 있다. Engine을 만들기 위해 Piston, Cylinder 등의 부품이 필요해질 수 있기 때문이다. 또는 Car를 만들기 위해 필요한 의존성이 많아질 수도 있다. 이러면 boilerplate 코드가 너무 길어지고, 의존성을 수동으로 관리하다 보면 실수할 수도 있다.

당연히 이런 문제점을 누군가가 이미 해결해 놓았다. 자동으로 의존성을 주입해 주는 라이브러리가 개발되어 널리 사용되고 있다. 라이브러리를 크게 두 가지로 나누면

  • 실행 시간에 의존성을 연결하는 동적 라이브러리와
  • 컴파일 시간에 의존성을 체크하는 정적 라이브러리

가 존재한다. 정적 라이브러리 중 유명한 것으로 Google의 Dagger가 있다. 안드로이드에서는 Dagger 기반의 Hilt를 사용할 수 있다.

Hilt

Hilt는 Android에서 DI를 적용할 때 사용할 수 있는 Jetpack 라이브러리이다. Hilt는 Dagger를 기반으로 개발되어, 타입 정확성, 런타임 성능, 확장성과 Android Studio 지원 등의 이점을 누릴 수 있다.

다음 글에서 Hilt를 직접 사용해 보자.

Comments