cell-grid RPG / 바이브코딩 워크플로우

Workflow

문자 그리드 3D RPG 바이브코딩 워크플로우

셀 그리드에 렌더링하는 1인칭 오픈월드 RPG를 코딩 에이전트와 함께 만드는 6단계 절차. 각 단계는 통과 게이트를 가지며, 게이트를 건너뛰면 되돌릴 수 없다.

itch.io에 배포된 Swaggerfall(holonaut64)의 클라이언트를 역분석해 확인한 구조에서 도출했다. wasm 심볼은 스트립되어 있었으나 패닉 문자열의 소스 경로, cargo 레지스트리 경로, 그리고 index.html에 남은 설계 주석으로 아키텍처를 복원할 수 있었다.

전체 흐름

각 Phase는 이전 게이트를 통과해야 시작한다. 특히 Phase 0을 건너뛰면 나중에 절대 고칠 수 없다.

Phase산출물통과 게이트
0 계약 고정구조 합의문 + CLAUDE.md코드 0줄
1 수직 슬라이스걸을 수 있는 지형30fps, 좌표 HUD
2 렌더러글리프 매핑 + LOD골든 테스트 통과
3 시스템 확장core/*.rs 하나씩파일별 단위 테스트
4 감각CRT / 사운드 / 입력렌더 루프 무변경
5 배포wasm + 정적 호스팅오프라인 구동

왜 제약을 먼저 고정하는가. 셀 그리드로 렌더 계약을 못 박는 순간 텍스처, 모델 포맷, 애니메이션 리깅, UI 레이아웃 엔진이 전부 사라진다. 남는 문제는 “이 셀에 어떤 글자를 어떤 색으로 찍을까” 하나뿐이고, 그건 코딩 에이전트가 가장 잘하는 종류의 문제다. 제약은 손해가 아니라 레버리지다.

Phase 0 — 계약 고정

Phase 0

코드를 쓰지 못하게 막는다

나중에 절대 못 바꾸는 것들을 먼저 못 박는 단계.

프롬프트
문자 셀 그리드에 렌더링하는 1인칭 오픈월드 RPG를 Rust로 만든다.

[렌더 계약]
- 화면은 가변 크기 문자 셀 그리드(기준 228x67). 픽셀 렌더링 금지.
- 모든 출력은 (글리프, 전경색, 배경색) 2D 버퍼로만 표현한다.
- 렌더러는 이 버퍼를 받는 trait 하나 뒤에 숨긴다.
  터미널 / 웹 / 테스트 구현을 각각 꽂을 수 있어야 한다.

[크레이트 분할]
- crates/core   : 게임 규칙. 렌더·입력·시간·IO 의존 금지.
- crates/client : core를 소비해 셀 버퍼 생성. 카메라, 렌더, UI.
- crates/web    : wasm 진입점. DOM 브리지만.

[성능 예산]
- 단일 스레드 30fps. 웹에서는 워커 없음을 전제.
- 프레임당 렌더 예산 30ms.
- 월드는 시드 기반 절차생성. 저장은 시드 + 델타만.

지금은 코드를 쓰지 마라. 이 제약을 만족하는 모듈 구조와 핵심
자료구조만 제안하고, 각 선택의 근거를 한 줄씩 붙여라.
완료 기준모듈 트리와 핵심 타입이 문서로 합의됨. CLAUDE.md 작성 완료.
실패 신호에이전트가 곧바로 main.rs를 쓰기 시작한다.

Phase 1 — 수직 슬라이스

Phase 1

끝에서 끝까지 한 줄기

넓히지 않는다. 한 줄기를 먼저 뚫는다.

프롬프트
수직 슬라이스를 만든다. 이것만 되면 된다:
- 시드로 생성한 높이맵 지형 위에 1인칭 카메라가 선다
- WASD로 걷고 방향키로 둘러본다
- 지형이 문자 셀로 그려진다 (거리에 따라 글리프 밀도와 명도 변화)
- 하단에 좌표 / 방위 / 고도를 표시한다

전투, NPC, 아이템, 인벤토리는 만들지 마라. 스텁도 만들지 마라.
core에는 지형 생성과 이동 판정만, client에는 카메라와 렌더만 넣는다.
완료 기준실제로 걸어다닐 수 있고, 지형이 지형으로 읽힌다.
실패 신호인벤토리 구조체와 전투 스텁은 생겼는데 지형이 평면이다.

Phase 2 — 렌더러

이 장르는 렌더러가 게임의 8할이다. 프롬프트 예산을 여기에 몰아준다.

Phase 2

승부처

목표는 “멀리 있는 산이 산처럼 읽히는 것”.

프롬프트
지형 렌더러를 개선한다. 목표는 "멀리 있는 산이 산처럼 읽히는 것".

- 레이 방향마다 거리·경사·재질을 구하고, 그것을 글리프 선택으로
  매핑하는 함수를 분리하라. 매핑 테이블은 데이터로 빼서 튜닝 가능하게.
- 거리별 LOD: 가까운 것은 촘촘한 글리프, 먼 것은 성기게.
  나무 / 건물 / 액터 각각 렌더 거리와 LOD 거리를 별도 설정값으로 노출.
- 30ms 예산 초과 시 무엇을 먼저 버릴지 우선순위를 정해 구현하라.

작업 후: 같은 시드로 렌더한 셀 버퍼를 텍스트로 덤프하는
골든 테스트를 추가하라.

골든 테스트 — 이 장르의 결정적 무기

출력이 평문이라 git diff가 그대로 리그레션 리포트가 된다. 픽셀 게임에서는 불가능한 이점이므로 반드시 챙긴다.

tests/golden/
  seed_42_north.txt      # 렌더 결과를 그대로 덤프
  seed_42_ridge.txt
완료 기준골든 테스트 존재, LOD 설정값을 런타임에 조절 가능.
실패 신호글리프 선택이 렌더 함수 안에 하드코딩되어 튜닝 불가.

Phase 3 — 시스템 확장

한 파일 한 시스템. 이 입도가 에이전트에게 가장 잘 맞는다. 한 세션에 한 파일.

Phase 3

파일 단위로 넓힌다

프롬프트 예시 · gossip
crates/core/src/gossip.rs 를 추가한다.

- NPC는 사건을 "들었다" 형태로 보유하고, 대화 시 일부를 전파한다
- 전파될수록 왜곡된다 (정확도 감소)
- 플레이어의 행동이 나중에 다른 마을에서 소문으로 돌아와야 한다

core 규칙만 구현하고 UI는 건드리지 마라.
공개 API는 5개 함수 이하로 유지하고, 단위 테스트를 함께 써라.

권장 순서 — 의존이 적은 것부터:

world → combat → talk → knowledge → gossip → 나머지
완료 기준각 파일이 독립 테스트 가능. client 수정 없이 core만으로 검증됨.
실패 신호새 시스템이 client 렌더 코드를 고쳐야만 동작한다 → 계약 위반, 되돌린다.

Phase 4 — 감각

Phase 4

여기서 게임이 게임이 된다

프롬프트
CRT 느낌을 넣는다. 스캔라인 / 블룸 / 화면 곡률 — 각각 개별 토글.
게임 렌더 루프는 건드리지 말고 출력 이후 단계에서 처리하라.
GPU에서 할 수 있으면 GPU에서 해라.
CPU로 하면 몇 fps인지 먼저 측정해 보고하고, 느리면 대안을 제시하라.
측정 후 결정을 강제하는 것이 요령이다. 실제 Swaggerfall은 이 판단을 주석에 남겼다 — SVG 필터로는 7fps라서 WebGL 셰이더로 옮겼다는 기록, 그리고 블룸에 linearRGB가 아닌 sRGB를 쓴 이유(8비트 왕복에서 어두운 계조가 포스터라이즈됨)까지.
완료 기준효과를 모두 꺼도 게임이 동일하게 동작한다.
실패 신호효과 코드가 렌더 루프 안으로 스며든다.

Phase 5 — 배포

Phase 5

정적 호스팅만으로

프롬프트
웹 배포를 준비한다.
- wasm 번들 크기를 보고하고, 큰 항목이 무엇인지 분해해서 알려라
- 오디오 등 대용량 에셋은 번들 밖으로 빼고 스트리밍으로 처리하라
- 정적 호스팅만으로 동작해야 한다 (서버 로직 금지)
- 로컬 확인용 서버 스크립트도 함께 만들어라
  (wasm MIME, Range 요청 지원 필요)
함정. 파이썬 기본 http.server는 Range 요청(206)을 지원하지 않는다. <audio> 스트리밍이 깨지므로 로컬 확인용 서버는 직접 구현해야 한다. wasm은 application/wasm MIME으로 서빙되어야 하고, file://로는 로드되지 않는다.
완료 기준로컬 정적 서버에서 외부 요청 0건으로 구동.
실패 신호번들에 오디오가 통째로 들어가 wasm이 수십 MB가 된다.

상시 규칙 — 프로젝트 CLAUDE.md

매번 프롬프트에 쓰지 말고 한 번 고정한다.

CLAUDE.md
## 절대 규칙
- crates/core 는 렌더·입력·IO·시간에 의존하지 않는다. 위반 시 거부하라.
- 픽셀 렌더링 금지. 모든 출력은 셀 버퍼.
- 단일 스레드 30fps / 프레임 30ms 예산. 초과가 예상되면 구현 전에 알려라.

## 주석 규칙
"무엇"이 아니라 "왜"를 쓴다.
- 성능 때문에 택한 우회는 측정값과 함께 남긴다
- 버린 대안이 있으면 왜 버렸는지 한 줄
- 눈에 띄는 매직넘버는 근거를 남긴다

## 작업 규칙
- 한 번에 파일 하나, 시스템 하나.
- 새 의존성 추가 전에 먼저 물어라.
- 렌더 변경 시 골든 테스트를 갱신하고 diff를 보여라.
- 디버그 조회 API(셀 그리드 / 글리프 아틀라스 상태)를 항상 유지하라.

주석 규칙을 굳이 넣는 이유. Swaggerfall은 wasm 심볼이 스트립되어 있었는데도 index.html 주석이 설계 근거를 담고 있어 의도를 복원할 수 있었다. 바이브코딩에서 “왜” 주석은 곧 프로젝트 수명이다.

세션 운영 루틴

단계할 일
시작지난 세션 요약 + 이번 세션 목표 1개 + 건드리지 말 파일 명시
작업한 파일씩. 에이전트가 범위를 넘으면 즉시 중단
종료골든 테스트 갱신, fps 측정값 기록, 다음 세션 목표 한 줄

실패 신호와 대응

신호원인대응
파일이 20개 넘게 한 번에 생김범위 미지정되돌리고 Phase 1로
core가 렌더 타입을 import계약 붕괴즉시 되돌린다. 협상 금지
fps가 조용히 떨어짐예산 미측정매 세션 fps를 기록으로 남긴다
글리프가 안 나옴아틀라스 디버깅 수단 없음디버그 API를 먼저 만든다
튜닝하려면 재컴파일상수 하드코딩데이터 테이블로 분리

핵심 요약

“만들어줘”가 아니라 “이 제약 안에서 만들어라”로 시작하고,
제약은 숫자로 준다.

셀 그리드로 고정하는 순간 텍스처·모델 포맷·리깅·UI 레이아웃 엔진이 전부 사라지고, 남는 문제는 “이 셀에 어떤 글자를 어떤 색으로 찍을까” 하나가 된다. 그건 코딩 에이전트가 가장 잘하는 종류의 문제다.