AI로 Mini Cross를 구축한 방법: 데일리 크로스워드, 스트릭 래더(Streak Ladder), 그리고 한계를 넘어선 파이프라인
요약
AI와 자동화 파이프라인을 활용해 구축한 데일리 크로스워드 게임 'Mini Cross'의 개발 과정을 다룹니다. 디자인 제약 조건 설정, DOM 기반의 그리드 구현, 스트릭 시스템 도입 등 게임의 핵심 설계 결정 사항을 설명합니다.
핵심 포인트
- 데일리 전용 제약 조건을 통해 스트릭 시스템의 의미를 강화함
- Canvas 대신 DOM Grid를 사용하여 접근성과 키보드 네비게이션을 확보함
- 반복적인 빌드 파이프라인을 통해 게임 콘텐츠를 자동 생성함
- 사이버 터미널 미학을 구현하기 위해 CSS와 스캔라인 오버레이 활용
NYT Mini는 특정 형태의 퍼즐을 대중화했습니다: 작은 그리드, 하루에 하나의 공유된 퍼즐, 그리고 몇 주에 걸쳐 누적되는 스트릭(Streak) 추적 방식입니다. Mini Cross는 그 형태를 채택하여 사이버 터미널(cyber-terminal) 미학으로 구현했습니다 — 거의 검은색에 가까운 배경 위의 인광 녹색(phosphor green), 고정 폭(monospace) 힌트 패널, 스캔라인 오버레이(scanline overlay)가 특징입니다. 이 게임은 네 번의 반복(iteration) 과정을 거쳐 우리의 QA 루브릭(rubric)에서 60점 만점에 59점을 기록하며 출시되었으며, 우리의 원격 빌드 파이프라인(remote build pipeline)의 여러 크론 파이어(cron fires)를 통해 구축된 카탈로그 내 첫 번째 게임입니다.
이 포스트는 이것이 어떻게 완성되었는지에 대한 이야기입니다: 스캐폴딩(scaffold) 단계 이전에 확정된 '데일리 전용(daily-only)' 제약 조건, 콘텐츠 제작 전 느낌을 조율하기 위한 반복 과정, 네 번째 반복(iter-4)에서 등장한 스트릭 래더(streak ladder), 그리고 빌드 도중 파이프라인이 스스로의 경계를 넘어선 순간에 대한 이야기입니다.
먼저 플레이한 다음 빌드 이야기를 읽고 싶으신가요? ▶ 지금 Mini Cross 플레이하기
코드 작성 전 확정된 제약 조건
Mini Cross를 위한 첫 번째 디자인 결정은 어떠한 스캐폴딩(scaffolding)도 이루어지기 전에 내려졌습니다: 모든 플레이어는 날짜 시드(date-seeded) 기반의 순환 방식에서 결정론적으로 선택된 동일한 퍼즐을 같은 날에 받게 됩니다. 사양(spec)에는 daysSinceEpoch % 60으로 인덱싱된 60개의 퍼즐 팩이 기술되어 있었습니다.
이것은 부수적인 것처럼 들릴 수 있습니다. 하지만 이것이 게임의 전부입니다. 대안은 자유 플레이 방식의 멀티 퍼즐 모드였습니다 — 플레이어가 원하는 만큼, 원하는 순서대로 퍼즐을 풀고 퍼즐별로 진행 상황을 저장하는 방식입니다. 우리는 스캐폴딩 단계 이전에 이를 거부했습니다. 만약 평일에 대비해
데일리 전용 방식이 중요한 이유: 스트릭 (Streak)은 단순히 덧붙이는 기능이 아니라, 의미를 만들어내는 제약 조건입니다. 만약 플레이어가 진행 상황을 저축(bank)할 수 있게 허용한다면, 그것은 다른 게임이 됩니다. 즉, 스트릭이 별다른 의미를 갖지 못하는 숫자에 불과한 게임이 되는 것입니다. 명세(spec) 단계에서 데일리 제약 조건을 고정함으로써, 이후의 모든 결정(회전 깊이, 리더보드 키, 마일스톤 임계값 등)은 제약 조건을 방어할 필요 없이 해당 제약 조건으로부터 자연스럽게 파생될 수 있었습니다.
Canvas가 아닌 DOM Grid
그리드는 div 셀로 구성된 5×5 CSS 그리드입니다. Canvas를 사용하지 않았습니다. 이 선택은 첫 번째 반복(iter-1) 단계에서 논의되었고 그대로 유지되었습니다.
Canvas를 사용했다면 애니메이션과 스캔라인 오버레이(scanline overlay)를 픽셀 단위로 정밀하게 제어할 수 있었을 것입니다. 또한 포커스 관리(focus management), 커서 깜빡임, 스크린 리더 흐름, 키보드 네비게이션 등을 모두 새로 구현해야 했을 것입니다. 이 모든 기능은 DOM이 기본적으로 제공하는 것들입니다. 우리가 실제로 필요로 했던 시각 효과(활성 셀 글로우, 정답/오답 셀 플래시, 완성된 단어 하이라이트)는 모두 CSS로 구현 가능했습니다.
이 선택은 스트릭 마일스톤 오버레이를 추가한 네 번째 반복(iter-4) 단계에서 다시 한번 빛을 발했습니다. DOM 배너에 role="status"와 aria-live="polite"를 추가하는 것은 속성 한 쌍을 더하는 작업일 뿐입니다. Canvas로 렌더링된 배너에서 동일한 작업을 수행하려면 알림 레이어(announcement layer)를 별도로 다시 구축해야 했을 것입니다.
모바일 키보드 문제
iOS Safari의 네이티브 키보드는 5×5 그리드를 망가뜨립니다. 키보드가 나타나면 페이지 레이아웃이 재배치(re-layout)되고, 그리드는 포커스를 잃으며, 플레이어가 탭한 셀이 화면 밖으로 스크롤되어 사라집니다. 네이티브 키보드를 사용하면서 이를 깔끔하게 해결할 방법은 없습니다. 레이아웃 리플로우(reflow)는 플랫폼의 동작 방식이며, 우리가 임의로 무시할 수 있는 것이 아니기 때문입니다.
해결책은 커스텀 가상 키보드 오버레이입니다. 모바일에서는 화면 하단에서 A-Z와 백스페이스 버튼 행이 슬라이드되어 올라오며, 각 버튼의 높이는 44픽셀(iOS의 최소 탭 대상 크기)로 설정됩니다. 모든 버튼은 touchstart와 preventDefault를 사용하여 네이티브 키보드가 아예 나타나지 않도록 억제합니다. 이렇게 하면 그리드의 포커스가 유지되고, 페이지 리플로우가 발생하지 않으며, 커서가 깔끔하게 이동합니다.
Path Runner도 동일한 방식(드래그와 의도를 구분하기 위한 최소 속도 임계값 설정)으로 스와이프 입력 문제를 해결했습니다. 이 패턴은 장르를 불문하고 재사용됩니다.
콘텐츠의 깊이보다 앞서는 점수 체감 (Score Feel)
iter-1은 60점 만점에 47점으로 출시되었습니다. 게임은 플레이 가능한 수준이었습니다. QA 플래그는 콘텐츠 품질 문제였습니다. 일부 세로(Down) 항목이 실제 영어 단어가 아니었습니다. 그것들은 구조적으로는 크로스워드 데이터로서 유효한(integration lint를 통과한) 자리 채우기용 플레이스홀더(placeholder)였고 퍼즐을 풀 수는 있었지만, 실제로 풀다 보면 "이게 단어가 맞나?"라는 의문이 들게 했습니다.
iter-2에서는 채우기(fill) 문제를 수정하고, Web Audio SFX(효과음)를 추가했으며, 44픽셀 가상 키보드를 확정했습니다. 점수: 60점 만점에 54점.
iter-3에 이르러서야 게임이 다시 돌아올 만한 가치가 있다는 느낌을 주기 시작했습니다. 점수 지속성(전체 최고 기록과 어제의 점수가 HUD에 실시간으로 표시됨), 하드 모드(+50 보너스, 안전장치로 쓸 수 없도록 확인/힌트 기능이 숨겨짐), 그리고 승리 시 캔버스 파티클 버스트(canvas particle burst)가 추가되었습니다. 승리 시 그리드 중심에서 60프레임에 걸쳐 80개의 녹색 및 청록색 사각형이 방출된 후 사라집니다. 퍼즐 팩은 10개에서 13개로 확장되었습니다. 점수: 60점 만점에 57점.
여기서 순서가 중요합니다. 점수 및 체감(Score-and-feel) 요소가 먼저 적용되었고, 콘텐츠의 깊이(더 많은 퍼즐)가 두 번째로 적용되었습니다. 순서를 뒤집어 60개의 퍼즐을 먼저 출시하고 승리 순간의 연출을 나중에 다듬을 수도 있었겠지만, 그 반대의 방식은 매일 재방문하는 플레이어에게 보상을 주지 못했을 것입니다. 점수 지속성은 어제의 퍼즐이 어려웠는지 기억하게 만드는 요소입니다. 이것이 없다면 모든 풀이는 단발성(one-shot)에 그치게 됩니다.
스트릭 래더 (The Streak Ladder)
iter-4에서는 명세(spec)에는 없던 축하 요소를 추가했습니다. 3일, 7일, 14일, 30일, 60일, 100일의 6단계 티어가 있으며, 각 단계마다 승리의 화음(triumphant chord stab)과 상승하는 아르페지오(ascending arpeggio), 그리고 CSS 키프레임(keyframes)의 scale-and-fade 애니메이션이 적용된 전체 화면 배너가 나타납니다.
티어(tier)의 선택은 중요합니다. 선형적인 'N일마다' 방식의 배너는 빠르게 피로감을 줍니다. 로그 스케일(Logarithmic) 티어(1, 2, 4, 8, ...)는 초반 게임 플레이에는 과도한 보상을 주지만, 지속적인 참여에는 보상이 부족합니다. 우리가 최종적으로 결정한 사다리(ladder) 방식은 자연스러운 몰입의 정체기(plateaus)에 맞춰져 있습니다: 3일은 "내일도 이걸 할 수도 있겠는데"라고 느끼는 순간이며, 7일은 일주일, 14일은 2주, 30일은 한 달, 60일은 두 달, 100일은 100일입니다. 3일 차의 팝업(pop)은 매우 결정적입니다. 참여도가 낮은 플레이어들이 이탈하기 전에 충분히 일찍 나타나기 때문입니다.
스트릭(streak) 배너를 출시한 이유: 스트릭 횟수는 이미 iteration-1(iter-1)부터 HUD에 표시되어 있었습니다. 플레이어들은 자신의 스트릭을 볼 수 있었습니다. 하지만 그들이 볼 수 없었던 것은 7일, 30일, 100일 스트릭 달성자가 되는 바로 그 '순간'이었습니다. 그 순간이 없다면 숫자는 그저 카운터에 불과합니다. 하지만 그 순간이 더해지면, 숫자는 목표가 됩니다.
최종 점수: 60점 만점에 59점. 우리의 58점 출시 규칙(ship rule)에 따라 iter-5는 건너뛰었습니다.
파이프라인이 스스로의 경계를 넘어서다
Mini Cross는 우리의 원격 빌드 파이프라인(remote build pipeline)의 여러 크론(cron) 실행에 걸쳐 구축된 카탈로그 내 첫 번째 게임입니다. 이전의 대부분의 게임은 단일 세션에서 구축되었습니다. Mini Cross는 달랐습니다. 사양(spec), 스캐폴드(scaffold), 그리고 처음 세 번의 반복(iteration)은 약 다섯 번의 별도 트리거 실행에 걸쳐 원격 상태 머신(remote state-machine)에서 실행되었습니다. 그 실행 중 하나는 비행 중(mid-flight) 발생한 git 병합 충돌(merge conflict)을 스스로 해결하기도 했습니다.
iter-4, iter-4 QA 패스, 그리고 마무리 작업은 원격 파이프라인이 아닌 오케스트레이터(orchestrator)에서 _로컬(locally)_로 실행되었습니다. 이유는 실용적이었습니다. 다음 원격 실행이 잠시 지연되었고, 폴리싱(polish) 단계가 다음 크론을 다시 기다리는 것보다 인라인(inline)으로 빠르게 끝낼 수 있을 만큼 충분히 작았으며, 통합 커밋(integration commit)에는 원격 실행이 제공하는 것보다 더 정밀한 인간의 검토가 필요했기 때문입니다.
컨텍스트 상태(context-state)가 중요한 무거운 반복 단계에서는 원격(remote)을 사용하고, 개발자가 더 긴밀한 제어를 원하는 최종 폴리싱 단계에서는 로컬(local)을 사용하는 이 하이브리드 로컬+원격 패턴은 이제 파이프라인의 표준 모드(canonical mode)가 되었습니다. Mini Cross는 이를 발견한 첫 번째 게임이었습니다. 계획된 것은 아니었지만, 결과적으로 그것이 가장 적합한 형태임이 밝혀졌습니다.
Mini Cross가 다시 돌아올 가치가 있는 이유
Mini Cross는 짧습니다. 대부분의 풀이 시간은 5분 미만입니다. 핵심은 세션의 깊이(depth-of-session)가 아니라, 연속 기록의 깊이(depth-of-streak)입니다. 매일 같은 퍼즐, 동일한 글로벌 리더보드, 그리고 3일, 7일, 14일, 30일, 60일, 100일 연속 달성 시 기다리고 있는 동일한 6단계 축하 사다리(celebration ladder)가 준비되어 있습니다.
퍼즐 팩은 점진적으로 성장할 것입니다. 콘텐츠 추가는 게임 코드 변경이 아닌 D1 마이그레이션(migrations)을 통해 배포되어, 콘텐츠 레이어(content layer)를 로직 레이어(logic layer)와 분리합니다. 게임을 다시 배포하지 않고도 새로운 퍼즐이 추가됩니다.
NYT Mini의 유료 결제 장벽(paywall) 때문에 망설였거나, 아침 크로스워드에 색다른 미학(aesthetic)을 원하신다면 한 번 시도해 보세요. 3일 연속 달성의 짜릿함은 생각보다 가까이 있습니다.
원문은 vibearcade.com/blog/how-we-built-mini-cross에 게시되었습니다. Vibe Arcade는 현재의 AI 모델이 자율적으로 무엇을 할 수 있는지, 거친 부분(rough edges)은 어디인지, 그리고 인간이 어디에서 루프(loop) 안에 머물러야 하는지를 이해하기 위한 1인 실험입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기