인터페이스 분리 원칙(ISP)은 클라이언트가 사용하지 않는 메서드에 의존하지 않도록 설계하는 방법입니다. 이 가이드는 TypeScript와 React 예제를 통해 ISP 적용법을 단계별로 설명해 결합도를 낮추고 유지보수를 쉽게 만드는 실전 팁을 제공합니다.
December 15, 2025 (4mo ago) — last updated March 29, 2026 (1mo ago)
인터페이스 분리 원칙(ISP) 개발자 가이드
TypeScript와 React 예제로 ISP를 적용해 응집도 높은 인터페이스와 유지보수 쉬운 코드를 만드는 방법을 배웁니다.
← Back to blog
인터페이스 분리 원칙(Interface Segregation Principle, ISP) - TypeScript & React
요약: 인터페이스 분리 원칙(ISP)을 적용해 TypeScript와 React 코드에서 응집도 높은 인터페이스를 설계하고 유지보수를 쉽게 만드는 실전 가이드를 제공합니다.
소개
인터페이스 분리 원칙(ISP)은 어떤 클라이언트도 자신이 사용하지 않는 메서드에 의존해선 안 된다고 말합니다. ISP를 따르면 결합도가 낮아지고 테스트가 쉬워지며 코드 변경이 안전해집니다. 이 가이드는 TypeScript와 React 예제를 통해 단계별로 ISP를 적용하는 방법을 보여줍니다. ISP와 SOLID 원칙에 대한 기초는 업계 표준 문서에서 확인할 수 있습니다1.
인터페이스 분리 원칙이란?

필요한 버튼이 “재생(Play)” 하나뿐인 상황에서 90개의 버튼이 달린 리모컨을 상상해보세요. 이런 불필요한 복잡함은 비대한 인터페이스를 구현해야 하는 소프트웨어 상황과 닮아 있습니다. ISP는 모든 것을 처리하는 하나의 계약 대신, 작고 클라이언트별 인터페이스를 설계하라고 권합니다. 이는 비대한 인터페이스나 ‘갓 인터페이스’ 같은 안티패턴을 피하게 하고 시스템을 더 명확하고 유연하게 만듭니다1.
왜 비대한 인터페이스가 문제인가
- 불필요한 의존성: 클래스가 호출하지 않는 메서드에 결합되어 관련 없는 변경이 위험해집니다.
- 인지적 과부하: 개발자가 필요한 것을 찾기 위해 관련 없는 메서드를 훑어봐야 합니다.
- 빈 구현: 사용하지 않는 메서드를 스텁으로 만들어 보일러플레이트가 늘어납니다.
핵심 아이디어는 단순합니다: 각 클라이언트에 정확히 필요한 것만 제공하세요. 이렇게 하면 컴포넌트를 이해하고 테스트하며 진화시키기가 쉬워집니다.
모놀리식 인터페이스 vs 분리된 인터페이스
나란히 비교하면 트레이드오프가 분명합니다.
| 속성 | 모놀리식 인터페이스(안티패턴) | 분리된 인터페이스(ISP) |
|---|---|---|
| 결합도 | 높음; 클래스가 사용하지 않는 메서드에 의존함. | 낮음; 클라이언트는 필요한 메서드에만 의존함. |
| 응집도 | 낮음; 관련 없는 메서드들이 함께 그룹화됨. | 높음; 각 인터페이스가 단일 역할을 담당함. |
| 유지보수성 | 어려움; 작은 변경이 넓게 전파됨. | 쉬움; 변경은 관련 클라이언트에만 영향. |
| 테스트 용이성 | 어려움; 목 객체가 크고 취약해짐. | 쉬움; 작은 인터페이스는 목 만들기 간단함. |
ISP를 선택하면 기능 추가나 리팩터링 시 코드베이스가 더 잘 적응합니다.
실무에서의 ISP: 일반적인 위반 사례
위반은 즉시 앱을 망가뜨리진 않지만 유지보수를 고통스럽게 만듭니다. 빈 메서드가 많은 클래스나, 수십 개의 선택적 props를 가진 컴포넌트를 찾아보세요.

