Bake It Tight의 초기 빌드를 만들면서 AI를 이전보다 훨씬 적극적으로 쓰게 됐다. Codex를 쓰면서 문서를 만들고, 티켓을 나누고, 구현하고, 테스트를 붙이는 속도도 확실히 빨라졌다.
처음에는 이 속도가 꽤 든든하게 느껴졌다. 이전의 나는 비교적 단순한 캐주얼 게임 개발에 더 익숙했다. 목표가 빠르게 보이고, 조작이 단순하고, 짧은 시간 안에 피드백이 돌아오는 게임을 주로 만들었다.
그런데 Bake It Tight는 조금 달랐다. 좁은 보드 위에 기계를 배치하고, 레일을 연결하고, 창구 주문에 맞춰 생산 라인을 계속 고쳐야 하는 게임이었다.
그래서 나름대로 준비를 했다. 게임의 큰 방향을 정리했고, 기획서도 썼고, 프로토타입으로 플레이 방향도 검증했다. 이후에는 초기 빌드에 필요한 티켓을 뽑고, Codex와 함께 하나씩 구현했다. 당시에는 꽤 체계적으로 개발하고 있다고 생각했다.
지금 돌아보면 그 판단이 안일했다. 특히 “무엇이 가능해야 하는가”는 비교적 많이 고민했지만, “무엇이 되면 안 되는가”는 충분히 고민하지 않았다. 데이터 구조와 설계도 마찬가지였다. 그 빈틈은 나중에 레일 시스템에서 큰 버그로 돌아왔다.
반복되는 버그
처음에는 버그가 각각 다른 문제처럼 보였다. 레일이 이상하게 저장되기도 했고, 기대와 다르게 바뀌기도 했다. 화면에서는 이어져 보이는데도 실제로는 인식되지 않는 경우가 있었다. 어떤 경우에는 이어진 것처럼 보였지만, 라인 계산에서는 유효한 경로가 아니었다.
처음에는 이런 버그를 하나씩 고치면 된다고 생각했다. 하지만 비슷한 문제가 계속 반복되자 원인이 더 깊은 곳에 있다는 느낌이 들었다. 문제는 레일을 그리는 코드 한 줄이 아니었다. 레일이라는 정보를 게임 안에서 어떤 층위로 다룰지 정하지 못하고 있었다.
레일은 화면에 그려진 선처럼 보이지만, 실제 기준은 내부에 저장된 연결 정보여야 했다. 어떤 칸에 레일이 있고 어디로 들어와 어디로 나가는지가 먼저 정해지고, 화면 표시나 라인 판정, 쿠키 이동은 그 정보를 바탕으로 따라와야 했다.
기존 구현에서는 이 우선순위가 흐릿했다. 화면은 화면대로 이어졌다고 말하고, 라인 계산은 라인 계산대로 경로가 없다고 말하고, 생산 시스템은 또 다른 기준으로 packet을 움직였다. 정보의 위계가 없으니 여러 상태가 섞였고, 그래서 버그가 반복됐다.
남아 있던 이전 구현의 잔재
또 다른 문제는 개발 도중 방향을 바꿨는데, 이전 구현의 잔재가 남아 있었다는 점이다.
처음 레일 구현은 드래그 + 추론에 가까웠다. 사용자가 레일을 그으면 드래그 경로를 보고, 주변 기계나 창구도 함께 보면서 레일 방향을 어느 정도 보정했다. 초기에는 이 방식이 편해 보였다. 사용자가 정확히 포트를 찍지 않아도 시스템이 알아서 이어주는 느낌이 있었기 때문이다.
실제로 구현하고 만져보니 이 방식은 모호했다. 플레이어가 드래그한 방향과 시스템이 추론한 방향이 다를 수 있었다. 주변에 기계가 있다는 이유로 사용자가 원하지 않은 방향이 열릴 수도 있었다.
그래서 방향을 바꿨다. 레일은 주변 오브젝트가 아니라 사용자의 드래그만으로 결정하기로 했다. 하지만 기존의 추론 코드는 완전히 지워지지 않았다. 에이전트에게 질문했을 때는 기존 추론 코드가 없어졌다고 했지만, 실제로는 일부 흔적이 남아 있었다.
새 규칙과 옛 규칙이 섞이면서 버그가 계속 생겼다. 에이전트는 종종 자신이 구현한 것에 대해 지나치게 확신한다. 그 확신을 내가 충분히 의심하지 않았다.
왜 이런 문제가 생겼을까
초기 기획에서 원하는 동작은 비교적 잘 설명했다. 플레이어가 기계를 놓는다. 레일을 그린다. 쿠키가 레일을 따라 이동한다. 반죽기, 토핑기, 오븐, 창구가 이어진다. 창구 주문과 맞는 쿠키를 납품한다.
하지만 더 세부적인 질문들을 간과했다.
- input만 있고 output이 없는 레일은 저장해도 되는가?
- 주변 기계가 있으면 자동으로 레일 방향을 열어도 되는가?
- 유효한 라인의 기준이 무엇인가?
- 레일 이미지가 데이터와 다를 때 무엇을 원본으로 믿어야 하는가?
이 질문들은 모두 구현 중에 반드시 답이 필요한 질문이었다. 그런데 나는 초기에 기획서도 썼고, 프로토타입도 만들었고, 티켓도 나누었다. 아무 문서도 없이 시작한 것이 아니었다. 그렇다면 왜 이런 공백이 생겼을까?
가장 큰 이유는 에이전트가 그 빈틈을 채워버렸기 때문이다. 문서에서 답하지 않은 질문이 생겼을 때 구현은 멈추지 않았다. 에이전트는 주변 맥락을 보고 그럴듯한 기본값을 만들었고, 나는 그것이 명시된 규칙이 아니라 추측이었다는 사실을 늦게 알아차렸다.
왜 그런 일이 생겼을까. 결국 역할을 나누지 않았기 때문이었다. 한 컨텍스트 안에서 기획, 구현, 수정, 검토를 모두 이어갔다. 그러다 보니 에이전트는 계속 앞으로 나아가기만 했고, 빈틈을 찾는 역할은 비어 있었다.
실제로 잘못 구현했는데도 에이전트가 자신이 구현한 방식이 맞다고 설명하는 순간이 있었다. 이전에 만든 구현이 있으면 그것을 객관적으로 다시 보기보다 기준점으로 삼고, 이후 설명을 그 구현에 맞춰 이어가려는 경향이 생긴다. 그래서 에이전트의 자가 검토만으로는 구현의 정확성을 보장하기 어렵다.
결국 문제는 문서를 쓰지 않은 것이 아니었다. 문서에 남은 빈칸을 찾고, 구현 후에 요구사항과 실제 결과를 다시 대조하는 과정이 부족했다.
다시 나눈 구조
결국 빈틈이 너무 많아 개별 버그를 계속 고치는 방식으로는 부족했다. 전체 리팩토링 계획을 다시 세우고, 상태와 책임을 분리하는 쪽으로 방향을 잡았다.
상태 분리
- 저장 파일에는 플레이 중 실제로 바뀌는 값만 둔다.
- 변하지 않는 상수와 화면 표시용 값은 저장하지 않는다.
- 화면과 캐시는 저장 상태와 밸런스 데이터에서 다시 만들 수 있어야 한다.
책임 분리
RunStateStore: 실행 중 상태의 단일 소유자Domain Service: 규칙 계산MonoBehaviour: 입력을 받고 서비스를 호출하는 조정자View: 상태 표시Writer: 상태 변경
이 과정을 거치고 나서야 나를 괴롭히던 버그들이 일제히 사라졌다. 지금 생각하면 처음부터 이런 아키텍처 구조와 개발 규칙을 더 명확히 썼어야 했다. 입력, 상태 변경, 계산, 화면 갱신이 한곳에 섞인 상태에서 레일처럼 예외가 많은 시스템을 얹으니 문제가 반복됐다.
검토 방식도 나누었다
구조를 다시 세우는 것만으로는 부족했다. 같은 방식으로 개발하면 비슷한 빈틈이 또 생길 수 있었다. 그래서 검토 방식도 함께 바꾸었다. 먼저 에이전트 2개를 두고 각각 Builder와 Reviewer라는 역할을 부여했다.
Builder 에이전트는 리팩토링 계획, 아키텍처 정리, 티켓 분해, 구현을 맡고, Reviewer 에이전트는 빈틈, 반례, 이전 구현의 잔재, 문서와 구현의 불일치를 찾는다.
계획 단계와 구현 단계 모두에서 Reviewer를 따로 둔다. 검토는 가능하면 새 컨텍스트에서 진행한다.
배운 것
AI는 구현 속도를 크게 올려준다. 이번에도 Codex가 없었다면 이만큼 빠르게 문서를 정리하고, 티켓을 나누고, 구현을 반복하기 어려웠을 것이다. 하지만 속도가 빨라질수록 명확히 정하지 않은 부분도 그만큼 빠르게 구현에 섞인다.
다음에는 구현 속도보다 먼저 데이터의 기준과 책임의 경계를 더 분명히 세울 것이다. 한쪽은 만들고, 다른 한쪽은 빈틈을 찾아야 한다. 그래야 AI가 채운 추측이 나도 모르게 게임의 규칙이 되거나, 구조와 데이터의 기준으로 굳어지는 일을 막을 수 있다.