Google Play 단독 출시로 정리 + 스토어 제출 자산 준비

iOS 타깃을 제거했다. Apple Developer Program 연 $99 와 맥이 필요한데
아이폰은 기존 웹 PWA 로 이미 커버되므로 비용 대비 실익이 없다고 판단했다.
commonMain 중심 구조는 그대로라 iosTarget 만 다시 넣으면 복구된다.

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

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

View File

@@ -1,4 +1,3 @@
import org.jetbrains.compose.desktop.application.dsl.TargetFormat
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import java.util.Properties
@@ -23,14 +22,9 @@ kotlin {
compilerOptions { jvmTarget.set(JvmTarget.JVM_17) }
}
// iOS 타깃 — 실제 컴파일은 macOS + Xcode 에서만 가능하다.
// 리눅스에서는 설정만 되고 빌드 태스크를 실행하지 않는다.
listOf(iosX64(), iosArm64(), iosSimulatorArm64()).forEach { iosTarget ->
iosTarget.binaries.framework {
baseName = "ComposeApp"
isStatic = true
}
}
// iOS 타깃은 뺐다 (2026-09-24 결정). Apple Developer Program 연 $99 + 맥이
// 필요해 비용 대비 실익이 없다고 판단. 아이폰은 웹 PWA(tetrig.pages.dev)로 커버한다.
// 구조는 commonMain 중심 그대로라, 나중에 iosTarget 만 다시 추가하면 된다.
sourceSets {
commonMain.dependencies {

View File

@@ -2,7 +2,7 @@ package kr.tkrmagid.tetrig.data
import androidx.compose.runtime.mutableStateMapOf
/** 플랫폼 키-값 저장소 (Android SharedPreferences / iOS NSUserDefaults). */
/** 플랫폼 키-값 저장소 (Android 는 SharedPreferences). 기기 밖으로 나가지 않는다. */
interface Store {
fun getString(key: String, default: String): String
fun putString(key: String, value: String)

View File

@@ -64,18 +64,18 @@ fun MainScreen(
ModeCard("솔로", "SPRINT · TIME ATTACK · ENDLESS", Palette.Cobalt, s) { showModes = true }
}
Box(Modifier.weight(1f)) {
ModeCard("멀티 · 리그전", "실시간 1:1 랭크 매치 · 배치 10경기", Palette.Safety, s) { onSoon("멀티 · 리그전") }
ModeCard("멀티 · 리그전", "실시간 1:1 랭크 매치 · 배치 10경기", Palette.Safety, s, soon = true) { onSoon("멀티 · 리그전") }
}
Box(Modifier.weight(1f)) {
ModeCard("커스텀", "코드로 만들고 참가 · 인원 제한 없음", null, s) { onSoon("커스텀") }
ModeCard("커스텀", "코드로 만들고 참가 · 인원 제한 없음", null, s, soon = true) { onSoon("커스텀") }
}
}
} else {
Box(Modifier.widthIn(max = 720.dp).align(Alignment.CenterHorizontally)) {
Column {
ModeCard("솔로", "SPRINT · TIME ATTACK · ENDLESS", Palette.Cobalt, s) { showModes = true }
ModeCard("멀티 · 리그전", "실시간 1:1 랭크 매치 · 배치 10경기", Palette.Safety, s) { onSoon("멀티 · 리그전") }
ModeCard("커스텀", "코드로 만들고 참가 · 인원 제한 없음", null, s) { onSoon("커스텀") }
ModeCard("멀티 · 리그전", "실시간 1:1 랭크 매치 · 배치 10경기", Palette.Safety, s, soon = true) { onSoon("멀티 · 리그전") }
ModeCard("커스텀", "코드로 만들고 참가 · 인원 제한 없음", null, s, soon = true) { onSoon("커스텀") }
}
}
}
@@ -144,6 +144,9 @@ private fun ModeCard(
sub: String,
barColor: Color?,
scale: Float,
// 아직 구현되지 않은 모드는 카드에 '준비 중' 배지를 달아 둔다. 스토어 스크린샷에
// 없는 기능이 있는 것처럼 보이면 Play 정책(허위 표시) 위반이 된다.
soon: Boolean = false,
onClick: () -> Unit,
) {
Box(Modifier.fillMaxWidth().padding(bottom = (14 * scale).dp)) {
@@ -159,7 +162,19 @@ private fun ModeCard(
.weight(1f)
.padding(start = (14 * scale).dp, top = (18 * scale).dp, end = 8.dp),
) {
Disp(title, size = (26 * scale).sp)
Row(verticalAlignment = Alignment.CenterVertically) {
Disp(title, size = (26 * scale).sp)
if (soon) {
Spacer(Modifier.width((8 * scale).dp))
Box(
Modifier
.background(Palette.Sub)
.padding(horizontal = (7 * scale).dp, vertical = (2 * scale).dp),
) {
Mono("준비 중", color = Palette.Bone, size = (11 * scale).sp, weight = FontWeight.Bold)
}
}
}
Spacer(Modifier.height(6.dp))
Mono(sub, color = Palette.Sub, size = (12 * scale).sp)
}

View File

@@ -1,7 +0,0 @@
package kr.tkrmagid.tetrig
import androidx.compose.ui.window.ComposeUIViewController
import kr.tkrmagid.tetrig.ui.App
@Suppress("unused", "FunctionName")
fun MainViewController() = ComposeUIViewController { App() }

View File

@@ -1,16 +0,0 @@
package kr.tkrmagid.tetrig.data
import platform.Foundation.NSUserDefaults
private class IosStore : Store {
private val defaults = NSUserDefaults.standardUserDefaults
override fun getString(key: String, default: String): String =
defaults.stringForKey(key) ?: default
override fun putString(key: String, value: String) {
defaults.setObject(value, forKey = key)
}
}
actual fun platformStore(): Store = IosStore()