TypeScript에서의 고전적인 “갓 인터페이스” (안티패턴)
// ANTI-PATTERN: A "fat interface" that violates ISP
interface IUserActions {
createUser(data: UserData): void;
editUser(id: string, data: UserData): void;
deleteUser(id: string): void;
viewUserProfile(id: string): UserProfile;
changeUserRole(id: string, newRole: Role): void;
publishArticle(article: Article): void;
approveComment(commentId: string): void;
}
이 인터페이스를 구현하도록 강제된 Viewer 클래스는 의미 없는 메서드나 예외를 던지는 메서드를 포함하게 되며, 이는 유지보수 비용과 위험을 증가시킵니다.
React에서의 과도한 컴포넌트 props
프론트엔드에서 흔한 증상은 많은 선택적 props를 가진 범용 Card 컴포넌트입니다. 이런 설계는 유효한 prop 조합에 대한 모호성을 만들고 복잡한 조건부 렌더링을 강요합니다.
// ANTI-PATTERN: A bloated props interface
interface CardProps {
title: string;
description?: string;
imageUrl?: string;
imageAltText?: string;
videoUrl?: string;
authorName?: string;
authorAvatarUrl?: string;
publicationDate?: string;
articleLink?: string;
onClick?: () => void;
// ... and many more optional props
}
이러한 안티패턴은 아래 리팩터링 단계로 이어집니다.
리팩터링 방법: 모놀리식에서 분리된 인터페이스로
ISP로 리팩터링하려면 대규모 재작성은 필요 없습니다. 책임을 분할하고 계약을 명확히하기 위해 작고 단계적인 변경을 적용하세요.

