diff --git a/flutter/.env.example b/flutter/.env.example
index e825d8e..5e26559 100644
--- a/flutter/.env.example
+++ b/flutter/.env.example
@@ -7,8 +7,9 @@
# 빌드 타임 분리가 필요하면 --dart-define 또는 .env.{flavor} 도입 고려.
# ==========================================
-# 백엔드 API 베이스 URL (백엔드 글로벌 prefix /api 포함)
-# 안드로이드 에뮬레이터에서 호스트 접근 시: http://10.0.2.2:3000/api
+# 백엔드 API 베이스 URL (백엔드 글로벌 prefix /api 포함) — 개발용
+# 안드로이드 에뮬레이터에서 호스트 접근 시: http://10.0.2.2:3000/api (또는 adb reverse tcp:3000 tcp:3000 후 localhost)
+# 출시 빌드는 이 값 대신 --dart-define=API_URL=https://<도메인>/api 로 넘긴다 (https·공개 주소가 아니면 앱이 시작하지 않는다)
API_URL=http://localhost:3000/api
# 기록을 어디에 둘지 — api: 서버(기본, 로그인한 회원의 기록) / local: 이 기기 (서버 없이 화면만 볼 때)
diff --git a/flutter/android/app/build.gradle.kts b/flutter/android/app/build.gradle.kts
index 6b66a87..c7f56eb 100644
--- a/flutter/android/app/build.gradle.kts
+++ b/flutter/android/app/build.gradle.kts
@@ -1,3 +1,6 @@
+import java.io.FileInputStream
+import java.util.Properties
+
plugins {
id("com.android.application")
id("kotlin-android")
@@ -5,8 +8,16 @@ plugins {
id("dev.flutter.flutter-gradle-plugin")
}
+// 출시 서명 키 — android/key.properties (커밋하지 않는다, 예시는 key.properties.example).
+// 파일이 없으면 디버그 키로 서명한다 — 스토어에는 올릴 수 없고, 기기에서 릴리스 빌드를 확인할 때만 쓴다.
+val keystorePropertiesFile = rootProject.file("key.properties")
+val keystoreProperties = Properties().apply {
+ if (keystorePropertiesFile.exists()) FileInputStream(keystorePropertiesFile).use { load(it) }
+}
+val hasReleaseKey = keystorePropertiesFile.exists()
+
android {
- namespace = "com.example.tma_app"
+ namespace = "com.ttipo.rup"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
@@ -20,21 +31,34 @@ android {
}
defaultConfig {
- // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
- applicationId = "com.example.tma_app"
- // You can update the following values to match your application needs.
- // For more information, see: https://flutter.dev/to/review-gradle-config.
+ // 스토어에 올린 뒤에는 바꿀 수 없다 (iOS 번들 ID 도 같은 값)
+ applicationId = "com.ttipo.rup"
+ // 버전은 pubspec.yaml 의 version(이름+빌드 번호)을 따른다
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
}
+ signingConfigs {
+ if (hasReleaseKey) {
+ create("release") {
+ keyAlias = keystoreProperties.getProperty("keyAlias")
+ keyPassword = keystoreProperties.getProperty("keyPassword")
+ storeFile = file(keystoreProperties.getProperty("storeFile"))
+ storePassword = keystoreProperties.getProperty("storePassword")
+ }
+ }
+ }
+
buildTypes {
release {
- // TODO: Add your own signing config for the release build.
- // Signing with the debug keys for now, so `flutter run --release` works.
- signingConfig = signingConfigs.getByName("debug")
+ signingConfig = if (hasReleaseKey) {
+ signingConfigs.getByName("release")
+ } else {
+ logger.warn("android/key.properties 가 없어 릴리스 빌드를 디버그 키로 서명합니다 — 스토어에 올릴 수 없습니다.")
+ signingConfigs.getByName("debug")
+ }
}
}
}
diff --git a/flutter/android/app/src/main/AndroidManifest.xml b/flutter/android/app/src/main/AndroidManifest.xml
index 779e3af..7ea99dd 100644
--- a/flutter/android/app/src/main/AndroidManifest.xml
+++ b/flutter/android/app/src/main/AndroidManifest.xml
@@ -2,9 +2,9 @@
-
+
-
-
-
-
+
diff --git a/flutter/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png b/flutter/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..e80f8c7
Binary files /dev/null and b/flutter/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png differ
diff --git a/flutter/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png b/flutter/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..e94069e
Binary files /dev/null and b/flutter/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png differ
diff --git a/flutter/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png b/flutter/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..8a58e98
Binary files /dev/null and b/flutter/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/flutter/android/app/src/main/res/drawable/launch_background.xml b/flutter/android/app/src/main/res/drawable/launch_background.xml
index 304732f..ec0caf5 100644
--- a/flutter/android/app/src/main/res/drawable/launch_background.xml
+++ b/flutter/android/app/src/main/res/drawable/launch_background.xml
@@ -1,12 +1,5 @@
-
+
-
-
-
-
+
diff --git a/flutter/android/app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml b/flutter/android/app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml
new file mode 100644
index 0000000..c79c58a
--- /dev/null
+++ b/flutter/android/app/src/main/res/mipmap-anydpi-v26/launcher_icon.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
diff --git a/flutter/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/flutter/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
deleted file mode 100644
index db77bb4..0000000
Binary files a/flutter/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ
diff --git a/flutter/android/app/src/main/res/mipmap-hdpi/launcher_icon.png b/flutter/android/app/src/main/res/mipmap-hdpi/launcher_icon.png
new file mode 100644
index 0000000..8fb1742
Binary files /dev/null and b/flutter/android/app/src/main/res/mipmap-hdpi/launcher_icon.png differ
diff --git a/flutter/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/flutter/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
deleted file mode 100644
index 17987b7..0000000
Binary files a/flutter/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ
diff --git a/flutter/android/app/src/main/res/mipmap-mdpi/launcher_icon.png b/flutter/android/app/src/main/res/mipmap-mdpi/launcher_icon.png
new file mode 100644
index 0000000..efc91d3
Binary files /dev/null and b/flutter/android/app/src/main/res/mipmap-mdpi/launcher_icon.png differ
diff --git a/flutter/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/flutter/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
deleted file mode 100644
index 09d4391..0000000
Binary files a/flutter/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/flutter/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png b/flutter/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png
new file mode 100644
index 0000000..5692743
Binary files /dev/null and b/flutter/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png differ
diff --git a/flutter/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/flutter/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
deleted file mode 100644
index d5f1c8d..0000000
Binary files a/flutter/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/flutter/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png b/flutter/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png
new file mode 100644
index 0000000..9e00070
Binary files /dev/null and b/flutter/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png differ
diff --git a/flutter/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/flutter/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
deleted file mode 100644
index 4d6372e..0000000
Binary files a/flutter/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ
diff --git a/flutter/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png b/flutter/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png
new file mode 100644
index 0000000..64c3b4a
Binary files /dev/null and b/flutter/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png differ
diff --git a/flutter/android/app/src/main/res/values-night-v31/styles.xml b/flutter/android/app/src/main/res/values-night-v31/styles.xml
new file mode 100644
index 0000000..7b72f4f
--- /dev/null
+++ b/flutter/android/app/src/main/res/values-night-v31/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/flutter/android/app/src/main/res/values-v31/styles.xml b/flutter/android/app/src/main/res/values-v31/styles.xml
new file mode 100644
index 0000000..7b72f4f
--- /dev/null
+++ b/flutter/android/app/src/main/res/values-v31/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/flutter/android/app/src/main/res/values/colors.xml b/flutter/android/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..76c67bf
--- /dev/null
+++ b/flutter/android/app/src/main/res/values/colors.xml
@@ -0,0 +1,7 @@
+
+
+
+ #FF7500
+
+ #FFDE84
+
diff --git a/flutter/android/key.properties.example b/flutter/android/key.properties.example
new file mode 100644
index 0000000..1900427
--- /dev/null
+++ b/flutter/android/key.properties.example
@@ -0,0 +1,10 @@
+# 출시 서명 키 설정 — 이 파일을 key.properties 로 복사해 값을 채운다 (key.properties 와 키 파일은 커밋하지 않는다).
+#
+# 키 만들기 (한 번만 · 잃어버리면 같은 앱으로 업데이트를 올릴 수 없으니 안전한 곳에 백업):
+# keytool -genkey -v -keystore ~/rup-upload.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload
+#
+# storeFile 은 android/app 기준 경로 또는 절대 경로
+storeFile=/path/to/rup-upload.jks
+storePassword=change-me-store-password
+keyAlias=upload
+keyPassword=change-me-key-password
diff --git a/flutter/branding/app_icon.png b/flutter/branding/app_icon.png
new file mode 100644
index 0000000..f841ff7
Binary files /dev/null and b/flutter/branding/app_icon.png differ
diff --git a/flutter/branding/app_icon_foreground.png b/flutter/branding/app_icon_foreground.png
new file mode 100644
index 0000000..910ae2f
Binary files /dev/null and b/flutter/branding/app_icon_foreground.png differ
diff --git a/flutter/ios/Runner.xcodeproj/project.pbxproj b/flutter/ios/Runner.xcodeproj/project.pbxproj
index 8fe3ebe..4c0355f 100644
--- a/flutter/ios/Runner.xcodeproj/project.pbxproj
+++ b/flutter/ios/Runner.xcodeproj/project.pbxproj
@@ -372,7 +372,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = com.example.tmaApp;
+ PRODUCT_BUNDLE_IDENTIFIER = com.ttipo.rup;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@@ -388,7 +388,7 @@
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = com.example.tmaApp.RunnerTests;
+ PRODUCT_BUNDLE_IDENTIFIER = com.ttipo.rup.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -405,7 +405,7 @@
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = com.example.tmaApp.RunnerTests;
+ PRODUCT_BUNDLE_IDENTIFIER = com.ttipo.rup.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
@@ -420,7 +420,7 @@
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = com.example.tmaApp.RunnerTests;
+ PRODUCT_BUNDLE_IDENTIFIER = com.ttipo.rup.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
@@ -431,7 +431,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
- ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
@@ -488,7 +488,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
- ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
@@ -551,7 +551,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = com.example.tmaApp;
+ PRODUCT_BUNDLE_IDENTIFIER = com.ttipo.rup;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -573,7 +573,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = com.example.tmaApp;
+ PRODUCT_BUNDLE_IDENTIFIER = com.ttipo.rup;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
diff --git a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
index d36b1fa..d0d98aa 100644
--- a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -1,122 +1 @@
-{
- "images" : [
- {
- "size" : "20x20",
- "idiom" : "iphone",
- "filename" : "Icon-App-20x20@2x.png",
- "scale" : "2x"
- },
- {
- "size" : "20x20",
- "idiom" : "iphone",
- "filename" : "Icon-App-20x20@3x.png",
- "scale" : "3x"
- },
- {
- "size" : "29x29",
- "idiom" : "iphone",
- "filename" : "Icon-App-29x29@1x.png",
- "scale" : "1x"
- },
- {
- "size" : "29x29",
- "idiom" : "iphone",
- "filename" : "Icon-App-29x29@2x.png",
- "scale" : "2x"
- },
- {
- "size" : "29x29",
- "idiom" : "iphone",
- "filename" : "Icon-App-29x29@3x.png",
- "scale" : "3x"
- },
- {
- "size" : "40x40",
- "idiom" : "iphone",
- "filename" : "Icon-App-40x40@2x.png",
- "scale" : "2x"
- },
- {
- "size" : "40x40",
- "idiom" : "iphone",
- "filename" : "Icon-App-40x40@3x.png",
- "scale" : "3x"
- },
- {
- "size" : "60x60",
- "idiom" : "iphone",
- "filename" : "Icon-App-60x60@2x.png",
- "scale" : "2x"
- },
- {
- "size" : "60x60",
- "idiom" : "iphone",
- "filename" : "Icon-App-60x60@3x.png",
- "scale" : "3x"
- },
- {
- "size" : "20x20",
- "idiom" : "ipad",
- "filename" : "Icon-App-20x20@1x.png",
- "scale" : "1x"
- },
- {
- "size" : "20x20",
- "idiom" : "ipad",
- "filename" : "Icon-App-20x20@2x.png",
- "scale" : "2x"
- },
- {
- "size" : "29x29",
- "idiom" : "ipad",
- "filename" : "Icon-App-29x29@1x.png",
- "scale" : "1x"
- },
- {
- "size" : "29x29",
- "idiom" : "ipad",
- "filename" : "Icon-App-29x29@2x.png",
- "scale" : "2x"
- },
- {
- "size" : "40x40",
- "idiom" : "ipad",
- "filename" : "Icon-App-40x40@1x.png",
- "scale" : "1x"
- },
- {
- "size" : "40x40",
- "idiom" : "ipad",
- "filename" : "Icon-App-40x40@2x.png",
- "scale" : "2x"
- },
- {
- "size" : "76x76",
- "idiom" : "ipad",
- "filename" : "Icon-App-76x76@1x.png",
- "scale" : "1x"
- },
- {
- "size" : "76x76",
- "idiom" : "ipad",
- "filename" : "Icon-App-76x76@2x.png",
- "scale" : "2x"
- },
- {
- "size" : "83.5x83.5",
- "idiom" : "ipad",
- "filename" : "Icon-App-83.5x83.5@2x.png",
- "scale" : "2x"
- },
- {
- "size" : "1024x1024",
- "idiom" : "ios-marketing",
- "filename" : "Icon-App-1024x1024@1x.png",
- "scale" : "1x"
- }
- ],
- "info" : {
- "version" : 1,
- "author" : "xcode"
- }
-}
+{"images":[{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"20x20","idiom":"iphone","filename":"Icon-App-20x20@3x.png","scale":"3x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"29x29","idiom":"iphone","filename":"Icon-App-29x29@3x.png","scale":"3x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"40x40","idiom":"iphone","filename":"Icon-App-40x40@3x.png","scale":"3x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@1x.png","scale":"1x"},{"size":"57x57","idiom":"iphone","filename":"Icon-App-57x57@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@2x.png","scale":"2x"},{"size":"60x60","idiom":"iphone","filename":"Icon-App-60x60@3x.png","scale":"3x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@1x.png","scale":"1x"},{"size":"20x20","idiom":"ipad","filename":"Icon-App-20x20@2x.png","scale":"2x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@1x.png","scale":"1x"},{"size":"29x29","idiom":"ipad","filename":"Icon-App-29x29@2x.png","scale":"2x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@1x.png","scale":"1x"},{"size":"40x40","idiom":"ipad","filename":"Icon-App-40x40@2x.png","scale":"2x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@1x.png","scale":"1x"},{"size":"50x50","idiom":"ipad","filename":"Icon-App-50x50@2x.png","scale":"2x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@1x.png","scale":"1x"},{"size":"72x72","idiom":"ipad","filename":"Icon-App-72x72@2x.png","scale":"2x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@1x.png","scale":"1x"},{"size":"76x76","idiom":"ipad","filename":"Icon-App-76x76@2x.png","scale":"2x"},{"size":"83.5x83.5","idiom":"ipad","filename":"Icon-App-83.5x83.5@2x.png","scale":"2x"},{"size":"1024x1024","idiom":"ios-marketing","filename":"Icon-App-1024x1024@1x.png","scale":"1x"}],"info":{"version":1,"author":"xcode"}}
\ No newline at end of file
diff --git a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
index dc9ada4..c85c428 100644
Binary files a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png and b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ
diff --git a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
index 7353c41..4c8c9df 100644
Binary files a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
index 797d452..0fa575d 100644
Binary files a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
index 6ed2d93..f8ece49 100644
Binary files a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
index 4cd7b00..965749f 100644
Binary files a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
index fe73094..56ba0d7 100644
Binary files a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
index 321773c..be88802 100644
Binary files a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
index 797d452..0fa575d 100644
Binary files a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
index 502f463..fec207a 100644
Binary files a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
index 0ec3034..8345665 100644
Binary files a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png
new file mode 100644
index 0000000..028b6dc
Binary files /dev/null and b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png differ
diff --git a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png
new file mode 100644
index 0000000..6425467
Binary files /dev/null and b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png differ
diff --git a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png
new file mode 100644
index 0000000..c40e977
Binary files /dev/null and b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png differ
diff --git a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png
new file mode 100644
index 0000000..6774b33
Binary files /dev/null and b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png differ
diff --git a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
index 0ec3034..8345665 100644
Binary files a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
index e9f5fea..1dc12e6 100644
Binary files a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png
new file mode 100644
index 0000000..708cb75
Binary files /dev/null and b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png differ
diff --git a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png
new file mode 100644
index 0000000..3771afd
Binary files /dev/null and b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png differ
diff --git a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
index 84ac32a..a379741 100644
Binary files a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
index 8953cba..6771dab 100644
Binary files a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
index 0467bf1..0ed1ebe 100644
Binary files a/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and b/flutter/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/flutter/ios/Runner/Base.lproj/LaunchScreen.storyboard b/flutter/ios/Runner/Base.lproj/LaunchScreen.storyboard
index f2e259c..0637349 100644
--- a/flutter/ios/Runner/Base.lproj/LaunchScreen.storyboard
+++ b/flutter/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -19,7 +19,7 @@
-
+
diff --git a/flutter/ios/Runner/Info.plist b/flutter/ios/Runner/Info.plist
index 9d62cc8..879ed5e 100644
--- a/flutter/ios/Runner/Info.plist
+++ b/flutter/ios/Runner/Info.plist
@@ -7,7 +7,7 @@
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleDisplayName
- Tma App
+ rup
CFBundleURLTypes
@@ -34,7 +34,7 @@
CFBundleInfoDictionaryVersion
6.0
CFBundleName
- tma_app
+ rup
CFBundlePackageType
APPL
CFBundleShortVersionString
diff --git a/flutter/lib/core/config/app_config.dart b/flutter/lib/core/config/app_config.dart
index 451c81c..a7e716a 100644
--- a/flutter/lib/core/config/app_config.dart
+++ b/flutter/lib/core/config/app_config.dart
@@ -15,8 +15,28 @@ DataMode readDataMode() {
return dotenv.maybeGet('DATA_MODE') == DataMode.local.name ? DataMode.local : DataMode.api;
}
-/// `.env` 의 API_URL — 안드로이드 에뮬레이터에서 호스트에 붙을 때는 http://10.0.2.2:3000/api
-String readApiBaseUrl() => (dotenv.isInitialized ? dotenv.maybeGet('API_URL') : null) ?? kDefaultApiUrl;
+/// 빌드할 때 넘긴 API 주소 — `flutter build … --dart-define=API_URL=https://…/api` (출시 빌드는 이것을 쓴다)
+const String _kDefinedApiUrl = String.fromEnvironment('API_URL');
+
+/// 서버 주소 — 빌드 때 넘긴 값 → `.env` 의 API_URL → 개발 서버 순.
+/// 안드로이드 에뮬레이터에서 호스트에 붙을 때는 http://10.0.2.2:3000/api (또는 adb reverse 로 localhost)
+String readApiBaseUrl() {
+ if (_kDefinedApiUrl.isNotEmpty) return _kDefinedApiUrl;
+ return (dotenv.isInitialized ? dotenv.maybeGet('API_URL') : null) ?? kDefaultApiUrl;
+}
+
+/// 내 기기에서만 닿는 주소 — 출시 빌드가 여기를 가리키면 사용자 기기에서 서버에 닿지 않는다
+const Set _kLocalHosts = {'localhost', '127.0.0.1', '10.0.2.2', '0.0.0.0'};
+
+/// 출시 빌드의 서버 주소에 문제가 있으면 그 이유, 괜찮으면 null.
+/// 출시 빌드는 https 만 쓸 수 있고(Android·iOS 모두 평문 http 를 막는다) 개발용 주소를 가리키면 안 된다
+String? releaseApiUrlProblem(String url) {
+ final uri = Uri.tryParse(url);
+ if (uri == null || uri.host.isEmpty) return '서버 주소(API_URL)를 읽을 수 없습니다: $url';
+ if (uri.scheme != 'https') return '출시 빌드의 서버 주소는 https 여야 합니다: $url';
+ if (_kLocalHosts.contains(uri.host)) return '출시 빌드가 개발용 서버 주소를 가리킵니다: $url';
+ return null;
+}
final dataModeProvider = Provider((ref) => readDataMode());
diff --git a/flutter/lib/main.dart b/flutter/lib/main.dart
index e487258..f57464b 100644
--- a/flutter/lib/main.dart
+++ b/flutter/lib/main.dart
@@ -1,3 +1,4 @@
+import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
@@ -20,6 +21,12 @@ Future main() async {
// .env 파일이 없는 경우 무시 (기본값 사용)
}
+ // 출시 빌드가 개발 서버·평문 http 를 가리키면 바로 멈춘다 — 사용자 기기에서 조용히 모든 요청이 실패하는 것보다 낫다
+ if (kReleaseMode && readDataMode() == DataMode.api) {
+ final problem = releaseApiUrlProblem(readApiBaseUrl());
+ if (problem != null) throw StateError(problem);
+ }
+
// 시안은 세로 화면 전용이다
await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
diff --git a/flutter/pubspec.lock b/flutter/pubspec.lock
index 2a33d1f..acceca8 100644
--- a/flutter/pubspec.lock
+++ b/flutter/pubspec.lock
@@ -17,6 +17,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "10.0.1"
+ archive:
+ dependency: transitive
+ description:
+ name: archive
+ sha256: "58c8d17d8d589c4259f773522815115de873c397b977a29993588487d3f9acb4"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.5.0"
args:
dependency: transitive
description:
@@ -49,6 +57,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.1"
+ checked_yaml:
+ dependency: transitive
+ description:
+ name: checked_yaml
+ sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.4"
cli_config:
dependency: transitive
description:
@@ -57,6 +73,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.2.0"
+ cli_util:
+ dependency: transitive
+ description:
+ name: cli_util
+ sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.4.2"
clock:
dependency: transitive
description:
@@ -222,6 +246,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.2.1"
+ flutter_launcher_icons:
+ dependency: "direct dev"
+ description:
+ name: flutter_launcher_icons
+ sha256: "10f13781741a2e3972126fae08393d3c4e01fa4cd7473326b94b72cf594195e7"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.14.4"
flutter_lints:
dependency: "direct dev"
description:
@@ -376,6 +408,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.1.2"
+ image:
+ dependency: transitive
+ description:
+ name: image
+ sha256: "89ed75b2f8231fe676808c5564f9590d728024ded7ac782a7ed8474f1c95b72a"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.12.0"
image_picker:
dependency: "direct main"
description:
@@ -464,6 +504,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.1"
+ json_annotation:
+ dependency: transitive
+ description:
+ name: json_annotation
+ sha256: "2a743920d81b7910627f68ee2c9ac1fc0bfee32b9fc3403587d7c6791ca12f80"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.12.0"
leak_tracker:
dependency: transitive
description:
@@ -672,6 +720,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.5.2"
+ posix:
+ dependency: transitive
+ description:
+ name: posix
+ sha256: bc1bad54ad2b735816e31f8d4600cfde6c7839975085ddfbca48b6c9f7c4044e
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.5.2"
pub_semver:
dependency: transitive
description:
diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml
index a5d1ba7..0023675 100644
--- a/flutter/pubspec.yaml
+++ b/flutter/pubspec.yaml
@@ -58,6 +58,7 @@ dev_dependencies:
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^6.0.0
+ flutter_launcher_icons: ^0.14.4
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
@@ -117,3 +118,15 @@ flutter:
- family: UhBeeKeongKeong
fonts:
- asset: assets/fonts/UhBeeKeongKeong.ttf
+
+# 앱 아이콘 — 원본은 branding/ (브랜드 주황 + 흰 워드마크, 1024px).
+# 원본을 바꾸면 `dart run flutter_launcher_icons` 로 Android·iOS 크기별 아이콘을 다시 만든다.
+flutter_launcher_icons:
+ android: launcher_icon
+ ios: true
+ image_path: branding/app_icon.png
+ remove_alpha_ios: true
+ min_sdk_android: 23
+ # Android 8+ 적응형 아이콘 — 바탕은 브랜드 주황, 앞은 안전 영역 안의 워드마크
+ adaptive_icon_background: "#FF7500"
+ adaptive_icon_foreground: branding/app_icon_foreground.png
diff --git a/flutter/test/app_config_test.dart b/flutter/test/app_config_test.dart
new file mode 100644
index 0000000..3b7bb4c
--- /dev/null
+++ b/flutter/test/app_config_test.dart
@@ -0,0 +1,27 @@
+import 'package:flutter_test/flutter_test.dart';
+import 'package:tma_app/core/config/app_config.dart';
+
+void main() {
+ group('releaseApiUrlProblem', () {
+ test('공개 https 주소는 출시 빌드에 쓸 수 있다', () {
+ expect(releaseApiUrlProblem('https://api.rup.app/api'), isNull);
+ });
+
+ test('평문 http 는 출시 빌드에 쓸 수 없다 (기기가 막는다)', () {
+ expect(releaseApiUrlProblem('http://api.rup.app/api'), contains('https'));
+ });
+
+ test('개발용 주소는 출시 빌드에 쓸 수 없다', () {
+ expect(releaseApiUrlProblem('https://localhost:3000/api'), contains('개발용'));
+ expect(releaseApiUrlProblem('https://10.0.2.2/api'), contains('개발용'));
+ });
+
+ test('읽을 수 없는 주소', () {
+ expect(releaseApiUrlProblem('api'), isNotNull);
+ });
+ });
+
+ test('빌드 때 넘긴 값이 없고 .env 도 읽지 않았으면 개발 서버 주소', () {
+ expect(readApiBaseUrl(), kDefaultApiUrl);
+ });
+}
diff --git a/flutter/web/index.html b/flutter/web/index.html
index 536a1d2..8ddd70c 100644
--- a/flutter/web/index.html
+++ b/flutter/web/index.html
@@ -23,13 +23,13 @@
-
+
- tma_app
+ rup
diff --git a/flutter/web/manifest.json b/flutter/web/manifest.json
index 5650593..845817c 100644
--- a/flutter/web/manifest.json
+++ b/flutter/web/manifest.json
@@ -1,6 +1,6 @@
{
- "name": "tma_app",
- "short_name": "tma_app",
+ "name": "rup",
+ "short_name": "rup",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",