안녕하세요 주인장입니다.
저번 글에 이어 오늘도 SOLID 원칙에 대해 알아보겠습니다.
SOLID 란 무엇인가?
SOLID는 다음 다섯 가지 원칙으로 이루어져 있습니다.
S - Single Responsibility Principle, 단일 책임 원칙
O - Open/Closed Principle, 개방-폐쇄 원칙
L - Liskov Substitution Principle, 리스코프 치환 원칙
I - Interface Segregation Principle, 인터페이스 분리 원칙
D - Dependency Inversion Principle, 의존성 역전 원칙
ISP(인터페이스 분리 원칙) - 필요한 기능만 제공하라
인터페이스 분리 원칙이란 객체는 자신이 사용하는 기능에만 의존해야하고,
사용하지 않는 기능에는 의존하면 안된다 라는 법칙입니다.
바꿔말하면,
"쓸데없이 많은 기능을 한 인터페이스에 몰아넣지 말고, 필요한 기능만 딱딱 나눠라"
라는 의미 입니다.
한 인터페이스가 너무 많은 기능을 갖고 있으면,
그걸 구현하는 클래스들은 사용하지 않는 기능까지 강제로 구현해야 하기 때문입니다.
그리고 이런 구조는 시간이 지나면 반드시 문제로 되돌아옵니다.
ISP가 지켜지지 않으면 생기는 문제들
1. 필요 없는 기능까지 억지로 구현해야 한다
예를 들어 IPlayerAction 인터페이스에 이런 기능들이 있다고 합시다
- Move()
- Jump()
- Attack()
- Fly()
- Swim()
그런데 설정상 어떤 캐릭터는 날지 못하고, 어떤 캐릭터는 수영을 못 합니다.
그러면 어떻게 될까요?
Fly() { /* 빈 함수 */ }
Swim() { /*Not Implemented*/}
이런 코드들이 등장합니다.
이는 클래스가 필요하지 않은 기능에 억지로 의존하고 있어 ISP 위반입니다.
또한 이런 필요없는 함수를 부모에서 호출 하는 순간 LSP까지 위반합니다.
2. 인터페이스가 커질수록 리팩토링이 어려워짐
인터페이스는 한 번 정해지면 바꾸기 어려운 구조입니다.
그런데 너무 많은 기능을 한 인터페이스에 몰아넣으면
인터페이스 수정 → 인터페이스를 구현하는 모든 클래스 수정 → 해당 클래스를 사용하는 모든 코드를 수정
이렇게 연쇄적인 스노우볼이 발생합니다.
인터페이스 하나를 수정하려고 프로젝트 전체가 흔들리는 구조는
확실히 좋은 구조가 아닙니다.
3. 변경 영향 범위가 넓어져 결국 유지보수성이 떨어짐
인터페이스가 비대해질수록 이를 구현하는 클래스는 많아지고,
그 클래스들은 서로 다른 기능때문에 필요 이상으로 연결됩니다.
결국 한 기능을 수정했을 뿐인데
전혀 관련 없는 클래스까지 다시 테스트해야 하는 상황이 생기죠.
이건 변경에 약한 구조를 의미합니다.
인터페이스는 가벼워야 합니다.
그 인터페이스가 가지고 있는 기능도 단일 목적을 지향해야 합니다.
Unity에서 ISP를 적용한 간단한 예시
ISP 위반 - Player 의 행동을 한 인터페이스에 몰아넣은 구조
public interface IPlayerAction
{
void Move();
void Jump();
void Attack();
void Fly();
void Swim();
}
문제점:
- 인간형 캐릭터는 Fly()가 필요없음
- 조류형 캐릭터는 Swim()이 필요없음
- 어류형 캐릭터는 Jump(), Attack()이 필요 없을 수도 있음
결국 사용하지 않는 메서드를 override해서 빈 메서드로 남기거나 예외를 던지게 됩니다.
ISP 준수 예시 - 기능을 작은 단위로 나눠주는 구조
public interface IMovable
{
void Move();
}
public interface IJumpable
{
void Jump();
}
public interface IAttackable
{
void Attack();
}
public interface IFlyable
{
void Fly();
}
public interface ISwimmable
{
void Swim();
}
이제 캐릭터는 필요한 인터페이스만 선택적으로 구현 할 수 있습니다
ex1) 인간형 캐릭터
public class Human : IMovable, IJumpable, IAttackable
{
// Move, Jump, Attack 구현
}
ex2) 조류형 캐릭터
public class Bird : IMovable, IFlyable, IAttackable
{
// Move, Fly, Attack 구현
}
ex3) 어류형 캐릭터
public class Fish : IMovable, ISwimmable
{
// Move, Swim 구현
}
이제 더 이상 필요하지 않는 기능에 의존할 필요가 없습니다.
인터페이스 분리 원칙 체크리스트
1. 한 인터페이스가 너무 많은 기능을 갖고 있는가?
→ 인터페이스가 비대해지고 구현 클래스가 불필요한 기능까지 의존한다, ISP 위반
2. 특정 클래스가 사용하지 않는 메서드를 구현하고 있는가?
→ 사용하지 않거나 비어있는 메서드로 인해 예외가 발생한다, ISP 위반
3. 인터페이스를 수정할 때 연관된 클래스들이 줄줄이 변경되는가?
→ 변경 영향 범위가 과도하게 넒은 인터페이스는 잘못된 것, ISP 위반
4. 기능을 독립된 역할 단위로 더 작게 쪼갤 수 있는가?
→ 기능이 서로 독립적이라면 분리해야 ISP에 맞는 구조가 됨
마치며
인터페이스 분리 원칙은 너무 단순해서
처음에는 "그냥 단순히 인터페이스를 잘게 쪼개라는 건가?" 싶을 수 있는데,
실제로는 구조의 안정성을 크게 좌우하는 중요한 원칙입니다.
인터페이스가 기능별로 작고 명확하게 나뉘어 있을수록
구조는 가벼워지고, 확장은 쉬워지고, 유지보수는 단단해집니다.
오늘의 기록은 여기까지, 주인장은 이만 로그아웃합니다. 모두 편안한 밤 되세요!
'개발 > Unity' 카테고리의 다른 글
| [Unity] Object Pool (0) | 2026.03.24 |
|---|---|
| [Unity] SOLID 원칙 (5) DIP : 의존성 역전 원칙 (0) | 2025.12.02 |
| [Unity] SOLID 원칙 (3) LSP : 리스코프 치환 원칙 (0) | 2025.11.26 |
| [Unity] SOLID 원칙 (2) OCP : 개방 폐쇄 원칙 (0) | 2025.11.25 |
| [Unity] SOLID 원칙 (1) SRP : 단일 책임 원칙 (0) | 2025.11.24 |