결제 규칙을 테스트할 때마다 실제 DB가 필요하고 DB 교체도 정책 코드 수정으로 이어집니다. DIP를 적용해 어떤 인터페이스와 의존 방향을 바꾸나요?
잠깐, 답변을 보기 전에
먼저 내 말로 설명해 볼까요?
답변과 점검 포인트 펼치기답변 접기
구두 답변
정의를 외우기보다, 원리와 조건을 연결해 설명해 보세요.
의존성 역전 원칙은 고수준 정책이 저수준 구현의 세부사항에 의존하지 않고, 둘 다 정책을 표현하는 추상화에 의존하도록 방향을 바꾸는 원칙입니다. 결제 정책은 ‘승인 시도와 결과를 처리한다’는 업무 규칙을 소유해야지 특정 SQL 드라이버나 HTTP SDK의 호출 방식까지 알아서는 안 됩니다. 중요한 것은 클래스 생성자를 통해 객체를 전달하는 의존성 주입 자체가 아니라, 무엇이 안정적인 정책이고 무엇이 교체 가능한 세부사항인지 경계를 뒤집는 것입니다.
예를 들어 CheckoutService가 new PostgresPaymentRepository()를 실행하면 고수준 코드가 저수준 구현의 생성과 API에 묶입니다. PaymentRecordStore처럼 결제 결과의 원자적 기록을 표현하는 업무 중심 추상 계약을 CheckoutService가 사용하고, PostgreSQL 구현이 그 계약을 구현하도록 하면 의존성 그래프의 소스 코드 방향이 정책 쪽을 향합니다. 실행 시 실제 구현을 조립하는 것은 의존성 주입이고, 그 조립만으로 DIP가 성립하는 것은 아닙니다. 추상화가 저장소의 CRUD 메서드를 그대로 복사하면 이름만 추상화하고 정책이 여전히 인프라에 끌려가는 결과가 됩니다.
DIP를 적용한다고 모든 함수와 외부 라이브러리를 인터페이스로 감싸지는 않겠습니다. 교체 가능성, 테스트에서 통제할 필요, 업무 규칙과 기술 세부사항의 변경 주기가 다를 때 경계를 만들겠습니다. 추상화 계층이 지나치게 많으면 흐름을 읽는 비용이 늘고 실제 제약을 숨길 수 있으므로, 정책이 요구하는 좁은 계약과 실제 구현의 계약 테스트를 함께 유지하겠습니다.
포트는 정책 언어로 정의합니다
CheckoutService가 PostgresPaymentRepository를 생성하면 정책이 SQL 드라이버와 저장소 오류에 묶입니다. 정책 계층이 PaymentRecordStore 같은 포트를 소유하고, 그 포트가 멱등 키·승인 결과·이미 처리됨·원자적 기록·재시도 가능 오류를 표현하게 합니다. save·find를 그대로 복사한 인터페이스는 DB 관점을 보존하므로, 결제 규칙이 판단할 의미를 중심으로 좁히겠습니다. 실제 구현을 조립하는 생성자 주입은 DIP를 실행하는 방법일 뿐 그 자체가 원칙은 아닙니다.
대역과 실제 구현을 함께 검증합니다
가짜 저장소로 잔액 부족·중복 요청·저장 실패를 빠르게 시험하되, 실제 DB에서는 트랜잭션 원자성·고유 제약·timeout을 확인합니다. 포트가 DB transaction 객체나 결제 SDK 예외를 그대로 반환하면 고수준 코드가 다시 세부사항에 결합됩니다. 모든 타입을 인터페이스로 만들지 않고 변경 주기와 교체 가능성, 테스트 통제 필요를 기준으로 경계를 정합니다. 구현 교체 뒤 정책 테스트가 그대로 통과하고 실제 계약 테스트도 같은 보장을 확인하는지가 성공 기준입니다.
구체적인 경계 사례
소스 코드 의존 방향과 런타임 호출 방향은 다를 수 있습니다. 실행 중 CheckoutService가 Postgres 어댑터를 호출해도, 어댑터가 정책 계층의 포트를 import하고 정책은 어댑터를 import하지 않으면 소스 의존성은 정책 쪽을 향합니다. 시작점이 양쪽을 알아 객체를 연결하는 것은 정상적인 조립 책임입니다.
추상 계약은 구현이 실제 제공할 수 있어야 합니다. 포트가 외부 결제와 DB 기록을 '절대 한 번에 성공'으로 약속하지만 어댑터가 두 시스템을 원자적으로 묶지 못한다면 추상화가 사실을 숨깁니다. 처리 중·결과 불확정·재조회 가능 상태를 계약에 포함하고, 구현 교체 시 기능뿐 아니라 내구성·순서·취소·오류 의미를 같은 계약 테스트로 확인하겠습니다.
득점 포인트
이해가 드러나는 지점을 확인해 보세요.
- 정책이 소유하는 좁은 포트와 DB·SDK 구현의 의존 방향을 설명한다.
- CRUD 복사본이 아닌 멱등성·원자성·오류 의미를 인터페이스에 담는다.
- DI와 DIP를 구분하고 가짜 구현·실제 구현의 계약 테스트를 함께 둔다.
감점 포인트
놓치기 쉬운 조건과 오해를 짚어 보세요.
- 생성자로 구현을 전달하면 언제나 DIP라고 말한다.
- DB CRUD와 SDK 오류를 그대로 인터페이스에 노출해 정책 결합을 남긴다.
- 모든 타입을 추상화해 실제 저장 제약과 실행 흐름을 숨긴다.
더 파고들 거리
하나 더 질문하며 이해의 범위를 넓혀 보세요.
- 인터페이스는 있지만 DB 패키지가 소유합니다. 추상화의 소유와 고수준 정책의 의존 방향은 어떻게 판단하나요?
- 외부 결제사의 오류·재시도·멱등성 계약을 추상화에 어떻게 표현할까요?
- DI 컨테이너 없이도 컴포지션 루트에서 의존성 방향을 유지하는 방법은 무엇인가요?
득점·감점 포인트는 학습용 자기 점검 기준이며, 특정 회사의 실제 채점표가 아닙니다.
원리부터 공부하고 싶다면
답변을 보충하는 개념·예제·슈도코드를 읽어 보세요.