개요
안드로이드에서 너무나도 많이 보이는 DI
안드로이드 개발 공부를 하다보면 DI라는 용어는 빈번하게 등장한다.
소프트웨어 공학에서 나온 용어라서 안드로이드 외의 다른 개발 프레임워크들에서도 많이 사용하기에 엄청나게 생소한 용어는 아니지만, 안드로이드에서는 이를 관리해주는 Hilt라는 라이브러리가 있어 굉장히 많이 거론되는 듯 하다. ▼
StateHoisting을 배우고, State를 Hoisting해줄 ViewModel을 인식하고, ViewModel을 UI의 어느 부분에 저장을 할 지 알아보던 중 DI와 Hilt에 대한 선행 지식이 없으면 안되었기에 더 자세하게 알아보게 되었다.
Dependency Injection
Dependency Injection이란
Dependency Injection, 줄여서 DI는 직역하자면 의존성 주입이다.
하나의 객체에 다른 객체의 "의존성"을 제공하는 기술이다.
의존성
"의존성, 의존관계는 뭔가요"
DI를 이해하기 위해서는 의존관계가 무엇인지에 대해서부터 이해할 필요가 있는데, 의존관계는 쉽게 말하면 참조하는 것이다.
왼쪽 그림과 같이 고라니 객체가 있다고 했을 때, 고라니 객체 안에 먹이 객체와 서식지 객체를 두게 되면 이는 고라니 객체가 먹이, 서식지 객체를 참조한다고 하며 의존관계로 표현된다.
허나 이렇게 되면 먹이 객체와 서식지 객체는 외부에서 자유롭게 접근하지 못한다.
이를 해결하기 위해 이 두 객체를 밖으로 빼고 setter 혹은 생성자를 통해 고라니 객체에 넣어주는 것이 의존성 주입이다. ▼
코드로 보는 DI
일반적인 의존 관계 코드
위의 예시, 그중에서도 일반적인 의존관계 예시를 코드로 작성하면 대략 이렇게 된다. ▼
class Gorani {
private val food = Food()
private val forest = Forest()
fun eatFood() {
food.eat()
}
fun goForest() {
forest.callGorani()
}
}
fun main(args: Array) {
val gorani = Gorani()
gorani.eatFood()
gorani.goForest()
}
`Gorani`는 `Food`와 `Forest`에 대한 강한 의존성을 가지고 있다.
`Food`와 `Forest`는 `Gorani` 내부에서 인스턴스화 되기 때문에 다음의 문제들이 발생할 수 있다.
- `Food`와 `Forest`의 하위 객체 사용 불가
`Gorani` 객체는 `Food`와 `Forest`의 구체적 구현에 의존하게 되므로 이 두 객체를 하위 객체로 대체할 수 없다.
하위 객체도 같이 사용하기 위해서는 해당 구현을 추상적으로 구현을 해야한다. - `Food`와 `Forest`의 생성자 수정
현재는 생성자가 설정되지 않아 간단하게 호출을 한 상황이지만, 만약에 생성자가 바뀌게 되면 `Food`와 `Forest`의 코드만 수정할 게 아니라 `Gorani`의 코드도 같이 수정해야한다. - 유닛 테스트의 어려움
`Gorani`가 객체를 직접 생성하므로 객체 생성 로직을 재정의하거나 이를 대체하기가 어려워진다.
`Gorani`의 특정 동작을 테스트하기 위한 유닛 테스트가 더 복잡해지는 결과를 초래한다.
DI로 바꾼 코드
이제 위의 코드를 DI로 바꾸면 이렇게 작성할 수 있다. ▼
class Gorani(private val food: Food, private val forest: Forest) {
fun eatFood() {
food.eat()
}
fun goForest() {
forest.callGorani()
}
}
fun main(args: Array) {
val food = Food()
val forest = Forest()
val gorani = Gorani(food, forest)
gorani.eatFood()
gorani.goForest()
}
`main()`에서 `Food`와 `Forest`의 인스턴스를 생성하고, 이를 `Gorani` 인스턴스에 넣어준다.
별거 아닌거 같지만 이렇게 작성하면 앞의 문제점들을 해결할 수 있다.
- 하위 객체 사용가능 (재사용성 증가)
Food와 Forest의 하위 객체들을 Gorani에게 넘겨줄 수 있게 된다. - 생성자 수정 필요 없어짐
Food와 Forest 객체의 생성자가 바뀌어도 Gorani 객체는 수정하지 않아도 된다. - 유닛테스트 용이
객체 생성 로직을 밖으로 빼내어 Gorani 객체는 이로부터 자유로워진다.
그렇기에 Gorani 객체의 동작만을 테스트 할 수 있게 된다.
마치며
DI는 안드로이드 개발에서 매우 중요한 개념이다. DI를 통해 코드의 재사용성과 유연성을 높이고, 의존성을 관리함으로써 더 유지보수하기 쉬운 코드를 작성할 수 있다. 또한, Hilt와 같은 라이브러리를 사용하면 DI를 더욱 쉽게 적용할 수 있다. DI의 개념을 이해하고 이를 코드에 적용함으로써 안드로이드 개발의 효율성을 높이고, 더 나은 소프트웨어를 개발할 수 있을 것이다.
'Develop > AndroidOS' 카테고리의 다른 글
[Android] Compose BOM (0) | 2024.06.25 |
---|---|
[Android][Compose] State Hoisting (0) | 2024.06.13 |
[Android][Compose] Compose의 레이아웃 (0) | 2024.06.13 |
[Android][Compose] JetPackCompose란? 그리고 Compose의 구성 (0) | 2024.06.12 |