Spring

DIP란 무엇인가?

통촏하여주시옵소서 2024. 11. 10. 17:01

DIP는 Dependency Inversion Principle의 약자로, 객체 지향 설계 원칙 중 하나입니다. 이 원칙은 의존성 역전 원칙이라고도 하며, SOLID 원칙 중 마지막에 해당합니다. DIP의 핵심은 상위 모듈(추상화된 모듈)이 하위 모듈(구체화된 모듈)에 의존하지 않도록 하여, 모듈 간의 결합도를 낮추고 코드의 유연성을 높이는 것입니다.

DIP의 기본 개념

DIP는 다음 두 가지 규칙으로 정의됩니다:

  1. 상위 모듈은 하위 모듈에 의존해서는 안 된다. 둘 다 추상화에 의존해야 한다.
  2. 추상화는 구체적인 사항에 의존하지 않는다. 구체적인 사항이 추상화에 의존해야 한다.

이 원칙에 따라 구체적인 구현이 아닌 추상화(인터페이스나 추상 클래스)에 의존하게 만들어야 합니다. 이렇게 하면, 구현을 바꾸더라도 상위 모듈에 미치는 영향을 최소화할 수 있어 유연하고 유지보수가 용이한 코드를 작성할 수 있습니다.

DIP가 필요한 이유

DIP가 없으면, 상위 모듈이 하위 모듈의 구체적인 구현에 의존하게 되어 두 모듈 간의 결합도가 높아집니다. 이렇게 되면, 하위 모듈의 변경 사항이 상위 모듈에 영향을 주기 때문에 코드 수정 시 변경 범위가 넓어지고 유지보수가 어려워집니다.

DIP를 적용하면 인터페이스나 추상 클래스를 통해 상위 모듈이 하위 모듈과 소통하므로, 하위 모듈의 변경이 상위 모듈에 미치는 영향을 줄일 수 있습니다.

DIP의 예제

예를 들어, 아래와 같은 KeyboardMonitor 클래스가 있고, 이를 사용하는 Computer 클래스가 있다고 가정해 보겠습니다.

class Keyboard {
    void type() {
        System.out.println("키보드를 입력합니다.");
    }
}

class Monitor {
    void display() {
        System.out.println("화면을 출력합니다.");
    }
}

class Computer {
    private Keyboard keyboard;
    private Monitor monitor;

    public Computer() {
        this.keyboard = new Keyboard();
        this.monitor = new Monitor();
    }

    void start() {
        keyboard.type();
        monitor.display();
    }
}

위의 코드에서는 Computer 클래스가 KeyboardMonitor 클래스에 직접 의존하고 있습니다. 이런 구조에서는 키보드나 모니터의 구체적인 구현을 바꾸거나 새롭게 추가해야 할 때, Computer 클래스도 함께 수정해야 합니다. 결합도가 높아지는 문제점이 발생합니다.

DIP 적용 예제

DIP를 적용하여 의존성을 낮추기 위해 KeyboardMonitor에 인터페이스를 정의하고, Computer는 인터페이스에 의존하도록 바꿔 보겠습니다.

interface InputDevice {
    void type();
}

interface DisplayDevice {
    void display();
}

class Keyboard implements InputDevice {
    public void type() {
        System.out.println("키보드를 입력합니다.");
    }
}

class Monitor implements DisplayDevice {
    public void display() {
        System.out.println("화면을 출력합니다.");
    }
}

class Computer {
    private InputDevice inputDevice;
    private DisplayDevice displayDevice;

    public Computer(InputDevice inputDevice, DisplayDevice displayDevice) {
        this.inputDevice = inputDevice;
        this.displayDevice = displayDevice;
    }

    void start() {
        inputDevice.type();
        displayDevice.display();
    }
}

이제 Computer 클래스는 KeyboardMonitor의 구체적인 구현이 아닌, InputDeviceDisplayDevice라는 인터페이스에 의존하게 됩니다. 따라서, 키보드나 모니터의 구체적인 구현을 교체하거나 새로운 입력 장치 또는 디스플레이 장치를 추가하더라도 Computer 클래스는 변경할 필요가 없습니다.

DIP의 장점

  • 유연성 향상: 코드가 구체적인 클래스가 아닌 추상화에 의존하므로 구현을 쉽게 교체할 수 있습니다.
  • 유지보수 용이성: 변경이 필요할 때 영향 범위가 줄어들어 수정이 용이합니다.
  • 테스트 용이성: 테스트 시 특정 구현을 쉽게 대체(mocking)할 수 있어 단위 테스트에 유리합니다.

DIP는 특히 의존성 주입(Dependency Injection)을 통해 구현되는 경우가 많으며, Spring과 같은 프레임워크에서도 DIP를 쉽게 적용할 수 있도록 지원합니다. DIP는 결합도를 줄이고 코드의 재사용성을 높이기 위한 중요한 원칙입니다.