2026.03.24AI 개발 패턴
Claude Code게임개발9-slice프로시저럴생성맵생성AI소통타워디펜스규칙검증

맵 자동 생성 - "1줄짜리 통로 금지" 규칙 전쟁

시작은 단순했습니다

타워디펜스 게임 myDGC의 맵을 매번 손으로 만드는 건 한계가 있었습니다. 9-slice 기반 블록 지형 시스템이 잘 돌아가고 있었고, 타일 에디터도 갖춰진 상태. 이제 자동 생성만 붙이면 무한한 맵을 뽑아낼 수 있을 거라 생각했습니다.

기존 맵의 구성 원칙을 Claude에게 분석시킨 결과는 꽤 깔끔했습니다.

304개 피스 전부 일치합니다. 저장된 데이터와 알고리즘 생성 결과가 0개 차이 — 규칙 파악이 정확합니다.

완벽한 출발이었습니다. 9-slice 렌더링 규칙도 정확히 파악했고, 5가지 맵 템플릿(fortress, valley, plateau, corridor, arena)까지 일사천리로 만들어졌습니다. 타일 에디터에 Generate 버튼도 붙이고, 템플릿 선택해서 클릭 한 번이면 3D 맵이 뚝딱 나오는 세상.

여기까지는 좋았습니다.

맵 자동 생성 시스템 개발 화면

"1칸짜리 통로가 왜 또 나와?"

9-slice 지형 시스템에는 치명적인 물리적 제약이 있습니다. 한 타일에는 한 방향의 피스만 배치할 수 있다는 것. 이게 무슨 뜻이냐면, 폭이 1칸인 통로는 양쪽 절벽을 동시에 표현할 수가 없습니다.

code
// 폭 1칸 통로 — 렌더링 불가능
2 0 2
2 0 2    ← 가운데 0칸: 동쪽도 절벽, 서쪽도 절벽. 어느 쪽을 그리지?
2 0 2

// 폭 2칸 통로 — 정상
2 0 0 2
2 0 0 2  ← 왼쪽 0: 서쪽 절벽, 오른쪽 0: 동쪽 절벽. 각자 담당
2 0 0 2

규칙은 명확합니다. 최소 2칸 폭. 이걸 Claude에게 설명하고, MapGenerator의 모든 템플릿에 적용하라고 했습니다.

맞습니다. 9-slice 시스템은 한 타일에 한 방향 피스만 배치하기 때문에, 1칸 폭 지형은 양쪽 절벽을 동시에 표현할 수 없어요. 최소 2칸 폭이 필요합니다.

Claude도 동의했습니다. 문제를 이해했다고 했습니다. 그리고 수정에 들어갔습니다.

문제는 하나가 아니었습니다

Claude가 찾아낸 핵심 문제 지점은 여러 곳에 산재해 있었습니다.

1. smoothHeightTransitions의 함정

높이 0에서 높이 2로 갑자기 올라가면 절벽이 너무 가파르니까, 중간에 높이 1을 삽입하는 smoothHeightTransitions 함수가 있었습니다. 그런데 이 함수가 h0과 h2 사이에 h1을 "1칸짜리로" 끼워넣고 있었습니다.

typescript
// 문제의 코드 — h0↔h2 사이에 h1 1칸 삽입
if (current === 0 && neighbor === 2) {
  tiles[between] = 1;  // 이 1칸짜리 h1이 규칙 위반
}

핵심 문제를 파악했습니다. smoothHeightTransitions에서 h0↔h2 사이에 h1을 넣는데, 이때 1칸짜리 h1 통로가 생깁니다.

2. carvePath의 1칸 파기

통로를 파는 carvePath 함수는 말 그대로 1칸씩 파고 있었습니다. 통로를 만드는 함수가 1칸 폭 통로를 만든다 — 아이러니의 극치입니다.

3. Entrance carving도 마찬가지

입구를 뚫는 로직도 1칸 폭으로 구멍을 내고 있었습니다.

4. Corridor 템플릿의 근본적 문제

S자 회랑을 만드는 corridor 템플릿 자체가 1칸 폭 경로를 디자인하고 있었습니다.

결국 생성 파이프라인의 거의 모든 단계에서 최소 폭 규칙이 무시되고 있었던 겁니다.

수정은 전방위로

