Android, 의존성 주입, DI(Dependency Injection)

이도현·2023년 8월 14일
0

Android 공부

목록 보기
22/30

0. 개요

2023 08 14 08시 Amit Shekhar는 말했다. "Android Developer, please understand Dependency Injection really well before you jump into Dagger/Dagger-Hilt/Koin" DI라는 용어는 이전에도 많이 언급되었지만 제대로 공부하는게 좋을 거 같다.

1.역사

  • 의존성 주입은 객체 지향의 프로그래밍에서 중요한 디자인 패턴 중 하나로, 객체가 자신이 사용하는 의존성(다른 객체나 서비스)을 직접 생성하거나 조회하지 않고 외부에서 주입받는 기법

  • 1990년대 후반: 마틴 파울러와 그의 동로들에 의해 정립되기 시작, 의존성 주입의 원리는 객체간의 느슨한 결합을 통해 코드의 재사용성, 유지보수, 테스트 용이성을 증가시키려는 목표

  • 2004년: 마틴파울러가 "Inversion of Control Continers and the Dependency Injection pattern"이라는 글을 통해 의존성 주입이라는 용어를 처음 도입하고 이에 대해 설명

  • 2000년대: 이 시기에 Java 등의 프로그래밍 언어와 환경에서 Spring Framework와 같은 프레임워크가 등장하면서 DI패턴이 대중화

  • 이후의 발전: 2010년대에 들어서 DI는 다양한 프로그램이 언어와 플랫폼에서 점점 더 널리 채택되었습니다. Google Guice, Microsoft의 Unity, Dagger등과 같은 많은 라이브러리와 프레임워크가 등장하여 이 패턴을 적용하는 것을 더욱 쉽게 만들었습니다.

2. DI가 사용되는 영역

1) 웹 애플리케이션 개발

웹 애플리케이션에서 DI는 컨트롤러, 서비스, 저장소 클래스 등 간의 의존성을 관리하는데 사용. Spring Framewokr와 ASP.NET Core와 같은 웹프레임워크에서 일반적으로 사용

2) 테스트 자동화

단위 테스트 및 통합 테스트에서 DI는 테스트하기 어려운 외부 의존성을 모의객체(mock objects)나 가짜 객체(fake objects)로 쉽게 교체할 수 있게 해줍니다. 이를 통해 테스트의 격리와 반복 가능성을 향상

3) 모바일 애플리케이션의 개발

Android와 같은 모바일 개발환경에서도 Dagger와 같은 DI 라이브러리가 사용되어 의존성 관리를 도와줍니다.

5) 게임 개발

게임에서 DI는 게임의 다양한 컴포넌트와 시스템 간의 의존성을 관리하는데 사용.

6) 클라우드 및 마이크로서비스 아키텍쳐

마이크로서비스 아키텍처에서 DI는 각 서비스 간의 상호작용을 단순화하고, 서비스 간의 결합도를 줄임

7) 사용자 정의 플러그인 시스템

플러그인 아키텍처에서 DI는 확장 가능한 방식으로 서드파티 플러그인과 메인 애플리케이션 간의 의존성을 관리

8) IoT 및 임베디드 시스템

IoT 애플리케이션 및 임베디드 시스템에서도 DI를 사용하여 하드웨어와 소프트웨어 컴포넌트 간의 상호 작용을 간소화하고 테스트를 용이하게 할 수 있다.

3. Android와 DI

  • Android에서 의존성 주입(Depenency Injection, DI)은 애플리케이션 구조를 개선하고 테스트 용이성을 높이는 방법으로 널리 사용.

1) Dagger2

  • 가장 널리 사용되는 DI라이브러리 중 하나. 코드 생성을 통해 DI를 구현하므로 실행 시점의 리플렉션을 사용하지 않아 성능이 뛰어남.
  • 주요 구성 요소
    - Compnent: 의존성 그래프의 엔트리 포인트로, 의존성을 제공하고 주입하는 역할
    • Module: 의존성의 구현을 제공하는 클래스, '@Provides' 어노테이션을 사용해 의존성을 정의
    • Inject: '@Inject" 어노테이션은 생성자, 필드, 메서드에 사용되어 의존성 주입 대상임을 표시
  • 사용 예시
    의존성 정의하기 (Module)
@Module
class NetworkModule {
    @Provides
    fun provideHttpClient(): OkHttpClient {
        return OkHttpClient.Builder().build()
    }
}

컴포넌트 생성하기

@Component(modules = [NetworkModule::class])
interface AppComponent {
    fun inject(activity: MainActivity)
}

의존성 주입하기

class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var httpClient: OkHttpClient

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        DaggerAppComponent.create().inject(this)
        // httpClient 사용하기
    }
}

