Files
fanuzu/app/BUILD.md
T

9.8 KiB

빌드 환경 — 실측 기록 (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 을 쓰는 쪽을 택했다.

fvm flutter config --jdk-dir <JDK 17 설치 경로>

brew install --cask temurin@17 은 sudo 를 요구한다. sudo 없이 쓰려면 Adoptium tar.gz 를 사용자 폴더에 풀고 그 경로를 지정하면 된다.

테스트

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/ 사본도 함께 맞춘다.

빌드

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 로 받는다(저장소 밖).

fvm flutter build apk --release --dart-define-from-file=config/kakao.env

LINE 채널 ID — 같은 config/kakao.env 에 FANUZU_LINE_CHANNEL_ID=<LINE Login 채널 ID(숫자)>. Dart 만 읽는다 (iOS 복귀 스킴은 line3rdp.<번들 ID> 라 키가 필요 없다). 없으면 LINE 로그인만 「설정 전」으로 막힌다. 서버 fnAuthLine 은 같은 값을 firebase/functions/.env.<프로젝트> 의 LINE_CHANNEL_ID 로 받는다(저장소 밖). LINE Developers 콘솔의 LINE Login 채널에 Android 패키지명 · iOS 번들 ID 를 등록한다.

Android Apple 로그인 — 앱에 넣을 값 없음

Android 는 Apple SDK 가 없어 Firebase 웹 흐름(apple.com 제공자 · signInWithProvider)으로 로그인한다. 값은 모두 콘솔에 둔다 — 빌드 옵션은 없다.

  1. Apple 개발자 계정 「Identifiers → Services IDs」에 서비스 ID 를 만들고 「Sign in with Apple」을 켠다.
    • Domains: <프로젝트 ID>.firebaseapp.com
    • Return URLs: https://<프로젝트 ID>.firebaseapp.com/__/auth/handler
  2. 「Keys」에서 Sign in with Apple 키를 만들고 .p8 를 받는다(키 ID · 팀 ID 기록). 키 파일은 저장소에 넣지 않는다.
  3. Firebase 콘솔 Authentication → 로그인 방법 → Apple 에 서비스 ID · 팀 ID · 키 ID · 비공개 키를 넣는다.
  4. 확인: Android 첫 화면 Apple → 브라우저 탭에서 Apple 로그인 → 앱 HOME.

iOS 는 지금처럼 Apple SDK(AppleSignInProvider)로 로그인한다.

서명

android/key.properties 가 없으면 릴리스 빌드가 의도적으로 실패한다 (android/app/build.gradle.kts). 디버그 키로 조용히 서명된 산출물이 Play 에서 거부되는 것을 막기 위한 것이다.

키스토어는 ~/.fanuzu-signing/fanuzu-upload.p12 (PKCS12 · 별칭 fanuzu-upload). 저장소에 넣지 않는다 — android/.gitignore 가 key.properties 를, 루트 .gitignore 가 *.p12 *.jks 를 막는다.

서명 확인:

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 로 우회한다.

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 빌드가 멈춘다.

# 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 이어야 한다