Conversation
Walkthrough가족 걸음 수 화면에서 구성원 프로필 이미지 해상 로직을 교체. 기존 단일 PrefsManager 조회를 제거하고, 사용자 uuid→userId→이름→현재 사용자→기본 이미지(0) 순으로 조회하는 비동기 헬퍼를 추가·호출. 나머지 데이터 바인딩은 동일하며 일부 서식만 변경. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as User
participant S as FamilyStepTrackerScreen
participant H as _getProfileImageForUser
participant P as PrefsManager
participant FM as FamilyMembers
participant CU as CurrentUser
participant A as DefaultImage(0)
U->>S: 화면 로드/리스트 렌더
loop 각 가족 구성원
S->>H: getProfileImageForUser(userId, userName, familyMembers)
H->>FM: uuid로 매칭 시도
alt uuid 매칭 성공
H-->>S: 이미지 경로
else uuid 불일치
H->>P: userId로 조회 시도
alt userId 매칭 성공
H-->>S: 이미지 경로
else userId 불일치
H->>FM: 이름으로 매칭 시도
alt 이름 매칭 성공
H-->>S: 이미지 경로
else 이름 불일치
H->>CU: 현재 사용자 정보 확인
alt 현재 사용자 매칭 성공
H-->>S: 이미지 경로
else 매칭 실패/오류
H->>A: 기본 이미지 선택
H-->>S: 기본 이미지 경로
end
end
end
end
end
S-->>U: 프로필 이미지 포함된 리스트 표시
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Caution
Changes requested ❌
Reviewed everything up to 2560f5a in 1 minute and 17 seconds. Click for details.
- Reviewed
143lines of code in1files - Skipped
0files when reviewing. - Skipped posting
2draft comments. View those below. - Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. frontend/ongi/lib/screens/health/family_step_tracker_screen.dart:139
- Draft comment:
Ensure 'firstOrNull' is available (e.g. via an extension or package import) as it's not standard in Dart. - Reason this comment was not posted:
Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 10% vs. threshold = 50% 1. firstOrNull is part of Dart's core library since Dart 2.15. 2. Most Flutter projects today use recent Dart versions. 3. If firstOrNull wasn't available, the code wouldn't compile. 4. The comment is asking for verification rather than pointing out a clear issue. 5. The comment violates the rule about not asking for verifications or double-checks. I could be wrong about which version of Dart this project uses - maybe it's using an older version where firstOrNull isn't available. Even if using an older Dart version, the build would fail immediately if firstOrNull wasn't available, making this comment unnecessary. The author would discover this without needing a comment. Delete this comment. It violates the rule about not asking for verifications, and any issues with firstOrNull would be caught by the build system.
2. frontend/ongi/lib/screens/health/family_step_tracker_screen.dart:173
- Draft comment:
Consider caching the currentUserInfo to avoid repeated async calls inside a loop. - Reason this comment was not posted:
Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 30% vs. threshold = 50% The comment has merit since getUserInfo() is an async call that could be expensive if it hits storage. However, this is only called when other matching attempts fail, not on every iteration. The optimization would be minor and the code is already quite complex. The current implementation favors clarity over minor performance gains. The comment identifies a real potential performance issue. Multiple async calls in a loop can be problematic. However, this is a fallback case that likely rarely executes, and the performance impact is probably minimal. The added complexity of caching may not be worth it. While technically correct, this optimization suggestion is likely premature and would add complexity for minimal benefit. The comment should be removed.
Workflow ID: wflow_u7tLqZ3U9oGHjShq
You can customize by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.
|
|
||
| return PrefsManager.getProfileImagePath(0); | ||
| } catch (e) { | ||
| return PrefsManager.getProfileImagePath(0); |
There was a problem hiding this comment.
Log the error in the catch block for better debugging instead of silently returning the default image.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (4)
frontend/ongi/lib/screens/health/family_step_tracker_screen.dart (4)
93-99: 루프 내 await로 인한 N회 I/O 발생: 현재 사용자 정보 캐싱 전달 권장각 멤버마다
_getProfileImageForUser에서 SharedPreferences(추정) 접근이 반복될 수 있습니다. 루프 시작 전에 현재 사용자 정보를 한 번만 조회·캐싱하여 헬퍼에 전달하는 방식이 효율적입니다. 아래처럼 헬퍼에currentUserInfo를 넘기도록 인터페이스를 확장하세요.- String profileImagePath = await _getProfileImageForUser( + String profileImagePath = await _getProfileImageForUser( userId, userName, familyMembers, - ); + currentUserInfo: currentUserInfo, + );루프 진입 전 한 번만 가져와 전달(참고용 보조 코드, 파일 다른 위치 수정 필요):
// for (final dynamic item in members) { ... } 바로 위 final Map<String, dynamic>? currentUserInfo = await PrefsManager.getUserInfo();
330-337: 숫자 천 단위 포매팅 중복/정규식 사용 → intl NumberFormat으로 단일 헬퍼화 권장정규식 포매팅은 가독성이 낮고 중복됩니다.
intl의NumberFormat.decimalPattern('ko')을 사용해 헬퍼로 치환하면 명확하고 재사용 가능합니다.- : _totalSteps - .toString() - .replaceAllMapped( - RegExp( - r'(\d{1,3})(?=(\d{3})+(?!\d))', - ), - (m) => '${m[1]},', - ) + - '걸음', + : '${_formatDecimal(_totalSteps)}걸음',보조 코드(파일 다른 위치):
import 'package:intl/intl.dart'; // State 클래스 내부 예: final NumberFormat _numberFormat = NumberFormat.decimalPattern('ko'); String _formatDecimal(int n) => _numberFormat.format(n);
512-515: 숫자 포매팅 로직 중복(아래 영역)도 동일 헬퍼로 치환위에서 제안한
_formatDecimal을 재사용하여 UI 전반의 일관성을 높이세요.- steps.toString().replaceAllMapped( - RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'), - (m) => '${m[1]},', - ), + _formatDecimal(steps),
132-186: 자동 필드 키/타입 검증 도구 도입 제안 (선택적 권장)현재 코드베이스에서 아래 네 가지 필드는 모두 일관된 타입으로 사용되고 있음을 확인했습니다.
uuid,userId,name→ StringprofileImageId→ int주요 사용처
• 모델:User.fromJson(maum_log.dart) 에서profileImageId: json['profileImageId'] ?? 0• 저장소:
PrefsManager.saveProfileImageId(int),getProfileImageId()
• 서비스:FamilyService.getFamilyMembers()→ 반환된Map<String, dynamic>에서
['uuid'],['userId'],['name'],['profileImageId']
• 화면:family_step_tracker_screen.dart등에서
member['uuid']?.toString(),member['profileImageId'] as int등으로 안전하게 처리런타임 예외 가능성은 낮지만, CI 파이프라인이나 빌드 단계에서 아래와 같은 자동 검증 스크립트나 로직을 추가하면 더 높은 안정성을 확보할 수 있습니다.
rg/ast-grep기반 필드 키·타입 스캔json_serializable등 코드 생성 기반 파서 도입- Map 구조 유효성 확인을 위한 유닛 테스트
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
frontend/ongi/lib/screens/health/family_step_tracker_screen.dart(7 hunks)
🔇 Additional comments (1)
frontend/ongi/lib/screens/health/family_step_tracker_screen.dart (1)
413-413: 왕관 표시 조건식 명료하고 안전합니다최상위이면서 0보다 큰 경우만 왕관을 보여주는 조건은 의도에 부합합니다.
| final Map<String, dynamic>? stepResult = | ||
| results[0] as Map<String, dynamic>?; | ||
| final List<Map<String, dynamic>> familyMembers = | ||
| results[1] as List<Map<String, dynamic>>; | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
비동기 이후 mounted 체크 누락 + Future.wait 결과 캐스팅 안전성 강화 필요
await 이후 위젯이 dispose되면 아래 setState 구간에서 예외가 발생할 수 있습니다. 또한 results[1] 캐스팅은 null/타입 변동 시 런타임 오류 위험이 있습니다. mounted 가드와 안전 캐스팅을 추가하세요.
- final results = await Future.wait(futures);
- final Map<String, dynamic>? stepResult =
- results[0] as Map<String, dynamic>?;
- final List<Map<String, dynamic>> familyMembers =
- results[1] as List<Map<String, dynamic>>;
+ final results = await Future.wait<dynamic>(futures);
+ if (!mounted) return;
+ final Map<String, dynamic>? stepResult =
+ results[0] as Map<String, dynamic>?;
+ final List<Map<String, dynamic>> familyMembers =
+ (results[1] as List?)?.cast<Map<String, dynamic>>() ??
+ const <Map<String, dynamic>>[];추가로, futures도 제네릭을 명시하면 캐스팅이 더 줄어듭니다(권장, 별도 변경):
- final futures = <Future>[ ... ];
🤖 Prompt for AI Agents
In frontend/ongi/lib/screens/health/family_step_tracker_screen.dart around lines
67 to 71, after awaiting Future.wait you must check mounted before calling
setState and make the Future.wait result casts safe: replace direct casts with
null-aware and type-checked extraction (e.g., verify results[0] is Map<String,
dynamic>? and results[1] is List and map its items to Map<String,dynamic> or
fallback to an empty list) and return early if values are invalid; add an if
(!mounted) return; guard immediately after the await and before any setState
call. Optionally, declare the futures with explicit generics (e.g.,
<Future<dynamic>> or more specific types) to reduce unsafe casts.
| Future<String> _getProfileImageForUser( | ||
| String userId, | ||
| String userName, | ||
| List<Map<String, dynamic>> familyMembers, | ||
| ) async { | ||
| try { | ||
| // 1. userId로 매칭 시도 (uuid 필드와 비교) | ||
| final memberByUuid = familyMembers | ||
| .where((member) => member['uuid']?.toString() == userId) | ||
| .firstOrNull; | ||
|
|
||
| if (memberByUuid != null && memberByUuid['profileImageId'] != null) { | ||
| return PrefsManager.getProfileImagePath( | ||
| memberByUuid['profileImageId'] as int, | ||
| ); | ||
| } | ||
|
|
||
| // 2. userId 필드와 직접 비교 | ||
| final memberByUserId = familyMembers | ||
| .where((member) => member['userId']?.toString() == userId) | ||
| .firstOrNull; | ||
|
|
||
| if (memberByUserId != null && memberByUserId['profileImageId'] != null) { | ||
| return PrefsManager.getProfileImagePath( | ||
| memberByUserId['profileImageId'] as int, | ||
| ); | ||
| } | ||
|
|
||
| // 3. userName으로 매칭 시도 | ||
| if (userName.isNotEmpty) { | ||
| final memberByName = familyMembers | ||
| .where((member) => member['name']?.toString() == userName) | ||
| .firstOrNull; | ||
|
|
||
| if (memberByName != null && memberByName['profileImageId'] != null) { | ||
| return PrefsManager.getProfileImagePath( | ||
| memberByName['profileImageId'] as int, | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| final currentUserInfo = await PrefsManager.getUserInfo(); | ||
| final currentUserUuid = currentUserInfo['uuid']; | ||
| final currentUserName = currentUserInfo['name']; | ||
|
|
||
| if (userId == currentUserUuid || userName == currentUserName) { | ||
| final profileImageId = currentUserInfo['profileImageId'] ?? 0; | ||
| return PrefsManager.getProfileImagePath(profileImageId); | ||
| } | ||
|
|
||
| return PrefsManager.getProfileImagePath(0); | ||
| } catch (e) { | ||
| return PrefsManager.getProfileImagePath(0); | ||
| } | ||
| } |
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
firstOrNull 사용으로 컴파일 실패 가능 + null/타입 안전성 및 비교 로직 보완 필요
- Dart SDK 기본 Iterable에는
firstOrNull이 없으며(프로젝트에package:collection의존/임포트 없으면 컴파일 오류), 안전하게는firstWhereOrNull(+ import)이 필요합니다. profileImageId as int는 API가 문자열을 반환하면 런타임 예외가 납니다. 안전 파싱으로 교체하세요.PrefsManager.getUserInfo()가 null/비맵인 경우를 가드해야 합니다.- 이름 비교는 공백/대소문자 차이를 최소화하도록 trim 비교 권장.
- 현재 사용자 조회를 매 호출마다 하지 않도록, 선택 인자로 전달 가능하게 인터페이스 확장 권장(위 93-99 코멘트와 연계).
아래처럼 보완을 제안합니다.
- Future<String> _getProfileImageForUser(
- String userId,
- String userName,
- List<Map<String, dynamic>> familyMembers,
- ) async {
+ Future<String> _getProfileImageForUser(
+ String userId,
+ String userName,
+ List<Map<String, dynamic>> familyMembers, {
+ Map<String, dynamic>? currentUserInfo,
+ }) async {
try {
- // 1. userId로 매칭 시도 (uuid 필드와 비교)
- final memberByUuid = familyMembers
- .where((member) => member['uuid']?.toString() == userId)
- .firstOrNull;
+ // 안전 파싱 헬퍼
+ int? _toInt(dynamic v) {
+ if (v is int) return v;
+ return int.tryParse(v?.toString() ?? '');
+ }
+
+ // 1. userId로 매칭 시도 (uuid 필드와 비교)
+ final memberByUuid = familyMembers.firstWhereOrNull(
+ (member) => member['uuid']?.toString() == userId,
+ );
- if (memberByUuid != null && memberByUuid['profileImageId'] != null) {
- return PrefsManager.getProfileImagePath(
- memberByUuid['profileImageId'] as int,
- );
+ final uuidImgId = _toInt(memberByUuid?['profileImageId']);
+ if (uuidImgId != null) {
+ return PrefsManager.getProfileImagePath(uuidImgId);
}
- // 2. userId 필드와 직접 비교
- final memberByUserId = familyMembers
- .where((member) => member['userId']?.toString() == userId)
- .firstOrNull;
+ // 2. userId 필드와 직접 비교
+ final memberByUserId = familyMembers.firstWhereOrNull(
+ (member) => member['userId']?.toString() == userId,
+ );
- if (memberByUserId != null && memberByUserId['profileImageId'] != null) {
- return PrefsManager.getProfileImagePath(
- memberByUserId['profileImageId'] as int,
- );
+ final userIdImgId = _toInt(memberByUserId?['profileImageId']);
+ if (userIdImgId != null) {
+ return PrefsManager.getProfileImagePath(userIdImgId);
}
// 3. userName으로 매칭 시도
if (userName.isNotEmpty) {
- final memberByName = familyMembers
- .where((member) => member['name']?.toString() == userName)
- .firstOrNull;
+ final nameTrim = userName.trim();
+ final memberByName = familyMembers.firstWhereOrNull(
+ (member) => member['name']?.toString().trim() == nameTrim,
+ );
- if (memberByName != null && memberByName['profileImageId'] != null) {
- return PrefsManager.getProfileImagePath(
- memberByName['profileImageId'] as int,
- );
+ final nameImgId = _toInt(memberByName?['profileImageId']);
+ if (nameImgId != null) {
+ return PrefsManager.getProfileImagePath(nameImgId);
}
}
- final currentUserInfo = await PrefsManager.getUserInfo();
- final currentUserUuid = currentUserInfo['uuid'];
- final currentUserName = currentUserInfo['name'];
+ final info = currentUserInfo ?? await PrefsManager.getUserInfo();
+ if (info is Map<String, dynamic>) {
+ final currentUserUuid = info['uuid']?.toString();
+ final currentUserName = info['name']?.toString();
- if (userId == currentUserUuid || userName == currentUserName) {
- final profileImageId = currentUserInfo['profileImageId'] ?? 0;
- return PrefsManager.getProfileImagePath(profileImageId);
+ if (userId == currentUserUuid ||
+ (userName.isNotEmpty &&
+ userName.trim() == currentUserName?.trim())) {
+ final profileImageId = _toInt(info['profileImageId']) ?? 0;
+ return PrefsManager.getProfileImagePath(profileImageId);
+ }
}
return PrefsManager.getProfileImagePath(0);
} catch (e) {
return PrefsManager.getProfileImagePath(0);
}
}보조 변경(파일 다른 위치, 임포트 추가 필요):
// 파일 상단 임포트 목록에 추가
import 'package:collection/collection.dart';스키마 확인 요청: FamilyService.getFamilyMembers()가 실제로 uuid, userId, name, profileImageId 키를 반환하는지, 그리고 타입(int/str) 일관성이 있는지 점검 부탁드립니다.
다음 스크립트를 실행하여 firstOrNull 사용 여부, collection 임포트, PrefsManager.getUserInfo 시그니처 등을 확인해주세요:
이 결과를 바탕으로 리뷰 코멘트의 타당성을 최종 검증하고, 필요한 경우 수정 제안을 반영하겠습니다.
🏁 Script executed:
#!/bin/bash
# 1. collection 패키지 임포트 확인
rg -n "import .*collection" frontend/ongi/lib/screens/health/family_step_tracker_screen.dart
# 2. firstOrNull 사용 위치 확인
rg -n "firstOrNull" frontend/ongi/lib/screens/health/family_step_tracker_screen.dart
# 3. firstWhereOrNull 사용 여부 확인
rg -n "firstWhereOrNull" frontend/ongi/lib/screens/health/family_step_tracker_screen.dart
# 4. getUserInfo 호출 위치 확인
rg -n "getUserInfo" frontend/ongi/lib/screens/health/family_step_tracker_screen.dart
# 5. PrefsManager 클래스 정의 및 getUserInfo 시그니처 확인
rg -n "class PrefsManager" -n .
rg -n "getUserInfo" -n .Length of output: 2859
다음 스크립트를 실행하여 PrefsManager.getUserInfo의 구현을 확인해주세요:
🏁 Script executed:
#!/bin/bash
# getUserInfo 구현 확인 (75~115행)
sed -n '75,115p' frontend/ongi/lib/utils/prefs_manager.dartLength of output: 1496
다음 스크립트를 실행하여 FamilyService.getFamilyMembers()의 반환 스키마와 타입 일관성을 확인해 주세요:
🏁 Script executed:
#!/bin/bash
# 1. getFamilyMembers가 정의된 파일 경로 찾기
family_file=$(rg -l "getFamilyMembers" -n frontend/ongi/lib/services | head -n 1)
echo "Found file: $family_file"
# 2. 함수 정의 및 반환부 확인 (앞뒤 컨텍스트 포함)
sed -n '1,200p' "$family_file" | sed -n '/getFamilyMembers/,/}/p'Length of output: 579
다음 스크립트를 실행하여 FamilyService.getFamilyMembers()의 전체 구현 및 반환 스키마를 확인해주세요:
🏁 Script executed:
#!/bin/bash
# 전체 라인 수 확인
wc -l frontend/ongi/lib/services/family_service.dart
# 파일 앞부분 확인 (1~200행)
sed -n '1,200p' frontend/ongi/lib/services/family_service.dart
# 구현부 뒷부분 확인 (200~400행)
sed -n '200,400p' frontend/ongi/lib/services/family_service.dartLength of output: 2107
firstOrNull 미사용으로 인한 컴파일 오류 및 타입·안전성 보강 필요
아래 항목을 우선적으로 적용해 주세요:
firstOrNull호출은 Dart 기본 Iterable에 없으므로
•package:collection을 임포트하거나
•Iterable.firstWhereOrNull로 교체
(파일:frontend/ongi/lib/screens/health/family_step_tracker_screen.dart, 141·152·164행)member['profileImageId'] as int캐스팅은 API가 문자열을 반환할 경우 런타임 예외 발생 우려
•int.tryParse(v?.toString() ?? '')기반 안전 파싱 헬퍼 사용 권장PrefsManager.getUserInfo()는 항상 Map<String,dynamic>을 반환하지만 내부 값(name,uuid,profileImageId)이null일 수 있으니
• 값이null일 때 기본값 또는 예외 처리를 추가userName비교 시 불필요한 공백·대소문자 차이 방지를 위해
•userName.trim()(필요 시toLowerCase()) 사용- 매 호출마다 사용자 정보를 다시 조회하지 않도록
•currentUserInfo를 선택적 파라미터로 받아 재사용하도록 인터페이스 확장 검토 - API 응답 스키마(
uuid,userId,name,profileImageId)의 키 존재 여부 및 타입(int vs. String) 일관성 확인 요청
🤖 Prompt for AI Agents
In frontend/ongi/lib/screens/health/family_step_tracker_screen.dart around lines
132 to 186, the code uses Iterable.firstOrNull (not in Dart core), unsafely
casts profileImageId with `as int`, re-fetches user info each call, and does raw
userName comparisons; replace firstOrNull by importing package:collection or use
firstWhere((c) => ..., orElse: () => null)/firstWhereOrNull, parse
profileImageId robustly via int.tryParse(member['profileImageId']?.toString() ??
'') with a safe fallback (0), guard currentUserInfo fields with defaults before
using them (e.g., uuid/name/profileImageId), normalize userName using trim()
(and toLowerCase() if case-insensitive), and consider changing the function
signature to accept an optional currentUserInfo to avoid repeated
PrefsManager.getUserInfo() calls; finally, validate the API response keys/types
(uuid, userId, name, profileImageId) and handle missing or string-typed ids
consistently.
Important
Enhances user profile image matching in
FamilyStepTrackerScreenusinguserId,userName, and family data._getProfileImageForUserinfamily_step_tracker_screen.dartto enhance user profile image matching.userId,userName, and family member data._fetchStepsForDateto use new image matching logic for displaying user icons.family_step_tracker_screen.dart.This description was created by
for 2560f5a. You can customize this summary. It will automatically update as commits are pushed.
Summary by CodeRabbit