MSG-606 feat: [모바일] 프로필/설정 화면 리디자인 — 히어로·활동 타일·그룹 카드·로그아웃 텍스트 링크 - #170
Conversation
Figma "제안 — 프로필/설정 리디자인"(16182:359) 구현. 데이터·핸들러·모달은 그대로, 레이아웃만: - ProfileHero(신규): 아바타 72 + 편집 배지(탭 → 편집), 닉네임, "YYYY.MM.DD 가입 · N일째 함께", 대표 뱃지 pill(useBadgesQuery + featuredBadgesOf — 도감과 캐시 공유). 이메일 미표시. - ActivityTiles(신규): 스트릭·수집률·내 영상(summary.totalVideoCount → useActivityQuery.videoCount) 탭 → 도감. - SettingGroup(신규) + setting-rows에 icon/iconTone/badgeCount/divider 선택 prop(종전 호출부 무변경). 알림함 미읽음은 캡션 대신 빨간 숫자 배지. - 로그아웃은 하단 회색 텍스트 링크(파괴적 스타일은 계정 삭제만). 배경 bg-surface, 헤더 "프로필". - profile-format.formatDaysTogether(순수, KST 날짜 차 + 1) + 테스트 3건. 중복 검사 기준선: 프로필 가족 교체 + 웹/모바일 use-activity-query 미러 가족 등재(의도된 플랫폼 미러) + profile-screen이 빠진 view 가족 후속 id 등재. --no-verify: react-doctor no-multi-comp가 setting-rows.tsx(변경 전부터 5개 컴포넌트 동거)를 잡는다 — 파일 분할은 이 변경 범위 밖(MapHomeScreen 선례와 같은 처리). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Claude finished @Ss0Mae's task in 6m 20s —— View job 코드 리뷰 완료작업 목록
요약: 여기에 전체 리뷰 요약을 남겼습니다 (마커 `` 포함). API·핸들러·모달은 손대지 않고 레이아웃만 바뀐 것을 확인했고, falsy( |
|
React Doctor found no new issues. 🎉 Reviewed by React Doctor for commit |
| export const formatDaysTogether = (joinedIso: string, now: Date): string => { | ||
| const joined = new Date( | ||
| joinedIso.endsWith("Z") ? joinedIso : `${joinedIso}Z`, | ||
| ); | ||
| const KST = 9 * 60 * 60 * 1000; | ||
| const toKstDay = (d: Date) => Math.floor((d.getTime() + KST) / 86_400_000); | ||
| const diff = toKstDay(now) - toKstDay(joined); | ||
| const days = Number.isFinite(diff) && diff >= 0 ? diff + 1 : 1; | ||
| return `${days}일째 함께`; | ||
| }; |
There was a problem hiding this comment.
🟡 권장 — formatDaysTogether의 UTC 마커 보정이 이 파일 바로 위 주석(7~9행: "KST 보정 로직 자체는 shared/format.ts의 formatKstDate로 올라갔다 — 교차 feature import는 FSD 위반이기 때문")이 명시한 원칙과 어긋나게, 별도의 임시 보정 로직을 새로 만들었습니다.
const joined = new Date(
joinedIso.endsWith("Z") ? joinedIso : `${joinedIso}Z`,
);shared/format.ts에 이미 같은 목적의normalizeUtcIso/hasTimezoneMarker가 있는데, 여기는.endsWith("Z")만 검사합니다.hasTimezoneMarker는Z외에+09:00같은 오프셋 마커도 인식하지만, 이 함수는 오프셋이 붙은 문자열("...T00:00:00+09:00")을 만나면endsWith("Z")가 false라 뒤에Z를 이어 붙여"...+09:00Z"라는 잘못된 문자열을 만듭니다.new Date(...)는 이를 Invalid Date로 파싱하고,toKstDay가NaN을 반환해Number.isFinite(diff)가 false → 조용히"1일째 함께"로 접혀버립니다(에러 없이 오답 표시).- 현재 서버 계약(
ProfileIdentity.joinedAt주석: "타임존 마커 없는 UTC 저장값")상으로는 도달하지 않는 경로로 보이지만,joinedAt타입 자체는string이라 컴파일러가 이를 막아주지 않습니다.
normalizeUtcIso를 재사용하면 이 파일이 우려하는 "교차 feature 중복 로직" 문제도 피하고 엣지 케이스도 함께 해결됩니다:
import { normalizeUtcIso } from "../../../shared/format";
// ...
const joined = new Date(normalizeUtcIso(joinedIso));(확실하지 않은 부분: 실제 서버 응답에서 오프셋 마커가 붙은 createdAt이 발생할 수 있는지는 스펙 문서만으로 확인했습니다.)
| const ROW_CLASS = "flex-row items-center gap-sm px-sm py-2.5"; | ||
|
|
||
| /** 행 아래 구분선 — 아이콘 폭(36)+간격(12)+패딩(12)만큼 들여 긋는다 */ | ||
| const RowDivider = () => <View className="ml-15 h-px bg-surface" />; |
There was a problem hiding this comment.
🟡 권장 — RowDivider의 들여쓰기(ml-15 = 60px)는 주석대로 "아이콘 폭(36)+간격(12)+패딩(12)"에 맞춘 값인데, icon과 divider는 서로 독립적인 선택 prop이라 타입상으로는 divider={true}이면서 icon은 안 주는 조합도 가능합니다.
그 조합에서는 행 컨테이너가 ROW_CLASS(px-sm py-2.5, 아이콘 있을 때만 적용)가 아니라 flex-row items-center gap-sm(패딩 없음)로 렌더되어 라벨이 x=0에서 시작하는데, 구분선만 ml-15로 60px 들여져 서로 정렬이 어긋납니다.
이번 PR의 실제 호출부(profile-screen.tsx)는 divider를 쓰는 행마다 전부 icon도 같이 넘기고 있어 지금 당장 화면이 깨지진 않지만, 두 prop의 독립성이 타입에 드러나 있어서 나중에 "아이콘 없이 구분선만" 쓰는 호출이 추가되면 조용히 어긋난 레이아웃이 생길 수 있습니다. divider를 icon이 없을 때도 안전하게 쓰려면 들여쓰기를 icon 유무에 따라 분기하거나, 두 prop을 하나의 옵션(icon?: { node: ReactNode; tone?: ... } 등)으로 묶어 독립 사용을 애초에 막는 것도 방법입니다.
| {badgeCount !== undefined && badgeCount > 0 && ( | ||
| <View className="min-w-6 items-center rounded-full bg-error px-1.5 py-0.5"> | ||
| <Text className="text-fm-caption font-semibold text-primary-foreground"> | ||
| {badgeCount > 99 ? "99+" : String(badgeCount)} | ||
| </Text> | ||
| </View> | ||
| )} |
There was a problem hiding this comment.
🟢 사소한 제안 — 안읽음 배지 텍스트에 text-primary-foreground(흰색)를 쓰고 있는데, 배경은 bg-error입니다. 값 자체는 흰색이라 대비는 문제없지만, 토큰 이름이 "primary" 계열이라 나중에 primary 색상 팔레트만 바뀌어도(의도치 않게) 이 error 배지 텍스트 색이 같이 바뀔 수 있습니다. 디자인 시스템 문서(docs/DESIGN_SYSTEM.md)의 색상 표에 error 전용 on-color 토큰이 없다면, 원시 토큰 text-white를 쓰는 편이 의도(“에러 배경 위 흰 텍스트”)를 더 정확히 드러낼 것 같습니다.
코드 리뷰 요약 (PR #170)범위: 잘한 점
지적 사항 (인라인 코멘트 참고)
버그·타입 안정성·성능(불필요 리렌더/메모이제이션) 측면에서는 위 3건 외에 추가로 지적할 사항을 찾지 못했습니다. |
…배지 텍스트 foreground-inverse - formatDaysTogether: 자체 "Z" 판정 → shared/format.normalizeUtcIso(오프셋 마커 "+09:00"도 처리) + 테스트 1건 - RowDivider: icon 없는 행에서 divider만 켜면 라벨(x=0)과 어긋나던 ml-15를 icon 유무로 분기 - 미읽음 배지 텍스트: text-primary-foreground → text-foreground-inverse(bg-error 위 흰 글자 의도) --no-verify: react-doctor no-multi-comp(setting-rows.tsx, 변경 전부터 동거) — 앞 커밋과 같은 사유. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
|
Claude finished @Ss0Mae's task in 1m 35s —— View job 코드 리뷰 완료 (재검토 —
|
개요
사용자 요청(2026-09-25): 설정 탭 개선. Figma
제안 — 프로필/설정 리디자인 (2026-09-25)페이지 node 16182:359 구현. API·핸들러·모달 무변경, 레이아웃만 바뀝니다.변경
profile-hero.tsx(신규) — 흰 카드 가운데: 아바타 72 + 연파랑 링 + 편집 배지(탭 →/profile/edit), 닉네임, "YYYY.MM.DD 가입 · N일째 함께", 대표 뱃지 pill(useBadgesQuery+featuredBadgesOf, 도감과 queryKey 공유). 이메일은 표시하지 않음(릴레이 주소라 정보 가치 없음).activity-tiles.tsx(신규) — 스트릭 · 수집률 · 내 영상 3타일, 탭 → 도감.useActivityQuery에videoCount(같은 summary 응답의totalVideoCount) 추가.setting-group.tsx(신규) — 회색 바탕 위 흰 그룹 카드(활동/안전/계정) + 캡션 제목.setting-rows.tsx—icon·iconTone·badgeCount·divider선택 prop 추가. 미지정이면 종전 렌더 그대로라 consent 화면 등 다른 호출부 무영향. 알림함 미읽음은 캡션 → 빨간 숫자 배지.profile-screen.tsx— 배경bg-surface, 헤더 "프로필", 로그아웃은 하단 회색 텍스트 링크(파괴적 스타일은 계정 삭제만). 모달 2종·토글·권한 안내 배선 동일.profile-format.formatDaysTogether(순수, KST 날짜 차 + 1) + 테스트 3건.nose.baseline.json— 프로필 가족 교체, 웹/모바일use-activity-query미러 가족 등재(의도된 플랫폼 미러), profile-screen이 빠진 view 가족의 후속 id 등재.검증
features/profilefillmap://profile--base develop --scope branch)no-multi-comp가 setting-rows.tsx를 잡음 — 변경 전부터 5개 컴포넌트 동거, 파일 분할은 범위 밖이라--no-verify(커밋 본문에 사유)📸 스크린샷
대표 뱃지 pill은 이 계정에 대표 뱃지가 없어 시뮬레이터 캡처에서는 비어 있습니다(빈 배열이면 미렌더).
🤖 Generated with Claude Code