맵시 PRD에 맞는 기능 구현 확인 점검 필요#16
Hidden character warning
Conversation
|
Caution Review failedThe pull request is closed. Walkthrough백엔드 API 통합, Dio 기반 네트워크 클라이언트와 인터셉터(인증/토큰 갱신/에러), 보안 토큰 저장소, 인증·온보딩 리포지토리 및 Riverpod 제공자들, 온보딩 4단계 UI와 라우터 경로를 추가합니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User as 사용자
participant Client as Flutter 클라이언트
participant Firebase as Firebase Auth
participant Server as 백엔드 API
participant Storage as TokenStorage
User->>Client: Google/Apple 로그인 시도
Client->>Firebase: ID 토큰 요청
Firebase-->>Client: ID 토큰 반환
Client->>Client: FCM 토큰·기기정보 수집
Client->>Server: POST /api/auth/sign-in (idToken, fcmToken, deviceInfo)
Server-->>Client: accessToken, refreshToken, requiresOnboarding
Client->>Storage: accessToken/refreshToken 및 온보딩 상태 저장
alt requiresOnboarding == true
Client->>Client: 온보딩 단계로 리다이렉트
else
Client->>Client: 홈으로 리다이렉트
end
sequenceDiagram
participant Client as 클라이언트
participant Dio as Dio
participant AuthInt as AuthInterceptor
participant RefreshInt as TokenRefreshInterceptor
participant ErrorInt as ErrorInterceptor
participant Server as 백엔드
participant Storage as TokenStorage
Client->>Dio: API 요청
Dio->>AuthInt: onRequest
AuthInt->>Storage: getAccessToken()
Storage-->>AuthInt: token
AuthInt->>Dio: Authorization 헤더 추가
Dio->>Server: 요청 전송
Server-->>Dio: 401 응답
Dio->>RefreshInt: onError(401)
RefreshInt->>Storage: getRefreshToken()
RefreshInt->>Dio: refreshDio POST /api/auth/reissue
Dio-->>RefreshInt: 새로운 토큰
RefreshInt->>Storage: saveTokens()
RefreshInt->>Dio: 원래 요청 재시도
Dio->>Server: 재시도 요청
Server-->>Dio: 200 응답
Dio->>ErrorInt: onResponse/onError 처리
ErrorInt-->>Client: 최종 응답/예외 변환 반환
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
- GENERATE_INFOPLIST_FILE = NO로 변경 (Debug, Release, Profile) - Info.plist의 Flutter 변수를 하드코딩으로 변경 - CFBundleShortVersionString: 1.0 - CFBundleVersion: 1 - Invalid placeholder attributes 오류 해결 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 10
🤖 Fix all issues with AI agents
In `@lib/core/services/fcm/firebase_messaging_service.dart`:
- Around line 13-17: The provider for FirebaseMessagingService is currently
auto-disposable and may drop listeners; update the provider to be kept alive for
the app lifetime by marking the generated provider as non-disposable—e.g.,
change the annotation for firebaseMessagingService to keep the provider alive
(add keepAlive: true to the `@riverpod` annotation or call ref.keepAlive() inside
firebaseMessagingService) so FirebaseMessagingService.instance(), its persistent
listeners (onMessage, onMessageOpenedApp, onTokenRefresh) and the
contentCompletedStream broadcast are not prematurely disposed.
In `@lib/features/auth/data/repositories/auth_repository_impl.dart`:
- Around line 124-128: The method getCurrentOnboardingStep references the
OnboardingStep type and OnboardingStep.fromString but the class isn't imported;
add an import for the OnboardingStep entity (from
../../domain/entities/onboarding_step.dart) at the top of the file so
getCurrentOnboardingStep and OnboardingStep.fromString resolve and the file
compiles.
- Line 13: Add the missing import for the OnboardingStep entity so references to
OnboardingStep and OnboardingStep.fromString resolve; update the top of
auth_repository_impl.dart (and auth_repository.dart where OnboardingStep? is
used in the return type) to import the onboarding_step.dart module (the file
that defines OnboardingStep) so the compiler recognizes OnboardingStep and its
fromString method while keeping the existing implements AuthRepository usage
intact.
In `@lib/features/auth/presentation/providers/auth_provider.dart`:
- Line 13: Remove the unused import
'../../domain/repositories/auth_repository.dart' and add the missing import for
OnboardingStep by adding import '../../domain/entities/onboarding_step.dart'; so
that the OnboardingStep type used around the AuthProvider (where OnboardingStep
is referenced at line ~253) resolves correctly.
In `@lib/features/onboarding/presentation/pages/terms_step_page.dart`:
- Around line 206-214: The code uses AppColors.withValues(alpha:) in
terms_step_page.dart which requires Flutter 3.27+, so update the package
environment to explicitly require that Flutter minimum: open pubspec.yaml and
add a flutter entry under environment (e.g. flutter: ">=3.27.0") alongside the
existing sdk line (^3.9.2); this makes the dependency on the newer Flutter API
explicit and avoids build/runtime mismatches for the withValues(alpha:) call.
In `@lib/features/onboarding/presentation/providers/onboarding_provider.dart`:
- Line 10: Remove the unused import of profile_request.dart from
onboarding_provider.dart; locate the import statement "import
'../../data/models/profile_request.dart';" at the top of the file and delete it
(or replace it with a used symbol if that model is needed elsewhere), ensuring
no references in the file to ProfileRequest remain before committing the change.
In `@lib/features/onboarding/presentation/widgets/step_indicator.dart`:
- Around line 56-92: Clamp the computed progress before passing it to the
LinearProgressIndicator: in the build method where progress is calculated from
currentStep.index and totalSteps (when currentStep can be
OnboardingStep.completed), ensure progress is constrained to the 0.0–1.0 range
(e.g., use a clamp or min/max on the progress value) and then use that clamped
value for the LinearProgressIndicator's value to avoid values >1.0.
In `@lib/router/app_router.dart`:
- Around line 112-118: The onboarding redirect block currently only allows
onboardingPaths and never redirects users who need onboarding; update the logic
in the router (near onboardingPaths and currentPath) to read the user's
onboarding-needed state (e.g., an isOnboardingRequired / needsOnboarding flag
from your auth/session state or a getOnboardingStatus() helper) and when
currentPath is not in onboardingPaths and the user needs onboarding, return a
redirect to the appropriate onboarding route (e.g., the first entry in
onboardingPaths or a named onboarding route) instead of null; ensure you
reference onboardingPaths, currentPath and the onboarding flag/getter you use so
the router sends users to onboarding when required.
- Around line 24-25: 현재 파일에 선언된 import '../core/network/token_storage.dart'가
사용되지 않아 CI 경고가 발생하므로 해당 미사용 import 문(import
'../core/network/token_storage.dart';)을 제거해 주세요; 관련 코드에서 TokenStorage 참조가 없음을
확인하고, 필요 없다면 import 라인을 삭제하여 불필요한 의존성을 제거하면 됩니다.
In `@macos/Runner.xcodeproj/project.pbxproj`:
- Around line 422-465: 빌드 단계 "[CP] Check Pods Manifest.lock"
(PBXShellScriptBuildPhase) 실패는 macOS 타겟의 Podfile.lock이 없기 때문입니다; 해결하려면 CI에 macOS
프로젝트 디렉터리로 이동해 pod install을 실행하도록 워크플로우를 추가(예: cd <macOS project dir> && pod
install)하거나 macOS용 Podfile.lock을 저장소에 커밋해 체크아웃 시 항상 존재하도록 만드세요.
🧹 Nitpick comments (20)
ios/com.elipair.mapsy.share/Info.plist (1)
19-22: 확장 버전 고정값은 릴리스 동기화 부담을 만듭니다.
앱 버전이 갱신될 때 Share Extension도 수동으로 맞춰야 해서 누락 위험이 있습니다. 확장 타깃에 값이 정의돼 있다면 MARKETING_VERSION/CURRENT_PROJECT_VERSION과 연결해 자동 동기화를 고려해 주세요.♻️ 제안 변경
- <string>1.0</string> + <string>$(MARKETING_VERSION)</string> ... - <string>1</string> + <string>$(CURRENT_PROJECT_VERSION)</string>lib/core/network/error_interceptor.dart (1)
27-34: DioException 생성 시 원본 stackTrace를 명시적으로 전달해야 합니다.새
DioException을 생성자로 만들 때stackTrace파라미터를 지정하지 않으면 원본err.stackTrace가 유실되어 디버깅과 크래시 분석에 영향을 줍니다.♻️ 제안 변경
handler.next(DioException( requestOptions: err.requestOptions, response: err.response, type: err.type, error: exception, message: exception.message, + stackTrace: err.stackTrace, ));또는
copyWith()를 사용하면stackTrace가 자동으로 보존됩니다:handler.next(err.copyWith( error: exception, message: exception.message, ));lib/core/services/device/device_info_service.dart (1)
213-227: 성능 최적화: 병렬 실행을 고려해 보세요.현재 디바이스 정보를 순차적으로 가져오고 있습니다.
Future.wait를 사용하면 병렬로 실행하여 응답 시간을 단축할 수 있습니다.♻️ 병렬 실행 제안
Future<DeviceInfo> getDeviceInfo() async { - final deviceName = await getDeviceName(); - final deviceType = getDeviceType(); - final deviceId = await DeviceIdManager.getOrCreateDeviceId(); - final osVersion = await getOSVersion(); - final physical = await isPhysicalDevice(); + final results = await Future.wait([ + getDeviceName(), + DeviceIdManager.getOrCreateDeviceId(), + getOSVersion(), + isPhysicalDevice(), + ]); + + final deviceName = results[0] as String; + final deviceType = getDeviceType(); + final deviceId = results[1] as String; + final osVersion = results[2] as String; + final physical = results[3] as bool; return DeviceInfo( deviceName: deviceName, deviceType: deviceType, deviceId: deviceId, osVersion: osVersion, isPhysicalDevice: physical, ); }lib/features/auth/domain/entities/onboarding_step.dart (2)
21-58: 코드 중복 감소를 고려해 보세요.
fromString과fromStringNullable메서드에 동일한 switch 로직이 중복되어 있습니다. 내부 헬퍼 메서드로 추출하면 유지보수성이 향상됩니다.♻️ 중복 제거 제안
+ static OnboardingStep? _parseString(String value) { + switch (value.toUpperCase()) { + case 'TERMS': + return OnboardingStep.terms; + case 'BIRTH_DATE': + return OnboardingStep.birthDate; + case 'GENDER': + return OnboardingStep.gender; + case 'NICKNAME': + return OnboardingStep.nickname; + case 'COMPLETED': + return OnboardingStep.completed; + default: + return null; + } + } + static OnboardingStep fromString(String? value) { - if (value == null) return OnboardingStep.terms; - switch (value.toUpperCase()) { - case 'TERMS': - return OnboardingStep.terms; - // ... 생략 - default: - return OnboardingStep.terms; - } + if (value == null) return OnboardingStep.terms; + return _parseString(value) ?? OnboardingStep.terms; } static OnboardingStep? fromStringNullable(String? value) { - if (value == null) return null; - switch (value.toUpperCase()) { - // ... 생략 - } + if (value == null) return null; + return _parseString(value); }
92-106:enum.index활용을 고려해 보세요.
stepIndexgetter는 enum의 기본index속성을 활용하면 더 간단해집니다.♻️ 간소화 제안
/// 단계 인덱스 (0-4) - int get stepIndex { - switch (this) { - case OnboardingStep.terms: - return 0; - case OnboardingStep.birthDate: - return 1; - case OnboardingStep.gender: - return 2; - case OnboardingStep.nickname: - return 3; - case OnboardingStep.completed: - return 4; - } - } + int get stepIndex => index;lib/features/onboarding/presentation/widgets/onboarding_button.dart (1)
39-46: 로딩 인디케이터 크기 일관성 확인
width: 24.w와height: 24.h가 화면 비율에 따라 다르게 계산될 수 있어 정사각형이 아닌 타원형 인디케이터가 될 수 있습니다. 일관된 크기를 원하시면.r사용을 고려해 보세요.♻️ 정사각형 크기 보장
child: SizedBox( - width: 24.w, - height: 24.h, + width: 24.r, + height: 24.r, child: const CircularProgressIndicator(lib/core/network/token_storage.dart (1)
111-122: 순차적 쓰기 작업을 병렬로 개선할 수 있습니다.
saveTokens와clearTokens에서는Future.wait을 사용하여 병렬로 처리하지만, 여기서는onboardingStep이 있을 때 순차적으로 작성됩니다. 일관성을 위해 병렬 처리를 고려해보세요.♻️ 제안된 수정
Future<void> saveOnboardingState({ required bool requiresOnboarding, String? onboardingStep, }) async { - await _storage.write( - key: _requiresOnboardingKey, - value: requiresOnboarding.toString(), - ); - if (onboardingStep != null) { - await _storage.write(key: _onboardingStepKey, value: onboardingStep); - } + final futures = <Future<void>>[ + _storage.write( + key: _requiresOnboardingKey, + value: requiresOnboarding.toString(), + ), + ]; + if (onboardingStep != null) { + futures.add(_storage.write(key: _onboardingStepKey, value: onboardingStep)); + } + await Future.wait(futures); }lib/core/network/auth_interceptor.dart (2)
53-56:contains()사용 시 잘못된 매칭 가능성이 있습니다.현재
path.contains(publicPath)방식은/api/auth/sign-in-admin같은 경로도/api/auth/sign-in과 매칭됩니다. 정확한 경로 매칭이 필요하다면 수정을 고려해보세요.♻️ 제안된 수정
bool _isPublicPath(String path) { - return _publicPaths.any((publicPath) => path.contains(publicPath)); + return _publicPaths.any((publicPath) => + path == publicPath || path.startsWith('$publicPath?') || path.startsWith('$publicPath/')); }또는 더 간단하게 정확한 매칭만 원하면:
return _publicPaths.contains(path);
32-33: 프로덕션 환경에서 debugPrint 제거를 고려해보세요.토큰 관련 로그는 보안상 민감할 수 있습니다. 현재 토큰 값 자체는 로그에 출력되지 않아 괜찮지만, 프로덕션에서는 이러한 디버그 로그를 비활성화하거나 로그 레벨을 조정하는 것이 좋습니다.
Also applies to: 42-44
lib/core/constants/api_endpoints.dart (1)
78-88: 동적 경로 빌더에서 입력값 검증을 고려해보세요.
placeId와 같은 사용자 입력이 경로에 직접 삽입됩니다. 현재는 문제가 없어 보이지만, 방어적으로 빈 문자열이나 특수 문자 검증을 추가하면 더 안전합니다.♻️ 예시
static String placeDetail(String placeId) { assert(placeId.isNotEmpty, 'placeId must not be empty'); return '/api/place/$placeId'; }lib/features/onboarding/presentation/pages/birth_date_step_page.dart (2)
27-29: 윤년을 고려하지 않은 날짜 계산
Duration(days: 365 * 20)은 윤년을 고려하지 않아 약 5일 정도 오차가 발생할 수 있습니다. 기본값 표시 목적으로는 큰 문제가 아니지만, 더 정확한 계산을 원하시면 아래와 같이 수정할 수 있습니다.♻️ 제안하는 수정
`@override` void initState() { super.initState(); // 기본값: 20년 전 - _selectedDate = DateTime.now().subtract(const Duration(days: 365 * 20)); + final now = DateTime.now(); + _selectedDate = DateTime(now.year - 20, now.month, now.day); }
133-136: 최대 날짜 범위 엣지 케이스
maxDate가DateTime(now.year - 10)으로 설정되어 해당 연도의 1월 1일이 됩니다. 예를 들어 현재가 2026년 7월이면, 2016년 7월에 태어난 만 10세 사용자의 생일을 선택할 수 없습니다.♻️ 제안하는 수정
void _showDatePicker(BuildContext context, OnboardingNotifier notifier) { final now = DateTime.now(); final minDate = DateTime(now.year - 100); - final maxDate = DateTime(now.year - 10); // 최소 10세 이상 + final maxDate = DateTime(now.year - 10, now.month, now.day); // 최소 10세 이상lib/core/network/api_client.dart (1)
38-51: 디버그 로그에 민감정보 노출 가능성 최소화 권장.
LogInterceptor가 요청/응답 바디를 출력하도록 설정되어 있어 인증/토큰 응답이 로그에 남을 수 있습니다. 디버그 환경이라도 최소화 또는 마스킹을 권장합니다.🔐 최소 로그 예시
LogInterceptor( requestHeader: true, - requestBody: true, + requestBody: false, responseHeader: false, - responseBody: true, + responseBody: false, error: true, logPrint: (obj) => debugPrint('📡 $obj'), ),lib/features/onboarding/presentation/pages/terms_step_page.dart (1)
170-188: TODO 주석 확인 필요
_getTermsContent에 플레이스홀더 약관 내용이 있습니다. 배포 전 실제 약관 내용으로 교체가 필요합니다.실제 약관 내용을 별도 파일이나 원격 설정에서 로드하도록 구현하는 것을 도와드릴까요? 새 이슈를 열어 이 작업을 추적할 수도 있습니다.
lib/core/network/token_refresh_interceptor.dart (2)
143-152: 대기 요청 재시도 시 에러 처리 개선 권장
_retryPendingRequests에서 개별 요청 실패가 다른 대기 요청에 영향을 주지 않도록 try-catch를 추가하는 것이 좋습니다.♻️ 개선 제안
void _retryPendingRequests() { + final requests = List.of(_pendingRequests); + _pendingRequests.clear(); - for (final pending in _pendingRequests) { + for (final pending in requests) { _retryRequest(pending.options).then( (response) => pending.handler.resolve(response), - onError: (error) => pending.handler.reject(error as DioException), + onError: (error) { + if (error is DioException) { + pending.handler.reject(error); + } else { + pending.handler.reject(DioException( + requestOptions: pending.options, + error: error, + )); + } + }, ); } - _pendingRequests.clear(); }
162-174: TODO: 로그인 화면 리다이렉트 미구현토큰 갱신 실패 시 로그인 화면으로 리다이렉트하는 로직이 TODO로 남아있습니다. 사용자 경험상 중요한 부분이므로 구현이 필요합니다.
GoRouter를 사용한 리다이렉트 구현을 도와드릴까요? 아니면 이 작업을 추적할 새 이슈를 열어드릴까요?
lib/features/onboarding/domain/repositories/onboarding_repository.dart (1)
1-2: Clean Architecture 의존성 방향 위반Domain 레이어에서 Data 레이어의 모델(
gender_request.dart,profile_request.dart)을 import하고 있습니다. Clean Architecture에서 Domain은 가장 내부 레이어로, 외부 레이어(Data)에 의존하면 안 됩니다.
Genderenum과CheckNameResponse를 Domain 레이어에 정의하고, Data 레이어에서 Domain을 참조하도록 변경하는 것을 권장합니다.♻️ 구조 개선 제안
lib/features/onboarding/domain/entities/에 엔티티 정의:// lib/features/onboarding/domain/entities/gender.dart enum Gender { male, female, other } // lib/features/onboarding/domain/entities/check_name_result.dart class CheckNameResult { final bool available; final String? message; const CheckNameResult({required this.available, this.message}); }
- Repository에서 domain 엔티티 사용:
-import '../../data/models/gender_request.dart'; -import '../../data/models/profile_request.dart'; +import '../entities/gender.dart'; +import '../entities/check_name_result.dart'; abstract class OnboardingRepository { // ... Future<void> submitGender(Gender gender); - Future<CheckNameResponse> checkName(String name); + Future<CheckNameResult> checkName(String name); // ... }lib/features/auth/domain/repositories/auth_repository.dart (1)
1-23: 도메인 레이어가 데이터 레이어 DTO에 의존하고 있습니다.
AuthRepository가 data 레이어의SignInResponse에 직접 의존해 경계가 흐려집니다. 도메인 엔티티로 승격하거나 매핑 계층을 두는 방향을 검토해주세요.lib/features/onboarding/data/repositories/onboarding_repository_impl.dart (1)
46-50: 온보딩 단계 문자열에 상수 사용을 권장합니다.
'BIRTH_DATE','GENDER','NICKNAME','COMPLETED'등의 하드코딩된 문자열이 여러 곳에서 사용됩니다. 오타 방지와 유지보수성을 위해 상수나 enum으로 정의하는 것이 좋습니다.♻️ 상수 정의 예시
// 예: lib/core/constants/onboarding_steps.dart class OnboardingSteps { OnboardingSteps._(); static const String terms = 'TERMS'; static const String birthDate = 'BIRTH_DATE'; static const String gender = 'GENDER'; static const String nickname = 'NICKNAME'; static const String completed = 'COMPLETED'; }lib/features/auth/data/models/sign_in_response.freezed.dart (1)
215-218: 보안 고려사항:toString()에서 민감한 토큰 노출 가능성자동 생성된
toString()메서드가accessToken과refreshToken을 포함하고 있습니다. 이 객체가 로그에 출력될 경우 민감한 인증 토큰이 노출될 수 있습니다.소스 파일(
sign_in_response.dart)에서 커스텀toString()메서드를 오버라이드하여 토큰 값을 마스킹하는 것을 고려해 보세요:`@override` String toString() => 'SignInResponse(accessToken: [REDACTED], refreshToken: [REDACTED], ...)';
| /// FirebaseMessagingService Provider | ||
| @riverpod | ||
| FirebaseMessagingService firebaseMessagingService(Ref ref) { | ||
| return FirebaseMessagingService.instance(); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
🧩 Analysis chain
🌐 Web query:
riverpod @riverpod keepAlive default behavior autoDispose
💡 Result:
With Riverpod code-generation (@riverpod / @Riverpod):
- Default behavior: providers are auto-disposed when they have no listeners for a full frame (i.e., state is destroyed after it stops being listened to). (riverpod.dev)
keepAlivedefault:@riverpodis effectively the same as@Riverpod(keepAlive: false)→ autoDispose is ON by default. (riverpod.dev)- Disable autoDispose: use
@Riverpod(keepAlive: true)to keep the provider alive even when it has no listeners. (riverpod.dev)
Also related (different thing): inside an auto-disposed provider you can call ref.keepAlive() to fine-tune/extend how long state is kept after listeners disappear. (riverpod.dev)
Citations:
- 1: https://riverpod.dev/docs/essentials/auto_dispose?utm_source=openai
- 2: https://riverpod.dev/docs/concepts/about_code_generation?utm_source=openai
- 3: https://riverpod.dev/docs/concepts2/auto_dispose?utm_source=openai
- 4: https://riverpod.dev/docs/concepts2/auto_dispose?utm_source=openai
🏁 Script executed:
# Find usages of firebaseMessagingService provider to understand listener patterns
rg "firebaseMessagingService" --type dart -B 2 -A 5Repository: MapSee-Lab/MapSy-FE
Length of output: 3408
🏁 Script executed:
# Check FirebaseMessagingService implementation to understand if it maintains persistent listeners
fd "firebase_messaging_service\.dart$" --type fRepository: MapSee-Lab/MapSy-FE
Length of output: 116
🏁 Script executed:
# Search for watch patterns or persistent listener usage of FCM service
rg "watch.*firebaseMessagingService|firebaseMessagingService.*watch|\.listen|onMessage" --type dart -B 2 -A 3Repository: MapSee-Lab/MapSy-FE
Length of output: 4166
🏁 Script executed:
# Check the FirebaseMessagingService implementation to see if it maintains persistent state/listeners
head -80 lib/core/services/fcm/firebase_messaging_service.dartRepository: MapSee-Lab/MapSy-FE
Length of output: 2477
앱 수명 동안 FCM 리스너 유지를 위해 keepAlive 옵션 필수 고려.
이 서비스는 FirebaseMessaging.onMessage, onMessageOpenedApp, onTokenRefresh 등 지속적인 리스너와 contentCompletedStream broadcast를 관리합니다. 기본 자동 dispose 동작으로 인해 활성 listener가 없을 때 이들 리스너가 의도치 않게 해제될 수 있습니다. FCM 메시지 수신을 보장하려면 keepAlive: true를 명시하는 것이 필수적입니다.
♻️ 권장 수정
-@riverpod
-FirebaseMessagingService firebaseMessagingService(Ref ref) {
+@riverpod(keepAlive: true)
+FirebaseMessagingService firebaseMessagingService(Ref ref) {
return FirebaseMessagingService.instance();
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /// FirebaseMessagingService Provider | |
| @riverpod | |
| FirebaseMessagingService firebaseMessagingService(Ref ref) { | |
| return FirebaseMessagingService.instance(); | |
| } | |
| /// FirebaseMessagingService Provider | |
| `@riverpod`(keepAlive: true) | |
| FirebaseMessagingService firebaseMessagingService(Ref ref) { | |
| return FirebaseMessagingService.instance(); | |
| } |
🤖 Prompt for AI Agents
In `@lib/core/services/fcm/firebase_messaging_service.dart` around lines 13 - 17,
The provider for FirebaseMessagingService is currently auto-disposable and may
drop listeners; update the provider to be kept alive for the app lifetime by
marking the generated provider as non-disposable—e.g., change the annotation for
firebaseMessagingService to keep the provider alive (add keepAlive: true to the
`@riverpod` annotation or call ref.keepAlive() inside firebaseMessagingService) so
FirebaseMessagingService.instance(), its persistent listeners (onMessage,
onMessageOpenedApp, onTokenRefresh) and the contentCompletedStream broadcast are
not prematurely disposed.
| import '../models/reissue_request.dart'; | ||
|
|
||
| part 'auth_repository_impl.g.dart'; | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, find the auth_repository_impl.dart file
fd -e dart "auth_repository_impl.dart" -x cat -n {}Repository: MapSee-Lab/MapSy-FE
Length of output: 4653
🏁 Script executed:
# Check where AuthRepository is exported from
rg -n "export.*auth_repository" --type dartRepository: MapSee-Lab/MapSy-FE
Length of output: 45
🏁 Script executed:
# Check if AuthRepository is re-exported from a barrel file
fd -e dart "\.dart$" lib/features/auth -x grep -l "export.*auth_repository"Repository: MapSee-Lab/MapSy-FE
Length of output: 45
🏁 Script executed:
# Check the auth_repository.dart file
fd -e dart "auth_repository.dart" -x cat -n {}Repository: MapSee-Lab/MapSy-FE
Length of output: 1504
🏁 Script executed:
# Find where OnboardingStep is defined
rg -n "class OnboardingStep\|enum OnboardingStep" --type dartRepository: MapSee-Lab/MapSy-FE
Length of output: 45
🏁 Script executed:
# Check where OnboardingStep should be imported from
rg -n "export.*OnboardingStep" --type dartRepository: MapSee-Lab/MapSy-FE
Length of output: 45
🏁 Script executed:
# Check if OnboardingStep is imported in any auth files
rg -n "import.*onboarding" --type dart lib/features/auth/Repository: MapSee-Lab/MapSy-FE
Length of output: 45
🏁 Script executed:
# Search for OnboardingStep more broadly without type restriction
rg -n "OnboardingStep" --max-count 20Repository: MapSee-Lab/MapSy-FE
Length of output: 6814
🏁 Script executed:
# Check the structure of lib/features to understand organization
fd -type f -name "*.dart" lib/features/ | head -30Repository: MapSee-Lab/MapSy-FE
Length of output: 233
🏁 Script executed:
# Check if auth_repository.dart imports OnboardingStep
cat -n lib/features/auth/domain/repositories/auth_repository.dart | head -10Repository: MapSee-Lab/MapSy-FE
Length of output: 382
🏁 Script executed:
# Check the onboarding_step.dart file to see full definition
cat -n lib/features/auth/domain/entities/onboarding_step.dartRepository: MapSee-Lab/MapSy-FE
Length of output: 3911
🏁 Script executed:
# Verify the complete auth_repository_impl.dart to see all imports
head -15 lib/features/auth/data/repositories/auth_repository_impl.dartRepository: MapSee-Lab/MapSy-FE
Length of output: 589
auth_repository.dart import는 실제로 사용 중입니다
파이프라인 경고는 잘못되었습니다. line 6의 auth_repository.dart import는 line 27의 implements AuthRepository에서 사용됩니다.
실제 누락된 import: OnboardingStep
auth_repository_impl.dart의 line 125-128에서 OnboardingStep.fromString()을 사용하지만 import가 없습니다. 또한 auth_repository.dart의 line 49도 OnboardingStep? 반환 타입을 사용하지만 import가 없습니다. 다음을 추가하세요:
import '../../domain/entities/onboarding_step.dart';🤖 Prompt for AI Agents
In `@lib/features/auth/data/repositories/auth_repository_impl.dart` at line 13,
Add the missing import for the OnboardingStep entity so references to
OnboardingStep and OnboardingStep.fromString resolve; update the top of
auth_repository_impl.dart (and auth_repository.dart where OnboardingStep? is
used in the return type) to import the onboarding_step.dart module (the file
that defines OnboardingStep) so the compiler recognizes OnboardingStep and its
fromString method while keeping the existing implements AuthRepository usage
intact.
| @override | ||
| Future<OnboardingStep?> getCurrentOnboardingStep() async { | ||
| final stepString = await _tokenStorage.getOnboardingStep(); | ||
| return OnboardingStep.fromString(stepString); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# auth_repository_impl.dart 파일의 import 구문 확인
head -n 30 lib/features/auth/data/repositories/auth_repository_impl.dartRepository: MapSee-Lab/MapSy-FE
Length of output: 1075
🏁 Script executed:
# OnboardingStep 정의 위치 찾기
rg -l "class OnboardingStep|enum OnboardingStep" --type dartRepository: MapSee-Lab/MapSy-FE
Length of output: 117
🏁 Script executed:
# auth_repository.dart의 export 확인
cat lib/features/auth/data/repositories/auth_repository.dartRepository: MapSee-Lab/MapSy-FE
Length of output: 151
🏁 Script executed:
# 추상 auth_repository.dart 확인
cat lib/features/auth/domain/repositories/auth_repository.dartRepository: MapSee-Lab/MapSy-FE
Length of output: 1154
🏁 Script executed:
# OnboardingStep 정의 확인
cat lib/features/auth/domain/entities/onboarding_step.dartRepository: MapSee-Lab/MapSy-FE
Length of output: 3050
🏁 Script executed:
# auth_repository_impl.dart의 전체 import 섹션과 해당 메소드 확인
sed -n '1,150p' lib/features/auth/data/repositories/auth_repository_impl.dartRepository: MapSee-Lab/MapSy-FE
Length of output: 3750
OnboardingStep 임포트 필수
이 파일에서 OnboardingStep이 사용되지만 임포트되지 않았습니다. 125-128줄의 반환 타입과 메서드 호출(OnboardingStep.fromString())에서 OnboardingStep이 직접 참조되므로 다음 임포트를 추가해야 합니다:
import '../../domain/entities/onboarding_step.dart';임포트가 없으면 컴파일 오류가 발생합니다.
🤖 Prompt for AI Agents
In `@lib/features/auth/data/repositories/auth_repository_impl.dart` around lines
124 - 128, The method getCurrentOnboardingStep references the OnboardingStep
type and OnboardingStep.fromString but the class isn't imported; add an import
for the OnboardingStep entity (from ../../domain/entities/onboarding_step.dart)
at the top of the file so getCurrentOnboardingStep and OnboardingStep.fromString
resolve and the file compiles.
| import '../../data/datasources/firebase_auth_datasource.dart'; | ||
| import '../../data/models/sign_in_response.dart'; | ||
| import '../../data/repositories/auth_repository_impl.dart'; | ||
| import '../../domain/repositories/auth_repository.dart'; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# auth_provider.dart 파일의 import 부분과 OnboardingStep 사용 부분 확인
cat -n lib/features/auth/presentation/providers/auth_provider.dart | head -30Repository: MapSee-Lab/MapSy-FE
Length of output: 1432
🏁 Script executed:
# OnboardingStep 정의 위치 찾기
rg -n "enum OnboardingStep|class OnboardingStep" --type dartRepository: MapSee-Lab/MapSy-FE
Length of output: 142
🏁 Script executed:
# auth_repository.dart 파일 확인
cat lib/features/auth/domain/repositories/auth_repository.dart | head -100Repository: MapSee-Lab/MapSy-FE
Length of output: 1154
🏁 Script executed:
# auth_provider.dart에서 OnboardingStep 사용 부분 확인
rg -n "OnboardingStep" lib/features/auth/presentation/providers/auth_provider.dartRepository: MapSee-Lab/MapSy-FE
Length of output: 193
🏁 Script executed:
# auth_provider.dart에서 OnboardingStep 및 AuthRepository 사용 확인
rg -n "import.*onboarding|import.*auth_repository" lib/features/auth/presentation/providers/auth_provider.dartRepository: MapSee-Lab/MapSy-FE
Length of output: 185
🏁 Script executed:
# auth_provider.dart 전체 import 부분 확인
head -50 lib/features/auth/presentation/providers/auth_provider.dartRepository: MapSee-Lab/MapSy-FE
Length of output: 1683
🏁 Script executed:
# AuthRepository 타입 직접 사용 확인
rg -n "AuthRepository[^I]|: AuthRepository|<AuthRepository" lib/features/auth/presentation/providers/auth_provider.dartRepository: MapSee-Lab/MapSy-FE
Length of output: 45
🏁 Script executed:
# auth_repository.dart에서 OnboardingStep import 확인
rg -n "import.*onboarding" lib/features/auth/domain/repositories/auth_repository.dartRepository: MapSee-Lab/MapSy-FE
Length of output: 45
🏁 Script executed:
# auth_provider.dart에서 OnboardingStep import 찾기
rg "import.*onboarding_step" lib/features/auth/presentation/providers/auth_provider.dartRepository: MapSee-Lab/MapSy-FE
Length of output: 45
🏁 Script executed:
# auth_provider.dart 전체에서 OnboardingStep 사용 context 확인
rg -B2 -A2 "OnboardingStep" lib/features/auth/presentation/providers/auth_provider.dartRepository: MapSee-Lab/MapSy-FE
Length of output: 350
🏁 Script executed:
# auth_repository.dart 전체 내용 확인
cat lib/features/auth/domain/repositories/auth_repository.dartRepository: MapSee-Lab/MapSy-FE
Length of output: 1154
🏁 Script executed:
# auth_repository.dart 전체 import 확인
head -100 lib/features/auth/domain/repositories/auth_repository.dartRepository: MapSee-Lab/MapSy-FE
Length of output: 1154
🏁 Script executed:
# auth_provider.dart 전체 파일에서 import 부분 전체 확인
sed -n '1,20p' lib/features/auth/presentation/providers/auth_provider.dartRepository: MapSee-Lab/MapSy-FE
Length of output: 949
🏁 Script executed:
# OnboardingStep이 어디서 import되어야 하는지 확인
find lib/features/auth -name "*.dart" -type f | xargs grep -l "export.*OnboardingStep\|import.*onboarding_step" | head -10Repository: MapSee-Lab/MapSy-FE
Length of output: 45
🏁 Script executed:
# OnboardingStep 관련 모든 import/export 찾기
rg -r "onboarding_step|OnboardingStep" lib/features/auth/domain/ --type dartRepository: MapSee-Lab/MapSy-FE
Length of output: 45
🏁 Script executed:
# auth_provider.g.dart 생성 파일에서 OnboardingStep 확인
grep -n "OnboardingStep" lib/features/auth/presentation/providers/auth_provider.g.dart 2>/dev/null || echo "파일 없음 또는 OnboardingStep 없음"Repository: MapSee-Lab/MapSy-FE
Length of output: 89
🏁 Script executed:
# lib/features/auth 전체 구조 확인
find lib/features/auth -type f -name "*.dart" | grep -E "(barrel|export)" | head -20Repository: MapSee-Lab/MapSy-FE
Length of output: 45
🏁 Script executed:
# onboarding_step.dart 파일 위치 및 export 확인
cat lib/features/auth/domain/entities/onboarding_step.dart | head -30Repository: MapSee-Lab/MapSy-FE
Length of output: 731
🏁 Script executed:
# auth_repository_impl.dart에서 OnboardingStep import/export 확인
cat lib/features/auth/data/repositories/auth_repository_impl.dart | head -30Repository: MapSee-Lab/MapSy-FE
Length of output: 1075
🏁 Script executed:
# 전체 도메인 레이어에서 OnboardingStep 위치 확인
find lib/features/auth/domain -type f -name "*.dart" -exec grep -l "OnboardingStep" {} \;Repository: MapSee-Lab/MapSy-FE
Length of output: 176
🏁 Script executed:
# auth_provider.dart에서 실제 onboarding_step 관련 모든 사용 라인 확인
rg -n "onboarding|Onboarding" lib/features/auth/presentation/providers/auth_provider.dartRepository: MapSee-Lab/MapSy-FE
Length of output: 577
🏁 Script executed:
# auth_repository.dart가 OnboardingStep을 export하는지 확인
cat lib/features/auth/domain/repositories/auth_repository.dart | grep -E "^export|^import.*onboarding"Repository: MapSee-Lab/MapSy-FE
Length of output: 45
🏁 Script executed:
# auth 모듈의 barrel 파일들 확인
find lib/features/auth -name "*.dart" -type f | xargs grep -l "^export" | head -10Repository: MapSee-Lab/MapSy-FE
Length of output: 114
🏁 Script executed:
# auth_provider.dart에서 사실상 AuthRepository 타입 직접 사용 여부 재확인
rg ":\s*AuthRepository|as AuthRepository|\bAuthRepository\b" lib/features/auth/presentation/providers/auth_provider.dartRepository: MapSee-Lab/MapSy-FE
Length of output: 45
Line 13의 import를 제거하세요. 또한 OnboardingStep을 import 추가해야 합니다.
auth_repository.dart는 이 파일에서 타입으로 사용되지 않으므로 제거할 수 있습니다. 하지만 더 중요한 문제가 있습니다: OnboardingStep이 라인 253에서 사용되지만 import되지 않았습니다. 다음을 추가하세요:
import '../../domain/entities/onboarding_step.dart';그리고 line 13을 제거하세요.
🧰 Tools
🪛 GitHub Actions: Project Flutter CI
[warning] 13-13: Unused import: '../../domain/repositories/auth_repository.dart'.
🤖 Prompt for AI Agents
In `@lib/features/auth/presentation/providers/auth_provider.dart` at line 13,
Remove the unused import '../../domain/repositories/auth_repository.dart' and
add the missing import for OnboardingStep by adding import
'../../domain/entities/onboarding_step.dart'; so that the OnboardingStep type
used around the AuthProvider (where OnboardingStep is referenced at line ~253)
resolves correctly.
| child: Container( | ||
| padding: EdgeInsets.all(16.w), | ||
| decoration: BoxDecoration( | ||
| color: isChecked ? AppColors.primary.withValues(alpha: 0.1) : AppColors.gray100, | ||
| borderRadius: BorderRadius.circular(12.r), | ||
| border: Border.all( | ||
| color: isChecked ? AppColors.primary : AppColors.gray200, | ||
| ), | ||
| ), |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
fd -t f "pubspec.yaml" --exec cat {} \;Repository: MapSee-Lab/MapSy-FE
Length of output: 3231
🌐 Web query:
Flutter withValues alpha method version requirement
💡 Result:
Color.withValues(alpha: ...) (the replacement for withOpacity) requires Flutter 3.27+. The wide-gamut color migration guide explicitly notes that before Flutter 3.27 colors used opacity/withOpacity, and since Flutter 3.27 you should use .a and .withValues() instead. [1]
So:
- Flutter ≥ 3.27:
color.withValues(alpha: 0.3)(alpha is adoublein 0.0–1.0) [2] - Flutter < 3.27:
withValuesdoesn’t exist—usecolor.withOpacity(0.3)(orwithAlpha(…)if you want 0–255). [3]
Sources:
[1] Flutter migration guide (wide gamut): https://docs.flutter.cn/release/breaking-changes/wide-gamut-cupertino-dynamic-color/
[2] Color.withValues API: https://api.flutter.dev/flutter/dart-ui/Color/withValues.html
[3] Report of missing withValues on Flutter 3.24.x (points to 3.27 introduction): https://stackoverflow.com/questions/79481509/the-method-withvalues-isnt-defined-for-the-type-color
명시적 Flutter 최소 버전 지정 필요
withValues(alpha:) 메서드는 Flutter 3.27 이상에서 사용 가능합니다. 현재 pubspec.yaml은 Dart SDK 버전(^3.9.2)만 지정하고 있는데, Dart 3.9.2는 Flutter 3.27+과 함께 배포됩니다. 코드에서 사용하는 Flutter 3.27+ 기능을 명확히 하기 위해 pubspec.yaml의 environment 섹션에 Flutter 최소 버전을 명시적으로 추가해주세요:
environment:
sdk: ^3.9.2
flutter: ">=3.27.0"🤖 Prompt for AI Agents
In `@lib/features/onboarding/presentation/pages/terms_step_page.dart` around lines
206 - 214, The code uses AppColors.withValues(alpha:) in terms_step_page.dart
which requires Flutter 3.27+, so update the package environment to explicitly
require that Flutter minimum: open pubspec.yaml and add a flutter entry under
environment (e.g. flutter: ">=3.27.0") alongside the existing sdk line (^3.9.2);
this makes the dependency on the newer Flutter API explicit and avoids
build/runtime mismatches for the withValues(alpha:) call.
lib/features/onboarding/presentation/providers/onboarding_provider.dart
Outdated
Show resolved
Hide resolved
| @override | ||
| Widget build(BuildContext context) { | ||
| final progress = (currentStep.index + 1) / totalSteps; | ||
|
|
||
| return Column( | ||
| children: [ | ||
| Row( | ||
| mainAxisAlignment: MainAxisAlignment.spaceBetween, | ||
| children: [ | ||
| Text( | ||
| _getStepText(), | ||
| style: TextStyle( | ||
| fontSize: 14.sp, | ||
| fontWeight: FontWeight.w500, | ||
| color: AppColors.gray600, | ||
| ), | ||
| ), | ||
| Text( | ||
| '${currentStep.index + 1}/$totalSteps', | ||
| style: TextStyle( | ||
| fontSize: 14.sp, | ||
| fontWeight: FontWeight.w500, | ||
| color: AppColors.primary, | ||
| ), | ||
| ), | ||
| ], | ||
| ), | ||
| SizedBox(height: 8.h), | ||
| LinearProgressIndicator( | ||
| value: progress, | ||
| backgroundColor: AppColors.gray200, | ||
| valueColor: const AlwaysStoppedAnimation<Color>(AppColors.primary), | ||
| minHeight: 4.h, | ||
| borderRadius: BorderRadius.circular(2.r), | ||
| ), | ||
| ], | ||
| ); |
There was a problem hiding this comment.
OnboardingStep.completed 상태에서 진행률이 1.0을 초과할 수 있습니다.
currentStep이 OnboardingStep.completed(index = 4)인 경우, progress는 (4 + 1) / 4 = 1.25가 됩니다. LinearProgressIndicator는 1.0 초과 값을 처리하지만, 의도치 않은 시각적 결과가 발생할 수 있습니다.
🔧 progress 값을 clamp하는 수정 제안
`@override`
Widget build(BuildContext context) {
- final progress = (currentStep.index + 1) / totalSteps;
+ final progress = ((currentStep.index + 1) / totalSteps).clamp(0.0, 1.0);
return Column(📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @override | |
| Widget build(BuildContext context) { | |
| final progress = (currentStep.index + 1) / totalSteps; | |
| return Column( | |
| children: [ | |
| Row( | |
| mainAxisAlignment: MainAxisAlignment.spaceBetween, | |
| children: [ | |
| Text( | |
| _getStepText(), | |
| style: TextStyle( | |
| fontSize: 14.sp, | |
| fontWeight: FontWeight.w500, | |
| color: AppColors.gray600, | |
| ), | |
| ), | |
| Text( | |
| '${currentStep.index + 1}/$totalSteps', | |
| style: TextStyle( | |
| fontSize: 14.sp, | |
| fontWeight: FontWeight.w500, | |
| color: AppColors.primary, | |
| ), | |
| ), | |
| ], | |
| ), | |
| SizedBox(height: 8.h), | |
| LinearProgressIndicator( | |
| value: progress, | |
| backgroundColor: AppColors.gray200, | |
| valueColor: const AlwaysStoppedAnimation<Color>(AppColors.primary), | |
| minHeight: 4.h, | |
| borderRadius: BorderRadius.circular(2.r), | |
| ), | |
| ], | |
| ); | |
| `@override` | |
| Widget build(BuildContext context) { | |
| final progress = ((currentStep.index + 1) / totalSteps).clamp(0.0, 1.0); | |
| return Column( | |
| children: [ | |
| Row( | |
| mainAxisAlignment: MainAxisAlignment.spaceBetween, | |
| children: [ | |
| Text( | |
| _getStepText(), | |
| style: TextStyle( | |
| fontSize: 14.sp, | |
| fontWeight: FontWeight.w500, | |
| color: AppColors.gray600, | |
| ), | |
| ), | |
| Text( | |
| '${currentStep.index + 1}/$totalSteps', | |
| style: TextStyle( | |
| fontSize: 14.sp, | |
| fontWeight: FontWeight.w500, | |
| color: AppColors.primary, | |
| ), | |
| ), | |
| ], | |
| ), | |
| SizedBox(height: 8.h), | |
| LinearProgressIndicator( | |
| value: progress, | |
| backgroundColor: AppColors.gray200, | |
| valueColor: const AlwaysStoppedAnimation<Color>(AppColors.primary), | |
| minHeight: 4.h, | |
| borderRadius: BorderRadius.circular(2.r), | |
| ), | |
| ], | |
| ); |
🤖 Prompt for AI Agents
In `@lib/features/onboarding/presentation/widgets/step_indicator.dart` around
lines 56 - 92, Clamp the computed progress before passing it to the
LinearProgressIndicator: in the build method where progress is calculated from
currentStep.index and totalSteps (when currentStep can be
OnboardingStep.completed), ensure progress is constrained to the 0.0–1.0 range
(e.g., use a clamp or min/max on the progress value) and then use that clamped
value for the LinearProgressIndicator's value to avoid values >1.0.
| // 3. 온보딩 중인 사용자가 홈에 접근 시 → 온보딩으로 리다이렉트 | ||
| // ==================================================================== | ||
| // TODO: 온보딩 완료 여부 확인 | ||
| // SharedPreferences나 Firestore에서 온보딩 완료 상태 확인 | ||
| // if (!onboardingCompleted && currentPath != RoutePaths.onboarding) { | ||
| // return RoutePaths.onboarding; | ||
| // } | ||
| // 온보딩 경로에 있으면 허용 | ||
| if (onboardingPaths.contains(currentPath)) { | ||
| return null; | ||
| } | ||
|
|
There was a problem hiding this comment.
온보딩 리다이렉트 로직이 실제로 동작하지 않습니다.
현재 블록은 온보딩 경로 허용만 하고, 온보딩 필요 여부를 확인해 리다이렉트하는 로직이 없습니다. 온보딩 필요 상태를 읽어 홈 접근 시 적절한 온보딩 경로로 보내는 처리가 필요합니다(또는 주석을 정정).
🤖 Prompt for AI Agents
In `@lib/router/app_router.dart` around lines 112 - 118, The onboarding redirect
block currently only allows onboardingPaths and never redirects users who need
onboarding; update the logic in the router (near onboardingPaths and
currentPath) to read the user's onboarding-needed state (e.g., an
isOnboardingRequired / needsOnboarding flag from your auth/session state or a
getOnboardingStatus() helper) and when currentPath is not in onboardingPaths and
the user needs onboarding, return a redirect to the appropriate onboarding route
(e.g., the first entry in onboardingPaths or a named onboarding route) instead
of null; ensure you reference onboardingPaths, currentPath and the onboarding
flag/getter you use so the router sends users to onboarding when required.
| 704003F8267A84DA97BF5147 /* [CP] Check Pods Manifest.lock */ = { | ||
| isa = PBXShellScriptBuildPhase; | ||
| buildActionMask = 2147483647; | ||
| files = ( | ||
| ); | ||
| inputFileListPaths = ( | ||
| ); | ||
| inputPaths = ( | ||
| "${PODS_PODFILE_DIR_PATH}/Podfile.lock", | ||
| "${PODS_ROOT}/Manifest.lock", | ||
| ); | ||
| name = "[CP] Check Pods Manifest.lock"; | ||
| outputFileListPaths = ( | ||
| ); | ||
| outputPaths = ( | ||
| "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", | ||
| ); | ||
| runOnlyForDeploymentPostprocessing = 0; | ||
| shellPath = /bin/sh; | ||
| shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; | ||
| showEnvVarsInLog = 0; | ||
| }; | ||
| 9A4B57CF7FC060BA107CC516 /* [CP] Check Pods Manifest.lock */ = { | ||
| isa = PBXShellScriptBuildPhase; | ||
| buildActionMask = 2147483647; | ||
| files = ( | ||
| ); | ||
| inputFileListPaths = ( | ||
| ); | ||
| inputPaths = ( | ||
| "${PODS_PODFILE_DIR_PATH}/Podfile.lock", | ||
| "${PODS_ROOT}/Manifest.lock", | ||
| ); | ||
| name = "[CP] Check Pods Manifest.lock"; | ||
| outputFileListPaths = ( | ||
| ); | ||
| outputPaths = ( | ||
| "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", | ||
| ); | ||
| runOnlyForDeploymentPostprocessing = 0; | ||
| shellPath = /bin/sh; | ||
| shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; | ||
| showEnvVarsInLog = 0; | ||
| }; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check for Podfile.lock existence and git tracking
echo "=== Checking for Podfile.lock files ==="
fd -a -g 'Podfile.lock'
echo -e "\n=== Checking git tracking ==="
git ls-files | grep -i 'podfile\.lock'
echo -e "\n=== Git status for Podfile.lock ==="
git status | grep -i 'podfile'
echo -e "\n=== Checking macOS directory structure ==="
ls -la macos/ | head -20
echo -e "\n=== Checking for Pods directory ==="
find macos -type d -name "Pods" 2>/dev/nullRepository: MapSee-Lab/MapSy-FE
Length of output: 849
🏁 Script executed:
#!/bin/bash
echo "=== Checking macos/.gitignore ==="
cat macos/.gitignore
echo -e "\n=== Git log for macos/Podfile.lock ==="
git log --oneline -20 -- macos/Podfile.lock 2>&1 | head -20
echo -e "\n=== Checking if Pods is gitignored ==="
grep -r "Pods/" macos/.gitignore 2>/dev/null
echo -e "\n=== Checking CI configuration ==="
find . -type f \( -name "*.yml" -o -name "*.yaml" \) | grep -E "(github|gitlab|circleci|action)" | head -10
echo -e "\n=== Checking for workflow files ==="
ls -la .github/workflows/ 2>/dev/null || echo "No .github/workflows found"Repository: MapSee-Lab/MapSy-FE
Length of output: 2451
🏁 Script executed:
#!/bin/bash
echo "=== Git log for project.pbxproj (last 10 commits) ==="
git log --oneline -10 -- macos/Runner.xcodeproj/project.pbxproj
echo -e "\n=== Checking if macos Podfile exists and is tracked ==="
git ls-files | grep macos/Podfile
echo -e "\n=== Recent changes to macos Podfile ==="
git log --oneline -5 -- macos/Podfile
echo -e "\n=== Checking iOS Podfile.lock size and content preview ==="
head -20 ios/Podfile.lock
echo -e "\n=== Checking pod-related scripts or setup in repo root ==="
grep -r "pod install" . --include="*.md" --include="*.sh" --include="*.yaml" 2>/dev/null | head -10Repository: MapSee-Lab/MapSy-FE
Length of output: 1556
🏁 Script executed:
#!/bin/bash
echo "=== Searching for macOS pod install in workflows ==="
grep -r "pod install" .github/workflows/ | grep -i "macos\|mac"
echo -e "\n=== Checking iOS workflow for pod install ==="
grep -A5 -B5 "pod install" .github/workflows/PROJECT-FLUTTER-IOS-TESTFLIGHT.yaml | head -20
echo -e "\n=== Checking if macOS build is part of any workflow ==="
grep -r "macos" .github/workflows/ | grep -i "build\|pod" | head -10
echo -e "\n=== Listing current git status ==="
git status --short | grep -E "(macos|ios).*Podfile"
echo -e "\n=== Checking PR/recent commits for context ==="
git log --all --oneline -5Repository: MapSee-Lab/MapSy-FE
Length of output: 1113
macOS 빌드 단계에서 Podfile.lock 누락으로 인한 빌드 실패
macos/Podfile.lock이 존재하지 않아 프로젝트의 Check Pods Manifest.lock 빌드 단계가 실패합니다. iOS는 CI에서 pod install을 실행하여 Podfile.lock을 생성하지만, macOS는 CI 구성에 해당 단계가 없습니다. 다음 중 하나를 해결해야 합니다:
- CI 워크플로우에
cd macos && pod install단계 추가 - 또는 macos/Podfile.lock을 git에 커밋 (iOS와 동일하게)
현재 상태에서는 로컬 개발자가 pod install을 수동으로 실행해야만 빌드할 수 있습니다.
🤖 Prompt for AI Agents
In `@macos/Runner.xcodeproj/project.pbxproj` around lines 422 - 465, 빌드 단계 "[CP]
Check Pods Manifest.lock" (PBXShellScriptBuildPhase) 실패는 macOS 타겟의 Podfile.lock이
없기 때문입니다; 해결하려면 CI에 macOS 프로젝트 디렉터리로 이동해 pod install을 실행하도록 워크플로우를 추가(예: cd
<macOS project dir> && pod install)하거나 macOS용 Podfile.lock을 저장소에 커밋해 체크아웃 시 항상
존재하도록 만드세요.
…. 무조건 login 페이지로 이동하는것보다 인증상태 확인 후 분기하는것으로 수정 #14
…com//issues/14
✨ 변경 사항
✅ 테스트
Summary by CodeRabbit
새로운 기능
버그 수정