1) TypeScript에서 "갓 인터페이스" 고치기
Step A: 클라이언트 역할을 식별하세요. 예: Admins, Editors, Viewers.
Step B: 역할별 인터페이스를 만드세요.
// GOOD: Focused interfaces
interface IViewerActions {
viewUserProfile(id: string): UserProfile;
}
interface IEditorActions {
publishArticle(article: Article): void;
approveComment(commentId: string): void;
}
interface IAdminActions {
createUser(data: UserData): void;
editUser(id: string, data: UserData): void;
deleteUser(id: string): void;
changeUserRole(id: string, newRole: Role): void;
}
Step C: 필요한 것만 구현하세요.
class Viewer implements IViewerActions {
viewUserProfile(id: string): UserProfile {
console.log(`Fetching profile for user ${id}...`);
// ... fetch and return profile
}
}
관리자는 필요에 따라 여러 인터페이스를 구현할 수 있습니다. 이러한 분리는 불필요한 재컴파일을 줄이고 각 기능의 소유권을 명확히 합니다.
2) 판별 유니언(discriminated unions)으로 과도한 React props 리팩터링
판별 유니언을 사용하면 각 컴포넌트 변형마다 명확한 계약을 가질 수 있습니다. TypeScript의 유니언 타입과 판별자는 이 목적에 적합합니다2.
// GOOD: Base props
type BaseCardProps = {
title: string;
onClick?: () => void;
};
// GOOD: Specific variants
type ImageCardProps = BaseCardProps & {
cardType: 'image';
imageUrl: string;
imageAltText: string;
};
type ArticleCardProps = BaseCardProps & {
cardType: 'article';
description: string;
authorName: string;
articleLink: string;
};
type CardProps = ImageCardProps | ArticleCardProps;
이 패턴은 에디터가 유효한 prop 조합을 즉시 강제하고 잘못된 조합을 방지합니다.
팀 전반에서 ISP를 감사하고 적용하는 방법
일회성 수정도 유용하지만, ISP를 팀 관행에 내재화하면 지속적인 가치를 얻을 수 있습니다. 명확한 감사 기준과 자동화된 검사를 결합해 인터페이스 건강을 유지하세요.
명확한 감사 기준
리뷰 중 ISP 위반을 발견할 수 있는 공유 체크리스트를 만드세요. 예시 레드 플래그:
- 많은 메서드를 가진 인터페이스(5~7개 이상이면 검토할 가치 있음).
- 구현체에 빈 메서드나 스텁 메서드가 있는 경우.
- “Manager”나 “Handler” 같은 모호한 인터페이스 이름.
- 많은 선택적 필드를 가진 컴포넌트 props.
수동 리뷰와 자동화 도구를 결합해 적용 범위를 넓히세요.
자동화된 적용
ESLint와 커스텀 규칙은 크기가 큰 props나 인터페이스를 구현하고도 메서드를 미구현 상태로 두는 패턴을 잡아내는 데 유용합니다3. AI 코딩 도구를 인터페이스 리뷰의 보조로 사용하면 디자인 냄새를 찾아내는 데 도움이 됩니다4.
자동화 검사와 인간 리뷰는 둘 다 중요합니다. 도구는 일관성을 제공하고, 사람은 컨텍스트와 비즈니스 의도를 포착합니다.
| 항목 | 수동 감사 | 자동화된 적용 |
|---|---|---|
| 정확성 | 맥락적 이슈에 대해 높음 | 정의된 규칙에 대해 일관됨 |
| 일관성 | 리뷰어에 따라 다름 | 동일한 규칙이 어디서나 적용됨 |
| 속도 | 대규모 코드베이스에 대해 느림 | 빠르며 IDE/CI에 통합 가능 |
둘을 결합하면 TDD 워크플로우가 개선됩니다. 더 작은 인터페이스는 목(Mock)과 테스트가 간단해 Red-Green-Refactor 사이클을 빠르게 합니다5.
ISP와 AI 주도 개발
AI 도구가 개발 워크플로우에 자리잡으면서, 명확한 인터페이스는 모델이 코드를 더 정확히 이해하고 변경 제안을 신뢰할 수 있게 만듭니다. 작은 인터페이스는 모호성을 줄여 생성 코드와 자동 리팩터링의 품질을 높입니다4.
깨끗한 인터페이스는 인간과 기계 모두에게 정확한 브리프처럼 작동합니다. 그 명확성은 추측을 줄이고 더 신뢰할 수 있는 자동 보조를 만들어냅니다.
빠른 리팩터링 체크리스트
- 각 인터페이스의 역할과 클라이언트를 식별하세요.
- 큰 인터페이스를 역할별 계약으로 분리하세요.
- 변형 컴포넌트 props에는 TypeScript 판별 유니언을 사용하세요2.
- 크기가 큰 인터페이스나 props를 플래그하는 린트 규칙을 추가하세요3.
- 자동 검사와 코드 리뷰를 결합해 미묘한 결정을 처리하세요.
세 가지 간결한 Q&A (개발자 주요 질문)
Q: ISP 위반을 빠르게 식별하려면 어떻게 하나요?
A: 관련 없는 많은 메서드를 가진 인터페이스, 빈 구현이나 예외를 던지는 클래스, 수십 개의 선택적 props를 가진 컴포넌트를 찾아보세요. 이런 징후는 계약을 분리해야 한다는 신호입니다.
Q: "비대한 인터페이스"를 가장 빠르게 고치는 방법은?
A: 클라이언트 역할을 명확히 식별하고 역할별로 집중된 인터페이스를 만든 뒤 구현체들이 필요한 것만 구현하도록 점진적으로 업데이트하세요.
Q: 성장하는 코드베이스에서 ISP 회귀를 어떻게 방지하나요?
A: 크기가 큰 인터페이스를 감지하는 린트 규칙과 CI 검사, 인터페이스 설계에 대한 리뷰 체크리스트를 도입하세요. 자동화로 일상 검사를 처리하고 리뷰로 설계 결정을 다루면 효과적입니다.
At Clean Code Guy, we help teams apply principles like ISP to build maintainable, AI-ready codebases. Get a free codebase audit to find hidden issues and improve long-term maintainability.
AI가 코드를 작성합니다.당신이 그것을 지속시킵니다.
AI 가속 시대에 클린 코드는 단순히 좋은 관행이 아닙니다 — 확장되는 시스템과 자체 무게로 붕괴되는 코드베이스의 차이입니다.