From 0b978a9610d3b5dc89e946cdff9ef17d222c5277 Mon Sep 17 00:00:00 2001 From: TtiPo Date: Sun, 11 Oct 2026 23:52:24 +0900 Subject: [PATCH] =?UTF-8?q?docs(flutter):=20=EC=B6=9C=EC=8B=9C=20=EB=B9=8C?= =?UTF-8?q?=EB=93=9C=20=EC=A0=88=EC=B0=A8=EC=99=80=20=EC=95=B1=20=EB=A7=81?= =?UTF-8?q?=ED=81=AC=C2=B7=EC=B6=9C=EC=8B=9C=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EA=B3=A8=EA=B2=A9=20=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 서버 주소를 빌드 때 넘기는 방법, 업로드 키 서명, 버전·아이콘·시작 화면을 바꿀 때 함께 고칠 곳을 한 곳에 모아 둔다. 앱 ID 는 스토어에 올린 뒤 바꿀 수 없고 scheme 은 두 플랫폼에 함께 등록되어 있어, 모르고 한쪽만 고치면 되돌리기 어렵다. Co-Authored-By: Claude Opus 5.5 --- .claude/skills/flutter/SKILL.md | 35 ++++++++++++++++++++++++++++++++- flutter/README.md | 24 ++++++++++------------ 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/.claude/skills/flutter/SKILL.md b/.claude/skills/flutter/SKILL.md index 6a53796..de3e43c 100644 --- a/.claude/skills/flutter/SKILL.md +++ b/.claude/skills/flutter/SKILL.md @@ -17,7 +17,7 @@ description: Flutter 모바일 앱(flutter/) 개발 규칙 — Dart Sound Null S | 제공 항목 | 위치 | 비고 | | --- | --- | --- | | 데이터 모드 | `lib/core/config/app_config.dart` | `.env` 의 `DATA_MODE` — `api`(기본, 서버) / `local`(기기 저장 · 서버 없이 화면 확인 · 테스트) | -| Dio 클라이언트 | `lib/core/api/dio_client.dart` + `dio_provider.dart` | Provider 로 주입(`ref.watch(dioProvider)`), baseUrl=dotenv `API_URL`, 타임아웃 | +| Dio 클라이언트 | `lib/core/api/dio_client.dart` + `dio_provider.dart` | Provider 로 주입(`ref.watch(dioProvider)`), baseUrl=`readApiBaseUrl()`(빌드 때 넘긴 `--dart-define=API_URL` → `.env` 의 `API_URL` 순), 타임아웃 | | Auth/Error 인터셉터 | `dio_client.dart` 의 `InterceptorsWrapper` | onRequest(토큰 첨부)·onResponse(언래핑)·onError(**401 → 토큰 갱신 후 한 번 재시도**·코드 매핑) — 인라인 | | **ApiException** | `lib/core/api/api_exception.dart` | `{code, message, traceId, statusCode}` 정규화 예외. 인터셉터가 `DioException.error` 에 실어준다 | | 실패 알림 | `apiErrorReporterProvider` → `rup_toast.dart` 의 `apiErrorToastReporter` | 사전 문구 토스트, 같은 코드는 잠깐 한 번만. 호출부가 처리하는 코드는 `kSilentCodesExtra` 로 끈다 | @@ -30,6 +30,9 @@ description: Flutter 모바일 앱(flutter/) 개발 규칙 — Dart Sound Null S | QR 스캐너 | `shared/widgets/qr_scanner_page.dart` | `QrScannerPage` — 문구·코드 꺼내기·불러오기만 넘긴다(명함 받기·다이어리 참여·일정 연동). 결과는 `ScanFound` / `ScanTypeCode`. 테스트는 `scannerCameraProvider` 로 카메라를 바꿔 끼운다 | | 함께 쓰는 기록 | `features/diary` · `features/schedule` | 서버가 준 작성자·멤버의 회원 id 가 나면 `kMeAuthorId`/`isMe` 로 바꿔 담는다. 남의 아이는 내 목록에 없으므로 서버가 준 이름·사진 요약(`targets`)으로 그린다. 기기 저장 모드는 `canShare`/`available` 이 false 라 안내만 띄운다 | | 상태관리 초기화 | `main.dart` 의 `ProviderScope` | 세션(토큰)을 첫 프레임 전에 읽어 override 한다 | +| 앱 링크 | `core/config/app_links.dart` + `features/links/` | `rup://app/<경로>` — Android 는 호스트를 버리므로 **경로로** 무엇을 열지 정한다. `LinkLandingPage` 가 코드를 확인해 받기·참여·연동으로 잇는다. 로그인 전에 받은 링크는 라우터가 기억했다가 진입을 마치면 연다 | +| 함께 쓰는 기록 새로 읽기 | `features/shell/.../shared_data_refresher.dart` | 앱으로 돌아올 때 + 보는 동안 3분마다. 뒤에서 읽으므로 `kSilentExtra` 로 실패 알림을 끈다 | +| 출시 설정 | `android/app/build.gradle.kts` · `ios/Runner` · `branding/` | 앱 ID `com.ttipo.rup`·이름 `rup`·아이콘·시작 화면·서명 — 아래 "출시 빌드" | | 도메인 feature(패턴 참고) | `lib/features/pet/` | 저장소 **계약 + `Local*` + `Api*`**, 프로바이더가 `DATA_MODE` 로 고른다. 알림기 build 는 `sessionEpochProvider` 를 watch | > **확장 시 따르는 규칙** (아래 "프로젝트 구조"는 목표 구조 — 기능 추가 시 점진적으로 채운다): @@ -226,6 +229,35 @@ await storage.deleteAll(); --- +## 출시 빌드 + +```bash +# Android — 스토어 업로드용 (서버 주소는 빌드 때 넘긴다) +flutter build appbundle --release --dart-define=API_URL=https://<도메인>/api + +# 기기에서 릴리스 동작만 확인할 때 +flutter build apk --release --dart-define=API_URL=https://<도메인>/api + +# iOS — macOS 에서, Xcode 로 팀·서명을 고른 뒤 +flutter build ipa --release --dart-define=API_URL=https://<도메인>/api +``` + +- **서버 주소**: 출시 빌드는 `.env` 대신 `--dart-define=API_URL` 로 넘긴다. https 가 아니거나 개발용 주소(`localhost`·`10.0.2.2` 등)면 + **앱이 시작하지 않는다**(`releaseApiUrlProblem()`) — 사용자 기기에서 모든 요청이 조용히 실패하는 것보다 낫다. +- **Android 서명**: `android/key.properties.example` → `android/key.properties` 복사 후 업로드 키 경로·비밀번호를 채운다. + 파일이 없으면 디버그 키로 서명되어(빌드 로그에 경고) **스토어에 올릴 수 없다.** `key.properties`·키 파일은 커밋하지 않는다(`.gitignore`). + 업로드 키를 잃어버리면 같은 앱으로 업데이트를 올릴 수 없으니 별도로 백업한다. +- **버전**: `pubspec.yaml` 의 `version: 이름+빌드번호` 하나만 올린다 — Android `versionCode`·iOS `CFBundleVersion` 이 이를 따른다. + 스토어는 같은 빌드 번호를 두 번 받지 않는다. +- **앱 ID**: `com.ttipo.rup`(Android `applicationId` · iOS 번들 ID). **스토어에 올린 뒤에는 바꿀 수 없다.** + Dart 패키지 이름(`tma_app`, import 경로)은 앱 ID 와 무관해 그대로 둔다. 데스크톱 폴더(windows·linux·macos)는 출시 대상이 아니라 손대지 않았다. +- **아이콘**: 원본은 `branding/app_icon.png`(전체)·`app_icon_foreground.png`(적응형 앞면, 안전 영역 안). 바꾸면 `dart run flutter_launcher_icons` 로 + 크기별 아이콘을 다시 만든다. 바탕색은 `pubspec.yaml` 의 `adaptive_icon_background`. +- **시작 화면 색**: `#FFDE84` — Android `res/values/colors.xml` 의 `launch_background`(12+ 는 `values-v31` 스타일), iOS `LaunchScreen.storyboard`. 바꿀 때 함께 고친다. +- **앱 링크 scheme** `rup` 은 AndroidManifest 의 intent-filter 와 iOS `CFBundleURLTypes` 에 함께 등록되어 있다 — 하나만 바꾸면 한쪽 기기에서 QR 이 앱을 열지 못한다. + +--- + ## 성능 & 최신 트렌드 추가 규칙 - **모델 직렬화**: `json_serializable` + `freezed` 사용 권장 (불변 모델) @@ -262,3 +294,4 @@ await storage.deleteAll(); - [ ] **env**: `flutter/.env.example` → `.env` 복사 후 `API_URL`·`DATA_MODE` 설정 (Android 에뮬레이터는 `10.0.2.2`). `.env` 는 `pubspec.yaml` 의 asset 이라 **없으면 `flutter analyze` 가 경고**한다. - [ ] **서버 계약 점검**: 서버 쪽 응답 모양을 바꿨다면 개발 서버를 띄우고 `flutter test test_live` (CI 에서는 돌지 않는다). +- [ ] **출시 빌드**: `--dart-define=API_URL=https://…` + `android/key.properties` + `pubspec.yaml` 빌드 번호 증가 (위 "출시 빌드"). diff --git a/flutter/README.md b/flutter/README.md index 16d2584..b47d556 100644 --- a/flutter/README.md +++ b/flutter/README.md @@ -1,17 +1,13 @@ -# tma_app +# rup — 모바일 앱 (Flutter) -A new Flutter project. +개발 규칙·구조·출시 빌드 절차는 [`.claude/skills/flutter/SKILL.md`](../.claude/skills/flutter/SKILL.md) 에 있다. -## Getting Started +```bash +cp .env.example .env # API_URL · DATA_MODE 확인 +flutter pub get +flutter run # 개발 — 백엔드는 루트에서 docker compose 로 띄운다 +flutter analyze && flutter test +``` -This project is a starting point for a Flutter application. - -A few resources to get you started if this is your first Flutter project: - -- [Learn Flutter](https://docs.flutter.dev/get-started/learn-flutter) -- [Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) -- [Flutter learning resources](https://docs.flutter.dev/reference/learning-resources) - -For help getting started with Flutter development, view the -[online documentation](https://docs.flutter.dev/), which offers tutorials, -samples, guidance on mobile development, and a full API reference. +출시 빌드는 서버 주소를 빌드 때 넘기고(`--dart-define=API_URL=https://<도메인>/api`), +Android 는 `android/key.properties`(예시: `key.properties.example`)로 서명한다.