base
인터페이스에 수십 개의 메서드가 들어있고, 사용하는 건 겨우 두세 개에 불과한 경우에 "이 기능은 쓰지도 않는데 왜 꼭 구현해야 하지?"라는 생각이 드는 경우가 잇습니다. 이렇게 하나의 인터페이스가 지나치게 크고 복잡할 때, 코드를 읽고 관리하는 것 자체가 부담스러워집니다. 이런 불편을 해결하기 위해 제안된 원칙이 바로 인터페이스 분리 원칙(Interface Segregation Principle, ISP)입니다.
실무에서는 “사용하지 않는 메서드를 억지로 구현해야 하는 상황”이 생각보다 자주 발생합니다. ISP는 바로 이러한 문제를 해결하기 위해 로버트 C. 마틴(Robert C. Martin, Uncle Bob)이 제안한 SOLID 원칙 중 네 번째 원칙입니다.
인터페이스 분리 원칙(ISP)은 다음과 같은 명확한 정의를 갖고 있습니다.
클라이언트는 자신이 사용하지 않는 메서드에 의존하지 않아야 한다.
쉽게 말해서 인터페이스를 너무 크고 복잡하게 만들지 말고, 필요한 기능만큼 작게 나누어야 한다는 것입니다. 커다란 인터페이스는 클라이언트가 실제로는 필요 없는 메서드까지 구현하도록 강제함으로써 코드의 복잡성과 유지보수 비용을 증가시킵니다. ISP를 준수하면 인터페이스가 클라이언트의 요구사항에 맞춰 작은 단위로 설계되어 코드의 재사용성과 가독성을 높일 수 있습니다.
구체적인 예시를 통해 ISP를 적용한 전과 후를 명확히 비교해 봅시다.
아래는 ISP를 위반한 대표적인 예시입니다.
interface Animal {
walk(): void
swim(): void
fly(): void
}
class Dog implements Animal {
walk() {
console.log('Dog is walking.')
}
swim() {
console.log('Dog is swimming.')
}
fly() {
throw new Error('Dog cannot fly.')
}
}
여기서
ISP를 준수하면 아래와 같이 인터페이스를 적절히 분리할 수 있습니다.
interface Walkable {
walk(): void
}
interface Swimmable {
swim(): void
}
interface Flyable {
fly(): void
}
class Dog implements Walkable, Swimmable {
walk() {
console.log('Dog is walking.')
}
swim() {
console.log('Dog is swimming.')
}
}
class Bird implements Walkable, Flyable {
walk() {
console.log('Bird is walking.')
}
fly() {
console.log('Bird is flying.')
}
}
이제 Dog는 자신에게 필요한 기능만 구현하게 됩니다. 인터페이스의 책임이 명확해졌고, 각 클래스의 유지보수성과 가독성이 크게 개선되었습니다.
ISP를 따를 경우 얻는 이점은 명확합니다.
인터페이스를 무조건 작게 나눈다고 해서 항상 좋은 설계가 되는 것은 아닙니다. 적절한 수준의 추상화와 균형이 중요합니다.
인터페이스 분리 원칙(ISP)은 단순히 인터페이스를 쪼개는 것을 넘어 클라이언트의 실제 요구사항과 코드 변경 가능성을 고려한 설계 원칙입니다. ISP를 효과적으로 적용하려면 다음 두 가지 핵심 사항을 기억하는 것이 중요합니다.
이러한 접근을 통해 유연하고, 확장성 높은 시스템 구조를 만들어 장기적으로 유지보수와 개발 생산성을 높일 수 있습니다.