핵심 인사이트 (3줄 요약)
- 본질: ISP (Interface Segregation Principle)는 객체지향 설계에서 클라이언트가 자신이 사용하지 않는 잉여 메서드에 의존하도록 강제되어서는 안 된다는 원칙이다.
- 가치: 하나의 거대하고 비대한 만능 인터페이스(Fat Interface)를 여러 개의 작고 구체적인 역할 인터페이스로 쪼개어, 불필요한 결합도를 낮추고 시스템 변경에 따른 파급력을 차단한다.
- 판단 포인트: 새로운 기능 요구사항이 생길 때 기존 인터페이스에 메서드를 무작정 덧붙일 것이 아니라, 해당 기능을 호출하는 클라이언트의 목적에 맞춰 새로운 전용 인터페이스를 독립시킬 수 있는지 판단해야 한다.
Ⅰ. 개요 및 필요성
ISP (Interface Segregation Principle)는 로버트 C. 마틴(Robert C. Martin)이 제안한 객체지향 설계의 SOLID 5원칙 중 'I'에 해당하는 강력한 규약이다. 이 원칙은 인터페이스를 정의할 때 제공자(구현 클래스) 중심이 아니라, 철저히 사용자(클라이언트) 관점에서 접근하고 분리할 것을 요구한다.
과거에는 여러 기능을 한곳에 모아둔 뚱뚱한 '만능 인터페이스'를 만드는 것이 코드 재사용성이 높다고 착각하는 안티패턴이 만연했다. 하지만 이런 만능 인터페이스를 구현하는 클래스는 자신이 전혀 쓰지 않는 잉여 메서드들까지 강제로 구현(Override)해야 하고, 텅 빈 예외(NotSupportedException) 코드를 작성해야 하는 기형적인 구조를 낳는다. ISP가 없으면 단 하나의 사소한 인터페이스 변경이 자신과 무관한 모든 클라이언트 시스템의 재컴파일을 유발하는 재앙으로 이어지게 된다.
- 📢 섹션 요약 비유: 헬스장에 등록했는데 수영장, 골프장, 사우나 이용료까지 전부 묶인 '통합 VIP 회원권'만 팔아서, 런닝머신만 뛰고 싶은 사람도 비싼 요금을 강요받는 억울한 상황과 같다. ISP는 시설물마다 '개별 회원권'을 쪼개어 파는 것이다.
Ⅱ. 아키텍처 및 핵심 원리
ISP의 핵심 원리는 '인터페이스의 세분화'와 그로 인한 '불필요한 의존성 차단'이다. 비대한 인터페이스는 구현 클래스에 무의미한 더미 코드를 강요할 뿐만 아니라, 샷건 수술(Shotgun Surgery) 문제를 직접적으로 유발한다.
| 설계 구조 | 특징 | 클라이언트 의존성 | 변경 파급력 |
|---|---|---|---|
| Fat Interface (위반) | 모든 기능(인쇄, 스캔, 팩스)이 하나의 통짜 인터페이스에 집중됨 | 자신이 안 쓰는 메서드 영역에도 억지로 의존함 | 기능 하나만 수정해도 모든 클라이언트와 구현체가 깨짐 |
| Role Interface (준수) | 클라이언트 역할별로 잘게 쪼개진 구체적 인터페이스 | 자신이 호출하는 메서드 접점에만 정확히 의존함 | 해당 역할을 쓰는 특정 클라이언트만 제한적으로 영향 받음 |
┌──────────────────────────────────────────────────────────────┐
│ ISP 위반과 준수 구조 아키텍처 비교 │
├──────────────────────────────────────────────────────────────┤
│ [위반: Fat Interface] │
│ +-------------------+ 강제 구현 (안 쓰는 기능도 억지 구현) │
│ │ MultiMachine │◀───────── [일반 흑백 프린터] │
│ │ - print() │ - print() : 정상 구현 │
│ │ - scan() │ - scan() : 텅 빈 에러 │
│ │ - fax() │ - fax() : 텅 빈 에러 │
│ +-------------------+ │
│ │
│ [준수: Segregated Interfaces] │
│ +-------------------+ +-------------------+ │
│ │ IPrinter │ │ IScanner │ │
│ │ - print() │ │ - scan() │ │
│ +-------------------+ +-------------------+ │
│ ▲ │
│ │ 필요한 계약(IPrinter)만 깔끔하게 구현 │
│ [일반 흑백 프린터] │
│ - print() : 정상 구현 │
└──────────────────────────────────────────────────────────────┘
잘게 쪼개진 인터페이스를 도입하면 복합기(Copier) 같이 다기능이 필요한 클래스는 IPrinter, IScanner, IFax를 모두 다중 상속(다중 구현)하여 묶어 쓰면 되고, 단일 기능이 필요한 일반 프린터는 IPrinter만 구현하면 되므로 전체 코드가 훨씬 가벼워진다.
- 📢 섹션 요약 비유: 회사에서 '직원 업무 매뉴얼' 한 권에 개발, 영업, 회계 규정을 다 몰아넣으면 신입사원이 멘붕에 빠진다. 업무별로 '개발자 매뉴얼', '영업 매뉴얼'을 분리해서 각자 필요한 매뉴얼만 보게 만드는 것이 ISP의 세분화 원리다.
Ⅲ. 비교 및 연결
ISP는 객체지향 원칙 중 SRP(Single Responsibility Principle, 단일 책임 원칙)와 매우 깊은 연관이 있으며, 흔히 동전의 양면처럼 함께 작용하여 시스템 품질을 높인다.
| 비교 항목 | ISP (인터페이스 분리 원칙) | SRP (단일 책임 원칙) |
|---|---|---|
| 설계 초점 | 클라이언트(호출자) 관점에서의 겉모습(인터페이스) 분리 | 클래스(구현자) 내부 관점에서의 비즈니스 역할/책임 분리 |
| 적용 대상 | 객체와 객체 간의 통신 접점 (Interface / Protocol) | 클래스나 모듈의 내부 로직 및 변경 사유 |
| 해결 문제 | 불필요한 의존성으로 인한 억지 예외 구현과 재컴파일 전파 방지 | 하나의 클래스가 여러 이유로 빈번히 수정되어 생기는 결합도 폭발 방지 |
| 상호 작용 | 인터페이스가 잘 분리되면 이를 구현하는 클래스의 책임도 자연스럽게 분산됨 | SRP를 잘 지킨 클래스는 보통 작고 명확한 ISP 기반 인터페이스를 외부에 노출함 |
결과적으로 두 원칙 모두 "비대해진 것을 작게 나누어 변경의 파급력을 가둔다"는 공통의 아키텍처 철학을 바탕으로 한다.
- 📢 섹션 요약 비유: SRP가 주방장, 서빙 직원, 카운터 직원의 '역할(책임)'을 명확히 분담하는 것이라면, ISP는 식당 손님에게 메뉴판 전체가 담긴 백과사전이 아니라 깔끔한 '점심 메뉴판'과 '저녁 메뉴판'을 따로 분리해 건네주는 배려다.
Ⅳ. 실무 적용 및 기술사 판단
실무 아키텍처나 대규모 MSA 시스템 설계 시 ISP는 소리 없이 번지는 인터페이스 오염(Interface Pollution)을 초기에 막아내는 훌륭한 방패가 된다.
체크리스트
- 더미 메서드 냄새(Smell): 특정 인터페이스를 상속받은 구현 클래스 내부에
NotImplementedException을 고의로 던지거나, 아무 내용도 없는 텅 빈 메서드들이 반복적으로 목격되는가? - 클라이언트의 제한적 사용: 해당 인터페이스를 주입받아 사용하는 클라이언트가 거대한 인터페이스 내의 수많은 메서드 중 극히 일부(예: 1~2개)만 고정적으로 호출하고 있는가?
- 다중 상속의 전략적 활용: Java, C#, TypeScript 등 언어가 지원하는 인터페이스 다중 상속 기능을 활용하여 잘게 쪼개진 원자적 인터페이스들을 필요에 따라 유연하게 묶어서(Composition) 사용하고 있는가?
실무 판단 전략 (레거시 환경의 우회 기법)
이미 거대해져 버린 레거시 인터페이스를 당장 쪼개는 것이 운영상 리스크가 크다면, 파사드(Facade) 패턴이나 어댑터(Adapter) 패턴을 중간에 배치하여 클라이언트 측에는 작게 분리된 맞춤형 인터페이스만 노출시키는 '점진적 리팩토링' 전략을 판단해야 한다. 또한 현대의 마이크로서비스 환경에서 모바일, 웹 등 각 프론트엔드 요구사항에 맞춰 API 응답을 분리하는 BFF(Backend For Frontend) 패턴 역시 아키텍처 레벨로 진화한 ISP의 우수 적용 사례로 볼 수 있다.
- 📢 섹션 요약 비유: 이미 단단히 뭉쳐져 만들어진 거대한 스위스 아미 나이프(레거시)를 함부로 부술 수 없다면, 일단 삐져나온 가위 쪽에만 덮개를 씌워 '가위 전용 도구(Adapter)'라고 이름표를 붙인 뒤 손님에게 건네주는 것이 안전한 실무적 지혜다.
Ⅴ. 기대효과 및 결론
ISP를 철저히 지켜내면 모듈 간의 결합도가 획기적으로 끊어져 유연하고 가벼운 시스템 구조를 오랫동안 유지할 수 있다. 변경 사항이 발생하더라도 해당 메서드를 실제로 호출하는 클라이언트와 구현 클래스만 국소적으로 영향을 받기 때문에 시스템의 유지보수성이 극대화된다. 또한, 단위 테스트(Unit Test) 작성 시 불필요한 메서드까지 거대하게 모킹(Mocking)해야 하는 고통을 없애주어 테스트 용이성 또한 폭발적으로 향상된다.
결론적으로 인터페이스는 '이 객체가 모든 것을 다 할 수 있다'고 자랑하는 백화점식 카탈로그가 아니라, '클라이언트가 지금 이 순간 무엇을 약속받을 수 있는가'를 보장하는 최소한의 엄격한 계약서여야 한다. 크고 두꺼운 계약서 한 장보다는 작고 구체적인 계약서 여러 장이 시스템을 훨씬 더 단단하게 지켜준다는 철학으로 기억해야 한다.
- 📢 섹션 요약 비유: 최고의 명장들이 쓰는 도구 상자는 한 칸에 모든 공구가 무식하게 뒤엉켜 있는 상자가 아니다. 십자 드라이버 칸, 렌치 칸이 정교하게 나뉘어 있어 눈을 감고도 원하는 도구만 쏙 빼쓸 수 있는 가벼운 상자다.
📌 관련 개념 맵
| 개념 | 연결 포인트 |
|---|---|
| SRP (단일 책임 원칙) | 클래스를 단일 책임으로 나누는 과정이 인터페이스 분리 설계를 가장 자연스럽게 유도함 |
| 어댑터 패턴 (Adapter Pattern) | 기존의 뚱뚱하고 변경 불가능한 인터페이스를 클라이언트가 원하는 얇은 인터페이스로 래핑하여 변환할 때 필수 |
| BFF (Backend For Frontend) | 단일 거대 API를 쓰는 대신, 아키텍처 수준에서 각 클라이언트(모바일/웹)의 요구에 딱 맞춰 통신 인터페이스를 분리 제공하는 패턴 |
| LSP (리스코프 치환 원칙) | 인터페이스 분리를 통해 의미 없는 에러 구현을 막아내면, 하위 타입이 상위 타입을 오류 없이 완벽히 대체(LSP 준수)하기가 훨씬 쉬워짐 |
📈 관련 키워드 및 발전 흐름도
객체지향 프로그래밍 (OOP)의 대중화 - 재사용성을 핑계로 한 거대 상속 구조 등장
│
▼
God Object 및 Fat Interface 문제 대두 - 결합도 폭발 및 샷건 수술 현상(수정 시 연쇄 붕괴) 발생
│
▼
SOLID 원칙 정립 - 로버트 C. 마틴에 의한 ISP (인터페이스 분리 원칙) 공식화
│
▼
역할 인터페이스 (Role Interface) 패턴 - 구현체 중심에서 클라이언트(사용자 역할) 중심으로 설계 패러다임 완전 이동
│
▼
컴포넌트 및 MSA 아키텍처로 확장 - 코드 레벨의 인터페이스 분리 원칙이 분산 시스템의 API 설계(BFF 등) 원칙으로 거대하게 진화
👶 어린이를 위한 3줄 비유 설명
- 집 TV 리모컨에 복잡한 버튼이 100개나 있으면 할머니는 헷갈려서 전원 켜기도 너무 어려워하세요.
- 하지만 채널이랑 볼륨 올리는 버튼만 딱 4개 있는 '할머니 전용 미니 리모컨'을 드리면 아주 편하게 웃으며 쓰실 수 있죠.
- 이렇게 복잡한 기능은 숨기고 쓰는 사람한테 꼭 필요한 버튼(기능)만 따로 떼어서 얇게 만들어주는 설계법이 바로 ISP랍니다!