# 빌드 환경 — 실측 기록 (2026-08-31) 이 조합으로 **릴리스 APK·AAB 가 실제로 나오는 것을 확인**했다. 버전 조합이 어긋나면 Gradle 이 조용히 실패하지 않고 명확히 죽으므로 아래를 그대로 맞춘다. | 구성 | 값 | 비고 | |---|---|---| | Flutter | **3.47.2** | `fvm` 으로 고정 — `app/.fvmrc` | | JDK | **Temurin 17** | `fvm flutter config --jdk-dir` 로 지정 | | Gradle | 8.14 | `android/gradle/wrapper/gradle-wrapper.properties` | | AGP | 8.11.1 | `android/settings.gradle.kts` | | Android SDK | `/opt/homebrew/share/android-commandlinetools` | `brew install --cask android-commandlinetools` | | build-tools | 35.0.1 | | | platform | android-34 · android-36 | 빌드가 34 를 추가로 받는다 | ## JDK 를 17 로 못 박은 이유 Android Studio 번들 JBR 은 **JDK 25** 이고 **Gradle 8.14 와 비호환**이다. 그대로 빌드하면 `Gradle build failed due to Java/Gradle incompatibility` 로 죽는다. Gradle 을 9.1+ 로 올리는 선택지도 있으나 AGP 8.11.1 과의 조합을 다시 검증해야 하므로, Flutter 권장인 JDK 17 을 쓰는 쪽을 택했다. ```bash fvm flutter config --jdk-dir ``` `brew install --cask temurin@17` 은 sudo 를 요구한다. sudo 없이 쓰려면 Adoptium tar.gz 를 사용자 폴더에 풀고 그 경로를 지정하면 된다. ## 테스트 ```bash export ANDROID_HOME=/opt/homebrew/share/android-commandlinetools fvm flutter analyze fvm flutter test ``` 스모크 테스트가 `craft-allowlist.yaml` 의 `APP_TABS` 를 직접 읽어 앱의 `appTabs` 와 정확 일치를 검증한다(5탭 계약 대조). 파일은 아래 순서로 찾는다. 1. `$FANUZU_DESIGN_DIR/craft-allowlist.yaml` — 다른 위치의 파일을 가리킬 때 쓴다. 2. `app/design/craft-allowlist.yaml` — 앱 폴더 안의 사본. 3. 저장소 루트 `design/craft-allowlist.yaml`. 하나도 못 찾으면 통과가 아니라 실패한다 — 계약 대조가 조용히 사라지면 안 된다. 루트 파일을 고칠 때는 `app/design/` 사본도 함께 맞춘다. ## 빌드 ```bash export ANDROID_HOME=/opt/homebrew/share/android-commandlinetools cd app fvm flutter build apk --release # build/app/outputs/flutter-apk/app-release.apk fvm flutter build appbundle --release # build/app/outputs/bundle/release/app-release.aab ``` ## 카카오 · LINE 로그인 설정 **카카오 네이티브 앱 키** — `config/kakao.env`(저장소 밖, `config/kakao.env.example` 복사) 한 줄 `FANUZU_KAKAO_NATIVE_APP_KEY=<키>`. 주석을 넣지 않는다(iOS xcconfig 가 그대로 include 한다). Android `build.gradle.kts`(리다이렉트 스킴) · iOS `Flutter/*.xcconfig`(URL 스킴) 가 이 파일을 읽고, Dart 는 실행·빌드 때 `--dart-define-from-file=config/kakao.env` 로 받는다. 없으면 카카오 로그인만 「설정 전」으로 막힌다. 서버 `fnAuthKakao` 는 카카오 **앱 ID**(숫자)를 `firebase/functions/.env.<프로젝트>` 의 `KAKAO_APP_ID` 로 받는다(저장소 밖). ```bash fvm flutter build apk --release --dart-define-from-file=config/kakao.env ``` **LINE** 은 Firebase Authentication 의 OpenID Connect 제공자 `oidc.line` 이다. 앱에 넣을 값은 없고, 콘솔 Authentication → 로그인 방법에 LINE Login 채널 ID · 채널 시크릿 · issuer `https://access.line.me` 를 등록하고, LINE Developers 콘솔의 Callback URL 에 `https://<프로젝트 ID>.firebaseapp.com/__/auth/handler` 를 넣는다. 콘솔에서 켜지 않았으면 로그인이 E-AUTH-11 로 끝난다. ## 서명 `android/key.properties` 가 없으면 **릴리스 빌드가 의도적으로 실패**한다 (`android/app/build.gradle.kts`). 디버그 키로 조용히 서명된 산출물이 Play 에서 거부되는 것을 막기 위한 것이다. 키스토어는 `~/.fanuzu-signing/fanuzu-upload.p12` (PKCS12 · 별칭 `fanuzu-upload`). **저장소에 넣지 않는다** — `android/.gitignore` 가 `key.properties` 를, 루트 `.gitignore` 가 `*.p12` `*.jks` 를 막는다. 서명 확인: ```bash apksigner verify --print-certs build/app/outputs/flutter-apk/app-release.apk # Signer #1 certificate DN: CN=FANUZU, OU=Development, O=Sweech Co Ltd, L=Daegu, C=KR # Signer #1 certificate SHA-256 digest: 5e83f4c9cd35357b11561a5b82c47d300af8c31e33d80d94bc32a4f10f46dee7 ``` ## 알려진 어긋남 — minSdk 문서(`2주차_플러그인_선검증리포트` · 별첨 1)는 **Android 10(API 29)** 을 하한으로 적었으나 현재 빌드는 **API 24** 로 나간다. 하한을 29 로 올리는 것은 위치 퀘스트의 `geofence_foreground_service` 인데 아직 넣지 않았다. **6주차 위치 퀘스트 구현 시점에 29 로 올라간다.** 그때 기준 단말 2종 충족을 다시 확인해 빌드 노트에 적는다. --- ## iOS **2026-08-31 실증** — `flutter build ios --release --no-codesign` 통과. | 구성 | 값 | |---|---| | Xcode | **27.0** (`~/Downloads/Xcode-beta.app`) | | CocoaPods | 1.17.0 (`brew install cocoapods`) | | 산출물 | `build/ios/iphoneos/Runner.app` · 21.7MB · arm64 | | bundle id | `kr.co.fanuzu.app` · 0.1.0(1) · label FANUZU | | **MinimumOSVersion** | **15.0** — 문서(별첨 1 · 2주차 선검증)와 **일치** | `xcode-select --switch` 는 sudo 를 요구하므로 **`DEVELOPER_DIR` 로 우회**한다. ```bash export DEVELOPER_DIR=~/Downloads/Xcode-beta.app/Contents/Developer fvm flutter build ios --release --no-codesign ``` ### 제출 빌드 — App Store Connect 서명 팀은 **Fancake Inc. `V28G543VK5`** 다(`Runner.xcodeproj` 의 `DEVELOPMENT_TEAM`, `ios/ExportOptions.plist` 의 `teamID`). 번들 ID `kr.co.fanuzu.app`. | 항목 | 파일 | 없으면 | |---|---|---| | Sign in with Apple · 푸시 환경 | `ios/Runner/Runner.entitlements` | Apple 로그인이 동작하지 않고 심사에서 반려된다 | | 암호화 사용 신고 | `Info.plist` `ITSAppUsesNonExemptEncryption` | 업로드마다 수동 응답을 요구한다 | | Google 콜백 스킴 | `Info.plist` + `ios/config/Google.xcconfig` | Google 로그인 콜백이 돌아오지 않는다 | | Firebase 설정 | `ios/Runner/GoogleService-Info.plist` (번들 리소스) | 목 모드로 뜨고 Google 로그인 client ID 를 못 읽는다 | | 내보내기 설정 | `ios/ExportOptions.plist` | `-exportArchive` 가 대화형으로 묻는다 | `GoogleService-Info.plist` 와 `ios/config/Google.xcconfig` 는 저장소에 넣지 않는다. Xcode 프로젝트가 `GoogleService-Info.plist` 를 리소스로 참조하므로 **파일이 없으면 iOS 빌드가 멈춘다.** ```bash # 1. Firebase 콘솔에서 받은 iOS 설정 파일을 둔다 cp <받은 파일> ios/Runner/GoogleService-Info.plist # 2. Google 콜백 스킴을 뽑는다 → ios/config/Google.xcconfig python3 tool/ios/inject_url_scheme.py # 3. 아카이브 · IPA fvm flutter build ipa --release --export-options-plist=ios/ExportOptions.plist ``` 배포 프로파일이 없으면 `xcodebuild` 로 나눠 `-allowProvisioningUpdates` 를 붙인다. ### 정리해야 할 것 — CocoaPods / SwiftPM 이중 상태 빌드 중 Flutter 가 경고한다. > All plugins found for ios are Swift Packages, but your project still has CocoaPods > integration. … Removing CocoaPods integration will improve the project's build time. iOS 플러그인이 전부 Swift Package 로 옮겨졌는데 프로젝트에 CocoaPods 통합이 남아 있다. 동작에는 지장이 없으나 빌드 시간이 늘고 구성이 이중이다. `pod deintegrate` 와 `Debug.xcconfig` · `Release.xcconfig` 의 Pods include 제거가 필요하다. **11주차 최적화 구간에서 정리한다.** ## 서버 없이 로그인을 돌려 보기 실 Firebase 프로젝트는 계정 명의가 확정되어야 열 수 있다. 그 전까지는 **에뮬레이터를 물려 실제 왕복을 돈다.** 설정 파일 대신 에뮬레이터를 붙이는 것이라, 이 절차가 통과하면 설정 파일이 들어오는 날 코드 수정 없이 동작한다. ### 1. 에뮬레이터 기동 ``` cd app/firebase firebase emulators:start --only auth --project demo-fanuzu ``` **JDK 21 이상이 필요하다.** firebase-tools 15 가 그 아래를 거부한다. Gradle 은 Temurin 17 을 쓰므로 둘이 다르며, 에뮬레이터를 띄울 때만 `JAVA_HOME` 을 21+ 로 바꾼다. ### 2. 앱을 에뮬레이터에 붙이기 ``` flutter run --dart-define=FANUZU_EMULATOR_HOST=10.0.2.2 # Android 에뮬레이터 flutter run --dart-define=FANUZU_EMULATOR_HOST=127.0.0.1 # iOS 시뮬레이터 ``` `10.0.2.2` 는 Android 에뮬레이터가 보는 호스트 PC 주소다. ### 평문 HTTP 예외에 대하여 에뮬레이터는 평문 HTTP 로 뜨는데 Android 9 부터 평문이 기본 차단이다. 예외를 `android/app/src/{debug,profile}/` 에만 두었으므로 **릴리스 빌드에는 들어가지 않는다.** 확인: ``` unzip -p build/app/outputs/flutter-apk/app-release.apk AndroidManifest.xml \ | strings | grep -c networkSecurityConfig # 0 이어야 한다 ```