generated from ttipo/project-base
35fcf5e4ef
앱 1단계의 바탕이다(계약 1절 "앱 로그인" — 서버는 다음 차례, 지금은 가짜 서버로 시험한다). - 토큰: 리프레시 토큰은 보안 저장소에만, 액세스 토큰은 메모리에만. 첫 실행 표시가 없으면 남은 토큰을 지우고, 안드로이드 백업을 끈다. 보안 저장소 일은 차례로 한다 - 인터셉터: 401 이면 갱신 한 번(동시 401 묶음) 뒤 다시 보내기, AUTH_010 은 me 를 다시 받아 그 단계로, 미리 갱신 30초 전 · 만료는 응답 Date 로, 세션 세대가 바뀐 뒤의 응답은 조용히 버린다, 비밀이 든 요청의 실패 예외는 본문 · Bearer 를 지운다 - 세션: 로그인 · 코드 · 새 비밀번호 · 2단계 켜기 단계의 상태와 늦은 결과 버리기(버린 로그인의 토큰은 조용히 logout), 로그아웃은 메모리부터 비운다, 유예 응답이면 세션 끝 + 조용한 logout - 모듈 틀: go_router StatefulShellRoute 와 아래 탭 막대 — 모듈 목록은 웹 app-modules.ts 와 짝, 첫 판은 [메일 · 설정]. 모듈끼리 import 하지 않는다 - Riverpod 자동 다시 하기를 끈다, Pretendard 1.3.9(OFL), 확정 글자 크기, 시험 126건 - 판은 Flutter 3.41.9(Dart 3.11.5) — pubspec 의 sdk 하한과 lock 을 함께 리뷰 세 번 반영(AUTH_010 다시 보내기 · 늦은 응답 · 저장 실패 · 로그아웃 차례 · 비밀이 실린 객체 등). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
54 lines
1.9 KiB
Dart
54 lines
1.9 KiB
Dart
import 'package:arasystem_app/app.dart';
|
|
import 'package:arasystem_app/core/api/dio_provider.dart';
|
|
import 'package:arasystem_app/core/api/user_agent.dart';
|
|
import 'package:arasystem_app/core/constants/app_info_provider.dart';
|
|
import 'package:arasystem_app/core/storage/first_run_marker.dart';
|
|
import 'package:arasystem_app/core/storage/secure_store.dart';
|
|
import 'package:arasystem_app/core/storage/token_store.dart';
|
|
import 'package:arasystem_app/core/storage/token_store_provider.dart';
|
|
import 'package:arasystem_app/core/theme/app_fonts.dart';
|
|
import 'package:arasystem_app/core/utils/provider_retry.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
|
|
/// `.env` 파일 이름 (pubspec assets)
|
|
const String _envFileName = '.env';
|
|
|
|
Future<void> main() async {
|
|
WidgetsFlutterBinding.ensureInitialized();
|
|
|
|
// .env 가 없으면 기본 주소로 동작한다 (ApiConfig.fromEnv)
|
|
try {
|
|
await dotenv.load(fileName: _envFileName);
|
|
} on FileNotFoundError {
|
|
// 기본값을 쓴다
|
|
} on EmptyEnvFileError {
|
|
// 기본값을 쓴다
|
|
}
|
|
|
|
AppFonts.registerFontLicenses();
|
|
|
|
// 토큰 보관 준비 — 첫 실행이면 보안 저장소에 남은 값을 지운다(앱을 띄우기 전에 끝낸다)
|
|
final TokenStore tokenStore = TokenStore(
|
|
secure: const FlutterSecureStore(),
|
|
firstRun: PrefsFirstRunMarker(),
|
|
);
|
|
await tokenStore.prepare();
|
|
final ({String? appVersion, String userAgent}) device =
|
|
await loadAppDeviceInfo();
|
|
|
|
runApp(
|
|
ProviderScope(
|
|
// 프로바이더의 실패를 저절로 다시 하지 않는다(core/utils/provider_retry.dart)
|
|
retry: noAutomaticRetry,
|
|
overrides: [
|
|
tokenStoreProvider.overrideWithValue(tokenStore),
|
|
userAgentProvider.overrideWithValue(device.userAgent),
|
|
appVersionProvider.overrideWithValue(device.appVersion),
|
|
],
|
|
child: const App(),
|
|
),
|
|
);
|
|
}
|