DI(Depengency Injection, 의존성 주입)이란?

Depengency Injection

오늘은 의존성 주입 패턴에 대해 이야기 해볼까 합니다.

의존성 주입은 프로그래밍에서 널리 사용되는 기법으로, 하나의 객체가 다른 객체의 의존성을 제공하는 디자인 패턴입니다.

의존성은 서비스로 사용할 수 있는 객체입니다. 클라이언트가 직접 어떤 서비스를 사용할 것인지 지정하는 대신, 클라이언트에게 어떤 서비스를 사용할 것인지 말해주는 것입니다. 예를 들어 컴퓨터 조립을 하기위해 필요한 부품들을 제공받는 것, 이것이 종속 항목 주입이라고 할 수 있습니다.

이 디자인패턴의 대표적인 실사용 예로는 안드로이드의 리사이클러뷰가 있습니다.
리사이클러뷰 클래스가 가지는 책임을 최소화 하기 위해, setAdapter()를 통하여 각각의 뷰 홀더를 생성해주고 데이터를 바인딩해주는 리사이클러뷰 어댑터 인스턴스를 외부에서 주입받아 사용합니다.

이번 포스팅은 의존성 주입이 필요한 이유와, 예시를 코드로 함께 살펴보는 시간을 가지도록 하겠습니다.

의존성 주입의 필요성

의존성 주입이 필요한 이유는 클래스의 책임을 최소화 하여 리팩토링을 용이하게 해주고, 코드를 재사용 가능하게 해주며, 테스트를 용이하게 해줍니다.

잘못된 코드 예시

코드에는 흔히 다른 클래스 참조가 필요합니다. 예를 들어 Computer 클래스는 부속품인 CPU, Graphic Card, RAM 과 같은 클래스의 참조가 필요할 수 있습니다. 이처럼 필요한 클래스를 종속 항목이라고 하며 Computer 클래스가 실행되기 위해서는 CPU클래스의 인스턴스가 필요합니다.
다음 코드는 의존성 주입 패턴을 준수하지 않고, 한 클래스 내에 종속 항목에 대한 생성까지 하는 코드입니다.

import CPU.IntelCPU;
import GraphicCard.GeforceGraphicCard;

public class Computer {
    private IntelCPU cpu;
    private GeforceGraphicCard vga;

    Computer() {
        cpu = new IntelCPU();
        vga = new GeforceGraphicCard();
    }

    void run() {
        // 컴퓨터 실행..
        
    }
}

위 코드는 일단 실행하는데에는 지장이 없는 클래스입니다. 그러나 컴퓨터에 CPUVGA에 대한 생성 책임이 생겼고, 각 부품 클래스와 컴퓨터 클래스는 강하게 밀접해있습니다. 만약 컴퓨터에 고장이 생겨 CPU고장이 의심되는 상황일 때, 인텔 CPU를 라이젠 CPU로 바꾸고 테스트를 해보려고 합니다. 위와 같은 코드는 부품과 컴퓨터 클래스는 강하게 결합되어 있기 때문에 CPU를 바꾸려고 한다면 다음과 같이 다시 생성해야합니다.

public class main {
    public static void main(String[] args) {
        IntelComputer computer = new IntelComputer();
        RygenComputer computer = new RygenComputer();
        
    }
}

위 코드의 문제점은 그래픽카드는 충분히 재사용할 수 있었는데 그래픽카드까지 다시 생성을 했습니다. 또한, 컴퓨터는 CPU와 그래픽카드 외에도 수많은 종속 항목이 존재하는데 다음과 같이 모든 부품에 대한 경우의수를 고려하여 클래스를 작성해야합니다.

ComputerWithIntelCPUAndGeforceGraphicCardAndSamsungRAM computer = new ComputerWithIntelCPUAndGeforceGraphicCardAndSamsungRAM(); //...?
...

그냥 봐도 엄청난 자원과 인력낭비가 필요해보입니다..
그렇다면, 이 문제를 해결하기 위해서는 어떻게 해야할까요?

올바른 코드 예시

이 문제를 해결하기 위해서는 인스턴스화된 객체를 Computer 클래스의 매개변수로 전달받는 것입니다. 이것이 앞서 말씀드린 종속성 주입입니다. 저희는 위 코드를 다음과 같이 수정해볼 수 있습니다.

public class Computer {
    CPU cpu;
    GraphicCard vga;

    Computer(CPU cpu, GraphicCard vga) {
        this.cpu = cpu;
        this.vga = vga;
    }

    void run() {
        // 컴퓨터 실행..
        
    }
}

위와 같이 각각의 종속성을 주입받을 수 있도록 작성한 다음, main 함수에서 각각의 인스턴스를 생성하고 컴퓨터를 켜주시면 됩니다.

public class main {
    public static void main(String[] args) {
        RygenCPU rygenCPU = new RygenCPU();
        IntelCPU intelCPU = new IntelCPU();
        GeforceGraphicCard geforceGraphicCard = new GeforceGraphicCard();
        RadeonGraphicCard radeonGraphicCard = new RadeonGraphicCard();

        Computer computer = new Computer(intelCPU, geforceGraphicCard);
        computer.run();
    }
}

정리

DI 디자인 패턴은 앞서 언급했듯 테스트와 재사용성 측면에서 강한모습을 드러냅니다.
틀린 내용이 있다면 지적 부탁드리겠습니다.

References

https://ko.wikipedia.org/wiki/%EC%9D%98%EC%A1%B4%EC%84%B1_%EC%A3%BC%EC%9E%85
https://developer.android.com/training/dependency-injection
https://www.youtube.com/watch?v=IKD2-MAkXyQ
https://www.youtube.com/watch?v=ZZ_qek0hGkM

좋은 웹페이지 즐겨찾기