SW 공부노트
[안드로이드] 의존성 주입(DI) 본문
Dependency 란?
Dependency는 '의존성'을 의미한다.
의존성이란 하나의 객체가 다른 객체에 의존하는 것을 말한다.
즉, 하나의 객체가 어떤 용로도 다른 객체에 필요한 것을 의미한다.
함수에 필요한 클래스 또는 참조변수나 객체에 의존하는 걸 예로 들 수 있다.
class Worker{
private car = Car()
fun commute(){
car.drive()
}
}
위 예제에서 Worker 객체는 Car 객체에 의존하고 있다.
Worker 객체 내에 Car 객체가 존재하므로 Worker 객체가 생성되면 Car 객체는 계속 존재해야 하는 것이다.
Worker 객체가 commute 함수를 사용하려면 Car 객체가 필요하기 때문에 Worker가 Car에 의존한다고 볼 수 있는 것이다.
이러한 필요한 클래스(Car)를 종속 항목이라고 한다.
위 예제의 경우 Car과 Worker가 밀접하게 연결되어 있기 때문에 코드의 재사용이 어렵고, 다양한 테스트를 하기 어렵다는 문제가 발생한다.
만약 위 예제에서 Car가 아니라 Bus를 탄다고 가정해보자.
class Worker{
private bus = Bus()
fun commute(){
bus.take()
}
}
Car 대신 Bus 객체로 바뀌고 함수 내 메서드도 객체에 맞게 바뀌었음을 알 수 있다. 따라서 의존하는 객체가 바뀌면 의존 객체를 사용하고 있는 모든 곳의 코드를 변경해주어야 한다. 지금은 간단한 코드였지만 프로젝트 규모가 커질수록 많은 수정이 필요할 것이다.
즉, 의존은 코드의 재사용을 어렵게 하며, 유지보수를 어렵게 한다.
Dependency Injection(DI)
Dependency Injection은 '의존성 주입'으로, 흔히 DI라고 부른다. DI는 객체 간의 의존 관계를 객체 - 객체가 아닌 외부에서 객체를 생성하고 전달하여 의존성을 제거하고 결합도를 낮추는 것을 말한다.
DI는 프로그래밍에 널리 사용되는 기법으로 Android 개발에 적합하다.
DI의 원칙을 따르면 훌륭한 앱 아키텍처를 위한 토대를 마련할 수 있다.
즉, 의존하는 클래스 객체를 직접 생성하는 것이 아니라 생성자와 메서드를 통해 외부에서 주입하는 방식이다.
interface Transportation{
fun ride()
}
class Worker(trans: Transportation){
private var trans: Transportation = trans
fun changeTransportation(new: Transportation){
this.trans = new
}
fun commute(){
trans.ride()
}
}
위의 방식대로 한다면 changeTransportation()를 통해 통근 수단을 쉽게 바꿀 수 있을 것이다.또한 각 교통수단(Car, Bus)을 나타낸 클래스에서는 Transportation 인터페이스의 ride()를 오버라이딩하여 각 클래스에 맞게(drive, take) 구현하면 된다.
위에서 설명한 것처럼 DI는 다음과 같은 장점이 있다.
- 코드 재사용 및 종속 항목 분리: 종속 항목 객체를 쉽게 교체할 수 있다.
- 리팩토링 편의성: 종속 항목은 구현 세부정보로 숨겨져있지 않고 노출되어 있어 객체 생성 및 컴파일 시 확인할 수 있다.
- 테스트 편의성: 테스트가 종속 항목을 관리하지 않으므로 다양한 모든 사례를 테스트할 수 있다.
안드로이드에서 의존성 주입하기(DI)
안드로이드에서 의존성을 주입하는 두 가지 방법은 다음과 같다.
- 생성자 삽입: 위에서 설명한 방법으로, 클래스의 종속 항목을 생성자에 전달하는 방법이다.
- 필드 삽입(setter 삽입): 객체가 초기화된 후, 메서드를 통해 의존하는 객체를 전달하는 방식
Android는 context의 영향을 많이 받는다. 즉, Activity, Fragment 내에서 선언되고 사용되는(의존하는) 인스턴스들은 해당 클래스의 수명주기와 같은 요소에 영향을 받는다. 따라서 다른 곳에서 인스턴스를 생성하고 Activity, Fragment에서는 생성된 인스턴스를 사용하면, 내부 환경과는 상관 없이 동작하는 범용적이고 재사용 가능한 인스턴스를 사용할 수 있다.
또한, Activity나 Fragment 같은 특정 안드로이드 프레임워크 클래스는 시스템에서 인스턴스화하기 때문에 생성자 삽입이 불가능하다. 따라서 필드 삽입을 통해 클래스가 생성된 후 종속 항목을 인스턴스화한다.
위와 다른 예로 Car 운전 시 Engine이 필요할 때 setter를 통해 의존성을 주입한 모습이다.
Car 클래스 내 engine을 지연초기화하고, 클래스가 생성되면 engine값을 설정해 사용한다.
class Car {
lateinit var engine: Engine
fun start() {
engine.start()
}
}
fun main(args: Array) {
val car = Car()
car.engine = Engine()
car.start()
}
하지만 이러한 DI도 구조가 복잡해지면 boilerplate code(여러 곳에서 재사용되며, 반복적으로 비슷한 형태의 코드)가 많아지고, 지연 초기화 등에 의해 의존성을 전달하기 전에는 의존성을 구성할 수 없는 등의 문제가 발생한다.
안드로이드의 DI 라이브러리
위의 예처럼 클래스의 종속 항목을 직접 생성, 제공, 관리하는 것을 수동 의존성 주입이라고 한다.종속 항목과 클래스가 많아지면 수동 의존성 주입을 사용할 때 문제가 발생할 수 있다. 따라서 종속 항목을 생성하고 제공하는 프로세스를 자동화해 문제를 해결하는 라이브러리를 사용하는 것이 좋다. 안드로이드에서 제공하는 DI 자동화 라이브러리는 다음과 같다.
- Dagger2
- Koin
- Hilt
이 중 Hilt는 Jetpack의 권장 라이브러리로 좋은 아키텍처를 설계하는 데 많은 도움이 된다.
Hilt는 프로젝트의 모든 Android 클래스에 컨테이너를 제공하고 수명 주기를 자동으로 관리함으로써 애플리케이션에서 DI를 실행하는 표준 방법을 정의한다. Hilt는 Dagger가 제공하는 컴파일 시간 정확성, 런타임 성능, 확장성 및 안드로이드 스튜디오 지원의 이점을 누리기 위해 Dagger를 기반으로 빌드되었다.
Reference
DI(Dependency Injection)에 대해 알아보자
요즘들어 삘을 받아서 폭풍 포스팅 중 입니다.🤗이번 포스팅은 DI(Dependency Injection) - 의존성 주입에 대한 내용인데요.먼저 DI가 무엇인지 알아보고 안드로이드에서 많이 쓰이는 DI 라이브러리들
velog.io
https://junyoung-developer.tistory.com/170
[DI/Android] 의존성 주입 (Dependency Injection)
1. Dependency Dependency는 '의존성'을 의미한다. 의존성이란, 하나의 객체가 다른 객체에 의존하는 것 (= 하나의 객체가 어떤 용도로 다른 객체에 필요한 것) class Worker { private car = Car() fun Commute() { car.d
junyoung-developer.tistory.com
'안드로이드 > 아키텍처 & 라이브러리' 카테고리의 다른 글
[안드로이드] Flow (0) | 2023.05.15 |
---|---|
[안드로이드] Android 앱에 Hilt 사용 (0) | 2023.04.02 |
[안드로이드] 앱 아키텍처 가이드 (0) | 2023.03.31 |
[안드로이드] Android Jetpack / AAC와 MVVM (0) | 2023.03.31 |
[안드로이드/아키텍처] MVC, MVP, MVVM (0) | 2023.03.29 |