Kotlin Multiplatform 네이티브 앱으로 전환 (Android + iOS 공용 코드)

TWA 래퍼를 걷어내고 Compose Multiplatform 으로 다시 만들었다. 엔진·UI·모드
전부 commonMain 한 벌이라 Android 와 iOS 가 같은 코드를 쓴다.

- engine/: 웹 JS 엔진을 Kotlin 으로 이식 (SRS+180 킥, 7-bag, T-spin, B2B,
  콤보, All Clear, 점수·레벨). commonTest 28개 통과
- game/: 모드 규칙 + 중력/락딜레이/DAS/ARR 타이밍
- ui/: 네오브루탈 컴포넌트, 메인/솔로시트/인게임/결과/설정
- ui/Responsive.kt: 고정 해상도를 버리고 가용 크기에서 셀 크기·배율 계산.
  폰 세로, 플립 초세로, 플립 커버(소형), 폴드 펼침, 태블릿/아이패드 가로 대응
- androidMain: resizeableActivity + configChanges 로 접기/펴기·회전 시 재시작 없음
- iosMain + iosApp/: MainViewController + SwiftUI 껍데기 + XcodeGen 설정
  (실제 iOS 빌드는 macOS + Xcode 필요)
- scripts/release.sh: 버전 상승 → 테스트 → 서명 빌드 → 태그 → Gitea 릴리스 자동화
This commit is contained in:
2026-09-23 10:37:17 +09:00
parent 0839705b97
commit cbd0f62319
87 changed files with 2706 additions and 783 deletions

View File

@@ -97,29 +97,84 @@ wrangler deploy # 배포
## 배포
웹(Cloudflare Pages) — `dist/` 에 `index.html`, `manifest.webmanifest`, `sw.js`,
아이콘 3종, `.well-known/assetlinks.json` 을 담아 올린다.
### 네이티브 앱 (Kotlin Multiplatform + Compose Multiplatform)
`app/` 이 실제 출시 대상이다. 엔진과 UI 전부 `commonMain` 한 벌이고
Android / iOS 가 그걸 공유한다.
```
app/composeApp/src/
commonMain/ 엔진(engine/) · 모드·타이밍(game/) · UI(ui/) · 저장소(data/)
commonTest/ 엔진 테스트 (Android/iOS 양쪽에서 동일하게 돈다)
androidMain/ MainActivity, 매니페스트, 아이콘, SharedPreferences 구현
iosMain/ MainViewController, NSUserDefaults 구현
app/iosApp/ SwiftUI 껍데기 + XcodeGen 설정
```
안드로이드 빌드 (이 저장소에서 바로 가능):
```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
```
iOS 빌드 (**macOS + Xcode 필요**. 리눅스에서는 코드만 컴파일 검증되고 실제
프레임워크 빌드는 되지 않는다):
```bash
brew install xcodegen
cd app/iosApp && xcodegen generate && open iosApp.xcodeproj
# Xcode 에서 실행하면 prebuild 스크립트가
# ./gradlew :composeApp:embedAndSignAppleFrameworkForXcode 를 돌려
# ComposeApp.framework 를 만들어 링크한다.
```
App Store 출시에는 Apple Developer Program 등록(연 $99)과 맥이 필요하다.
### 기기 호환
고정 해상도를 전제하지 않고 `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 는 그대로 유지한다 (아이폰 미설치 사용자용 / 데모).
```bash
node build.js
wrangler pages deploy dist --project-name tetrig --branch main
```
안드로이드(Play Store) — `android/` 는 위 사이트를 감싸는 TWA(Trusted Web Activity)
프로젝트다. 패키지는 `kr.tkrmagid.tetrig`. Bubblewrap 으로 빌드한다.
```bash
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 # Bubblewrap 은 JDK 17 고정
export BUBBLEWRAP_KEYSTORE_PASSWORD=... BUBBLEWRAP_KEY_PASSWORD=...
cd android && bubblewrap build --skipPwaValidation
# → app-release-signed.apk (테스트 설치용), app-release-bundle.aab (Play 업로드용)
```
서명 키는 저장소에 넣지 않는다. 키를 바꾸면 `.well-known/assetlinks.json` 의
SHA-256 지문도 같이 갱신해야 TWA 가 주소창 없이 전체화면으로 뜬다.
Play App Signing 을 쓰면 Play Console 이 발급한 앱 서명 키 지문도
assetlinks 에 함께 넣어야 한다.
## 구현 가드레일 (반드시 지킬 것)
- **IP 금지**: 사용자 노출 텍스트에 "Tetris/테트리스" 금지, 테트리스 음악(Korobeiniki 등) 금지, 표준 가이드라인 색상 1:1 복제 금지, TETR.IO 에셋·명칭 복제 금지.