Dependency Injection 말그대로 "의존성 주입"입니다.
객체지향 프로그래밍을 공부하다보면 한번쯤은 나오고, 들어봤을 용어입니다.
그래서 오늘은 이 DI(Dependency Injection), 의존성 주입이 어떤 것인지 알아보려고 합니다.
DI로 인하여 아래와 같은 이점들을 가질 수 있습니다.
먼저 의존성에 대해서 알아보도록 하겠습니다.
흔히 객체지향 프로그래밍으로 개발을 진행하면, 하나의 클래스는 다른 클래스의 참조가 필요하게 됩니다.
말로는 잘 모르겠습니다. 예시를 저희한테 꼭 필요한 Computer
객체를 통해 들어보도록 하겠습니다.
Computer
는 동작을 시작하거나 지속하기 위해서 Power
를 필요로 합니다. Power
가 없다면 Computer
는 동작할 수 없기 때문이죠. (Developer 도 Computer 가 없다면 동작할 수 없듯이말이죠...)
class Computer {
private val power = Power()
fun start() {
power.on()
}
}
class Power {
fun on() {
println("Power ON!")
}
}
위의 코드처럼 start()
메소드를 동작시키기 위해 Computer
클래스 내에 변수로 Power
클래스의 인스턴스를 생성함으로써 Computer
클래스는 Power
클래스에 의존하게 되었습니다.
한 마디로 위에서 설명한 예시처럼, Computer
를 start
하기 위해서는 꼭 Power
가 필요하기 때문에 Computer
가 Power
에 의존한다는 것입니다.
의존성을 설명하였으니, 이제 주입 (Injection) 에 대해 알아야 합니다.
주입이란, 위의 Computer
클래스처럼 클래스 내에서 다른 클래스의 인스턴스를 생성하여 사용하는 것이 아니라 클래스 외부에서 인스턴스를 생성하여 넘겨주는 것을 의미합니다.
또 말로만 해서는 잘 모르겠습니다. 위의 Computer
클래스 코드를 조금 고쳐보겠습니다.
class Computer(private val power: Power) {
fun start() {
power.on()
}
}
class Power {
fun on() {
println("Power ON!")
}
}
수정된 코드의 Computer
클래스를 보면, 생성자의 매개변수로 Power
인스턴스를 받고 있습니다.
어딘가(외부) 에서 Computer
인스턴스를 생성하기 위해서는 Power
의 인스턴스도 (외부에서) 함께 생성하여 Computer
의 생성자로 해당 인스턴스를 주입해주어야 한다는 의미입니다.
이렇게 주입까지 알아보았습니다.
그렇다면 위에서 다룬 의존성과 주입을 더하면 의존성 주입, 즉 DI가 완성 될 것 같습니다.
다음의 main()
함수를 보도록 하겠습니다.
fun main() {
val power = Power()
val computer = Computer(power)
computer.start()
}
Computer
클래스의 생성을 위해서 Power
클래스의 인스턴스를 먼저 생성한 후, Computer
클래스의 생성자에 넣어주는 것을 확인할 수 있습니다.
위에서 설명하였듯이, Computer
는 Power
에 의존하는데 Computer
의 생성자에 Power
인스턴스를 주입하고 있습니다.
이렇게 되면 의존성+주입이 완성되었습니다.
그러면 의존성주입이 주는 이점들을 한번 다시 확인해보겠습니다.
코드의 재사용성을 높여준다.
우리는 이제 Computer
를 바꿀 때, 같은 Power
를 계속 사용할 수 있게 된 것입니다.
테스트의 편의성을 높여준다.
우리는 이제 Computer
가 정상적으로 동작하는 지 확인할 때 여러가지 Power
들로 테스트할 수 있게 되었습니다.
객체간의 의존성을 줄이거나 없앨 수 있다.
주입을 진행하지 않았다면 Computer
클래스의 생성과 함께 Power
클래스가 생성되는 것과 마찬가지인데, 그렇게 되면 Computer
와 Power
는 완전의존성을 가진다고 볼 수 있습니다.
하지만, 주입을 통해 Power
클래스 생성과 Computer
클래스의 생성을 독립적으로 진행함으로써 의존성을 줄였다고 생각할 수 있습니다.
객체간의 결합도를 낮출 수 있다.
위의 3번과 마찬가지입니다. Computer
클래스가 생성되지 않으면 Power
클래스가 생성될 수 없기 때문에 결합도가 아주 높아집니다. 하지만 주입으로 인해 결합도가 낮아졌다고 볼 수 있습니다.
위와 같은 장점들로 인하여 리팩터링의 편의성도 함께 높아진다.
우리는 이제 Power
가 고장나면 Power
만 고치면 됩니다. 물론 Computer
내에 다른 부분이 고장이 났다면 Power
를 고칠 필요도 없다는 뜻입니다.
이렇게 의존성 주입에 대해서 알아보았습니다.
참고 링크 (좋은 설명들 감사합니다!):
https://developer.android.com/training/dependency-injection?hl=ko
https://velog.io/@wlsdud2194/what-is-di
https://medium.com/@jang.wangsu/di-dependency-injection-이란-1b12fdefec4f