소프트웨어 아키텍처 패턴
DIP란 무엇인가?
silbaram
2025. 6. 24. 21:13
728x90
1. DIP란 무엇인가?
1-1. 공식 정의
- 정의 ― “고수준 모듈은 저수준 모듈의 구현 세부사항이 아니라 추상화(인터페이스·추상 클래스) 에 의존해야 한다”.
- 추가 규칙 ― “추상화는 세부사항에 의존하지 말고, 세부사항이 추상화에 의존하도록 뒤집어라”.
1-2. 역사와 기원
Robert C. Martin(‘Uncle Bob’)이 Xerox 프린터 관리 시스템에서 “Job” 클래스가 하위 드라이버 로직에 엉겨 붙어 빌드·배포가 지연되던 문제를 해결하며 제안.
2. ‘전통적 의존성’의 문제점
증상 설명
수정 전파 | 하위 모듈 내부 변경이 상위 서비스까지 연쇄 수정·재배포를 유발 |
테스트 어려움 | 구체 클래스 결합으로 목(mock)‧스텁 교체가 번거로움 |
재사용성 저하 | DB·파일·네트워크 등 구현에 종속된 로직은 다른 환경 재사용이 어려움 |
3. DIP 적용 전략 4선
3-1. 추상화 계층 도입
기능 경계마다 Port(interface) ↔ Adapter(implementation) 구조를 만들어 상·하위 모듈을 분리한다. 이는 헥사고날 아키텍처의 핵심 아이디어이기도 하다.
3-2. DI(Dependency Injection)로 추상화 주입
DIP가 “무엇을”(추상화에 의존) 말한다면 DI는 “어떻게” 객체를 주입할지(생성자/메서드/필드) 해법을 제공한다. 스프링·Guice 같은 IoC 컨테이너를 사용하면 DIP를 자연스럽게 실천할 수 있다.
3-3. 레이어 경계 맞춤
서비스 → 도메인 → 인프라 순으로 “안쪽이 바깥을 모른다”는 방향성을 지키면 하위 기술 교체가 상위 모듈에 영향을 주지 않는다.
3-4. 패키지·모듈 분리
core(비즈니스 규칙)·infra(구현) 모듈을 분리하고, 공통 인터페이스만 core에 위치시켜 교차 의존을 차단한다.
4. 코드로 보는 DIP
4-1. Java 예제 (결제 시스템)
// 추상화 (Port)
public interface PaymentGateway {
void pay(int amount);
}
// 고수준 모듈
public class CheckoutService {
private final PaymentGateway gateway;
public CheckoutService(PaymentGateway gateway) { this.gateway = gateway; }
public void checkout(int price) { gateway.pay(price); }
}
// 저수준 모듈 (Adapter)
public class StripeGateway implements PaymentGateway {
public void pay(int amount) { /* Stripe API 호출 */ }
}
CheckoutService(비즈니스) 는 StripeGateway 가 아닌 PaymentGateway 인터페이스에만 의존한다.
4-2. Kotlin 예제 (모바일 알림)
// Port
interface Notifier { fun send(msg: String) }
// Core
class AlarmService(private val notifier: Notifier) {
fun trigger() = notifier.send("Wake up!")
}
// Adapters
class SmsNotifier : Notifier { override fun send(msg: String) { /* SMS */ } }
class EmailNotifier : Notifier { override fun send(msg: String) { /* Email */ } }
알림 채널 교체가 AlarmService 에 영향 없이 가능해 테스트·확장이 쉬워진다.
5. DIP와 DI 컨테이너의 시너지
스프링 Boot 예를 들면 @Configuration 에서 PaymentGateway 구현을 주입(Bean 등록)함으로써 상위 모듈이 구현체를 몰라도 동작한다. 컨테이너는 객체 생명주기·스코프·프로파일 분기를 제공해 DIP를 실용적으로 구현한다.
6. 다른 SOLID 원칙과의 관계
원칙 시너지 포인트
SRP | 비즈니스 로직과 기술 세부사항을 분리해 각자의 단일 책임 유지 |
OCP | 추상화 추가만으로 새 구현을 플러그인할 수 있어 폐쇄·확장성 달성 |
ISP | 세분화된 인터페이스가 DIP 적용 시 과도한 의존을 방지 |
7. 실전 체크리스트
- 비즈니스 레이어가 jdbc.*, aws.* 같은 구체 패키지를 직접 import 하고 있지 않은가?
- 단위 테스트에서 실제 DB·네트워크를 꼭 띄워야만 동작하는가?
- 인터페이스가 구현체와 같은 패키지에 뒤섞여 있지 않은가?
- 컨테이너 없는 ‘순수 객체’ 상태에서도 비즈니스 로직이 실행 가능한가?
728x90