-
Notifications
You must be signed in to change notification settings - Fork 0
20251202 #378 기능개선 마이페이지 피그마 명세대로 수정 #390
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The head ref may contain hidden characters: "20251202_#378_\uAE30\uB2A5\uAC1C\uC120_\uB9C8\uC774\uD398\uC774\uC9C0_\uD53C\uADF8\uB9C8_\uBA85\uC138\uB300\uB85C_\uC218\uC815"
Conversation
Walkthrough아이콘 상수명 변경·추가, Member 모델 필드 추가 및 (de)직렬화 반영, navigateTo 반환형 변경, RangeSlider에 햅틱·렌더링 재작성, 마이페이지 대규모 UI/로직 변경(좋아요 목록·프로필 편집·위치 인증 등)과 관련 API 확장 등이 포함된 변경입니다. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant MyLikeListScreen
participant ItemApi
participant Server
participant ItemDetailDescriptionScreen
User->>MyLikeListScreen: 화면 진입
MyLikeListScreen->>ItemApi: getLikeList(pageNumber, pageSize)
ItemApi->>Server: POST /api/item/like/get
Server-->>ItemApi: ItemResponse
ItemApi-->>MyLikeListScreen: ItemResponse
MyLikeListScreen->>MyLikeListScreen: Item -> _LikedItem 변환
MyLikeListScreen->>MyLikeListScreen: 비동기 지오코딩(좌표 → 주소)
MyLikeListScreen->>User: 목록 렌더링
User->>MyLikeListScreen: 항목 탭 (await 네비게이트)
MyLikeListScreen->>ItemDetailDescriptionScreen: 네비게이트 (await)
ItemDetailDescriptionScreen->>User: 상세 표시
User->>ItemDetailDescriptionScreen: 뒤로가기 (조건부로 itemId 반환)
ItemDetailDescriptionScreen-->>MyLikeListScreen: itemId (nullable) 반환
MyLikeListScreen->>MyLikeListScreen: 반환값으로 목록 갱신(있으면 제거)
sequenceDiagram
participant User
participant MyProfileEditScreen
participant ImagePicker
participant ImageApi
participant MemberApi
participant Server
User->>MyProfileEditScreen: 진입
MyProfileEditScreen->>MemberApi: GET /api/members/profile
MemberApi->>Server: 요청
Server-->>MemberApi: 사용자 데이터
MemberApi-->>MyProfileEditScreen: 프로필 반영
User->>MyProfileEditScreen: 이미지 선택
MyProfileEditScreen->>ImagePicker: 이미지 선택 요청
ImagePicker-->>MyProfileEditScreen: XFile 반환
alt 이미지 선택됨
MyProfileEditScreen->>ImageApi: 이미지 업로드
ImageApi->>Server: POST /api/upload/image
Server-->>ImageApi: imageUrl 반환
ImageApi-->>MyProfileEditScreen: imageUrl 수신
end
User->>MyProfileEditScreen: 저장 클릭
MyProfileEditScreen->>MemberApi: updateMemberProfile(nickname, imageUrl)
MemberApi->>Server: multipart POST /api/members/profile/update
Server-->>MemberApi: 성공 응답
MemberApi-->>MyProfileEditScreen: 완료
MyProfileEditScreen->>User: 성공 스낵바 및 화면 닫기
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ 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 |
|
❌ 프로젝트 빌드 실패 실패한 단계: 알 수 없는 단계 빌드 로그를 확인하여 문제를 해결해주세요. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (12)
lib/widgets/user_profile_circular_avatar.dart (1)
103-111:AppColors를 사용해주세요.코딩 가이드라인에 따라
Colors.grey[300]및Colors.grey[700]대신AppColors에 정의된 색상 상수를 사용해야 합니다.🔎 제안하는 수정
Widget _buildDefaultImage() { return Container( - color: Colors.grey[300], + color: AppColors.opacity10White, // 또는 적절한 AppColors 상수 child: Icon( Icons.person, size: 40, - color: Colors.grey[700], + color: AppColors.opacity60White, // 또는 적절한 AppColors 상수 ), ); }lib/services/apis/member_api.dart (1)
143-157: 반환 타입 일관성 및 불필요한.toString()호출두 가지 개선 사항이 있습니다:
일관성 없는 반환 타입: 다른 유사한 메서드들(
saveTermsAgreement,deleteMember,saveSearchRadius)은 성공 여부를bool로 반환하지만, 이 메서드는void를 반환합니다. 호출자가 성공/실패를 알 수 없습니다.불필요한
.toString()호출:nickname과profileUrl은 이미String타입이므로.toString()호출이 불필요합니다.🔎 제안하는 수정
- Future<void> updateMemberProfile(String nickname, String profileUrl) async { + Future<bool> updateMemberProfile(String nickname, String profileUrl) async { const String url = '${AppUrls.baseUrl}/api/members/profile/update'; + bool isSuccess = false; await ApiClient.sendMultipartRequest( url: url, fields: { - 'nickname': nickname.toString(), - 'profileUrl': profileUrl.toString(), + 'nickname': nickname, + 'profileUrl': profileUrl, }, isAuthRequired: true, onSuccess: (_) { debugPrint('회원 프로필 변경 성공: $nickname'); + isSuccess = true; }, ); + return isSuccess; }lib/icons/app_icons.dart (1)
22-22: 네이밍 일관성:profilelikecount→profileLikeCount다른 아이콘들(
arrowUpward,addItemPlus,itemRegisterHeart)은 camelCase를 사용하고 있으므로,profilelikecount도profileLikeCount로 변경하는 것이 일관성 있습니다.🔎 제안하는 수정
- static const IconData profilelikecount = IconData(0xe817, fontFamily: _kFontFam, fontPackage: _kFontPkg); + static const IconData profileLikeCount = IconData(0xe817, fontFamily: _kFontFam, fontPackage: _kFontPkg);lib/screens/my_page/my_like_list_screen.dart (3)
106-158: 순차 geocoding 호출로 인한 성능 저하 가능성
_convertToLikeItems에서 각 아이템마다 순차적으로getAddressFromCoordinates를 호출합니다. 10개 아이템 기준, 네트워크 지연이 누적되어 사용자 경험이 저하될 수 있습니다.개선 방안:
Future.wait로 병렬 처리- 위치 정보를 서버에서 미리 변환하여 전달
- 위치 표시를 지연 로딩으로 처리
🔎 병렬 처리 예시
Future<List<_LikedItem>> _convertToLikeItems(List<Item> details) async { return Future.wait(details.asMap().entries.map((entry) async { final index = entry.key; final d = entry.value; // ... 변환 로직 })); }
113-128: 빈 catch 블록은 디버깅을 어렵게 합니다.
catch (_) {}는 예외를 무시하므로 예상치 못한 문제 발생 시 원인 파악이 어렵습니다. 최소한 디버그 로그를 추가하는 것을 권장합니다.🔎 제안하는 수정
try { cond = item_condtion_enum.ItemCondition.values.firstWhere( (e) => e.serverName == d.itemCondition, ); - } catch (_) {} + } catch (e) { + debugPrint('ItemCondition 매핑 실패: ${d.itemCondition}, $e'); + }
204-223:context.navigateTo()사용 권장코딩 가이드라인에 따라 iOS 스와이프 제스처 지원을 위해
Navigator.push대신context.navigateTo()를 사용해야 합니다.🔎 제안하는 수정
- final result = await Navigator.push<dynamic>( - context, - MaterialPageRoute( - builder: (_) => ItemDetailDescriptionScreen( - itemId: itemId, - imageSize: Size( - MediaQuery.of(context).size.width, - 400.h, - ), - currentImageIndex: 0, - heroTag: 'itemImage_${itemId}_0', - isMyItem: false, - isRequestManagement: false, - ), - ), - ); + final result = await context.navigateTo<dynamic>( + ItemDetailDescriptionScreen( + itemId: itemId, + imageSize: Size( + MediaQuery.of(context).size.width, + 400.h, + ), + currentImageIndex: 0, + heroTag: 'itemImage_${itemId}_0', + isMyItem: false, + isRequestManagement: false, + ), + );lib/screens/my_page/my_location_verification_screen.dart (3)
26-30: 변수명 오타 수정이 필요합니다.
currentAdress는currentAddress로,eupMyoenDong은eupMyeonDong으로 수정해야 합니다. 오타가 코드베이스 전반에 일관되게 사용되고 있는지 확인이 필요합니다.🔎 제안된 수정
- String currentAdress = ''; + String currentAddress = ''; String siDo = ''; String siGunGu = ''; - String eupMyoenDong = ''; + String eupMyeonDong = ''; String? ri;
209-228: 중복 래퍼 메서드 제거를 고려해주세요.
getAddressByNaverApi는 단순히_loadAddressInfo를 호출하는 래퍼입니다. 하나의 메서드로 통합하거나, 래퍼가 필요한 이유가 있다면 주석으로 명시하는 것이 좋습니다.🔎 제안된 수정
- // 주소 정보 로드 메서드 - Future<void> _loadAddressInfo(NLatLng position) async { + // NaverMap에서 주소 가져오는 메서드 + Future<void> getAddressByNaverApi(NLatLng position) async { final addressInfo = await _locationService.getAddressFromCoordinates( position, ); if (addressInfo != null) { setState(() { siDo = addressInfo.siDo; siGunGu = addressInfo.siGunGu; - eupMyoenDong = addressInfo.eupMyoenDong; + eupMyeonDong = addressInfo.eupMyeonDong; ri = addressInfo.ri; - currentAdress = addressInfo.currentAddress; + currentAddress = addressInfo.currentAddress; }); } } - - // NaverMap에서 주소 가져오는 메서드를 새 메서드로 호출 - Future<void> getAddressByNaverApi(NLatLng position) async { - await _loadAddressInfo(position); - } }
54-55: Expanded flex 값에 대한 의도 명확화가 필요합니다.
flex: 350과flex: 370은 일반적이지 않은 값입니다. 의도가 명확하다면 주석을 추가하거나, 비율로 단순화하는 것을 고려해주세요 (예: 35:37 → 7:7.4 또는 정수 비율).Also applies to: 103-104
lib/screens/my_page/my_profile_edit_screen.dart (2)
173-196: async/await 패턴 사용을 권장합니다.
.then().catchError()체인 대신async/await와try/catch를 사용하면 가독성이 향상됩니다.🔎 제안된 수정
- onTap: () async { - if (_isProfileEdited && (_nickname.isNotEmpty)) { - await MemberApi() - .updateMemberProfile(nicknameController.text, imageUrl) - .then((_) { - if (context.mounted) { - CommonSnackBar.show( - context: context, - message: '프로필이 성공적으로 업데이트되었습니다.', - type: SnackBarType.success, - ); - Navigator.of(context).pop(true); - } - }) - .catchError((e) { - if (context.mounted) { - CommonSnackBar.show( - context: context, - message: '프로필 업데이트에 실패했습니다: $e', - type: SnackBarType.error, - ); - } - }); - } - }, + onTap: () async { + if (_isProfileEdited && _nickname.isNotEmpty) { + try { + await MemberApi().updateMemberProfile( + nicknameController.text, + imageUrl, + ); + if (context.mounted) { + CommonSnackBar.show( + context: context, + message: '프로필이 성공적으로 업데이트되었습니다.', + type: SnackBarType.success, + ); + Navigator.of(context).pop(true); + } + } catch (e) { + if (context.mounted) { + CommonSnackBar.show( + context: context, + message: '프로필 업데이트에 실패했습니다: $e', + type: SnackBarType.error, + ); + } + } + } + },
362-362: EdgeInsets에 ScreenUtil 스케일링이 누락되었습니다.다른 곳에서는
.w와.h확장을 사용하는데, 여기서는const EdgeInsets.all(8)을 사용합니다. 일관성을 위해EdgeInsets.all(8.w)를 사용하는 것이 좋습니다.🔎 제안된 수정
- contentPadding: const EdgeInsets.all(8), + contentPadding: EdgeInsets.all(8.w),lib/screens/my_page_tab_screen.dart (1)
242-251:Wrap대신Column사용 검토현재 코드의 각 메뉴 항목은 고정 높이(54.h)의 전체 너비 아이템으로 세로로 배열됩니다.
Wrap의runSpacing은 줄 바꿈 시 행 간의 수직 간격을 위한 속성이므로, 의미론적으로Column과spacing: 8.w를 사용하는 것이 더 적합합니다.참고: 현재 구현은 모든 코딩 가이드라인(CustomTextStyles, AppColors, context.navigateTo)을 올바르게 따르고 있습니다.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (5)
assets/fonts/AppIcons.ttfis excluded by!**/*.ttfassets/images/camera.svgis excluded by!**/*.svgassets/images/editPen.svgis excluded by!**/*.svgios/Podfile.lockis excluded by!**/*.lockmacos/Podfile.lockis excluded by!**/*.lock
📒 Files selected for processing (17)
lib/icons/app_icons.dart(1 hunks)lib/icons/config.json(3 hunks)lib/models/apis/objects/member.dart(2 hunks)lib/models/apis/objects/member.g.dart(2 hunks)lib/screens/chat_room_screen.dart(1 hunks)lib/screens/item_detail_description_screen.dart(7 hunks)lib/screens/my_page/my_category_settings_screen.dart(5 hunks)lib/screens/my_page/my_like_list_screen.dart(1 hunks)lib/screens/my_page/my_location_verification_screen.dart(1 hunks)lib/screens/my_page/my_profile_edit_screen.dart(7 hunks)lib/screens/my_page_tab_screen.dart(13 hunks)lib/screens/search_range_setting_screen.dart(10 hunks)lib/services/apis/item_api.dart(1 hunks)lib/services/apis/member_api.dart(4 hunks)lib/utils/common_utils.dart(3 hunks)lib/widgets/common/range_slider_widget.dart(10 hunks)lib/widgets/user_profile_circular_avatar.dart(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.dart
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.dart: Use CustomTextStyles for all text styling in Flutter widgets - never define TextStyle directly
Use AppColors for all color values - never use direct Color codes or Colors constants
Use the team's common extension methodcontext.navigateTo()for screen navigation to support iOS swipe gesture handling
Files:
lib/models/apis/objects/member.dartlib/services/apis/item_api.dartlib/icons/app_icons.dartlib/screens/my_page/my_like_list_screen.dartlib/models/apis/objects/member.g.dartlib/screens/item_detail_description_screen.dartlib/screens/my_page/my_location_verification_screen.dartlib/widgets/user_profile_circular_avatar.dartlib/screens/my_page/my_profile_edit_screen.dartlib/widgets/common/range_slider_widget.dartlib/screens/chat_room_screen.dartlib/screens/search_range_setting_screen.dartlib/screens/my_page/my_category_settings_screen.dartlib/services/apis/member_api.dartlib/screens/my_page_tab_screen.dartlib/utils/common_utils.dart
**/lib/widgets/**/*.dart
📄 CodeRabbit inference engine (CLAUDE.md)
Do not use unnecessary
_widgetsuffix in widget filenames
Files:
lib/widgets/user_profile_circular_avatar.dartlib/widgets/common/range_slider_widget.dart
🧠 Learnings (5)
📓 Common learnings
Learnt from: SeoHyun1024
Repo: TEAM-ROMROM/RomRom-FE PR: 130
File: lib/screens/item_detail_description_screen.dart:38-38
Timestamp: 2025-06-25T01:35:06.847Z
Learning: ItemDetailDescriptionScreen에서 like 변수의 하드코딩된 초기값은 임시 구현이며, 사용자가 API 연동 시에 실제 좋아요 상태로 업데이트할 예정입니다.
📚 Learning: 2025-06-25T01:35:06.847Z
Learnt from: SeoHyun1024
Repo: TEAM-ROMROM/RomRom-FE PR: 130
File: lib/screens/item_detail_description_screen.dart:38-38
Timestamp: 2025-06-25T01:35:06.847Z
Learning: ItemDetailDescriptionScreen에서 like 변수의 하드코딩된 초기값은 임시 구현이며, 사용자가 API 연동 시에 실제 좋아요 상태로 업데이트할 예정입니다.
Applied to files:
lib/screens/my_page/my_like_list_screen.dart
📚 Learning: 2025-11-28T09:18:17.218Z
Learnt from: CR
Repo: TEAM-ROMROM/RomRom-FE PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T09:18:17.218Z
Learning: Applies to **/*.dart : Use the team's common extension method `context.navigateTo()` for screen navigation to support iOS swipe gesture handling
Applied to files:
lib/screens/my_page/my_location_verification_screen.dartlib/widgets/common/range_slider_widget.dartlib/screens/my_page_tab_screen.dartlib/utils/common_utils.dart
📚 Learning: 2025-11-28T09:18:17.218Z
Learnt from: CR
Repo: TEAM-ROMROM/RomRom-FE PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T09:18:17.218Z
Learning: Applies to **/*.dart : Use AppColors for all color values - never use direct Color codes or Colors constants
Applied to files:
lib/widgets/common/range_slider_widget.dartlib/screens/my_page_tab_screen.dart
📚 Learning: 2025-11-28T09:18:17.218Z
Learnt from: CR
Repo: TEAM-ROMROM/RomRom-FE PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T09:18:17.218Z
Learning: Applies to **/*.dart : Use CustomTextStyles for all text styling in Flutter widgets - never define TextStyle directly
Applied to files:
lib/widgets/common/range_slider_widget.dart
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: 프로젝트 빌드 테스트
🔇 Additional comments (20)
lib/services/apis/item_api.dart (1)
276-299: LGTM!새로운
getLikeList메서드가 기존 API 패턴을 잘 따르고 있습니다.pageNumber와pageSize를 사용한 페이지네이션 처리가 다른 목록 조회 API와 일관성 있게 구현되어 있습니다.lib/screens/item_detail_description_screen.dart (1)
801-808: 좋아요 취소 상태 반환 로직좋아요가 취소된 상태(
isLikedVN.value == false)일 때itemId를 반환하여 목록 화면에서 해당 아이템을 갱신할 수 있도록 하는 로직이 적절합니다. 다만, 초기에 좋아요가 없던 아이템을 조회만 하고 나가는 경우에도itemId가 반환될 수 있으므로, 실제로 좋아요 상태가 변경되었는지 추적하는 것이 더 정확할 수 있습니다.lib/utils/common_utils.dart (2)
14-56:navigateTo의 제네릭 반환 타입 개선
navigateTo가 이제Future<T?>를 반환하도록 변경되어 호출자가 네비게이션 결과를 받을 수 있게 되었습니다. iOS 스와이프 제스처를 지원하면서도 결과 값을 처리할 수 있어 유용한 개선입니다.
138-164: 시간 유틸리티 함수 개선
isSameMinute과formatMessageTime함수에서 UTC 변환 및 null 체크가 잘 처리되어 있습니다. 12시간제 변환 로직도 올바르게 구현되었습니다.lib/screens/search_range_setting_screen.dart (1)
126-151: 위치 초기화 로직 개선
try-catch로 에러 처리를 감싸고,MemberApi에서 저장된searchRadiusInMeters를 가져와 초기 범위 인덱스를 설정하는 로직이 잘 구현되어 있습니다. 기본값7500.0도 적절합니다.lib/screens/chat_room_screen.dart (1)
746-746: LGTM!아이콘 이름이
AppIcons.arrowUpward로 변경된 것은 PR 전체의 아이콘 네이밍 컨벤션(camelCase) 변경과 일치합니다.lib/models/apis/objects/member.dart (1)
24-25: LGTM!
totalLikeCount와searchRadiusInMeters필드가 올바르게 추가되었습니다. nullable 타입 사용이 적절합니다.Also applies to: 45-46
lib/models/apis/objects/member.g.dart (1)
1-55: 생성된 파일입니다.이 파일은
build_runner에 의해 자동 생성된 파일이므로 수동 검토가 필요하지 않습니다.lib/screens/my_page/my_category_settings_screen.dart (1)
83-88: 레이아웃 구조 확인 필요
SizedBox(height: 1.h)는 매우 작은 높이(약 1 논리적 픽셀)이며,Expanded(child: Container())는 빈 공간 확보용으로 보입니다. 의도한 디자인이 맞는지 확인해주세요.만약 칩 영역과 버튼 사이 공간을 확보하려는 의도라면,
Spacer()위젯을 사용하는 것이 더 명확합니다.🔎 대안 제안
- SizedBox(height: 1.h,), // 카테고리 칩 표시 SingleChildScrollView( child: _buildCategoryChips(context), ), - Expanded(child: Container()), + const Spacer(),lib/screens/my_page/my_like_list_screen.dart (1)
391-396: 좋아요 토글이 로컬 상태만 업데이트합니다.현재 좋아요 버튼 탭 시 로컬
isLiked상태만 변경되고 서버 API 호출이 없습니다. 화면을 새로고침하면 변경사항이 사라집니다.서버와의 동기화가 의도적으로 누락된 것인지 확인해주세요.
lib/screens/my_page/my_location_verification_screen.dart (1)
41-48: LGTM!AppColors, CustomTextStyles를 올바르게 사용하고 있으며, 뒤로가기 동작은
Navigator.pop을 사용하는 것이 적절합니다.lib/screens/my_page_tab_screen.dart (3)
96-118: LGTM!
context.navigateTo()를 사용하여 iOS 스와이프 제스처를 지원하는 네비게이션을 올바르게 구현했습니다. 코딩 가이드라인을 잘 따르고 있습니다.
156-165: LGTM!프로필 편집 화면에서 돌아올 때 결과값을 받아 사용자 정보를 새로고침하는 패턴이 잘 구현되어 있습니다.
182-186: LGTM!
UserProfileCircularAvatar위젯을 사용하여 프로필 이미지를 표시하고,AppColors와CustomTextStyles를 일관되게 사용하고 있습니다.lib/widgets/common/range_slider_widget.dart (3)
36-37: LGTM!햅틱 피드백 구현이 잘 되어 있습니다.
_lastHapticIndex를 사용하여 중복 피드백을 방지하고,kIsWeb체크로 웹 환경에서의 오류를 방지합니다.Also applies to: 107-113, 120-122, 130-132
303-319: LGTM!노브(knob)와 그림자 렌더링이 잘 구현되어 있습니다.
withValues(alpha: 0.25)는 deprecated된withOpacity대신 올바른 최신 API 사용입니다.
323-328: LGTM!
shouldRepaint에서primaryColor와inactiveColor변경을 체크하도록 확장하여 불필요한 리페인트를 방지합니다.lib/screens/my_page/my_profile_edit_screen.dart (1)
243-288: LGTM!프로필 이미지 섹션이 잘 구현되어 있습니다. 로딩 상태 표시,
UserProfileCircularAvatar사용, 카메라 아이콘 오버레이가 적절합니다.lib/icons/config.json (2)
248-249: LGTM!
arrow_upward를arrowUpward로 변경하여 camelCase 네이밍 컨벤션을 따릅니다.
275-316: LGTM!새 아이콘들 (
camera,edit,profilelikecount)이 일관된 포맷으로 추가되었습니다.
| } catch (e) { | ||
| setState(() { | ||
| _avatarUrl = null; | ||
| _avatarUrl = 'assets/images/basicProfile.svg'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
로컬 에셋 경로가 Image.network에서 작동하지 않습니다.
_avatarUrl에 로컬 에셋 경로 ('assets/images/basicProfile.svg')를 할당하지만, 빌드 메서드(라인 91-97)에서는 Image.network로 렌더링합니다. 이는 다음 두 가지 문제를 야기합니다:
Image.network는 HTTP/HTTPS URL만 지원하므로 로컬 에셋 경로는 로드되지 않습니다.- SVG 파일은
Image.network또는Image.asset으로 렌더링할 수 없으며,flutter_svg패키지가 필요합니다.
에러 발생 시 _avatarUrl을 null로 설정하여 _buildDefaultImage()가 호출되도록 하거나, SVG 에셋을 별도로 처리하는 로직이 필요합니다.
🔎 제안하는 수정 방안
} catch (e) {
setState(() {
- _avatarUrl = 'assets/images/basicProfile.svg';
+ _avatarUrl = null;
_isLoading = false;
});
}📝 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.
| _avatarUrl = 'assets/images/basicProfile.svg'; | |
| } catch (e) { | |
| setState(() { | |
| _avatarUrl = null; | |
| _isLoading = false; | |
| }); | |
| } |
🤖 Prompt for AI Agents
In lib/widgets/user_profile_circular_avatar.dart around line 63, _avatarUrl is
set to a local SVG asset but the build uses Image.network (lines ~91-97); fix by
distinguishing asset vs network and handling SVGs: if _avatarUrl is a network
URL use Image.network with errorBuilder that sets _avatarUrl = null to trigger
_buildDefaultImage(), otherwise if the path is a local asset and
endsWith('.svg') use flutter_svg's SvgPicture.asset (add import and dependency),
and for non-SVG local assets use Image.asset; ensure fallback paths and error
handling (try/catch or errorBuilder) set _avatarUrl=null so default image is
shown on failure.
|
❌ 프로젝트 빌드 실패 실패한 단계: 알 수 없는 단계 빌드 로그를 확인하여 문제를 해결해주세요. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
lib/screens/item_detail_description_screen.dart (1)
824-831:context.navigateTo()사용으로 iOS 스와이프 제스처 지원 추가코딩 가이드라인에 따라
context.navigateTo()를 사용해야 합니다. 이 메서드는 iOS에서CupertinoPageRoute를 사용하여 스와이프 백 제스처를 지원하며, 제네릭 타입<bool>로 결과값 반환을 완전히 지원합니다.final bool? reported = await context.navigateTo<bool>( screen: ReportScreen(itemId: widget.itemId), );
🧹 Nitpick comments (7)
lib/screens/item_detail_description_screen.dart (1)
801-808: 불필요한async키워드
onTap콜백이async로 선언되어 있지만 내부에서await를 사용하지 않습니다. 불필요한async는 제거하는 것이 좋습니다.🔎 수정 제안
- onTap: () async { + onTap: () { // 뒤로갈 때 좋아요가 취소된 상태면 목록에 반영되도록 정보 반환 if (isLikedVN.value == false) { Navigator.of(context).pop(widget.itemId); } else { Navigator.of(context).pop(); } }, -lib/screens/my_page/my_like_list_screen.dart (3)
6-18: 중복 import 정리 필요
ItemCondition이 두 번 import되어 있습니다 (라인 6과 18). alias가 필요한 경우 하나의 import만 유지하세요.🔎 수정 제안
-import 'package:romrom_fe/enums/item_condition.dart'; import 'package:romrom_fe/enums/item_status.dart'; import 'package:romrom_fe/enums/item_trade_option.dart'; ... -import 'package:romrom_fe/enums/item_condition.dart' as item_condition_enum; +import 'package:romrom_fe/enums/item_condition.dart';그리고 라인 115에서 alias 없이 사용:
- cond = item_condition_enum.ItemCondition.values.firstWhere( + cond = ItemCondition.values.firstWhere(
109-155: 순차적 geocoding으로 인한 성능 저하각 아이템에 대해 순차적으로
await하여 geocoding을 수행하고 있습니다. 아이템이 많을 경우 로딩 시간이 크게 증가할 수 있습니다.
Future.wait를 사용하여 병렬 처리하거나, geocoding 실패 시에도 다른 아이템에 영향을 주지 않도록 개선을 고려해 주세요.🔎 병렬 처리 예시
Future<List<_LikedItem>> _convertToLikeItems(List<Item> details) async { return Future.wait(details.asMap().entries.map((entry) async { final index = entry.key; final d = entry.value; // ... condition/options 매핑 로직 ... String locationText = '미지정'; if (d.latitude != null && d.longitude != null) { try { final address = await LocationService().getAddressFromCoordinates( NLatLng(d.latitude!, d.longitude!), ); if (address != null) { locationText = '${address.siDo} ${address.siGunGu} ${address.eupMyoenDong}'; } } catch (_) { // 개별 실패는 무시 } } return _LikedItem(/* ... */); })); }
204-223: 코딩 가이드라인:context.navigateTo()사용 검토코딩 가이드라인에 따르면 iOS 스와이프 제스처 지원을 위해
context.navigateTo()를 사용해야 합니다.navigateTo가 결과값 반환을 지원하는 경우,Navigator.push대신 사용하는 것을 검토해 주세요.lib/icons/app_icons.dart (1)
34-37: 아이콘 추가 확인새로운 아이콘들이 올바르게 추가되었습니다. 다만
profilelikecount는 다른 아이콘들(arrowUpward,currentLocation등)과 일관성을 위해profileLikeCount로 명명하는 것이 더 좋을 수 있습니다.lib/screens/my_page/my_profile_edit_screen.dart (2)
180-203:async/await와try/catch사용 권장
.then().catchError()패턴 대신async/await와try/catch를 사용하면 가독성과 에러 처리가 더 명확해집니다.또한
_nickname.isNotEmpty대신nicknameController.text.isNotEmpty를 사용하면 상태 동기화 문제를 방지할 수 있습니다.🔎 수정 제안
onTap: () async { final nickname = nicknameController.text; if (_isProfileEdited && nickname.isNotEmpty) { try { await MemberApi().updateMemberProfile(nickname, imageUrl); if (context.mounted) { CommonSnackBar.show( context: context, message: '프로필이 성공적으로 업데이트되었습니다.', type: SnackBarType.success, ); Navigator.of(context).pop(true); } } catch (e) { if (context.mounted) { CommonSnackBar.show( context: context, message: '프로필 업데이트에 실패했습니다: $e', type: SnackBarType.error, ); } } } },
258-271: 로딩 상태 조건 검토
_hasImageBeenTouched && imageFile != null조건으로 로딩 스피너를 표시하고 있습니다. 그러나_hasImageBeenTouched는 이미지 선택 시작 시true가 되고,imageFile은 선택 완료 후에 설정됩니다.이미지 업로드 중인 상태를 나타내려면
_isUploading과 같은 명확한 플래그를 사용하는 것이 더 직관적일 수 있습니다.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
assets/fonts/AppIcons.ttfis excluded by!**/*.ttf
📒 Files selected for processing (7)
lib/icons/app_icons.dart(2 hunks)lib/icons/config.json(3 hunks)lib/screens/item_detail_description_screen.dart(7 hunks)lib/screens/my_page/my_like_list_screen.dart(1 hunks)lib/screens/my_page/my_profile_edit_screen.dart(7 hunks)lib/screens/search_range_setting_screen.dart(10 hunks)lib/widgets/user_profile_circular_avatar.dart(4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- lib/screens/search_range_setting_screen.dart
🧰 Additional context used
📓 Path-based instructions (2)
**/*.dart
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.dart: Use CustomTextStyles for all text styling in Flutter widgets - never define TextStyle directly
Use AppColors for all color values - never use direct Color codes or Colors constants
Use the team's common extension methodcontext.navigateTo()for screen navigation to support iOS swipe gesture handling
Files:
lib/screens/my_page/my_like_list_screen.dartlib/screens/my_page/my_profile_edit_screen.dartlib/screens/item_detail_description_screen.dartlib/widgets/user_profile_circular_avatar.dartlib/icons/app_icons.dart
**/lib/widgets/**/*.dart
📄 CodeRabbit inference engine (CLAUDE.md)
Do not use unnecessary
_widgetsuffix in widget filenames
Files:
lib/widgets/user_profile_circular_avatar.dart
🧠 Learnings (3)
📓 Common learnings
Learnt from: SeoHyun1024
Repo: TEAM-ROMROM/RomRom-FE PR: 130
File: lib/screens/item_detail_description_screen.dart:38-38
Timestamp: 2025-06-25T01:35:06.847Z
Learning: ItemDetailDescriptionScreen에서 like 변수의 하드코딩된 초기값은 임시 구현이며, 사용자가 API 연동 시에 실제 좋아요 상태로 업데이트할 예정입니다.
📚 Learning: 2025-06-25T01:35:06.847Z
Learnt from: SeoHyun1024
Repo: TEAM-ROMROM/RomRom-FE PR: 130
File: lib/screens/item_detail_description_screen.dart:38-38
Timestamp: 2025-06-25T01:35:06.847Z
Learning: ItemDetailDescriptionScreen에서 like 변수의 하드코딩된 초기값은 임시 구현이며, 사용자가 API 연동 시에 실제 좋아요 상태로 업데이트할 예정입니다.
Applied to files:
lib/screens/my_page/my_like_list_screen.dart
📚 Learning: 2025-11-28T09:18:17.218Z
Learnt from: CR
Repo: TEAM-ROMROM/RomRom-FE PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T09:18:17.218Z
Learning: Applies to **/*.dart : Use CustomTextStyles for all text styling in Flutter widgets - never define TextStyle directly
Applied to files:
lib/screens/item_detail_description_screen.dart
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: 프로젝트 빌드 테스트
🔇 Additional comments (11)
lib/screens/item_detail_description_screen.dart (1)
356-366: 이전 리뷰 피드백 반영 확인
TextStyle직접 사용 문제가 해결되었습니다. 이제CustomTextStyles.h3를 올바르게 사용하고 있습니다.lib/screens/my_page/my_like_list_screen.dart (1)
437-457: LGTM!
_LikedItem클래스가 적절하게 정의되어 있으며, 필요한 모든 필드를 포함하고 있습니다.lib/widgets/user_profile_circular_avatar.dart (1)
93-103: 이전 리뷰 피드백 반영 확인이전에 지적된 문제가 해결되었습니다. 이제
_kDefaultProfileAsset을 별도로 처리하여Image.network로 로컬 에셋을 로드하려는 시도를 방지하고, SVG는SvgPicture.asset으로 올바르게 렌더링합니다.lib/screens/my_page/my_profile_edit_screen.dart (2)
45-50: 이전 리뷰 피드백 반영 확인
TextEditingController와FocusNode의dispose()가 올바르게 추가되었습니다. 메모리 누수 문제가 해결되었습니다.
309-431: 닉네임 편집 UI 구현 확인포커스 상태에 따른 조건부 렌더링과 아이콘 위치 계산이 잘 구현되어 있습니다.
_measureTextWidth를 사용하여 동적으로 편집 아이콘 위치를 계산하는 접근 방식이 적절합니다.lib/icons/config.json (6)
23-36: 새 아이콘 추가 확인됨.
profilelikecount아이콘이 올바르게 추가되었습니다. 구조와 필드가 다른 아이콘 항목들과 일관성을 유지하고 있습니다.
262-274: 새 아이콘 추가 확인됨.
camera아이콘이 올바르게 추가되었습니다. 프로필 이미지 업로드 등의 기능에 사용될 것으로 보입니다.
289-302: 새 아이콘 추가 확인됨.
edit아이콘이 올바르게 추가되었습니다. 프로필 편집 등의 기능에 사용될 것으로 보입니다.
276-288:detailView아이콘 정리 완료 확인.사용되지 않는
detailview아이콘(uid: eb85802a6428dc9a61f6a3d9639b80cb)이 제거되었으며, 현재는 활성화된detailView아이콘(uid: aab12ac34d786019442b42d61a477f36)만 남아 있습니다.
220-232:information아이콘 uid 변경은 기존 코드에 영향을 주지 않습니다.아이콘은 코드포인트(codepoint) 값(0xe803)으로 참조되며, uid는 아이콘 생성 도구의 메타데이터일 뿐입니다. 설정 파일의
"code": 59395값이 유지되므로 Dart 코드의AppIcons.information참조는 정상 작동합니다.
234-246: 검색어 명명 규칙 일관성 확인 필요:arrow_upward를 다른 아이콘과 동일한 형식으로 정렬하세요.현재 이 아이콘의 검색어는
"arrow_upward"(snake_case with underscore)로 표기되어 있으며, 이는 코드베이스의 다른 대부분 아이콘과 다릅니다. 다른 아이콘들은 언더스코어 없이 소문자만 사용하거나 (예:"mypagedisable","registerdisable") 또는 camelCase를 따릅니다. 이 아이콘의 검색어를 일관된 명명 규칙으로 수정하세요.Likely an incorrect or invalid review comment.
| GestureDetector( | ||
| onTap: () { | ||
| setState( | ||
| () => item.isLiked = !item.isLiked, | ||
| ); | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
좋아요 토글이 서버와 동기화되지 않음
현재 좋아요 버튼을 누르면 로컬 상태만 변경되고 서버 API 호출이 없습니다. 이로 인해 사용자가 좋아요를 취소했다고 생각하지만 실제로는 서버에 반영되지 않는 데이터 불일치가 발생할 수 있습니다.
ItemDetailDescriptionScreen의 _toggleLike() 메서드처럼 API 호출과 롤백 로직을 추가하는 것이 좋습니다.
🤖 Prompt for AI Agents
In lib/screens/my_page/my_like_list_screen.dart around lines 391 to 396, the
GestureDetector onTap currently flips item.isLiked only in local state without
calling the server; change this to perform an optimistic update, call the
like/unlike API (awaiting the response), and rollback the local state if the API
fails. Implement the same pattern as ItemDetailDescriptionScreen._toggleLike():
store the previous value, setState to the optimistic value, call the async API,
on success keep the value, on failure revert to the previous value inside
setState and show/log the error. Ensure the onTap handler is async, awaits the
network call, and handles exceptions to avoid leaving UI in an inconsistent
state.
|
❌ 프로젝트 빌드 실패 실패한 단계: 알 수 없는 단계 빌드 로그를 확인하여 문제를 해결해주세요. |
|
❌ 프로젝트 빌드 실패 실패한 단계: 알 수 없는 단계 빌드 로그를 확인하여 문제를 해결해주세요. |
|
❌ 프로젝트 빌드 실패 실패한 단계: 알 수 없는 단계 빌드 로그를 확인하여 문제를 해결해주세요. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
lib/utils/common_utils.dart (1)
133-175: 시간 관련 헬퍼 함수 리팩토링 확인
isSameMinute,formatMessageTime,getLastActivityTime함수들이 재작성되었습니다. 로직은 동일해 보이지만, UTC/로컬 시간 처리 및 포맷 변경사항을 확인해주세요.특히:
formatMessageTime의 12시간제 변환 로직 (h12계산)- UTC → 로컬 변환 처리
기존 동작과 일치하는지 확인하려면 단위 테스트 추가를 고려하세요:
// 예시 테스트 케이스 void main() { test('formatMessageTime handles midnight correctly', () { final midnight = DateTime(2025, 1, 1, 0, 5); expect(formatMessageTime(midnight), '오전 12:05'); }); test('formatMessageTime handles noon correctly', () { final noon = DateTime(2025, 1, 1, 12, 30); expect(formatMessageTime(noon), '오후 12:30'); }); test('formatMessageTime handles afternoon correctly', () { final afternoon = DateTime(2025, 1, 1, 13, 0); expect(formatMessageTime(afternoon), '오후 1:00'); }); }
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
lib/screens/chat_room_screen.dart(1 hunks)lib/screens/item_detail_description_screen.dart(7 hunks)lib/utils/common_utils.dart(3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- lib/screens/chat_room_screen.dart
🧰 Additional context used
📓 Path-based instructions (1)
**/*.dart
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.dart: Use CustomTextStyles for all text styling in Flutter widgets - never define TextStyle directly
Use AppColors for all color values - never use direct Color codes or Colors constants
Use the team's common extension methodcontext.navigateTo()for screen navigation to support iOS swipe gesture handling
Files:
lib/utils/common_utils.dartlib/screens/item_detail_description_screen.dart
🧠 Learnings (2)
📚 Learning: 2025-11-28T09:18:17.218Z
Learnt from: CR
Repo: TEAM-ROMROM/RomRom-FE PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T09:18:17.218Z
Learning: Applies to **/*.dart : Use the team's common extension method `context.navigateTo()` for screen navigation to support iOS swipe gesture handling
Applied to files:
lib/utils/common_utils.dart
📚 Learning: 2025-11-28T09:18:17.218Z
Learnt from: CR
Repo: TEAM-ROMROM/RomRom-FE PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T09:18:17.218Z
Learning: Applies to **/*.dart : Use CustomTextStyles for all text styling in Flutter widgets - never define TextStyle directly
Applied to files:
lib/screens/item_detail_description_screen.dart
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: 프로젝트 빌드 테스트
🔇 Additional comments (2)
lib/screens/item_detail_description_screen.dart (1)
357-365: 코딩 가이드라인 준수 - 개선 확인
TextStyle을 직접 사용하는 대신CustomTextStyles.h3를 사용하도록 수정되었습니다. 이전 리뷰 코멘트가 적절히 반영되었습니다.lib/utils/common_utils.dart (1)
13-55: 코드 검증 결과: 이슈 없음
context.navigateTo()확장 함수는 이미lib/utils/common_utils.dart의NavigationExtension에서 올바르게 구현되어 있으며, iOS 스와이프 백 제스처를 지원하도록CupertinoPageRoute를 사용하고 있습니다. 모든 호출 지점에서Future<T?>반환 타입을 올바르게 처리하고 있으며, 팀의 코딩 가이드라인을 준수합니다.Likely an incorrect or invalid review comment.
| onTap: () async { | ||
| // 뒤로갈 때 좋아요가 취소된 상태면 목록에 반영되도록 정보 반환 | ||
| if (isLikedVN.value == false) { | ||
| Navigator.of(context).pop(widget.itemId); | ||
| } else { | ||
| Navigator.of(context).pop(); | ||
| } | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for all ItemDetailDescriptionScreen navigation calls
echo "=== Searching for ItemDetailDescriptionScreen navigation calls ==="
rg -nP --type=dart 'ItemDetailDescriptionScreen|\.navigateTo.*ItemDetailDescription|Navigator.*ItemDetailDescription' -B2 -A2 -g '!**/item_detail_description_screen.dart'Repository: TEAM-ROMROM/RomRom-FE
Length of output: 4355
🏁 Script executed:
#!/bin/bash
# Search for navigateTo usage pattern in the codebase
echo "=== Checking for context.navigateTo implementation ==="
rg -nP --type=dart 'extension.*navigateTo|fun.*navigateTo' -A5Repository: TEAM-ROMROM/RomRom-FE
Length of output: 119
🏁 Script executed:
#!/bin/bash
# Find the navigateTo extension implementation
echo "=== Finding context.navigateTo() implementation ==="
rg -nP --type=dart 'extension.*on.*BuildContext|extension.*on.*Context' -A10 | grep -A10 'navigateTo'Repository: TEAM-ROMROM/RomRom-FE
Length of output: 877
🏁 Script executed:
#!/bin/bash
# Check each calling site for return value handling
echo "=== Checking home_feed_item_widget.dart line 130 ==="
sed -n '125,140p' lib/widgets/home_feed_item_widget.dart
echo -e "\n=== Checking chat_room_screen.dart lines 450-460 ==="
sed -n '450,465p' lib/screens/chat_room_screen.dart
echo -e "\n=== Checking home_tab_screen.dart lines 115-130 ==="
sed -n '115,130p' lib/screens/home_tab_screen.dart
echo -e "\n=== Checking register_tab_screen.dart lines 658-670 ==="
sed -n '658,670p' lib/screens/register_tab_screen.dart
echo -e "\n=== Checking my_like_list_screen.dart lines 205-220 ==="
sed -n '205,220p' lib/screens/my_page/my_like_list_screen.dartRepository: TEAM-ROMROM/RomRom-FE
Length of output: 3557
🏁 Script executed:
#!/bin/bash
# Check what happens after navigation in each calling location
echo "=== home_feed_item_widget.dart: what happens after Navigator.push (line 130) ==="
sed -n '127,155p' lib/widgets/home_feed_item_widget.dart
echo -e "\n=== chat_room_screen.dart: what happens after context.navigateTo (line 453) ==="
sed -n '450,475p' lib/screens/chat_room_screen.dart
echo -e "\n=== home_tab_screen.dart: what happens after context.navigateTo (line 120) ==="
sed -n '119,135p' lib/screens/home_tab_screen.dart
echo -e "\n=== request_management_tab_screen.dart: what happens after context.navigateTo (line 356) ==="
sed -n '354,375p' lib/screens/request_management_tab_screen.dart
echo -e "\n=== Check if result is used in register_tab_screen.dart (line 660) ==="
sed -n '658,685p' lib/screens/register_tab_screen.dart
echo -e "\n=== Check if result is used in my_like_list_screen.dart (line 209) ==="
sed -n '207,235p' lib/screens/my_page/my_like_list_screen.dartRepository: TEAM-ROMROM/RomRom-FE
Length of output: 6656
🏁 Script executed:
#!/bin/bash
# Check the full context.navigateTo() implementation
echo "=== Full context.navigateTo() implementation ==="
sed -n '13,80p' lib/utils/common_utils.dart
echo -e "\n=== Check ContextExtension definition ==="
sed -n '81,90p' lib/utils/common_utils.dartRepository: TEAM-ROMROM/RomRom-FE
Length of output: 2396
조건부 데이터 반환 처리 및 코딩 가이드라인 준수 필요
좋아요가 취소된 상태(isLikedVN.value == false)일 때 itemId를 반환하는 조건부 pop 동작은 일부 호출처에서만 제대로 처리되고 있습니다.
현황:
Navigator.push()를 직접 사용하는 3곳(home_feed_item_widget, register_tab_screen, my_like_list_screen)에서는 결과값을 awaiting하여 처리 중context.navigateTo()를 사용하는 3곳(chat_room_screen, home_tab_screen, request_management_tab_screen)에서는 결과값을 awaiting하지 않아 반환값을 받지 못함- 코딩 가이드라인에 따라 iOS 스와이프 제스처 지원을 위해 모든 화면 네비게이션에서
context.navigateTo()사용 필요
대응 방법:
chat_room_screen.dart(line 453),home_tab_screen.dart(line 120),request_management_tab_screen.dart(line 356, 622, 828)의context.navigateTo()호출에서 결과값을 await하여 처리home_feed_item_widget.dart(line 130),register_tab_screen.dart(line 660),my_like_list_screen.dart(line 209)의 직접Navigator.push()사용을context.navigateTo()로 변경
🤖 Prompt for AI Agents
In lib/screens/item_detail_description_screen.dart around lines 801-808 the pop
call conditionally returns widget.itemId when isLikedVN.value == false; callers
must await this value and use context.navigateTo() per guideline. Update the
following callers to await the navigation result and handle a returned itemId:
modify chat_room_screen.dart (line 453), home_tab_screen.dart (line 120), and
request_management_tab_screen.dart (lines 356, 622, 828) to await
context.navigateTo(...) and process the result; replace direct
Navigator.push(...) with context.navigateTo(...) in home_feed_item_widget.dart
(line 130), register_tab_screen.dart (line 660), and my_like_list_screen.dart
(line 209) so all navigations are uniform and return values are awaited.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
♻️ Duplicate comments (1)
lib/screens/my_page/my_profile_edit_screen.dart (1)
45-50: dispose() 메서드가 올바르게 추가되었습니다!이전 리뷰에서 지적된 메모리 누수 문제가 해결되었습니다.
nicknameController와nicknameFocusNode가 적절히 해제됩니다.
🧹 Nitpick comments (3)
lib/screens/my_page/my_profile_edit_screen.dart (3)
177-200: async/await와 then/catchError 패턴이 혼용되어 있습니다.
async함수 내에서.then()과.catchError()대신try-catch를 사용하면 코드가 더 일관되고 가독성이 좋아집니다.🔎 제안된 수정
onTap: () async { if (_isProfileEdited && (_nickname.isNotEmpty)) { - await MemberApi() - .updateMemberProfile(nicknameController.text, imageUrl) - .then((_) { - if (context.mounted) { - CommonSnackBar.show( - context: context, - message: '프로필이 성공적으로 업데이트되었습니다.', - type: SnackBarType.success, - ); - Navigator.of(context).pop(true); - } - }) - .catchError((e) { - if (context.mounted) { - CommonSnackBar.show( - context: context, - message: '프로필 업데이트에 실패했습니다: $e', - type: SnackBarType.error, - ); - } - }); + try { + await MemberApi().updateMemberProfile( + nicknameController.text, + imageUrl, + ); + if (context.mounted) { + CommonSnackBar.show( + context: context, + message: '프로필이 성공적으로 업데이트되었습니다.', + type: SnackBarType.success, + ); + Navigator.of(context).pop(true); + } + } catch (e) { + if (context.mounted) { + CommonSnackBar.show( + context: context, + message: '프로필 업데이트에 실패했습니다: $e', + type: SnackBarType.error, + ); + } + } } },
366-366: 화면 크기에 따른 스케일링이 적용되지 않은 값이 있습니다.
contentPadding에서EdgeInsets.all(8)대신EdgeInsets.all(8.w)를 사용하여 다른 값들과 일관성을 유지하세요.🔎 제안된 수정
- contentPadding: const EdgeInsets.all(8), + contentPadding: EdgeInsets.all(8.w),
434-434: 하드코딩된 오프셋 값에 스케일링이 누락되었습니다.
-14.0값에도.h스케일링을 적용하여 다양한 화면 크기에서 일관된 레이아웃을 유지하세요.🔎 제안된 수정
- offset: Offset(iconOffsetX, -14.0), + offset: Offset(iconOffsetX, -14.0.h),
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
lib/screens/my_page/my_profile_edit_screen.dart(7 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.dart
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.dart: Use CustomTextStyles for all text styling in Flutter widgets - never define TextStyle directly
Use AppColors for all color values - never use direct Color codes or Colors constants
Use the team's common extension methodcontext.navigateTo()for screen navigation to support iOS swipe gesture handling
Files:
lib/screens/my_page/my_profile_edit_screen.dart
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: 프로젝트 빌드 테스트
🔇 Additional comments (2)
lib/screens/my_page/my_profile_edit_screen.dart (2)
246-293: 프로필 이미지 섹션 구현이 잘 되었습니다!GestureDetector로 탭 이벤트 처리, 업로드 중 로딩 인디케이터 표시, UserProfileCircularAvatar 사용 등이 적절하게 구현되었습니다.
460-527: 정보 섹션과 좋아요 섹션이 잘 구현되었습니다!
CustomTextStyles와AppColors를 적절히 사용하고 있으며, 레이아웃이 일관되게 구성되어 있습니다.
| // 사용자가 취소했거나 선택 없음 | ||
| if (picked == null) { | ||
| debugPrint('프로필 이미지 변경 취소'); | ||
| return; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
사용자가 이미지 선택을 취소할 때 상태가 초기화되지 않습니다.
사용자가 갤러리에서 이미지를 선택하지 않고 취소하면, _hasImageBeenTouched와 _showProfileSaveButton이 true로 남아있게 됩니다. 이로 인해 UI 상태가 불일치할 수 있습니다.
🔎 제안된 수정
// 사용자가 취소했거나 선택 없음
if (picked == null) {
debugPrint('프로필 이미지 변경 취소');
+ setState(() {
+ _hasImageBeenTouched = false;
+ // _showProfileSaveButton은 다른 편집 여부에 따라 결정 필요
+ if (!_isProfileEdited) {
+ _showProfileSaveButton = false;
+ }
+ });
return;
}🤖 Prompt for AI Agents
In lib/screens/my_page/my_profile_edit_screen.dart around lines 94 to 98, when
image picking returns null the code returns early but does not reset
_hasImageBeenTouched and _showProfileSaveButton; update the null branch to set
those booleans to false (inside setState if within a StatefulWidget) before
returning so the UI reflects no change and the save button is hidden.
| child: nicknameFocusNode.hasFocus || _nickname.isEmpty | ||
| ? Column( | ||
| crossAxisAlignment: CrossAxisAlignment.start, | ||
| children: [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FocusNode 상태 변경 시 UI가 자동으로 업데이트되지 않을 수 있습니다.
nicknameFocusNode.hasFocus는 포커스 변경 시 자동으로 위젯을 다시 빌드하지 않습니다. onTap과 onTapOutside에서 setState를 호출하고 있지만, 다른 방식으로 포커스가 변경되면 (예: 시스템 이벤트, 다른 위젯 터치) UI가 업데이트되지 않을 수 있습니다.
🔎 제안된 수정 - initState에 리스너 추가
@override
void initState() {
super.initState();
_loadUserInfo();
+ nicknameFocusNode.addListener(_onFocusChange);
}
+ void _onFocusChange() {
+ setState(() {});
+ }
@override
void dispose() {
+ nicknameFocusNode.removeListener(_onFocusChange);
nicknameController.dispose();
nicknameFocusNode.dispose();
super.dispose();
}🤖 Prompt for AI Agents
In lib/screens/my_page/my_profile_edit_screen.dart around lines 325-328, using
nicknameFocusNode.hasFocus directly in build can miss updates when focus changes
outside onTap handlers; add a FocusNode listener in initState that calls
setState() when the focus changes and remove that listener in dispose (or call
nicknameFocusNode.removeListener) so the UI rebuilds reliably whenever focus
changes.
|
✅ 프로젝트 빌드 성공 APK 빌드가 완료되었습니다. |
#378
Summary by CodeRabbit
새 기능
디자인
개선
✏️ Tip: You can customize this high-level summary in your review settings.