숏컷 코드
Player
- MovementComponent
- HealthComponent
- WeaponComponent문법과 예시
상속 계층이 기능 조합을 따라가지 못하는 이유
캐릭터, 아이템, 투사체는 "한 가지 정체성"보다 "여러 기능의 조합"으로 움직입니다. Enemy -> FlyingEnemy -> FireFlyingEnemy처럼 계층을 내려가다 보면 기능 하나를 추가할 때마다 새 클래스가 필요해집니다. IceFlyingEnemy, FireGroundEnemy, IceGroundBossEnemy처럼 기능 조합 수만큼 클래스가 폭발하는 현상을 "조합 폭발"이라 부릅니다. 상속은 계층 관계를 코드에 고정시키므로, 기획 변경이 곧 클래스 계층 재구조화로 이어집니다. 게임 개발에서 기획은 항상 바뀌기 때문에 이 비용이 반복적으로 발생합니다.
// 상속으로 만들면:
FireMageEnemy
IceMageEnemy
FireBossEnemy
IceBossEnemy
SummonerBossEnemy // 새 기능 추가 → 새 클래스
// 컴포지션으로 만들면:
Enemy + CastSpell(Fire)
Enemy + CastSpell(Ice)
Boss + CastSpell(Fire)
Boss + Summon() // 새 기능 추가 → 컴포넌트 하나 추가컴포지션은 재사용과 교체가 유연하지만 의존성 관리가 책임이 된다
컴포지션은 기능을 독립적인 컴포넌트로 나눠 조합하므로, 이동 로직을 플레이어와 적이 공유하거나 체력 시스템을 캐릭터와 오브젝트가 함께 쓰기 쉬워집니다. Unity의 GameObject + Component 구조가 이 방식의 대표 사례입니다. 단점은 컴포넌트 간 의존성이 명시적이지 않으면 "WeaponComponent가 HealthComponent를 직접 참조"하는 관계가 생기고, 이것이 쌓이면 "컴포넌트 스파게티"가 됩니다. 컴포지션을 쓸 때는 컴포넌트 간 통신 방식(이벤트, 메시지, 인터페이스)을 명시적으로 설계해야 재사용성을 실제로 얻을 수 있습니다.
상속이 여전히 적합한 경우: 작고 명확한 종류 계층
상속이 맞는 경우는 "이것은 저것의 한 종류이다"가 실제로 성립하고, 그 계층이 얕으며, 공통 인터페이스 계약을 강하게 보장해야 할 때입니다. Collider -> BoxCollider / SphereCollider, UIElement -> Button / Slider 같은 단순하고 안정적인 계층이 대표적입니다. 인터페이스(IAttackable, IDamageable)만 상속하고 구현은 컴포지션으로 위임하는 방식이 실무에서 자주 쓰이는 절충입니다.
// 인터페이스 상속 + 컴포지션 구현 절충
class Enemy : IDamageable {
HealthComponent health;
void TakeDamage(int dmg) { health.Apply(dmg); } // 컴포넌트에 위임
}기능 조합 차이인가, 본질적 종류 차이인가가 판단 기준이다
설계를 선택하는 핵심 질문은 "이 차이가 기능 추가/제거의 차이인가, 아니면 본질적인 종류 차이인가"입니다. 기능 추가라면 컴포지션이 더 오래 갑니다. 종류 차이이고 계층이 얕다면 상속이 더 읽기 쉬울 수 있습니다. ECS(Entity Component System)는 컴포지션을 데이터 지향으로 극단까지 밀어붙인 구조로, 캐시 친화성이 높아 대규모 개체 시뮬레이션에 적합하지만 구조 설계 난이도도 높아집니다.
구조 선택을 할 때 핵심
| 상황 | 적합한 선택 |
|---|---|
| 기능 조합이 다양하고 기획 변경이 잦은 경우 | 컴포지션 우선 |
| 안정적이고 얕은 종류 계층 (Collider, UIElement 등) | 상속 허용 |
| 공통 계약을 강하게 보장해야 할 때 | 인터페이스 상속 + 컴포지션 구현 |
| 수천 개 개체를 매 프레임 처리할 때 | ECS 방식 고려 |
| 컴포넌트 간 통신이 복잡해질 때 | 이벤트 또는 메시지 버스로 명시적 분리 |
| 기능은 자주 바뀌지만 계약만 고정해야 할 때 | 인터페이스 + 컴포지션 조합 우선 |
특이 케이스와 주의할 점
흔한 실패는 상속을 피하겠다고 컴포넌트를 무한히 쪼갠 뒤, 결국 컴포넌트끼리 서로 직접 참조해 더 복잡해지는 것입니다. 반대로 얕고 안정적인 종류 계층까지 억지로 컴포지션으로만 풀 필요도 없습니다. 조합 차이인지, 진짜 종류 차이인지부터 먼저 구분하세요.
참고 링크
1 sources