-
Notifications
You must be signed in to change notification settings - Fork 1
Feat(client): 대시보드 레이아웃 구현 #66
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
Conversation
WalkthroughRemind/MyBookmark을 정적 UI에서 상태 기반 상호작용 UI로 변경하고 Badge를 제어형으로 전환했으며 REMIND_MOCK_DATA로 Card 목록을 렌더링하고 Layout 구조를 flex + 스크롤 가능한 main으로 재구성하고 scrollbar-hide 유틸을 추가했습니다. Changes
Sequence Diagram(s)sequenceDiagram
actor U as 사용자
participant RP as RemindPage
participant B1 as Badge("안 읽음")
participant B2 as Badge("읽음")
participant DS as REMIND_MOCK_DATA
participant UI as CardList
U->>RP: 페이지 접근
RP->>DS: REMIND_MOCK_DATA 읽기
RP->>UI: Card 목록 렌더(데이터 바인딩)
RP->>B1: props { isActive, onClick }
RP->>B2: props { isActive, onClick }
U->>B2: 클릭
B2->>RP: onClick()
RP->>RP: setState(activeBadge)
RP->>UI: 재렌더(선택 반영)
note right of RP: 서버 필터링은 TODO(현재 로컬 상태만 반영)
sequenceDiagram
participant Parent as 상위 컴포넌트
participant Badge as Badge(Controlled)
Parent->>Badge: props { text, countNum, isActive, onClick }
Note right of Badge: 내부 상태 제거 → 이벤트는 부모로 전달
U->>Badge: 클릭 이벤트
Badge->>Parent: onClick()
Parent->>Parent: isActive 업데이트(setState)
Parent->>Badge: updated props (re-render)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks (3 passed, 2 warnings)❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
Poem
✨ 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. Comment |
|
✅ Storybook chromatic 배포 확인: |
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 (7)
packages/tailwind-config/shared-styles.css (1)
161-169: scrollbar-hide는 Tailwind 레이어(@layer utilities) 안에 선언 권장Tailwind 빌드/정렬 우선순위 및 purge 안정성을 위해 유틸리티는 @layer utilities 안에 두는 것이 안전합니다. 아래처럼 감싸 주세요.
- .scrollbar-hide::-webkit-scrollbar { - display: none; - } - - /* 기타 브라우저용 (IE, Edge 구버전, Firefox) */ - .scrollbar-hide { - -ms-overflow-style: none; /* IE and Edge */ - scrollbar-width: none; /* Firefox */ - } +@layer utilities { + .scrollbar-hide::-webkit-scrollbar { + display: none; + } + + /* 기타 브라우저용 (IE, Edge 구버전, Firefox) */ + .scrollbar-hide { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ + } +}apps/client/src/pages/remind/constants/index.ts (1)
108-113: 15/16번 항목이 동일한 콘텐츠로 중복됨의도된 중복이 아니라면 샘플 다양성을 위해 하나를 변경하는 편이 좋습니다.
{ id: 16, - title: '팀 회의록 정리', - content: '어제 진행된 스프린트 계획 회의 내용 정리해서 공유', + title: '팀 회의록 최종 검토', + content: '피드백 반영 후 최종본 정리 및 공유', timeRemaining: '완료', category: '업무', },packages/design-system/src/components/badge/Badge.tsx (1)
5-6: onClick을 필수로 두면 비대화형 배지 재사용성이 떨어집니다HTMLButton 속성을 확장하고 onClick을 선택으로 두면 범용성이 올라갑니다. 위 diff와 함께 적용해 주세요.
-import { cva } from 'class-variance-authority'; +import { cva } from 'class-variance-authority'; +import type { ButtonHTMLAttributes } from 'react'; -export interface BadgeProps { +export interface BadgeProps extends ButtonHTMLAttributes<HTMLButtonElement> { text: string; countNum?: number; - isActive: boolean; - onClick: () => void; + isActive?: boolean; }apps/client/src/pages/remind/Remind.tsx (4)
6-10: activeBadge에 리터럴 유니온 타입 적용으로 오타 방지문자열 상수 대신 유니온 타입으로 제약하면 안전합니다.
- const [activeBadge, setActiveBadge] = useState('notRead'); + type BadgeType = 'notRead' | 'read'; + const [activeBadge, setActiveBadge] = useState<BadgeType>('notRead'); - const handleBadgeClick = (badgeType: string) => { + const handleBadgeClick = (badgeType: BadgeType) => { setActiveBadge(badgeType); };
16-27: 배지 카운트 하드코딩 — 데이터 기반으로 계산하도록 후속 처리API 연동 시 읽음/안읽음 플래그를 기준으로 countNum을 계산하도록 TODO 남겨두는 것이 좋습니다.
원하시면 mock에 isRead 필드를 추가해 필터링/카운팅 로직까지 같이 제안드릴게요.
30-42: 스크롤바 완전 숨김 사용은 접근성/발견성 저하 우려가능하면 기본은 .ds-scrollbar를 쓰고, 캐러셀 등 제한 케이스에만 .scrollbar-hide를 적용하세요. 이미 .ds-scrollbar 유틸이 있으므로 우선 적용 검토를 권장합니다.
32-32: 미사용 변수(index) 제거하여 lint 경고 해결map 콜백에서 index를 쓰지 않습니다. 제거해 주세요.
- {REMIND_MOCK_DATA.map((data, index) => ( + {REMIND_MOCK_DATA.map((data) => ( <Card key={data.id} type="remind" title={data.title} content={data.content} timeRemaining={data.timeRemaining} category={data.category} /> ))}
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/client/src/pages/remind/Remind.tsx(1 hunks)apps/client/src/pages/remind/constants/index.ts(1 hunks)packages/design-system/src/components/badge/Badge.tsx(1 hunks)packages/tailwind-config/shared-styles.css(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#2
File: pnpm-workspace.yaml:3-3
Timestamp: 2025-08-18T13:48:59.065Z
Learning: constantly-dev는 docs 디렉터리를 컨벤션 문서 추가용으로 사용할 예정이라고 명시했습니다.
🧬 Code graph analysis (1)
apps/client/src/pages/remind/Remind.tsx (2)
packages/design-system/src/components/card/Card.stories.tsx (1)
Remind(42-53)apps/client/src/pages/remind/constants/index.ts (1)
REMIND_MOCK_DATA(1-114)
🪛 GitHub Check: lint
apps/client/src/pages/remind/Remind.tsx
[warning] 32-32:
'index' is defined but never used
🔇 Additional comments (3)
apps/client/src/pages/remind/constants/index.ts (1)
1-114:satisfies를 활용한 목데이터 타입 안전성 적용 가능 확인됨
루트와 design-system 패키지의 TypeScript 버전이 모두 5.9.2로satisfies사용이 가능합니다. 제안된RemindItem타입 정의 및export const REMIND_MOCK_DATA satisfies ReadonlyArray<RemindItem> = […] as const;변경을 적용하세요.packages/design-system/src/components/badge/Badge.tsx (1)
21-34: CVA variant를 active로 통일한 점 깔끔합니다텍스트/카운트 스타일 모두 동일 키로 제어되어 유지보수가 용이합니다.
apps/client/src/pages/remind/Remind.tsx (1)
1-4: PR 제목(대시보드 레이아웃)과 변경 내용(Remind 페이지/Badge/공용 스타일)이 불일치리뷰어/릴리즈 노트 혼선을 줄이기 위해 제목 또는 PR 범위를 정렬해 주세요. 필요 시 Remind 관련 변경은 별도 PR로 분리 권장.
| const Badge = ({ text, countNum, isActive, onClick }: BadgeProps) => { | ||
| return ( | ||
| <div | ||
| className="flex cursor-pointer items-center justify-center gap-[0.8rem]" | ||
| onClick={() => setIsClick(true)} | ||
| onClick={onClick} | ||
| > | ||
| <span className={BadgeTxtStyleVariants({ click: isClick })}>{text}</span> | ||
| <span className={BadgeStyleVariants({ click: isClick })}>{countNum}</span> | ||
| <span className={BadgeTxtStyleVariants({ active: isActive })}> | ||
| {text} | ||
| </span> | ||
| <span className={BadgeStyleVariants({ active: isActive })}> | ||
| {countNum} | ||
| </span> |
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.
🛠️ Refactor suggestion
div+onClick은 키보드 접근 불가 — 네이티브 button으로 교체 필요
현재 구조는 스크린리더/키보드 사용자가 접근하기 어렵습니다. design-system 컴포넌트이므로 초기에 바로잡는 게 좋습니다.
-const Badge = ({ text, countNum, isActive, onClick }: BadgeProps) => {
+const Badge = ({ text, countNum, isActive = false, onClick, ...rest }: BadgeProps) => {
return (
- <div
- className="flex cursor-pointer items-center justify-center gap-[0.8rem]"
- onClick={onClick}
- >
+ <button
+ type="button"
+ aria-pressed={isActive}
+ onClick={onClick}
+ className="flex cursor-pointer items-center justify-center gap-[0.8rem] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-main500 focus-visible:ring-offset-2"
+ {...rest}
+ >
<span className={BadgeTxtStyleVariants({ active: isActive })}>
{text}
</span>
<span className={BadgeStyleVariants({ active: isActive })}>
{countNum}
</span>
- </div>
+ </button>
);
};🤖 Prompt for AI Agents
In packages/design-system/src/components/badge/Badge.tsx around lines 36 to 47,
the interactive element is a div with an onClick which is not
keyboard-accessible or semantic; replace the outer div with a native <button>
element, move the className and onClick onto that button, add type="button" to
avoid form submit behavior, preserve styling (including focus/hover states
instead of cursor-pointer), and ensure any needed ARIA attributes/keyboard
handlers are removed because the native button provides accessibility by
default.
e0b98cf to
84faa8d
Compare
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: 0
♻️ Duplicate comments (1)
packages/design-system/src/components/badge/Badge.tsx (1)
36-49: div+onClick → 네이티브 button으로 교체 (키보드 접근성/의미론)
현재 div에 onClick을 사용해 포커스/키보드 접근이 불가합니다. design-system 컴포넌트이므로 버튼 요소로 전환하고 aria-pressed 및 포커스 링을 제공합니다. 이전 커멘트와 동일 이슈입니다.적용 제안(diff):
-const Badge = ({ text, countNum, isActive, onClick }: BadgeProps) => { +const Badge = ({ text, countNum, isActive = false, onClick }: BadgeProps) => { return ( - <div - className="flex cursor-pointer items-center justify-center gap-[0.8rem]" - onClick={onClick} - > - <span className={BadgeTxtStyleVariants({ active: isActive })}> - {text} - </span> - <span className={BadgeStyleVariants({ active: isActive })}> - {countNum} - </span> - </div> + <button + type="button" + aria-pressed={isActive} + onClick={onClick} + className="flex items-center justify-center gap-[0.8rem] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-main500 focus-visible:ring-offset-2" + > + <span className={BadgeTxtStyleVariants({ active: isActive })}>{text}</span> + {typeof countNum === 'number' && ( + <span className={BadgeStyleVariants({ active: isActive })}>{countNum}</span> + )} + </button> ); };
🧹 Nitpick comments (5)
packages/design-system/src/components/badge/Badge.tsx (2)
5-6: isActive를 선택(provided-optional)으로 전환하거나 기본값 지정
호출 측 단순화를 위해 isActive를 선택 속성으로 두고, 컴포넌트에서 기본값 false를 처리하세요.-export interface BadgeProps { +export interface BadgeProps { text: string; countNum?: number; - isActive: boolean; - onClick: () => void; + isActive?: boolean; + onClick: () => void; }(함수 파라미터에서
isActive = false기본값은 위 버튼 변환 diff에 포함되어 있습니다.)
42-47: countNum가 undefined일 때 뱃지 숨김 처리
현재 undefined가 렌더링될 수 있습니다. 조건부 렌더링으로 UI 안정화하세요. 상단 버튼 변환 diff에서 이미 반영했습니다.apps/client/src/layout/Layout.tsx (1)
7-12: 레이아웃 리팩터: main에 min-w-0 추가 및 불필요 Fragment 제거
- 긴 콘텐츠에서 수평 오버플로 방지를 위해 flex row 자식인 main에
min-w-0권장.- 최상위 Fragment는 불필요합니다.
- <> - <div className="flex h-screen"> + <div className="flex h-screen"> <Sidebar /> - <main className="flex-1 overflow-y-auto"> + <main className="min-w-0 flex-1 overflow-y-auto"> <Outlet /> </main> - </div> - </> + </div>추가로, 모바일 브라우저 주소창 수축 이슈를 고려하면
h-screen대신h-dvh사용을 검토해 주세요. (Tailwind 버전/브라우저 타겟에 따라 선택)apps/client/src/pages/remind/Remind.tsx (2)
6-10: 상태/핸들러에 유니온 타입 적용으로 타입 안정성 강화
가능한 값이 제한적이므로 유니온 타입으로 명확히 표현하세요.- const [activeBadge, setActiveBadge] = useState('notRead'); + type BadgeType = 'notRead' | 'read'; + const [activeBadge, setActiveBadge] = useState<BadgeType>('notRead'); - const handleBadgeClick = (badgeType: string) => { - setActiveBadge(badgeType); - }; + const handleBadgeClick = (badgeType: BadgeType) => { + setActiveBadge(badgeType); + };
30-33: 사용되지 않는 index 제거 (lint 경고 해결)
불필요한 파라미터를 제거해 린트 경고를 없애세요.- {REMIND_MOCK_DATA.map((data, index) => ( + {REMIND_MOCK_DATA.map((data) => ( <Card key={data.id}
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
apps/client/src/layout/Layout.tsx(1 hunks)apps/client/src/pages/remind/Remind.tsx(1 hunks)apps/client/src/pages/remind/constants/index.ts(1 hunks)packages/design-system/src/components/badge/Badge.tsx(1 hunks)packages/tailwind-config/shared-styles.css(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- apps/client/src/pages/remind/constants/index.ts
- packages/tailwind-config/shared-styles.css
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#2
File: pnpm-workspace.yaml:3-3
Timestamp: 2025-08-18T13:48:59.065Z
Learning: constantly-dev는 docs 디렉터리를 컨벤션 문서 추가용으로 사용할 예정이라고 명시했습니다.
📚 Learning: 2025-07-15T20:00:13.756Z
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#80
File: apps/client/src/shared/components/ui/modalPop/ModalPop.tsx:36-41
Timestamp: 2025-07-15T20:00:13.756Z
Learning: In apps/client/src/shared/components/ui/modalPop/ModalPop.tsx, the InfoBox component uses hardcoded values for title, location, and icon URL as temporary test data. These should be replaced with dynamic data from props when implementing actual functionality and should be marked with TODO comments for future changes.
Applied to files:
apps/client/src/pages/remind/Remind.tsx
🧬 Code graph analysis (2)
apps/client/src/layout/Layout.tsx (1)
apps/client/src/shared/components/sidebar/Sidebar.tsx (1)
Sidebar(27-150)
apps/client/src/pages/remind/Remind.tsx (2)
packages/design-system/src/components/card/Card.stories.tsx (1)
Remind(42-53)apps/client/src/pages/remind/constants/index.ts (1)
REMIND_MOCK_DATA(1-114)
🪛 GitHub Check: lint
apps/client/src/pages/remind/Remind.tsx
[warning] 32-32:
'index' is defined but never used
🔇 Additional comments (3)
packages/design-system/src/components/badge/Badge.tsx (1)
11-19: 스타일 variant 리네이밍(click→active) 및 기본값 설정 LGTM
variant 명세와 기본값이 명확하며, 텍스트/카운트 모두 동일한 active 상태를 공유하도록 구성된 점 좋습니다.Also applies to: 25-33
apps/client/src/pages/remind/Remind.tsx (2)
16-27: Badge 제어형(isActive/onClick) 연결 LGTM
상태와 props 연결이 간결하고 의도 명확합니다.
30-42: 중첩 스크롤 여부 확인 요청 (main과 섹션 모두 overflow-y-auto)
Layout의 main과 본 섹션 모두 세로 스크롤을 갖습니다. UX 의도에 따라 하나만 스크롤되도록 정리하는 것이 일반적입니다. 메인만 스크롤하려면 본 섹션의overflow-y-auto제거를 권장합니다.
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
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
apps/client/src/pages/level/Level.tsx(1 hunks)apps/client/src/pages/remind/Remind.tsx(1 hunks)packages/design-system/src/components/badge/Badge.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/design-system/src/components/badge/Badge.tsx
- apps/client/src/pages/remind/Remind.tsx
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#2
File: pnpm-workspace.yaml:3-3
Timestamp: 2025-08-18T13:48:59.065Z
Learning: constantly-dev는 docs 디렉터리를 컨벤션 문서 추가용으로 사용할 예정이라고 명시했습니다.
⏰ 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: storybook
| </div> | ||
|
|
||
| <Badge text="오늘 모은 도토리 개수" countNum={acorns} /> | ||
| <Badge text="오늘 모은 도토리 개수" countNum={acorns} isActive={true} /> |
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.
💡 Verification agent
🧩 Analysis chain
불리언 리터럴 제거(축약형 사용) + 활성 상태 의도 확인
JSX에서는 isActive가 true일 때 속성 축약이 가능합니다. 또한 이 배지가 Level 페이지에서 항상 활성 스타일이어야 하는지 의도를 한 번만 확인해주세요(토글/선택 개념이 아니라 상시 표시라면 OK).
다음처럼 간소화 가능:
- <Badge text="오늘 모은 도토리 개수" countNum={acorns} isActive={true} />
+ <Badge text="오늘 모은 도토리 개수" countNum={acorns} isActive />리팩터 후 디자인시스템 변경(isActive 필수)에 따라 다른 사용처 누락이 없는지 확인하려면 아래 스크립트를 실행하세요.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Badge를 import하는 파일 나열
mapfile -t FILES < <(rg -nP --type=ts --type=tsx 'import\s*{\s*Badge\s*}\s*from' -o --no-filename | cut -d: -f1 | sort -u)
if [ "${#FILES[@]}" -eq 0 ]; then
# fallback: 파일 경로를 함께 뽑기
mapfile -t FILES < <(rg -nP --type=ts --type=tsx 'import\s*{\s*Badge\s*}\s*from' | cut -d: -f1 | sort -u)
fi
# 2) 각 파일에서 <Badge ...> 사용시 isActive 존재 여부 확인
for f in "${FILES[@]}"; do
echo "=== $f ==="
rg -nP -C2 '<Badge\b' "$f" || true
if ! rg -nP '(<Badge\b[^>]*\bisActive\b)' "$f" >/dev/null; then
echo "[WARN] isActive prop 누락 가능성: $f"
fi
doneLength of output: 84
기존 스크립트가 bash의 프로세스 치환과 rg --type=tsx 인식 오류로 실패했습니다. 아래 스크립트로 Badge 사용처를 다시 조회하고 isActive 누락 여부를 확인해 주세요.
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# 1) Badge를 import하는 파일 나열 (*.ts, *.tsx)
mapfile -t FILES < <(rg -l -g '*.ts' -g '*.tsx' "import\s*{\s*Badge\s*}\s*from")
if [ "${#FILES[@]}" -eq 0 ]; then
echo "Badge를 import한 파일이 없습니다."
exit 0
fi
# 2) 각 파일에서 <Badge ...> 사용 시 isActive 존재 여부 확인
for f in "${FILES[@]}"; do
echo "=== $f ==="
rg -nC2 '<Badge\b' "$f" || true
if ! rg -q '<Badge\b[^>]*\bisActive\b' "$f"; then
echo "[WARN] isActive prop 누락 의심: $f"
fi
doneLength of output: 178
아래 스크립트는 중괄호를 이스케이프하고 PCRE 옵션(-P)을 사용해 Badge import 파일을 찾은 뒤 isActive 누락 여부를 확인합니다. 실행 후 결과를 공유해주세요.
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
# Badge를 import하는 파일 검색 (*.ts, *.tsx)
rg -lP -g '*.ts' -g '*.tsx' 'import\s*\{\s*Badge\s*\}\s*from' | while IFS= read -r f; do
echo "=== $f ==="
rg -nP -C2 '<Badge\b' "$f" || true
if ! rg -qP '<Badge\b[^>]*\bisActive\b' "$f"; then
echo "[WARN] isActive prop 누락 의심: $f"
fi
doneLength of output: 280
아래 스크립트로 Badge 사용처를 다시 검사하고, isActive 속성 누락 여부를 확인해주세요. 실행 후 결과를 공유해 주세요.
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
# 1) Badge를 import하는 파일 검색 (*.ts, *.tsx)
FILES=$(rg -lP -g '*.ts' -g '*.tsx' 'import\s*\{\s*Badge\s*\}\s*from')
if [ -z "$FILES" ]; then
echo "Badge를 import한 파일이 없습니다."
exit 0
fi
# 2) 각 파일에서 <Badge ...> 사용 줄별로 확인
for f in $FILES; do
echo "=== $f ==="
rg -nP '<Badge\b[^>]*>' "$f" | while IFS= read -r line; do
echo "$line"
if [[ "$line" != *isActive* ]]; then
# line 형식: 파일경로:라인번호:코드
location="${line%%:*}:${line#*:}"
echo "[WARN] isActive prop 누락 의심: $location"
fi
done
doneLength of output: 60
아래 스크립트로 Badge 사용처 전부를 나열하고 isActive 누락 여부를 동시에 확인해 보세요. 실행 후 결과를 공유해 주세요.
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
echo "▶ Badge 사용처 및 isActive 누락 점검"
# 모든 ts/tsx 파일에서 <Badge 사용 줄 찾기
rg -nP '<Badge\b' -g '*.{ts,tsx}' | while IFS= read -r line; do
# 파일:라인:코드 분리
file=${line%%:*}
rest=${line#*:}
lineno=${rest%%:*}
code=${rest#*:*}
# 출력
echo "$file:$lineno: $code"
# isActive 포함 여부 확인
if ! echo "$code" | grep -q 'isActive'; then
echo "[WARN] isActive prop 누락 의심: $file:$lineno"
fi
doneLength of output: 387
Badge 컴포넌트 isActive prop 일관성 적용
- apps/client/src/pages/level/Level.tsx (45):
isActive={true}→isActive로 축약 - apps/client/src/pages/remind/Remind.tsx (16, 22):
isActiveprop 누락 → 디자인 시스템 변경(isActive필수)에 따라 추가 필요 - Level 페이지 배지가 항상 활성 상태로 유지되는 것이 의도된 동작인지 확인
🤖 Prompt for AI Agents
In apps/client/src/pages/level/Level.tsx around line 45 and
apps/client/src/pages/remind/Remind.tsx around lines 16 and 22, the Badge
component usage must follow the design-system change that makes the isActive
prop required; replace verbose isActive={true} with the shorthand isActive in
Level.tsx (if the badge should always be active) and add isActive (true or false
as appropriate) to the two Badge usages in Remind.tsx; also confirm with product
whether the Level page badge is intended to always be active—if not, set
isActive based on the correct state/logic.
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
🧹 Nitpick comments (3)
apps/client/src/pages/myBookmark/MyBookmark.tsx (3)
30-30: 중첩 스크롤 제거 권장페이지 레이아웃(main)에서 이미 스크롤을 담당한다면, 내부 그리드에 overflow-y-auto를 주면 UX가 나빠집니다.
- <div className="scrollbar-hide mt-[2.6rem] flex flex-wrap gap-[1.6rem] overflow-y-auto scroll-smooth max-w-[104rem]"> + <div className="scrollbar-hide mt-[2.6rem] flex flex-wrap gap-[1.6rem] scroll-smooth max-w-[104rem]">
1-1: 리마인드 목데이터에 대한 의존 분리북마크 페이지가 리마인드 전용 목데이터에 의존하고 있습니다. 북마크 전용 BOOKMARK_MOCK_DATA(또는 shared mock)로 분리하거나 API 스키마에 맞춘 타입을 정의해 주세요.
원하시면 shared mock 모듈/타입 초안을 만들어 드릴게요.
39-39: 하드코딩된 날짜 제거고정 문자열 대신 데이터 필드(예: bookmarkedAt)를 받아 포맷팅하거나, 최소한 유틸 함수를 통해 처리해 주세요.
// 예시 // <Card date={formatDate(data.bookmarkedAt)} ... />
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
apps/client/src/pages/myBookmark/MyBookmark.tsx(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#2
File: pnpm-workspace.yaml:3-3
Timestamp: 2025-08-18T13:48:59.065Z
Learning: constantly-dev는 docs 디렉터리를 컨벤션 문서 추가용으로 사용할 예정이라고 명시했습니다.
🧬 Code graph analysis (1)
apps/client/src/pages/myBookmark/MyBookmark.tsx (1)
apps/client/src/pages/remind/constants/index.ts (1)
REMIND_MOCK_DATA(1-114)
🔇 Additional comments (2)
apps/client/src/pages/myBookmark/MyBookmark.tsx (2)
16-27: Badge 접근성(aria) 확인 요청isActive 토글 시 Badge가 aria-pressed 등 상태를 노출하는지 확인이 필요합니다. 키보드 포커스 스타일도 점검해 주세요.
5-5: PR 범위 정합성 확인PR 타이틀/이슈(#50 대시보드 레이아웃) 대비 MyBookmark 수정 범위가 포함되는지 확인 부탁드립니다. 범위 밖이면 분리 권장.
| @@ -1,5 +1,47 @@ | |||
| import { REMIND_MOCK_DATA } from "@pages/remind/constants"; | |||
| import { Badge, Card } from "@pinback/design-system/ui"; | |||
| import { useState } from "react"; | |||
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.
배지 상태가 리스트/카운트에 반영되지 않습니다
배지 클릭 시 state는 바뀌지만 목록은 필터링되지 않고 배지 카운트도 하드코딩되어 있습니다. 최소한 현재 목데이터 기준(예: timeRemaining !== '완료'를 미열람으로 간주)으로 동작하도록 보완해 주세요.
-import { useState } from "react";
+import { useMemo, useState } from "react";- const [activeBadge, setActiveBadge] = useState('all');
+ const [activeBadge, setActiveBadge] = useState<'all' | 'notRead'>('all');- const handleBadgeClick = (badgeType: string) => {
+ const handleBadgeClick = (badgeType: 'all' | 'notRead') => {
setActiveBadge(badgeType);
};- countNum={5}
+ countNum={counts.all}- countNum={10}
+ countNum={counts.notRead}-{REMIND_MOCK_DATA.map((data) => (
+{filtered.map((data) => (아래 코드를 Line 10 바로 아래에 추가해 주세요(새 라인 추가):
// 임시 기준: '완료'가 아닌 항목을 '안 읽음'으로 간주
const filtered = useMemo(() => {
if (activeBadge === 'notRead') {
return REMIND_MOCK_DATA.filter((item) => item.timeRemaining !== '완료');
}
return REMIND_MOCK_DATA;
}, [activeBadge]);
const counts = useMemo(() => {
const all = REMIND_MOCK_DATA.length;
const notRead = REMIND_MOCK_DATA.filter((item) => item.timeRemaining !== '완료').length;
return { all, notRead };
}, []);Also applies to: 6-6, 8-10, 18-18, 24-24, 32-32
🤖 Prompt for AI Agents
In apps/client/src/pages/myBookmark/MyBookmark.tsx around line 3 and insert at
line 10, the badge state changes but the list and counts are not updated; update
the import to include useMemo (import { useState, useMemo } from "react"), then
add two useMemo hooks just below line 10: one that computes filtered list based
on activeBadge (treat items with timeRemaining !== '완료' as "notRead") and one
that computes counts (all and notRead) from REMIND_MOCK_DATA; ensure the
counts/useMemo dependency array references REMIND_MOCK_DATA and the filtered
useMemo depends on activeBadge and REMIND_MOCK_DATA, and replace any hardcoded
badge counts and list source with these new values.
jllee000
left a 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.
확인했습니다! 목데이터 따로 만드셔서 작업하셨네요 굿굿 이후에 해당 파일은 꼭 지우기!
고생하셨습니다 :)
| content={data.content} | ||
| category={data.category} | ||
| date="2024.08.15" | ||
| /> |
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.
요거 static한 데이터!! 추후에 바꿔주세요
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.
넵~ 일단 카드에 쓰인 데이터 모두 mock data여서 api연결 후에 바꾸겠습니다 👍
| countNum?: number; | ||
| isActive: boolean; | ||
| onClick?: () => void; |
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.
활성 제어를 위해서 추가가 필요했군녀
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.
맞아요! 실제 작동하는 로직은 외부에 책임을 두고 내부에서는 isActive를 통해 활성화가 되었는지만 담당합니다!
이에 따라 스타일만 변경하면 되는 구조여서 이렇게 수정하게 되었습니다.
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: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/design-system/src/components/badge/Badge.stories.tsx (1)
38-40: 문서 설명과 실제 동작 불일치(토글) — 문구 수정 필요설명은 “토글 아님”인데, ActiveBadge는 토글을 시연합니다. 혼동을 줄이기 위해 문구를 업데이트하세요.
- '클릭 시 내부 state(isClick)가 true로 바뀌며 스타일이 활성화됩니다. 토글 방식은 아닙니다.', + '제어형 컴포넌트입니다. isActive prop으로 스타일을 제어하며, 예시 스토리(ActiveBadge)는 onClick으로 토글합니다.',
🧹 Nitpick comments (4)
packages/design-system/src/components/badge/Badge.stories.tsx (4)
10-15: 기본 onClick(alert) 제거 및 기본 활성 상태는 false 권장alert는 스토리 실행/CI에 방해가 되고 액션 패널/스파이와도 불일치합니다. 기본값은 fn()으로, isActive는 비활성 기본값이 자연스럽습니다.
다음처럼 바꾸세요:
- onClick: () => alert('onClick 실행'), - isActive: true, + onClick: fn(), + isActive: false,
23-34: onClick argTypes에 타입 요약 추가Docs 테이블에 함수 시그니처를 노출하면 이해가 빠릅니다. (ActiveBadge는 story args로 fn()을 주입하므로 action 중첩 영향은 없습니다.)
onClick: { action: 'clicked', description: '뱃지 클릭 시 호출되는 콜백 함수(옵션)', + table: { type: { summary: '() => void' } }, },
58-76: render 내부에서 컴포넌트 정의 → 필요 시 외부로 hoist 제안현재도 문제 없지만, 내부에서 컴포넌트를 매 렌더마다 재정의합니다. 가독성과 디프 줄이기 목적이라면 바깥으로 hoist하는 방법도 고려해보세요.
예시(파일 어딘가 상단/하단에 배치):
function ActiveBadgeInner(args: React.ComponentProps<typeof Badge>) { const [isActive, setIsActive] = useState(false); return ( <Badge {...args} isActive={isActive} onClick={() => { setIsActive((prev) => !prev); args.onClick?.(); }} /> ); } // 사용처: // render: (args) => <ActiveBadgeInner {...args} />,
2-2: play 단계에 단언 추가로 상호작용 검증 강화클릭만 수행하고 검증이 없어 회귀를 잡기 어렵습니다. onClick 호출 횟수를 단언하고, 이를 위해 expect 임포트를 추가하세요.
-import { within, userEvent, fn } from '@storybook/test'; +import { within, userEvent, fn, expect } from '@storybook/test';const canvas = within(canvasElement); await userEvent.click(await canvas.findByText(String(args.text))); + await expect(args.onClick).toHaveBeenCalledTimes(1);Also applies to: 82-85
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
packages/design-system/src/components/badge/Badge.stories.tsx(3 hunks)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#2
File: pnpm-workspace.yaml:3-3
Timestamp: 2025-08-18T13:48:59.065Z
Learning: constantly-dev는 docs 디렉터리를 컨벤션 문서 추가용으로 사용할 예정이라고 명시했습니다.
⏰ 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: storybook
🔇 Additional comments (4)
packages/design-system/src/components/badge/Badge.stories.tsx (4)
4-4: useState 임포트 적절제어형 스토리 구현에 필요한 최소 임포트로 적절합니다.
51-52: 0 카운트 케이스 스토리 추가 좋습니다빈/0 상태를 명시적으로 검증할 수 있어 유용합니다.
77-81: 스파이(fn)로 onClick 검증 준비 완료상호작용 테스트에서 호출 여부를 단언하기에 적절한 설정입니다.
88-89: 스토리 설명 간결하고 명확합니다ActiveBadge의 동작을 짧게 요약해 문맥 파악에 도움이 됩니다.
📌 Related Issues
📄 Tasks
⭐ PR Point (To Reviewer)
크게 어려운 내용은 없을 것 같아요!
remind 페이지랑 myBookmark 페이지 2개에서 카드가 피그마 명세대로 row당 최대 4개까지 정렬되도록 했습니다. 그리고 정민님이 작업해주신 sidebar받아서 Layout.tsx에 main페이지와 sidebar 레이아웃 잡았습니다.
📷 Screenshot
Summary by CodeRabbit
New Features
Style
Other