Files
tetrig/README.md
tkrmagid 26bf54fb43 Google Play 단독 출시로 정리 + 스토어 제출 자산 준비
iOS 타깃을 제거했다. Apple Developer Program 연 $99 와 맥이 필요한데
아이폰은 기존 웹 PWA 로 이미 커버되므로 비용 대비 실익이 없다고 판단했다.
commonMain 중심 구조는 그대로라 iosTarget 만 다시 넣으면 복구된다.

멀티·리그전/커스텀 카드에 '준비 중' 배지를 붙였다. 구현되지 않은 모드가
스토어 스크린샷에서 동작하는 것처럼 보이면 Play 의 허위 표시 정책에 걸린다.

store/ 에 Play Console 제출 자산을 모았다: 등록정보 문구, 데이터 보안 설문
답안(릴리스 APK 에 INTERNET 권한이 없음을 aapt2 로 확인), 개인정보처리방침,
피처 그래픽 1024x500, 스토어 아이콘 512x512, 폰 4장 + 태블릿 3장 스크린샷.
2026-09-24 16:28:59 +09:00

201 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TETRIG (테트리그)
> 쌓아라, 부숴라, 올라가라.
TETR.IO 스타일의 **모바일 실시간 대전 블록 스태커** 웹 게임. 솔로 3모드 + 인원 무제한 커스텀 룸(FFA) + Glicko-2 랭크 리그전을 하나의 단일 HTML 클라이언트와 Cloudflare Workers 서버로 구현한다.
이 문서는 앱 제작을 위한 **최종 빌드 진입점(entry point)** 이다. 모든 설계 결정·수치·에셋의 **정본(source of truth)은 [`TETRIG-최종명세서.md`](./TETRIG-최종명세서.md)** 이며, 구현 시 반드시 명세서와 UX 목업을 함께 열어 작업한다.
> **배포 경로 (2026-09-24 확정)**: 출시 대상은 **Google Play 안드로이드 네이티브 앱**
> (`app/`, Kotlin Multiplatform + Compose). 아이폰은 App Store 대신 **웹 PWA**
> (`https://tetrig.pages.dev`, "홈 화면에 추가")로 커버한다 — iOS 네이티브는
> Apple Developer Program 연 $99 + 맥이 필요해 접었다. 필요한 비용은 Play 개발자
> 등록비 $25(1회)뿐이고, 웹/서버는 Cloudflare 무료 티어로 간다. 웹 클라이언트
> 진입점은 [`index.html`](./index.html)(빌드 산출물), 소스는 [`src/`](./src),
> 서버 계획(Cloudflare Workers + D1 + WebSocket)은 명세서 그대로.
---
## 무엇을 만드는가
산출물은 딱 2개다.
1. **클라이언트** — 단일 HTML 파일 (HTML + CSS + JS, Canvas 2D). **외부 에셋 0개**, 사운드는 100% Web Audio 신스 생성.
2. **서버** — Cloudflare Workers 프로젝트 (wrangler 표준 구조: Durable Objects 2종 + D1 + Cron).
게임 구성:
- **솔로 3모드**: SPRINT(40줄 최속) · TIME ATTACK(2분 점수) · ENDLESS(무한)
- **커스텀 룸**: 6자리 초대 코드, 인원 무제한 FFA, 호스트 규칙 설정
- **리그전**: Glicko-2 레이팅 + 18단계 랭크 + 배치 10경기 + 시즌제
## 아키텍처
```
[모바일 브라우저] [Cloudflare]
클라이언트 (단일 HTML, Canvas) ⇄ WSS ⇄ Workers
├ 게임 엔진 (SRS+, 판정, 렌더) ├ MatchmakerDO (리그 매칭 큐, 싱글턴)
├ 솔로 모드 3종 ├ GameRoomDO (경기방 — 리그 & 커스텀 공용)
└ 멀티/커스텀/프로필/리더보드 UI ├ D1 (계정·레이팅·전적·리더보드)
└ Cron (RD 감쇠, 시즌 처리)
```
- 클라이언트: Cloudflare Pages(또는 Workers 정적 자산)로 호스팅, 시작 주소는 `tetrig.pages.dev`.
- 서버: 무료 티어로 운영 가능 (DO SQLite 백엔드 + WebSocket Hibernation).
- 로컬 개발: `wrangler dev` + 브라우저 탭 여러 개로 다인전 테스트.
## 저장소 파일
| 파일 | 용도 |
|---|---|
| `TETRIG-최종명세서.md` | **정본 명세서** — 모든 규칙·수치·좌표·스키마·프로토콜 |
| `tetrig-ux-mockup.png` | 9화면 UX 목업 (1390×2944) — **UI 구현의 시각 기준** |
| `tetrig-logo.svg` / `tetrig-logo.png` | 가로형 워드마크 로고 (실사용 에셋 / 렌더 미리보기) |
| `tetrig-icon.svg` / `tetrig-icon.png` | 앱 아이콘 512×512 (실사용 에셋 / 렌더 미리보기) |
명세서의 화면 번호 ①~⑨는 목업 시트의 화면과 1:1 대응한다. **시각 판단은 목업 이미지를 따르고**, 명세서 §11~§13의 좌표·팔레트 수치로 정확도를 보정한다.
## 핵심 규칙 요약 (상세는 명세서 §4~§8)
- **보드**: 10열 × 40행(내부), 하단 20행 표시. 랜덤 7-bag, 넥스트 5, 홀드 1회/피스, 고스트 피스.
- **회전**: SRS + 180° 킥테이블 (명세서 §4.1). 락 딜레이 500ms / 최대 15회 리셋.
- **판정**: T-spin / T-spin mini, B2B, 콤보, All Clear. 4줄 클리어는 **"QUAD"** (Tetris/테트리스 단어 금지).
- **대전**: 가비지 전송 테이블 + 상쇄(counter) + 8줄/회 상한 + 장기전 방지 배율.
- **리그**: Glicko-2 (R=1500, RD=350, σ=0.06, τ=0.5) → 표시용 TR(0~25,000) → 18단계 랭크. 배치 10경기, RD<100일 때만 랭크 표시.
- **매치메이킹**: MatchmakerDO 싱글턴 큐, R ± 2×RD 구간이 겹치면 성사. 형식은 높은 랭크 기준 A+ 이하 FT3 / S- 이상 FT5.
## 디자인 시스템 (NEOBRUTAL — 상세는 명세서 §13)
- **테마 이원화**: 메뉴/로비/결과는 BONE(#EDE7DA) 라이트, 인게임은 INK(#17161B) 다크.
- **컴포넌트**: 2px 잉크 보더 + 블러 0 하드 오프셋 섀도 + 우하단 챔퍼컷 + 고대비 플랫 컬러.
- **시그니처**: 45° 해저드 스트라이프(가비지·공격·광고·난투에만 절제 사용).
- **금지**: 다크 네이비 배경 · 네온 글로우 · 그라디언트 · 블러 섀도 · 과도한 라운딩.
- **피스 7색(v2 확정)**: I `#3EC1B6` / O `#F5C531` / T `#A76BF2` / S `#7FCC4C` / Z `#F25C4C` / J `#4C7DF2` / L `#F2913D` / 가비지 `#46464F`.
## 개발 로드맵 (이 순서로 진행)
각 Phase 완료 시 실행 가능한 파일 + 코어 로직 단위 테스트 통과를 완료 기준으로 삼고, 사용자 확인 후 다음으로 넘어간다.
| 단계 | 내용 | 완료 기준 |
|---|---|---|
| Phase 1 | 코어 엔진: 보드·피스·SRS+180 킥·7-bag·홀드·고스트·락딜레이·클리어 | 키보드 플레이 + 킥/판정 단위 테스트 통과 |
| Phase 2 | 판정·점수 + 솔로 3모드 + 솔로 결과 화면 | 3모드 정상 플레이·기록 저장 |
| Phase 3 | 가상 버튼 + 스와이프 + 핸들링 설정 + 메뉴 화면들 | 모바일 브라우저 실기 조작 확인 |
| Phase 4 | 서버 뼈대: 게스트/계정·기록 이전·GameRoomDO·WS·가비지 원장 + 커스텀 1:1 | 탭 2개로 코드 방 1:1 대전 성사 |
| Phase 5 | 커스텀 FFA: 다인 타겟팅·미니뷰 그리드·KO 관전·도중 입장·리매치 투표 | 탭 4개+ FFA 라운드 정상 진행 |
| Phase 6 | MatchmakerDO + Glicko-2 + TR/랭크 + 리더보드·프로필·전적 + 리그 결과 | 매칭→경기→TR 반영 전체 사이클 |
| Phase 7 | 연출·사운드·시즌·광고 슬롯 연결·재접속/부하 점검·폴리시 | v1 배포 (wrangler deploy + Pages) |
## 개발 환경
```bash
# 서버 (Cloudflare Workers)
npm create cloudflare@latest # wrangler 프로젝트 초기화
wrangler dev # 로컬 개발 서버 + WS
wrangler deploy # 배포
# 클라이언트
# 단일 HTML 파일 — 브라우저로 직접 열거나 Pages/Workers 정적 자산으로 서빙
```
준비물(필요 시점에 사용자에게 확인 — 명세서 §17):
- Google OAuth 클라이언트 ID (Phase 4)
- 애드센스 계정 ID (Phase 7)
## 배포
### 네이티브 앱 (Kotlin Multiplatform + Compose Multiplatform)
`app/` 이 실제 출시 대상이고, **출시 플랫폼은 Google Play(안드로이드) 하나다.**
iOS 타깃은 2026-09-24 에 제거했다 — Apple Developer Program 연 $99 와 맥이
필요해 비용 대비 실익이 없다고 판단했다. 아이폰 사용자는 웹 PWA
(`https://tetrig.pages.dev`)로 커버한다. 엔진과 UI 전부 `commonMain` 한 벌이라,
나중에 iOS 가 필요해지면 `build.gradle.kts` 에 `iosTarget` 만 다시 넣으면 된다.
```
app/composeApp/src/
commonMain/ 엔진(engine/) · 모드·타이밍(game/) · UI(ui/) · 저장소(data/)
commonTest/ 엔진 테스트 28개
androidMain/ MainActivity, 매니페스트, 아이콘, SharedPreferences 구현
```
안드로이드 빌드:
```bash
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
export ANDROID_HOME=$HOME/android-sdk
cd app
cp keystore.properties.sample keystore.properties # 서명 키 경로/비번 입력
./gradlew :composeApp:testDebugUnitTest :composeApp:assembleRelease :composeApp:bundleRelease
# → composeApp/build/outputs/apk/release/composeApp-release.apk
# → composeApp/build/outputs/bundle/release/composeApp-release.aab
```
### Play Console 제출 자산
`store/` 에 스토어 등록에 필요한 것이 다 들어 있다.
```
store/listing.md 앱 이름·설명·데이터보안 설문 답안·콘텐츠 등급 가이드
store/privacy.html 개인정보처리방침 (https://tetrig.pages.dev/privacy 로 배포됨)
store/feature_graphic.png 1024x500 피처 그래픽
store/icon-512-play.png 512x512 스토어 아이콘 (알파 없는 정사각형)
store/shots/0*.png 휴대전화 스크린샷 1080x2400 4장
store/shots/tablet_*.png 태블릿 세로 1600x2560 2장 + 가로 2560x1600 1장
store/make_feature_graphic.py, store/make_play_icon.py 자산 재생성 스크립트
```
앱은 `INTERNET` 권한조차 요청하지 않는다(`aapt2 dump badging` 으로 확인).
그래서 Play 데이터 보안 설문은 전 항목 "수집 안 함"으로 끝난다.
### 기기 호환
고정 해상도를 전제하지 않고 `BoxWithConstraints` 로 실제 가용 크기에서
셀 크기·UI 배율을 계산한다 (`ui/Responsive.kt`).
| 폼팩터 | 동작 |
| --- | --- |
| 일반 폰 세로 | 보드 위 + 조작 아래 |
| 갤럭시 Z 플립 본화면(초세로) | 보드를 세로로 더 키움 |
| 갤럭시 Z 플립 커버(소형) | 축소 배율 + 넥스트 3개로 감축 |
| 갤럭시 Z 폴드 펼침(정사각형에 가까움) | 넓은 폭 레이아웃, 카드 가로 배치 |
| 태블릿 가로 | 보드 가운데, 조작을 좌우 엄지 영역으로 분리 |
접었다 펴기·회전은 액티비티 재시작 없이 처리한다
(`configChanges` + `resizeableActivity=true`).
### 버전 관리
- `app/gradle.properties` 의 `appVersionName`(semver) / `appVersionCode`(단조 증가)가 원본
- 빌드 시 `-PappVersionName` `-PappVersionCode` 로 덮어쓸 수 있다
- 릴리스는 스크립트 한 방:
```bash
scripts/release.sh patch # 또는 minor / major
```
버전 상승 → 테스트 → 서명 빌드 → 커밋 → `vX.Y.Z` 태그 → Gitea push →
릴리스 생성 + APK/AAB 업로드까지 자동으로 한다.
### 웹 (Cloudflare Pages)
`index.html` 기반 PWA 는 그대로 유지한다 (아이폰 사용자용 / 데모).
`dist/privacy.html` 은 개인정보처리방침이고 `/privacy` 로 서빙된다.
```bash
node build.js
wrangler pages deploy dist --project-name tetrig --branch main
```
## 구현 가드레일 (반드시 지킬 것)
- **IP 금지**: 사용자 노출 텍스트에 "Tetris/테트리스" 금지, 테트리스 음악(Korobeiniki 등) 금지, 표준 가이드라인 색상 1:1 복제 금지, TETR.IO 에셋·명칭 복제 금지.
- **BRAND 상수화**: 앱 이름·로고 문구는 코드 1곳(`const BRAND = "TETRIG"` / `BRAND_KO = "테트리그"`)에서 관리해 1분 내 리브랜딩 가능하게.
- **튜닝 수치 상수화**: 중력·TR 커브·콤보 테이블 등 조정 대상은 코드 상단 상수로 분리.
- **언어**: 사용자 노출 텍스트는 한국어 (게임 용어 SPRINT/HOLD 등 영문 허용).
- **테스트 필수**: 킥테이블·T-spin 판정은 반드시 jsdom/node 단위 테스트로 검증.
- **명세 준수**: 명세서에 값이 명시된 것은 그대로 구현하고, 없는 것만 질문한다.
## 라이선스 / 상표
앱 이름 "테트리그(TETRIG)"는 상표 유사성 리스크를 인지한 뒤 **스토어 출시·수익화(광고 게시)까지 최종 확정**한 이름이다(2026-08-21). 이름 재검토 트리거는 없다. `BRAND` 상수화는 리브랜딩 대비가 아니라 표기 일관성·유지보수를 위한 단일 소스로서 유지한다 (명세서 §2).