코딩하는 개굴이

[안드로이드] 의존성 주입 (DI) 란? 본문

안드로이드

[안드로이드] 의존성 주입 (DI) 란?

개굴이모자 2021. 3. 13. 20:04
반응형

의존성 주입 (DI)이란?

외부에서 의존 객체를 생성하여 넘겨주는 것을 의미한다.
일반적인 객체 생성과 DI를 적용한 객체 생성을 비교하자면,
일반적인 객체 생성은 클래스 안에서 사용할 객체를 생성하지만, DI를 적용한 객체 생성은, 외부에서 생성된 객체를 주입 받는 방식이다.

의존성이란?

class Doraemon {
    private Pocket pocket;
    public Doraemon() {
        this.pocket = new Pocket();
    }

    public startDoremon() {
        this.pocket.searchPocket();
    }
}

Doraemon 클래스의 startDoramon 함수의 호출을 위해서는 Pocket 클래스가 필수적이다. 이런 상황에서, Doraemon 클래스 Pocket 클래스의 의존성을 가진다.

의존성이 생길 경우, 코드의 재활용성이 떨어지고, Pocket 클래스가 수정될 경우, Programmer 클래스도 함께 수정되어야 하므로, 결합도 (coupling)이 높아진다.

의존성 주입을 해야 하는 이유

  • 코드의 가독성을 높여준다.
  • Unit Test 가 쉬워진다.
  • 코드의 재활용성을 높인다.
  • 객체 간의 의존성(종속성)을 직접 설정하여 줄이거나 없앨 수 있다.
  • 객체 간의 결합도를 낮추면서 유연하게 만들 수 있다.
class Doraemon {
    private Pocket pocket;
    public Doraemon(Pocket pocket) {
        this.pocket = pocket;
    }

    public startDoremon() {
        this.pocket.searchPocket();
    }
}

만약, pocket에 들어가는 요소가 달라지거나, 종류가 달라지는 등의 수정이 생긴다면 의존성을 주입하기 전의 경우는 Doraemon 또한 수정이 필요하지만, 위의 케이스의 경우 개발자가 직접 Doraemon을 부르며 pocket 을 넣을 수 있으므로, 객체 간의 의존성을 줄일 수 있다.

안드로이드에서의 DI 방법

안드로이드에서는 두가지 방식의 의존성 주입이 가능하다.

  • 생성자 주입 (Constructor Injection) : 생성자를 통해 의존하는 객체 전달
    • 위의 예시의 방법이 생성자 주입에 해당한다.
  • 필드 주입 (Field Injection) : 객체가 초기화된 후에 의존하는 객체 전달

직접 코드로 구현하는 것도 가능하지만, 라이브러리들을 사용하여 쉽게 구현 가능하다.

  • Koin, Dagger, Hilt 등..

Koin을 이용한 의존성 유입

Koin 설치

jcenter 레포지토리가 등록되어있는지 확인하고 koin_version 변수를 원하는 버전으로 선언한다.

buildscript {
    ext.koin_version = '버전 코드'
    repositories {
        jcenter()
    }
}

...

//build.gradle 파일
dependencies {
    implementation "org.koin:koin-androidx-viewmodel:$koin_version"
    testImplementation "org.koin:koin-test:$koin_version"
}

Module 선언

  • Repository 클래스와 이 클래스 객체를 파라미터로 받는 Controller, ViewModel 클래스를 만든다.
  • module 키워드로 주입받을 객체를 모듈로 만들어 선언
  • 생성한 모듈들을 Application 클래스의 onCreate LifeCycle 에서 startKoin을 호출하여 넘겨준다.
    • Application 클래스를 새로 생성한 경우 manifest.xml에 등록한다.
  • 키워드
    • single : 앱이 실행되는 동안 계속 유지되는 싱글톤 객체를 생성
    • factory : 요청할때마다 매번 새로운 객체를 생성
    • get() : 컴포넌트 내에서 이미 생성된 의존성을 주입 받을 수 있음
      • 이미 생성된 객체 중 해당 타입에 알맞는 객체를 Koin 이 주입해준다.
    • androidLogger : AndroidLogger를 Koin Logger로 사용한다.
    • androidContext : 해당 안드로이드의 application Context를 사용한다
    • modules : 사용할 모듈을 등록한다.
class DoraemonRepository() {
    val tools = "MiniDora"
}

class DoremonController(val repo : DoraemonRepository) {
    fun printTools() {
        Log.d("Print tools", repo.tools)
    }
}

class DoraemonViewModel : ViewModel() {
    ...
}

//Module 선언
val appModule = module {
    single { DoremonRepository() }
    factory { DoremonController(get())}
}

val viewModelModule = module {
    viewModel {DoremonViewModel()}
}

//생성한 모듈들을 Koin에 등록
class AppApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin {
            androidLogger()
            androidContext(this@AppApplication)
            modules(appModule)
            modules(viewModelModule)
        }
    }
}

//manifest.xml
<application
        android:name=".AppApplication"
        ...

의존성 주입 받기

위의 과정에서 등록한 모듈을 주입 받아 사용할 수 있다.

  • by inject()로 lazy하게 controller를 주입 받을 수 있다.
  • viewModel 키워드로 모듈을 등록하면, 일반적인 클래스들과는 달리, Koin이 해당 ViewModel을 ViewModelFactory에 등록하고 현재 컴포넌트와 바인딩하여, 주입받을 때도 ViewModelFactory에서 해당 ViewModel 객체를 불러온다.
    • get()키워드를 이용하면 non-lazy하게 주입받을 수 있다.
    • getViewModel() 키워드를 이용하면 non-lazy하게 주입받을 수 있다.
  • 키워드 :
    • Lazy : 프로그램의 퍼포먼스를 높이기 위해 처음부터 초기화하는 것이 아니라 필요한 순간까지 최대한 초기화를 지연시킨다는 것을 의미한다.
      • 실행 시간 및 메로리 효율을 개선할 수 있다.
//by inject
class DoraemonActivity : AppCompatActivity() {
    val controller : DoremonController by inject()
    ...
}

//viewModel에서의 injection
val viewModel : DoremonViewModel by viewModel()
//단, viewModel의 경우, get을 이용해서 non-lazy하게 의존성을 주입할 수 있다.
//DoraemonViewModel에서 Repo를 주입받는다고 할 때
//Module 선언
val viewModelModule = module {
    viewModel {DoremonViewModel(get())}
}
//viewmodel
class DoraemonViewModel : ViewModel(val repo : DoraemonRepository) {
    ...
}
//위와 같이도 사용할 수 있다.

참고 링크

반응형
Comments