2) Koin

  • Kotlin을 위해 설계된 경량화된 DI 라이브러리로 ,Dagger보다 선언적이고 단단
  • 리플렉션을 사용하지 않고 DSL을 통해의존성을 정의하므로 쉽게 구현
  • 주요 구성 요소
    - Module: 의존성을 정의하는 단위, 'module'블록 내에서 선언
    • Inject: Koin 컨테이너에서 의존성을 가져오기 위해 'inject()' 또는 'get()'함수를 사용
  • 사용예시
    의존성 정의하기
kotlin
Copy code
val appModule = module {
    single { OkHttpClient.Builder().build() }
}

Koin 시작하기

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin {
            modules(appModule)
        }
    }
}

의존성 주입하기

kotlin
Copy code
class MainActivity : AppCompatActivity() {
    private val httpClient: OkHttpClient by inject()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // httpClient 사용하기
    }
}

3) Hilt

  • Dagger를 기반으로 하는 Android의 공식 DI라이브러리, Dagger의 복잡성을 줄이기 위해 설계, Android의 생명주기 및 기타 특정요소와의 통합이 더 쉽습니다.
  • 주요 구성요소
    - Compnent: Hilt에서의 컴포넌트는 Dagger와 유사하게 동작
    • Module: 의존성을 제공하는 단위로, Dagger와 유사한 어노테이션을 사용
    • Entry Point: 의존성 그래프에 직접 액세스할 수 있는 방법
  • 사용예시
    의존성 정의하기
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
    @Provides
    fun provideHttpClient(): OkHttpClient {
        return OkHttpClient.Builder().build()
    }
}

의존성 주입 준비하기

@HiltAndroidApp
class MyApplication : Application()
kotlin
Copy code
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    @Inject
    lateinit var httpClient: OkHttpClient

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // httpClient 사용하기
    }
}

4. Dagger2 vs koin vs Hilt

  • Dagger2: 높은 성능과 유연성이 필요한 대규모 프로젝트에 적합
  • Koin: 간결하고 선언적인 문법을 선호, Kotlin과의 호환성을 중시할 때 적합
  • Hilt: Android생명주기와 구성요소와의 통합이 중요하며 , Dagger2의 복잡성 줄이고자할 때 적합

5. Android의 DI와 Gradle/dependency{implementation''}

  • 둘의 차이를 확실히 하고 싶다. 햇갈렸다.
  • Gradle의 'implementation'은 외부 라이브러리를 프로젝트에 포함시키는 빌드 시스템의 메커니즘
  • DI는 런타임에 객체 간의 의존성을 관리하고 조정하는 프로그래밍 패턴

6. 부제: 의존성 주입이란 무엇인가?

1) 의존성 주입을 알아보기전이 의존성이란 무엇인가?
객체 지향프로그래밍에서 클래스간에 의존성이 있다는 것은 클래스간에 의존관계가 있다는 것을 뜻한다. 즉, 의존관계란 한 클래스가 바뀔 때 다른 클래스가 영향을 받는다는 것

  • 예를 들어, 우리나라 자동차라는라는 분류 안에 현대자동차와 기아자동차가 있다고 가정하자. 그렇다면 우리나라 자동차는 철 차체를 사용한다고 정의 되었다고 하였을 때 현대자동차와 기아자동차는 모두 철 차체를 사용할 것이다. 그런데 범이 바뀌어 철 차체가 금지되고, 알루미늄으로 모두 대체하기로한다면 우리나라 자동차에 자동차는 철 차제라는 정의가 알루미늄 철 차제로 변경될 것이고, 현대와 기아는 모두 알루미늄 철 차제를 사용할 것이다. 이렇게 변화하는 하나가 변경되면 다른 것이 변경되는 것이 의존의 한 예이다.
  • 만약 위와 같은 상황이 발생된다면, 철 차제를 사용하도록 설계된 공장 설비와, 기존에 존재하던 많은 것들이 변경되어야 할 것이고, 관련된 교체와 비용에 막대한 자원과 비용 시간이 투입될 것이다. 이를 보았을 대 의존성이 높다는 것은 빈번하게 변화가 이루어지는 현장이라면 비효율을 뜻한다는 것을 알 수 있다.(=결합도가 높다.)

2) 의존성 주입이란?

  • 의존성 주입이란 위와같은 비효율을 개선하기 위함이다.
  • 주입이란. 클래스 외부에서 객체를 생성하여 해당 객체를 클래스 내부에 주입하는 것
  • 예를 들어, 우리나라 자동차에 사용되는 차제를 철에서 알루미늄으로 변경하도록 법이 제정되었고, 자동차 회사가 10개 존재하고 모두 철로 자동차를 만드는 공장설비만 있다고 가정하자.
    알루미늄으로 자동차를 만드는 공장설비를 개발하는 비용이 1이라고 한다면 각각의 자동차 회사가 따로따로 개발하여 총 10의 비용이 발생할 것이다.
    만약 알루미늄으로 자동차를 만드는 공장설비를 0.5에 판매하는 회사가 있다면? 각각의 회사는 1의 개발비용을 지불할 필요없이 0.5의 비용으로 공장설비를 얻을 수 있다.
profile
좋은 지식 나누어요

0개의 댓글