Quick Flow
SRP는 class를 작게 만드는 규칙이 아니라 변경 이유와 state owner를 하나로 묶는 기준입니다. Player object에서 input, simulation, presentation, persistence가 독립적으로 바뀌면 coordinator는 흐름만 연결하고 각 component·service가 자기 state와 lifecycle을 소유하게 합니다.
InputReader -> MoveCommand -> PlayerMotor -> PlayerState
-> Health / Combat
PlayerState events -> Presenter -> HUD / audio / VFX
SaveService <-> snapshot contract| 함께 바뀌는 이유 | 둘 위치 | 섞였을 때 증상 |
|---|---|---|
| key mapping·device input | InputReader | UI change가 player movement code를 수정 |
| physics·movement tuning | Motor | HUD가 FixedUpdate state를 직접 변경 |
| 표시·animation·sound | Presenter | combat rule에 UI reference가 늘어남 |
| save format·migration | Persistence | gameplay object가 file I/O를 소유 |
책임과 흐름을 함께 보기
PlayerController가 커졌다는 사실만으로 나눌 필요는 없습니다. input rule, motor physics, health rule, HUD format, save schema처럼 서로 다른 변경이 같은 class를 수정하게 되는지 봅니다. 분리 뒤에도 각 component가 서로 GetComponent로 모든 sibling을 찾으면 책임은 파일만 옮긴 것입니다.
coordinator는 command 전달·lifecycle 연결·결과 event처럼 흐름을 소유할 수 있지만, 모든 domain rule과 UI를 다시 구현하는 god object가 되면 안 됩니다. 어떤 component가 state를 변경하고 누가 observe하는지 data flow를 draw해 direct reference와 event를 필요한 곳에만 둡니다.
Unity lifecycle을 경계로 쓰기
Update, FixedUpdate, LateUpdate, OnEnable, OnDisable, scene unload는 다른 time domain입니다. input component가 fixed physics state를 직접 여러 번 바꾸거나 UI presenter가 disabled 뒤에도 event를 받으면 분리한 책임이 다시 섞입니다. subscription, coroutine, async task의 start/stop owner를 component별로 둡니다.
과분리를 피하기
서로 항상 같이 바뀌고 같은 invariant를 가진 field·method는 한 component에 남겨야 합니다. PlayerMoveData, PlayerMoveHelper, PlayerMoveUtils처럼 이름만 나눈 file은 SRP가 아닙니다. 변경 요구 하나를 넣을 때 어떤 class·scene reference·test가 바뀌는지 보고 응집도를 검증합니다.
Inspector에서 연결하기 편하다는 이유로 모든 reference를 MonoBehaviour 하나에 모으면 변경 이유도 함께 모입니다. file 수가 아니라 source of truth·data flow·lifecycle owner가 분리됐는지 확인하세요.
참고 링크
2 sources