[Kotlin] 의존성 주입, DI(Dependency Injection) 기초

wooyoung-tom
5 min readJan 31, 2021
Photo by Diana Polekhina on Unsplash

DI(Dependency Injection), 객체지향 프로그래밍을 공부하다보면 한번쯤은 나오고, 들어봤을 용어입니다.

그래서 오늘은 이 DI(Dependency Injection), 의존성 주입이 어떤 것인지 알아보려고 합니다.

먼저, DI의 장점들에 대해서 보도록 하겠습니다.

DI의 장점

DI로 인하여 아래와 같은 이점들을 가질 수 있습니다.

  • 코드의 재사용성을 높여준다.
  • 테스트의 편의성을 높여준다.
  • 객체간의 의존성을 줄이거나 없앨 수 있다.
  • 객체간의 결합도를 낮출 수 있다.
  • 위와 같은 장점들로 인하여 리팩터링의 편의성도 함께 높아진다.
  • 등등…

그럼 이제 의존성과 주입에 대해서 나누어 생각해 보도록 하겠습니다.

의존성 (Dependency)

먼저 의존성에 대해서 알아보도록 하겠습니다.

흔히 객체지향 프로그래밍으로 개발을 진행하면, 어떤 클래스는 다른 클래스의 참조가 필요하게 됩니다.

말로는 잘 모르겠습니다. 예시를 들어 한번 보도록 하겠습니다. 예시는 우리에게 꼭 필요한 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 클래스에 의존하게 되었습니다.

한 마디로 위에서 설명한 예시처럼, Computerstart 하기 위해서는 꼭 Power 가 필요하기 때문에 ComputerPower 에 의존한다는 것입니다.

의존성을 설명 하였으니, 이제 주입(Injection)에 대해 알아야 합니다.

주입 (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(Dependency Injection)가 완성 될 것 같습니다.

다음의 main() 함수를 보도록 하겠습니다.

fun main() {
val power = Power()
val computer = Computer(power)
computer.start()
}

Computer 클래스의 생성을 위해서 Power 클래스의 인스턴스를 먼저 생성한 후, Computer 클래스의 생성자에 넣어주는 것을 확인할 수 있습니다.

위에서 설명하였듯이, ComputerPower 에 의존하는데 Computer 의 생성자에 Power 인스턴스를 주입하고 있습니다.

이렇게 되면 의존성+주입이 완성되었습니다.

결론

그러면 의존성주입이 주는 이점들을 한번 다시 확인해보겠습니다.

  1. 코드의 재사용성을 높여준다.
    → 우리는 이제 Computer 를 바꿀 때, 같은 Power 를 계속 사용할 수 있게 되었습니다!
  2. 테스트의 편의성을 높여준다.
    → 우리는 이제 Computer 가 정상적으로 동작하는 지 확인할 때 여러가지 Power 들로 테스트할 수 있게 되었습니다!
  3. 객체간의 의존성을 줄이거나 없앨 수 있다.
    → 주입을 진행하지 않았다면 Computer 클래스의 생성과 함께 Power 클래스가 생성되는 것과 마찬가지인데, 그렇게 되면 ComputerPower 는 완전의존성을 가진다고 볼 수 있습니다.
    하지만, 주입을 통해 Power 클래스 생성과 Computer 클래스의 생성을 독립적으로 진행함으로써 의존성을 줄였다고 생각할 수 있습니다.
  4. 객체간의 결합도를 낮출 수 있다.
    → 위의 3번과 마찬가지입니다. Computer 클래스가 생성되지 않으면 Power 클래스가 생성될 수 없기 때문에 결합도가 아주 높아집니다.
    하지만 주입으로 인해 결합도가 낮아졌다고 볼 수 있습니다.
  5. 위와 같은 장점들로 인하여 리팩터링의 편의성도 함께 높아진다.
    → 우리는 이제 Power 가 고장나면 Power 만 고치면 됩니다.
    물론 Computer 내에 다른 부분이 고장이 났다면 Power 를 고칠 필요도 없다는 뜻입니다.

의존성 분리, 의존성 역전, 제어 역전과 같이 공부할 것들이 많이 남았지만, 가장 기본적인 의존성 주입 부터 공부해보았습니다.

아래는 참고하여 공부하는 데에 도움이 된 페이지들 입니다.

--

--