전체 흐름
각 Phase는 이전 게이트를 통과해야 시작한다. 특히 Phase 0을 건너뛰면 나중에 절대 고칠 수 없다.
| Phase | 산출물 | 통과 게이트 |
|---|---|---|
| 0 계약 고정 | 구조 합의문 + CLAUDE.md | 코드 0줄 |
| 1 수직 슬라이스 | 걸을 수 있는 지형 | 30fps, 좌표 HUD |
| 2 렌더러 | 글리프 매핑 + LOD | 골든 테스트 통과 |
| 3 시스템 확장 | core/*.rs 하나씩 | 파일별 단위 테스트 |
| 4 감각 | CRT / 사운드 / 입력 | 렌더 루프 무변경 |
| 5 배포 | wasm + 정적 호스팅 | 오프라인 구동 |
왜 제약을 먼저 고정하는가. 셀 그리드로 렌더 계약을 못 박는 순간 텍스처, 모델 포맷, 애니메이션 리깅, UI 레이아웃 엔진이 전부 사라진다. 남는 문제는 “이 셀에 어떤 글자를 어떤 색으로 찍을까” 하나뿐이고, 그건 코딩 에이전트가 가장 잘하는 종류의 문제다. 제약은 손해가 아니라 레버리지다.
Phase 0 — 계약 고정
코드를 쓰지 못하게 막는다
나중에 절대 못 바꾸는 것들을 먼저 못 박는 단계.
문자 셀 그리드에 렌더링하는 1인칭 오픈월드 RPG를 Rust로 만든다. [렌더 계약] - 화면은 가변 크기 문자 셀 그리드(기준 228x67). 픽셀 렌더링 금지. - 모든 출력은 (글리프, 전경색, 배경색) 2D 버퍼로만 표현한다. - 렌더러는 이 버퍼를 받는 trait 하나 뒤에 숨긴다. 터미널 / 웹 / 테스트 구현을 각각 꽂을 수 있어야 한다. [크레이트 분할] - crates/core : 게임 규칙. 렌더·입력·시간·IO 의존 금지. - crates/client : core를 소비해 셀 버퍼 생성. 카메라, 렌더, UI. - crates/web : wasm 진입점. DOM 브리지만. [성능 예산] - 단일 스레드 30fps. 웹에서는 워커 없음을 전제. - 프레임당 렌더 예산 30ms. - 월드는 시드 기반 절차생성. 저장은 시드 + 델타만. 지금은 코드를 쓰지 마라. 이 제약을 만족하는 모듈 구조와 핵심 자료구조만 제안하고, 각 선택의 근거를 한 줄씩 붙여라.
Phase 1 — 수직 슬라이스
끝에서 끝까지 한 줄기
넓히지 않는다. 한 줄기를 먼저 뚫는다.
수직 슬라이스를 만든다. 이것만 되면 된다: - 시드로 생성한 높이맵 지형 위에 1인칭 카메라가 선다 - WASD로 걷고 방향키로 둘러본다 - 지형이 문자 셀로 그려진다 (거리에 따라 글리프 밀도와 명도 변화) - 하단에 좌표 / 방위 / 고도를 표시한다 전투, NPC, 아이템, 인벤토리는 만들지 마라. 스텁도 만들지 마라. core에는 지형 생성과 이동 판정만, client에는 카메라와 렌더만 넣는다.
Phase 2 — 렌더러
이 장르는 렌더러가 게임의 8할이다. 프롬프트 예산을 여기에 몰아준다.
승부처
목표는 “멀리 있는 산이 산처럼 읽히는 것”.
지형 렌더러를 개선한다. 목표는 "멀리 있는 산이 산처럼 읽히는 것". - 레이 방향마다 거리·경사·재질을 구하고, 그것을 글리프 선택으로 매핑하는 함수를 분리하라. 매핑 테이블은 데이터로 빼서 튜닝 가능하게. - 거리별 LOD: 가까운 것은 촘촘한 글리프, 먼 것은 성기게. 나무 / 건물 / 액터 각각 렌더 거리와 LOD 거리를 별도 설정값으로 노출. - 30ms 예산 초과 시 무엇을 먼저 버릴지 우선순위를 정해 구현하라. 작업 후: 같은 시드로 렌더한 셀 버퍼를 텍스트로 덤프하는 골든 테스트를 추가하라.
골든 테스트 — 이 장르의 결정적 무기
출력이 평문이라 git diff가 그대로 리그레션 리포트가 된다.
픽셀 게임에서는 불가능한 이점이므로 반드시 챙긴다.
tests/golden/ seed_42_north.txt # 렌더 결과를 그대로 덤프 seed_42_ridge.txt
Phase 3 — 시스템 확장
한 파일 한 시스템. 이 입도가 에이전트에게 가장 잘 맞는다. 한 세션에 한 파일.
파일 단위로 넓힌다
crates/core/src/gossip.rs 를 추가한다. - NPC는 사건을 "들었다" 형태로 보유하고, 대화 시 일부를 전파한다 - 전파될수록 왜곡된다 (정확도 감소) - 플레이어의 행동이 나중에 다른 마을에서 소문으로 돌아와야 한다 core 규칙만 구현하고 UI는 건드리지 마라. 공개 API는 5개 함수 이하로 유지하고, 단위 테스트를 함께 써라.
권장 순서 — 의존이 적은 것부터:
world → combat → talk → knowledge → gossip → 나머지
Phase 4 — 감각
여기서 게임이 게임이 된다
CRT 느낌을 넣는다. 스캔라인 / 블룸 / 화면 곡률 — 각각 개별 토글. 게임 렌더 루프는 건드리지 말고 출력 이후 단계에서 처리하라. GPU에서 할 수 있으면 GPU에서 해라. CPU로 하면 몇 fps인지 먼저 측정해 보고하고, 느리면 대안을 제시하라.
Phase 5 — 배포
정적 호스팅만으로
웹 배포를 준비한다. - wasm 번들 크기를 보고하고, 큰 항목이 무엇인지 분해해서 알려라 - 오디오 등 대용량 에셋은 번들 밖으로 빼고 스트리밍으로 처리하라 - 정적 호스팅만으로 동작해야 한다 (서버 로직 금지) - 로컬 확인용 서버 스크립트도 함께 만들어라 (wasm MIME, Range 요청 지원 필요)
http.server는 Range 요청(206)을
지원하지 않는다. <audio> 스트리밍이 깨지므로 로컬
확인용 서버는 직접 구현해야 한다. wasm은 application/wasm
MIME으로 서빙되어야 하고, file://로는 로드되지 않는다.
상시 규칙 — 프로젝트 CLAUDE.md
매번 프롬프트에 쓰지 말고 한 번 고정한다.
## 절대 규칙 - crates/core 는 렌더·입력·IO·시간에 의존하지 않는다. 위반 시 거부하라. - 픽셀 렌더링 금지. 모든 출력은 셀 버퍼. - 단일 스레드 30fps / 프레임 30ms 예산. 초과가 예상되면 구현 전에 알려라. ## 주석 규칙 "무엇"이 아니라 "왜"를 쓴다. - 성능 때문에 택한 우회는 측정값과 함께 남긴다 - 버린 대안이 있으면 왜 버렸는지 한 줄 - 눈에 띄는 매직넘버는 근거를 남긴다 ## 작업 규칙 - 한 번에 파일 하나, 시스템 하나. - 새 의존성 추가 전에 먼저 물어라. - 렌더 변경 시 골든 테스트를 갱신하고 diff를 보여라. - 디버그 조회 API(셀 그리드 / 글리프 아틀라스 상태)를 항상 유지하라.
주석 규칙을 굳이 넣는 이유.
Swaggerfall은 wasm 심볼이 스트립되어 있었는데도
index.html 주석이 설계 근거를 담고 있어 의도를 복원할 수
있었다. 바이브코딩에서 “왜” 주석은 곧 프로젝트 수명이다.
세션 운영 루틴
| 단계 | 할 일 |
|---|---|
| 시작 | 지난 세션 요약 + 이번 세션 목표 1개 + 건드리지 말 파일 명시 |
| 작업 | 한 파일씩. 에이전트가 범위를 넘으면 즉시 중단 |
| 종료 | 골든 테스트 갱신, fps 측정값 기록, 다음 세션 목표 한 줄 |
실패 신호와 대응
| 신호 | 원인 | 대응 |
|---|---|---|
| 파일이 20개 넘게 한 번에 생김 | 범위 미지정 | 되돌리고 Phase 1로 |
| core가 렌더 타입을 import | 계약 붕괴 | 즉시 되돌린다. 협상 금지 |
| fps가 조용히 떨어짐 | 예산 미측정 | 매 세션 fps를 기록으로 남긴다 |
| 글리프가 안 나옴 | 아틀라스 디버깅 수단 없음 | 디버그 API를 먼저 만든다 |
| 튜닝하려면 재컴파일 | 상수 하드코딩 | 데이터 테이블로 분리 |
핵심 요약
“만들어줘”가 아니라 “이 제약 안에서 만들어라”로 시작하고,
제약은 숫자로 준다.
셀 그리드로 고정하는 순간 텍스처·모델 포맷·리깅·UI 레이아웃 엔진이 전부 사라지고, 남는 문제는 “이 셀에 어떤 글자를 어떤 색으로 찍을까” 하나가 된다. 그건 코딩 에이전트가 가장 잘하는 종류의 문제다.