Quick Flow
SOLID는 다섯 가지를 동시에 적용하는 설계 양식이 아니라, 변경이 비싸진 이유를 찾는 진단 순서입니다. 새 요구 하나를 골라 바뀌는 class, asset, scene reference, test를 따라가고, 가장 큰 결합을 먼저 줄입니다.
요구: 새 무기 규칙 추가
-> central switch와 call site가 함께 바뀌는가? OCP
-> 구현을 바꾸면 caller 결과·cleanup이 달라지는가? LSP
-> consumer가 쓰지 않는 member까지 요구받는가? ISP
-> gameplay가 구체 scene component를 직접 만드는가? DIP
요구: HUD 또는 저장 형식 변경
-> combat class까지 같이 바뀌는가? SRP| 증상 | 먼저 보는 원칙 | 고칠 대상 |
|---|---|---|
| input·physics·HUD·save가 한 component에 섞임 | SRP | 변경 이유와 state owner |
| 새 type마다 central switch가 커짐 | OCP | variation contract와 composition 지점 |
| subtype가 실패·반환·cleanup 의미를 바꿈 | LSP | caller가 의존하는 contract |
| 큰 interface에서 빈 구현이 늘어남 | ISP | consumer별 capability |
high-level rule이 Find·GetComponent로 concrete object를 찾음 | DIP | bootstrap wiring과 dependency 방향 |
원칙을 Unity에 적용하기
SRP는 PlayerController를 파일 여러 개로 쪼개라는 뜻이 아닙니다. input mapping, physics motor, combat rule, HUD formatting, save schema가 서로 다른 이유로 바뀌면 flow를 연결하는 coordinator와 각 state owner를 분리합니다. 반대로 항상 함께 바뀌는 invariant는 한 component에 남깁니다.
OCP와 DIP는 Inspector 사용 여부를 결정하지 않습니다. ScriptableObject는 shared authoring definition에, MonoBehaviour reference는 scene runtime object에, interface는 code contract에 맞습니다. Unity 기본 serializer는 일반 interface field를 그대로 저장하지 않으므로 interface를 도입한 뒤에는 constructor/installer, ScriptableObject base class, [SerializeReference] 중 실제 wiring 방식을 함께 선택합니다.
LSP는 상속 문법보다 caller contract입니다. IAttack.Execute가 success/failure, animation 완료, resource 소비, cancel과 cleanup을 어떤 순서로 보장하는지 먼저 정의합니다. ISP는 consumer가 IMovable, IDamageable처럼 필요한 capability만 받게 하되, member 이름만 작아지고 lifecycle 의미가 모호한 interface를 늘리지 않게 합니다.
과설계를 막는 확인
작은 UI popup이나 안정적인 enum branch는 plain class·switch가 가장 읽기 쉬울 수 있습니다. variation이 실제로 반복되기 전에는 factory·strategy·interface 층을 미리 쌓지 않습니다. 리팩터링 뒤에는 새 요구를 추가할 때 수정 파일 수가 줄었는지, ownership과 cancellation path가 더 명확해졌는지 확인합니다.
Unity lifecycle도 contract 일부입니다. OnEnable에서 event를 구독했다면 OnDisable에서 해제할지, scene unload에서 dispose할지는 consumer scope에 맞춰 결정합니다. SOLID를 적용해도 static event, destroyed object, duplicate bootstrap이 남아 있으면 실제 결합은 줄지 않습니다.
다음 판단
SRP·OCP·DIP를 먼저 봐도 해결되지 않으면 substitutability(LSP)와 capability 경계(ISP)를 확인합니다. 패턴은 원칙의 대체물이 아니라 구현 수단입니다. strategy는 variation을, factory는 생성을, observer는 notification을 다루므로 각 카드에서 owner와 lifecycle을 별도로 확인합니다.
interface, abstract class, manager를 늘린 뒤에도 새 요구가 같은 수의 파일과 scene reference를 건드린다면 SOLID를 적용한 것이 아니라 이름만 분산한 것입니다. 항상 실제 변경 한 건으로 구조를 검증하세요.
참고 링크
2 sources