Claude는 문제 지점을 하나씩 잡아갔습니다.

최소 2칸 폭 규칙을 전체 파이프라인에 적용해야 합니다.

smoothHeightTransitions을 고치고, entrance carving을 고치고, corridor 템플릿을 고치고, carvePath도 고쳤습니다. 매번 "이번에야말로 됐다"고 생각했지만, Generate를 누를 때마다 어딘가에서 1칸짜리가 다시 튀어나왔습니다.

이 과정에서 Vite 서버를 재시작한 횟수만 10회가 넘었습니다. 수정하고, 빌드하고, 새로고침하고, 맵 생성하고, 1칸짜리 발견하고, 다시 수정하고... 이 사이클의 반복.

"이해가 안 가나? 또 그러네? 1줄짜리가 나오면 안 된다니까?"

결국 나온 해결책은 "니가 스스로 체크해봐" — 자가 검증 요구였습니다. 맵을 생성한 후에 생성기 스스로가 전체 타일을 스캔해서, 어떤 높이 영역이든 1칸 폭으로 존재하는 곳이 있으면 자동으로 넓히거나 메워버리는 후처리 단계를 추가하는 것.

디버깅 과정의 좌절과 반복

이 규칙 전쟁에서 배운 것들

1. 프로시저럴 생성에서 "제약 조건"은 모든 레이어에 관통해야 합니다

"최소 2칸 폭"이라는 단순한 규칙 하나가, 경로 생성 → 높이 스무딩 → 입구 뚫기 → 템플릿 디자인까지 전 단계에 영향을 미쳤습니다. 한 곳만 고치면 다른 곳에서 터지고, 그 곳을 고치면 또 다른 곳에서 터집니다. 제약 조건은 파이프라인의 마지막이 아니라, 각 단계의 설계 시점에 녹아들어야 합니다.

2. AI에게 규칙을 말로 설명하는 것과 코드로 강제하는 것은 다릅니다

Claude는 "1칸 폭 금지" 규칙을 완벽히 이해했습니다. 설명도 정확하게 했습니다. 하지만 여러 함수에 걸친 생성 파이프라인에서 그 규칙을 모든 경우의 수에 대해 코드로 보장하는 건 별개의 문제였습니다. AI가 "이해했다"고 하는 것과 "구현했다"는 같지 않습니다.

3. "니가 스스로 체크해봐" 패턴의 위력

생성 로직의 모든 단계에서 규칙을 지키게 만드는 것보다, 최종 결과물을 검증하는 후처리 단계를 추가하는 게 훨씬 확실합니다. 이건 AI 협업뿐 아니라 일반적인 프로시저럴 생성에서도 유효한 패턴입니다.

typescript
// 후처리 검증 패턴
function validateMinWidth(tiles: number[][], minWidth: number = 2): boolean {
  // 모든 높이 레벨에 대해
  // 해당 높이의 연결 영역이 minWidth 미만인 곳이 있는지 스캔
  // 발견 시 자동 보정 또는 재생성
}

4. 이 여정은 사실 더 긴 여정의 일부였습니다

맵 자동 생성에 도달하기까지, 타일 에디터의 파일 저장 문제, 피스 중복 배치 버그, 머티리얼 공유 삭제 문제, variant 매핑 불일치, deco 렌더링 누락, 조명 시스템 통합까지 — 수많은 문제를 하나씩 해결해왔습니다. 각각은 작은 문제였지만, 전부 이 순간을 위한 빌드업이었습니다.

마치며

프로시저럴 맵 생성은 "규칙을 정의하면 끝"이 아닙니다. 규칙이 생성 파이프라인의 모든 단계에서 존중되는지 확인하고, 최종 결과물을 검증하는 안전장치까지 갖춰야 비로소 "규칙이 지켜진다"고 말할 수 있습니다.

AI와 협업할 때도 마찬가지입니다. AI가 규칙을 이해하는 것과 모든 코드 경로에서 그 규칙을 준수하는 것은 완전히 다른 차원의 문제입니다. "이해했다"는 답변에 안심하지 말고, 검증 가능한 형태로 규칙을 코드에 심어야 합니다.

10번 넘게 서버를 재시작하며 배운 교훈: 규칙은 말이 아니라 코드로 강제하는 것입니다.