스택큐힙리스트

인터페이스는 “딱 쓰는 만큼만!” ― ISP(인터페이스 분리 원칙) 완벽 가이드 본문

개발

인터페이스는 “딱 쓰는 만큼만!” ― ISP(인터페이스 분리 원칙) 완벽 가이드

스택큐힙리스트 2025. 7. 13. 15:18
반응형

클라이언트는 자신이 사용하지 않는 메서드에 의존해선 안 된다.
― 로버트 C. 마틴

SOLID 다섯 형제 중 네 번째, ISP(Interface Segregation Principle) 는 “세분화된 인터페이스”로 코드 의존도를 슬림하게 만드는 비법입니다.


1. ISP가 왜 필요할까?

  • 쓸데없는 구현 강요 OUT
    거대한 인터페이스는 “안 쓰는 메서드”까지 구현하게 만들고, 결국 버그와 의존성 폭탄을 낳습니다.
  • 변경 범위 최소화
    잘게 나뉜 인터페이스는 새로운 기능이 추가돼도 영향 범위가 좁아 회귀 위험이 적습니다.
  • 테스트·모킹이 쉬워진다
    필요한 기능만 담긴 인터페이스를 모킹하면 단위 테스트가 간단해집니다.

2. ISP가 깨질 때 나타나는 신호

  1. ‘God Interface’: 메서드 수십 개짜리 인터페이스를 구현하느라 빈 몸통 코드가 늘어난다.
  2. 불필요한 예외 처리: 사용하지 않는 메서드에 UnsupportedOperationException을 던진다.
  3. 타입 체크 남발: “이 객체는 이 기능이 없으니 if (obj is SomeType)” 같은 코드가 자주 보인다.

3. Kotlin 예제로 보는 Before ↔ After

❌ Before: 거대 인터페이스

interface Machine {
    fun print()
    fun scan()
    fun fax()
}

class SimplePrinter : Machine {
    override fun print() { /* ... */ }
    override fun scan() = error("Unsupported")
    override fun fax()  = error("Unsupported")
}

SimplePrinter 는 인쇄 기능만 필요하지만 쓸모없는 메서드 두 개를 구현합니다.

✅ After: 인터페이스 분리

interface Printer { fun print() }
interface Scanner { fun scan() }

class SimplePrinter : Printer {
    override fun print() { /* ... */ }
}

class MultiFunctionPrinter : Printer, Scanner {
    override fun print() { /* ... */ }
    override fun scan()  { /* ... */ }
}

이제 각 클래스는 딱 필요한 기능만 구현해도 됩니다. 변경 요구가 생겨도 인터페이스 한두 개만 손보면 끝!


4. 실무 적용 체크리스트

  1. 역할별로 인터페이스 나누기
    “출력”, “저장”, “전송”처럼 기능 단위로 인터페이스를 정의하세요.
  2. DI 컨테이너 활용
    Spring Bean·Koin 등으로 인터페이스 구현체를 주입하면 교체가 자유로워집니다.
  3. CI에서 인터페이스 린트
    SonarQube·Detekt 규칙으로 ‘메서드 10개 초과 인터페이스’ 알람을 걸어두면 예방 효과가 큽니다.
  4. 모듈 경계에 적용
    마이크로서비스 간 API도 ISP 관점으로 작게 쪼개면 배포 충돌을 줄일 수 있습니다.

5. 오늘 당장 시작해 볼 것

  • 프로젝트 검색창에 UnsupportedOperationException을 입력해보세요.
    많이 나오면 ISP 위반 후보입니다!
  • 거대 인터페이스를 행동별로 잘라서 새 파일로 추출하고, 의존성 주입 설정만 수정해보세요.
  • PR 리뷰에 “이 인터페이스, 클라이언트가 전부 필요로 하나요?” 체크리스트를 추가하세요.
반응형
Comments