의미
- 기본적으로 컴포넌트는 다른 컴포넌트를 직접 참조하여 사용한다.
예) A -> B (A가 B를 직접 참조) - 하지만 DIP에서는 다음과 같은 구조를 권장한다:
A -> Interface <- B
즉, 구체 클래스가 아닌 추상 인터페이스를 사이에 두고 의존한다.
why? 구체 클래스보다 추상 인터페이스가 변동성이 낮다.
이 방향 전환, 즉 구체가 추상에 의존하는 구조가 바로 "의존성 역전(Dependency Inversion)"이다.
의존성 역전을 고려하지 않아도 되는 경우
- 매우 안정적이고 변동성이 없는 요소들에는 DIP를 적용하지 않아도 된다.
예: String, int 같은 기본 타입, 운영체제 API, 플랫폼 API 등
변경될 가능성이 없다면 의존해도 된다.
따라서 우리가 보통 개발하는 대부분의 비즈니스 로직 관련 클래스는 DIP 적용이 필요하다.
책에서 제시하는 원칙 요약
- 변동성이 큰 구체 클래스를 직접 참조하지 말 것
- 대신 추상 인터페이스를 참조하라.
- 변동성이 큰 구체 클래스로부터의 상속은 신중히
- 상속도 의존성이다. 변경의 영향을 그대로 받는다.
- 구체 함수를 오버라이드하지 말 것
- 구현된 메서드를 자식 클래스에서 재정의하면, 부모 변경이 자식에 직접 전파된다.
- 가능하면 추상 메서드만 상속해서 자식에서 구현하는 것이 안전하다.
의존성을 처리하는 방법
책에서는 추상 팩토리(Abstract Factory)를 제시한다.
- 구체 클래스 생성을 팩토리에 위임함으로써, 클라이언트가 구체 구현을 알 필요가 없어진다.
- 팩토리는 추상 인터페이스를 반환하고, 클라이언트는 이 인터페이스만 사용한다.
하지만 이 글에서는 의존성 주입(DI, Dependency Injection) 방식을 설명하겠다.
의존성 주입 (DI)
의존성 주입은 클라이언트 객체가 사용할 구체 구현을 스스로 생성하지 않고, 외부에서 주입받는 방식이다.
이 방식은 클라이언트가 구체 클래스에 직접 의존하지 않도록 만들기 위한 도구이며, DIP를 실천할 수 있게 해준다.
예시 코드 (Java, 생성자 주입)
// 추상 인터페이스
interface MessageService {
void send(String msg);
}
// 구체 구현체 A
class EmailService implements MessageService {
public void send(String msg) {
System.out.println("Email: " + msg);
}
}
class Notification {
private final MessageService service;
public Notification(MessageService service) {
this.service = service;
}
public void alert(String msg) {
service.send(msg);
}
}
// 구성 루트(main)
public class Main {
public static void main(String[] args) {
MessageService service = new EmailService(); // 구체 클래스 생성은 여기서만
Notification notification = new Notification(service);
notification.alert("DIP 적용 완료");
}
}
이 예시에서는 Notification이 EmailService에 직접 의존하지 않고, 추상 MessageService에 의존한다.
EmailService를 FileService, SMSService 등으로 쉽게 교체할 수 있고, 테스트도 수월해진다.
모든 부분을 DIP를 지켜야하는가?
DIP와 DI를 통해 많은 의존성을 추상화할 수 있지만, 모든 의존성을 추상화할 수는 없다.
대부분의 시스템은 적어도 하나의 구체 컴포넌트를 의존하는 컴포넌트를 포함하며, 이는 보통 main()을 포함하는 시작점이다.
그곳에서 구체 인스턴스를 생성하고, 나머지 시스템에 주입하는 구조로 만든다.
※ 본 글은 『Clean Architecture』(로버트 C. 마틴 저) 3부의 11장을 기반으로 학습 목적으로 요약한 글입니다.
※ 이 글은 책의 내용을 요약한 것으로, 원문 없이 읽을 경우 오해의 여지가 있을 수 있습니다. 정확한 이해를 위해 원서의 정독을 권장합니다.
'아키텍처' 카테고리의 다른 글
Clean Architecture 정리 - 컴포넌트 원칙 (2) (1) | 2025.07.05 |
---|---|
Clean Architecture 정리 - 4부 컴포넌트 원칙(1) (0) | 2025.06.21 |
Clean Architecture 정리 - 3부 10장 인터페이스 분리 법칙 (0) | 2025.06.14 |
Clean Architecture 정리 - 3부 9장 리스코프 치환 원칙 (0) | 2025.06.13 |
Clean Architecture 정리 - 3부 8장 개방-폐쇄 원칙 (3) | 2025.06.12 |