숏컷 코드
계약만 필요
-> interface
공통 기본 구현과 상태 필요
-> abstract class문법과 예시
인터페이스는 "무엇을 할 수 있는가"를 약속하고, 추상 클래스는 기본 골격을 제공한다
IDamageable, IInteractable, ISaveTarget처럼 여러 계층에서 공통 계약만 필요하면 인터페이스가 자연스럽습니다.
반대로 공통 필드, 기본 메서드, 템플릿 메서드 흐름이 필요하다면 추상 클래스가 더 적합합니다.
interface
- TakeDamage()
- Interact()
abstract class
- currentHp
- maxHp
- ApplyDamage()
- Die() 일부 기본 구현Unity에서는 이미 상속 중인 경우가 많아서 인터페이스 가치가 더 커진다
MonoBehaviour를 상속해야 하는 순간, 다른 추상 클래스 상속 여지는 줄어듭니다. 그래서 Unity에서는 "공통 계약은 인터페이스, 공통 로직은 컴포넌트 또는 헬퍼로 분리"가 자주 나옵니다. 추상 클래스는 base presenter, base ability, base state처럼 실제 공유 골격이 강할 때만 쓰는 편이 안전합니다.
인터페이스가 항상 더 가볍진 않다
인터페이스만 잔뜩 만들면 구현이 어디에도 모이지 않아 공통 규칙이 복제될 수 있습니다. 예를 들어 모든 적이 비슷한 체력 규칙을 쓰는데 IDamageable만 두고
구현을 각 클래스에 복사하면 중복이 쌓입니다. 이 경우는 추상 클래스나 공통 컴포넌트가 더 낫습니다.
가장 흔한 실전형은 "인터페이스 + 공통 컴포넌트" 조합이다
호출부는 인터페이스를 보고, 실제 공유 로직은 별도 컴포넌트나 서비스에 위임하면 Unity와 잘 맞습니다. 이 방식은 계약과 재사용을 동시에 잡기 쉽습니다.
UI와 gameplay는 선택 기준이 조금 다르다
gameplay 쪽은 IDamageable, ITargetable처럼 호출부 계약 분리가 우선이고, UI 쪽은 base presenter나 base panel처럼 공통 생명주기 골격이 더 자주 필요합니다.
그래서 gameplay는 인터페이스 쪽으로, UI는 추상 클래스 혼합 쪽으로 기우는 경우가 많습니다.
인터페이스와 추상 클래스를 고를 때 핵심
| 상황 | 적합한 선택 |
|---|---|
| 계약만 맞추면 되고 구현은 제각각일 때 | interface |
| 공통 상태와 기본 구현이 강할 때 | abstract class |
| MonoBehaviour 상속이 이미 필요한 경우 | interface 또는 컴포넌트 조합 우선 |
| 공통 규칙이 여러 구현에 반복될 때 | abstract class 또는 공통 컴포넌트 |
| 호출부는 계약만 알고 싶고 구현은 교체 가능해야 할 때 | interface |
특이 케이스와 주의할 점
흔한 실패는 "유연해 보여서" 모든 것을 인터페이스로만 만드는 것입니다. 계약은 분리됐지만 공통 로직이 복사되면 유지보수는 더 어려워집니다. 반대로 공유 필드가 없는데도 추상 클래스로 묶으면 불필요한 계층만 생깁니다.
실패 예시
- IEnemy, IFlyEnemy, IMeleeEnemy, ICasterEnemy만 늘어남
- 실제 공통 체력/피격 규칙은 각 클래스가 복붙
결과
- 계약은 많지만 재사용은 약함
- 수정 지점이 여러 구현에 퍼짐참고 링크
1 sources