Android/이론 학습

[Android] 안드로이드의 DI (Dependency Injection)

노소래 2022. 2. 16. 07:59

[DI 개념과 필요한 이유]

한 클래스는 다른 클래스를 참조해야하는 일이 빈번하다. (특히 디자인패턴을 다루면서)

예를 들어 Computer 클래스를 실행시키려면 Computer 클래스 내에서 Cpu 클래스 객체를 참조해야한다고 해보자.

이 상황에서 Computer 클래스가 Cpu 클래스에 의존한다고 한다.

이 방법에는 당장 생각나는 두 가지가 있을 것이다.

  1. Cpu 객체를 클래스 내부에서 직접 생성하기
    Computer 클래스가 한 타입의 Cpu 객체만 사용해버린다. 즉 서브클래스를 쉽게 사용할 수 없다.
    그렇다면 Intel Cpu, AMD Cpu 각각을 사용하는 Computer 클래스를 각각 만들어야 할 것이다.
    Computer 에서 Cpu 객체를 직접 생성하면 테스트도 어렵게 함
  2. 생성자나 메소드 파라미터로 Cpu 객체를 받아오기
    Computer 의 재사용성이 증가한다.
    왜냐하면 Cpu 의 여러 구현체를 Computer 에 넘겨줄 수 있기 때문이다.

여기서 2번이 이번에 말하려는 DI 이다.

즉 안에서 생성해서 의존하는 것이 아니라 밖에서 '주입' 인 것이다.

 

[위에서 살펴본 DI 의 이점]

  • 코드의 재사용성 및 decoupling of dependencies
  • 리팽토링 용이성
  • 테트트 용이성
  • 확장성

 

[수작업으로 직접 DI 하는 주요한 두 가지 방식]

  • Constructor Injection
    말 그대로 생성자로 원하는 객체를 주입하는 방식이다.
  • Field Injection (or Setter Injection)
    액티비티, 프래그먼트 같은 특정한 안드로이드 프레임워크 클래스들은 시스템에의해 초기화 된다.
    예를 들면 앞서말한 클래스에 lateinit var 로 멤버변수로 두고, 초기화 된 후에  밖에서 접근해서 초기화해주는 방식이다.

하지만 프로젝트 규모가 커지면 많은 보일러 플레이트코드가 요구된다는 문제가 있다. 

예를들면 Constructor Injection 으로 의존성을 주입하는 방식에서 서로 다른 두 가지 곳에 객체를 생성해야한다면 코드가 중복된다.

 

그래서 그러한 문제를 해결해주는 DI 라이브러리가 있다.

 

[대표 DI 라이브러리]

  • Dagger
    컴파일 시에 의존성을 연결하는 코드를 생성하는 Static Solution. 따라서 compile-time dependencies
    compile-time correctness, runtime performance, scalability 측면에서 이득
    의존성들의 그래프를 만들고 관리해줘서 DI 를 돕는다.
    구글이 관리함
  • Hilt
    힐트는 제트팩에서 권장하는 DI 라이브러리이다.
    힐트는 Dagger 위에 구축되어있다. (compile-time correctness, runtime performance, scalability 측면에서 이득)
    따라서 힐트 역시 compile-time dependencies, runtime performance, scalability 라는 이점이 있다.
    프로젝트에 있는 모든 안드로이드 클래스에 컨테이너를 제공하고 그들의 라이프사이클을 자동으로 관리해준다.
  • Koin (DI 를 하는 대체제는 Service Locator)
    내가 최근 프로젝트에 적용했던 DI 라이브러리이다.
    장점은 러닝커브가 낮고 모듈에서 의존성을 가진 객체와 컴포넌트를 한 눈에 알아 볼 수 있다는 것이다.
    하지만 단점은 DI 관련 에러를 런타임 중에 알게된다는 점과 런타임 퍼포먼스가 있다는 이슈가있다.
    또한 아래 사진은 Hilt 를 사용한 프로젝트 일부 사진인데 왼쪽 아이콘을 누르면 의존성이 어디로부터 주입되었는지 알아볼 수 있다. 하지만 Koin 은 이런 것이 없다는 것도 단점으로 다가왔다.

 

 

 

[수작업 의존성 주입이 아닌 라이브러리를 사용했을 때 장점]

  • DI 라이브러리는 의존성 주입에 필요한 코드를 자동으로 생성해주니까 보일러플레이트 코드를 줄임
     -> (가독성 up , 사소한 버그 down?)
  • 메모리 관리를 위해 scope 와 lifecycle 을 직접 관리할 필요가 없음



Koin 은 위의 의존성 개념과 이점을 알고 아래 공식문서만 보면 10분도 안돼서 익힐 수 있었다.

 

Kotlin | Koin

This tutorial lets you write a Kotlin application and use Koin inject and retrieve your components.

insert-koin.io

 

이후 상대적으로 러닝커브가 높다는 Hilt 에 관해 공부하고 사용해보면서 글을 작성하여 링크를 추가하겠다.

(여기에 링크가 추가될 것)