Mahjong Joy 개발기 (1) — 마작에서 족보를 빼면 게임이 될까? 기획 분석과 작업 계획
Mahjong Joy 시리즈
- 기획 분석과 작업 계획 ← 이번 글
- 핵심 로직 — 승리 판정 알고리즘
- 게임 엔진과 AI 만들기
- 파스텔 UI와 타일 애니메이션
- 점수 시스템과 메인화면
마작은 재밌는데, 너무 어렵다
마작을 배워보려고 한 적이 있다면 공감할 거다. 패 맞추는 것 자체는 금방 이해되는데, 그다음이 문제다. 리치, 역패, 탕야오, 핑후... 수백 가지 족보와 점수 계산법 앞에서 대부분 포기한다.
그래서 이런 생각을 했다. 족보를 전부 없애면 어떨까? 마작의 본질적인 재미인 "3개짜리 몸통 4개 + 2개짜리 머리 1개(3-3-3-3-2)를 완성하는 짝맞추기"만 남기는 거다. 이름하여 Mahjong Joy(마작 조이).
이번 시리즈는 이 아이디어를 Claude Code와 함께 실제로 플레이 가능한 Flutter 게임으로 만든 과정의 기록이다. 1편에서는 기획 문서를 AI에게 분석시키고 작업 계획을 세운 과정을 다룬다.
준비물: 기획 문서 두 개
시작할 때 손에 있던 건 두 개였다.
| 문서 | 내용 |
|---|---|
simple_mahjong_game_work.md | AI에게 전달할 작업지시서. 데이터 구조, 승리 판정 알고리즘, 턴 루프 UI 3단계 |
Mahjong Joy_ Development Plan.pdf | 게임 컨셉 기획안. 파스텔 비주얼, 용어 간소화, 개발 로드맵 |
여기서 첫 번째 팁. 기획 문서는 대충이라도 만들어두고 시작하는 게 좋다. "심플한 마작 게임 만들어줘" 한 줄로 시작하는 것과, 승리 조건과 컨셉이 정리된 문서를 주고 시작하는 것은 결과물의 방향성이 완전히 다르다.
Claude Code에게 두 파일을 주고 "분석하고 작업계획 세워줘"라고 요청했다. 재미있는 건 PDF 처리 과정이었는데, PDF 렌더링 도구가 없는 환경이라 같은 내용의 PPTX 파일을 찾아서 압축을 풀고 슬라이드 XML에서 텍스트를 직접 추출했다. AI가 도구가 없으면 우회로를 찾는 걸 보는 재미가 있다.
Step 1: 문서 충돌 발견 — 기술 스택 결정
분석 결과, 두 문서가 서로 다른 이야기를 하고 있었다.
- 작업지시서: "웹(React/Next.js) 또는 모바일 환경"
- 기획안 PDF: "Flutter 기반" (Flame 엔진, Rive 애니메이션 언급)
Claude Code가 이 충돌을 짚어내고 선택지를 물어왔다. 브라우저에서 바로 확인 가능한 React가 반복 개발에는 빠르다는 추천과 함께. 하지만 나는 Flutter를 선택했다. iOS/Android 동시 출시가 최종 목표였고, Flutter는 웹 빌드도 지원하니 개발 중 확인은 웹으로 하면 된다.
💡 문서 여러 개를 AI에게 줄 때는 문서 간 충돌이 있는지 먼저 확인하게 하자. 나중에 발견하면 뒤엎는 비용이 크다.
범위도 이때 정했다. 원래 계획은 "혼자 플레이 모드부터"였는데, 기본 마작 룰(4인 대전)은 지키고 싶어서 나 + AI 3명의 4인 테이블로 확정했다.
Step 2: 게임 룰 명세 확정
간소화 마작의 룰을 문서로 박제했다. 이게 이후 모든 구현의 기준이 된다.
- 타일 136장: 수패 3종(1~9) 각 4장 + 자패 7종 각 4장
- 4인 테이블, 각자 13장으로 시작
- 내 턴: 1장 뽑고(14장) → 1장 버리기(13장)
- 승리 조건은 단 하나: 몸통 4개 + 머리 1쌍 = 3-3-3-3-2
- 뺏어오기: 치/퐁 구분 없이, 누가 버리든 세트 완성 가능하면 획득
- 완성: 론/츠모 구분 없이, 완성되는 순간 선언
기획안의 표현을 빌리면 "복잡성 80% 감소". 전통 마작의 용어(치, 퐁, 론, 츠모)를 "뺏어오기"와 "완성" 두 개로 통합한 것이 핵심이다.
Step 3: 페이즈 나누기 — 검증 기준까지 함께
작업 계획은 WORK_PLAN.md로 저장했다. 페이즈마다 **완료 기준(어떻게 검증할 것인가)**을 함께 정의한 게 포인트다.
| Phase | 내용 | 완료 기준 |
|---|---|---|
| 0. 셋업 | flutter create, 폴더 구조 | 빌드 성공 |
| 1. Core | 타일 생성, 승리 판정, 대기패 계산 | flutter test 전수 통과 |
| 2. 흐름+AI | 4인 턴 루프, AI 3명, 유국 처리 | AI 자동 대국 시뮬레이션 정상 종료 |
| 3. UI/UX | 파스텔 테마, 타일 위젯, 레이아웃 | 시뮬레이터 육안 확인 |
| 4. Polish | 사운드, 튜토리얼, 스킨 | 스토어 제출 가능 |
계획 단계에서 내린 판단 중 하나가 나중에 꽤 잘한 선택이 됐다. 기획안에는 Flame 엔진과 Rive가 언급되어 있었지만 둘 다 쓰지 않기로 했다. 턴제 보드게임 UI는 순수 Flutter 위젯과 내장 애니메이션으로 충분하고, 의존성이 적을수록 삽질도 적다. 실제로 4편에서 다룰 타일 애니메이션까지 전부 기본 위젯으로 해결됐다.
계획 문서를 저장소에 두는 이유
WORK_PLAN.md는 그냥 계획서가 아니라 진행 상황 대시보드로 계속 갱신했다. 세션이 바뀌어도 AI가 이 파일만 읽으면 어디까지 했는지 파악할 수 있다. AI와 협업할 때 컨텍스트를 파일로 남기는 습관은 생각보다 강력하다.
## 진행 상황 (2026-07-10)
- [x] Phase 0 — 프로젝트 셋업
- [x] Phase 1 — Core 로직 + 단위 테스트 25개 통과
- [ ] Phase 2 — 게임 엔진·AI
...
정리
- 기획 문서(작업지시서 + 컨셉 기획안)를 먼저 준비하고 AI에게 분석시켰다
- 문서 간 충돌(React vs Flutter)을 초반에 발견하고 Flutter로 확정했다
- 간소화 룰(3-3-3-3-2, 뺏어오기/완성 통합)을 명세로 박제했다
- 페이즈마다 검증 기준을 붙인
WORK_PLAN.md를 만들고 계속 갱신했다 - Flame/Rive 같은 추가 의존성은 과감히 제외했다
다음 편에서는 이 게임의 심장인 승리 판정 알고리즘을 구현한다. 14장의 패가 3-3-3-3-2로 쪼개지는지 어떻게 판단할까? 재귀와 백트래킹이 등장한다.
backtodev
40대 PM, 다시 개발자로 돌아갑니다. 실패하고 배우며 성장하는 기록.