유니티 사화 리팩토링

촉박한 일정과 잦은 기획 변경으로 유니티 사화의 코드가 스파게티화되어, 유저 피드백 반영이 어려운 상황이었습니다.

유지보수 가능한 구조로 다시 짜는 것이 목적이었고, TODO를 거쳐 Steam·Google Play에 업데이트로 배포했습니다.

유니티 사화 대표 이미지

프로젝트 개요

프로젝트 정보

  • 장르: 3D 액션 어드벤처
  • 플랫폼: Windows, Android
  • 엔진: Unity 6 (6000.3.11f1)
  • 언어: C#
  • 기간: 2026.04 ~ 2026.09 (1인 프로젝트)

개발 정보

  • 일부 코드를 제외한 코드 설계(결합도 낮추기, 책임 분리) 진행
  • 설계 방향과 판단은 본인이, 평가와 반복 작업은 일부 Claude Code가 진행
  • 주요활동 요약
  • 그 외 활동 요약

리팩토링 전후 유지보수 비교

(몬스터, 기믹, 테스트코드 제외)

리팩토링 이전 리팩토링 이후

클래스 간 직접 결합

▼ 100%

싱글톤 · public 직접 참조 155곳을 인터페이스 주입으로 교체(인터페이스 2 → 53개) → 연쇄 수정 차단

ⓘ 싱글톤 · public 직접 참조 155곳을 인터페이스 주입으로 교체(인터페이스 2 → 53개) → 연쇄 수정 차단
155 곳
0 곳

책임 분리

▼ 82%

판정 · 연출 · 사운드 · 카메라를 한 클래스(AnimalMaskSkill)가 쥐고 있던 것을 책임 단위로 해체 → 가장 긴 파일 기준 줄 수

ⓘ 판정 · 연출 · 사운드 · 카메라를 한 클래스(AnimalMaskSkill)가 쥐고 있던 것을 책임 단위로 해체 → 가장 긴 파일 기준 줄 수
1,154 줄
212 줄

상시 실행 코루틴

▼ 95%

매 프레임 폴링을 애니메이션 진행률 이벤트로 교체

ⓘ 매 프레임 폴링을 애니메이션 진행률 이벤트로 교체
87 개
4 개

주요 활동

01

플레이어 상태 시스템 재설계

클래스 하나에 여러 플레이어 상태가 코루틴으로 구현되고, 각 코루틴 내부에서 판단/물리/연출(애니메이션·히트박스·이펙트 등) 기능을 while 문으로 호출

책임이 분리되지 않고 코드가 강하게 결합되어 있어, 기능을 수정하거나 추가하려면 여러 곳을 손대야 하는 구조
  • 상태를 데이터 에셋으로 정의하여 상태머신에서 실행되도록 구현. 상태가 실행될 때, 애니메이션 진행률에 따라 연출 기능들이 독립적으로 호출되도록 이벤트 발행.
  • 데이터 에셋을 추가하는 것으로 상태 추가 가능. 상태 간의 결합, 상태와 기능 간의 결합을 분리함.
02

의존성 주입 시스템 자체 구현

시스템 간 참조를 싱글톤과 `.instance` 호출로 연결해, 어떤 클래스가 무엇에 의존하는지 선언부만으로 파악 불가

초기화 순서가 `Awake` 실행 순서에 좌우되고, 수치 데이터 클래스까지 싱글톤이라 교체·테스트 불가능
외부 DI 프레임워크 대신 `[Inject]` 어트리뷰트 기반 주입 컨테이너를 직접 구현
  • 의존 관계가 선언부(`[Inject]` 필드)에 그대로 드러나 코드만 보고 파악 가능, 필수/선택 의존도 구분되어 표시
03

시스템 경계 재정의

폴더 10개로 나뉘어 있었지만 namespace·인터페이스가 거의 없어 폴더를 넘나드는 참조가 흔했음

폴더는 분류 기준일 뿐 실제 경계 역할은 못 하고 있었던 것이 원인
폴더 하나를 시스템 하나로 못 박고, 코드보다 역할·진입점·외부 연결·설계 규칙을 문서로 먼저 확정하도록 순서 변경
  • 시스템 간 연결이 인터페이스·채널로 한정되어, 한 시스템을 고쳐도 다른 시스템이 그 여파를 받지 않음
04

AI를 품질 게이트로 세운 협업 방식

1인 리팩토링이라 코드 판정을 스스로 내려야 했고, 자기가 짠 코드는 스스로 읽기 쉬운지 판단하기 어려운 구조

팀 프로젝트에서 리뷰어가 걸러주던 판단 편향과 무한정 계속되는 리팩토링을 멈출 기준이 없는 것이 원인
AI에게 코드를 대신 짜게 하는 대신, 정한 기준으로 코드를 검사하게 하는 리뷰어 역할로 고정
  • 판정 기준이 문서·커맨드로 고정되어 코드 리뷰가 취향 논쟁이 아닌 문서 대조 작업이 됨

그 외 활동

창 관리 구조 재설계

창 9개를 `MenuUI` 클래스 하나가 전부 직접 켜고 끄고, 뒤로가기도 if-else 다단 중첩으로 판단 → 창을 추가할 때마다 같은 클래스를 고쳐야 하고, UI가 카메라·플레이어·세이브까지 직접 참조하는 구조가 원인 → 스택이 열린 순서를 대신 기억하게 하고, 창은 `IWindow` 계약만 지키도록 변경

창 추가 시 `MenuUI`를 고칠 필요 없이 `IWindow`를 구현한 창 하나만 만들면 자동으로 등록·관리됨

입력 · 게임 상태 분리

입력을 받아도 되는지를 매 프레임 가드 조건 여러 개를 나열해 판별하고, 캐릭터 분기가 입력 처리 안에 섞여 있음 → 게임 상태(게임플레이·메뉴·컷씬)를 한 곳에서 판단하지 않고 각자 bool을 조회한 것이 원인 → 지금 게임 상태가 도메인 핸들러를 고르고, 핸들러가 처리하는 구조로 변경

입력 소비자가 입력 원본(PC/모바일)이나 캐릭터 종류를 몰라도 되고, 일시정지 등 상태 전환 지점이 한 곳으로 모임

툴 및 편의 기능

빌드 용량 초과, 렌더링 부하, 시야 가림, 데이터 편집 불편 등 플레이어·UI·입력 시스템 밖에서도 문제 산재 → 원인이 서로 다른 문제라 각각 계측·역추적으로 원인을 특정하는 과정이 필요 → 문제별로 도구를 직접 만들어 추측 대신 계측 기반으로 해결

Steam·Google Play 업데이트 배포까지 마쳐, 구조 변경이 실제 환경에서도 문제없이 동작함을 확인