의존성 주입

MSU·2024년 6월 26일

Android

목록 보기
9/36

Dependency Injection (DI)

객체 인스턴스의 생성을 직접하지 않고 (다른 곳에서 만들어져서) 필요한 곳에 전달되도록 코드를 조직화하는 기법

  • 객체 인스턴스를 사용하는 곳을 클라이언트(client)라고 부름
  • 의존성(a dependency) : 사용되는 객체 인스턴스
  • 클라이언트가 아닌 잘 조직화 된 곳에서 일관성 있는 형태로 객체 생성 방법을 정의함

Injector

의존성을 클라이언트에게 제공하는 역할

의존성을 클라이언트 안에서 직접 생성하지 않고 밖에서 주입받는 이유 중 가장 큰 이유는 재사용성 때문

예를 들어, 만약 아래와 같이 의존성을 클라이언트 안에서 직접 생성하는 경우라면

class Repository{
    private val dataSource: DataSource = DataSourceA()
}

나중에 DataSourceA DataSourceBDataSourceC로 변경하고 싶을 때 클라이언트 Repository의 코드도 수정을 해줘야 한다.

하지만, 아래와 같이 의존성을 밖에서 주입받도록 변경한다면

class Repository(private val dataSource: DataSource){

}

DataSourceA가 변경되거나 삭제되어도 클라이언트 Repository 코드는 수정할 필요가 없어진다.

클라이언트는 interface로만 객체를 알고있으면 되므로, 보다 나은 설계를 가능하게 한다.

더욱 추상화, 멀티모듈에서 모듈별 의존성을 떼어내는데 유용

재사용 뿐만 아니라 테스트도 쉬워진다.

객체의 생성 방법을 설정에 따라 다르게 할 수 있다.
(프로덕션을 위한 구현을 테스트용으로 간단하게 전환 가능)

의존성 주입 프레임워크 사용 이유

  • 의존성 주입 과정에서 많은 양의 보일러 플레이트 코드가 필요함
  • Injector 클래스를 유지보수 하는 것은 프로젝트가 클 수록 힘들어짐
  • 앱 빌드의 설정에 따라 다른 종류의 Injector를 구현하는 것이 상당히 많은 양의 코드 중복을 만들게 됨
  • DI 프레임워크가 아니면 Injector 클래스가 분리가 어렵기 때문에 역설적으로 확장을 어렵게 만들 수 있다.
  • DI 프레임워크들은 멀티 모듈을 쉽게 해준다.
  • DI 프레임워크는 보통 의존성 정의를 위한 여러 단계의 계층을 제공해주기 때문에 의존성 정의를 쉽게 유지보수하고 더 작은 단위로 쪼갤 수 있게 해준다.

DI 프레임워크 종류

ServiceLocator 패턴

  • 전역적으로 관리해야 할 클래스들이 아주 복잡하지 않다면
val container = Container(context)
container
  .bind(Foo1::class, FooImpl1())
  .bind(Foo2::class, FooImpl2())
  .bind(Foo3::class, FooImpl3())

// in client
val foo = container.get(Foo1::class)

// in another place
val childContainer = Container(context.parent = container)
childContainer.bind(Bar::class, BarImpl())

val foo2 = childContainer.get(Foo2::class)
val bar = childContainer.get(Bar::class)

Dagger

JSR-300 표준 프레임워크
안드로이드 전용은 아니므로 미지원 기능은 직접 구현해줘야할 필요가 있음(액티비티나 뷰모델 구성 등)
대거 안드로이드라는 라이브러리가 있긴 함

Hilt

안드로이드를 위한 기능들이 다 갖춰져 있음

Anvil

Hilt와 유사함

profile
안드로이드공부

0개의 댓글