[FEAT] 태그/투두 개수 제한 안내 토스트 UI 추가 - #100
Conversation
- 디자인 시스템에 경고 아이콘을 포함한 Toast 컴포넌트를 추가했습니다 - Toast 컴포넌트의 Storybook 스토리를 추가했습니다
- 디자인 시스템 Toast를 감싸는 애니메이션 래퍼 컴포넌트를 추가했습니다 - 태그 개수 제한 안내 컨테이너를 추가했습니다 - 투두 개수 제한 안내 컨테이너를 추가했습니다
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Walkthrough디자인 시스템에 Toast 컴포넌트와 Storybook 스토리를 신규 추가하고, timo-web에 애니메이션 처리된 AnimatedToast 래퍼 컴포넌트를 도입했다. 이를 활용해 태그(최대 8개) 및 투두(최대 20개) 개수 제한 안내 토스트 컨테이너를 구현했다. Changes토스트 UI 추가
Estimated code review effort: 2 (Simple) | ~15 minutes Sequence Diagram(s)sequenceDiagram
participant Container as TagLimitToastContainer/TodoLimitToastContainer
participant AnimatedToast
participant Toast
Container->>AnimatedToast: isOpen=true, message, onClose 전달
AnimatedToast->>AnimatedToast: shouldRender=true, requestAnimationFrame으로 isVisible=true
AnimatedToast->>Toast: message 렌더링
Container->>AnimatedToast: onClose 호출 (isOpen=false)
AnimatedToast->>AnimatedToast: isVisible=false, 200ms 후 shouldRender=false
AnimatedToast->>Toast: 언마운트
Suggested labels: Suggested reviewers: 🐇 토스트가 뜨고 지고, 애니메이션 살랑살랑, 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/timo-web/app/home/_containers/TagLimitToastContainer.tsx`:
- Line 10: The TagLimitToastContainer still has a Korean-only TODO, so wire its
toast text through the existing English/i18n path instead of leaving a
placeholder. Update the message source in TagLimitToastContainer to use the same
localized content pattern used by Toast.stories.tsx and any shared translation
helper, so the container renders the correct language at runtime.
- Line 7: The exported component name is inconsistent with the file name, so
rename TagLimitTagContainer to match TagLimitToastContainer and update any
related references accordingly. Use the existing export in
TagLimitToastContainer.tsx as the source of truth, and keep the
component/function name aligned with the file for easier future imports and
maintenance.
In `@apps/timo-web/app/home/_containers/TodoLimitToastContainer.tsx`:
- Line 10: The TodoLimitToastContainer still has a Korean TODO placeholder and
needs the same multilingual message handling as TagLimitToastContainer. Update
TodoLimitToastContainer to use the app’s i18n/message lookup flow at the toast
render point instead of leaving a hardcoded TODO, and align the implementation
with the existing localization pattern used in TagLimitToastContainer so the
user-facing text is language-aware.
In `@apps/timo-web/components/toast/AnimatedToast.tsx`:
- Around line 1-70: AnimatedToast is placed in a shared components path even
though the architecture doc says domain-only state/timer UI should live under
app/**/_containers/. Move the AnimatedToast component out of components/toast
and into the appropriate container location used by its domain, and keep its
state/timeout logic there. Update any imports/usages so the unique symbol
AnimatedToast continues to work from its new location.
- Around line 46-54: The auto-close effect in AnimatedToast re-runs whenever
onClose gets a new function reference, which can reset the timer unexpectedly.
Update the useEffect logic to avoid depending directly on onClose by storing the
latest callback in a ref and invoking that ref inside the timeout, while keeping
the timer effect keyed only to the open/duration state.
In `@packages/timo-design-system/src/components/toast/Toast.tsx`:
- Line 15: `Toast`의 `WarningWhiteIcon`은 장식용이라 스크린리더 중복 읽기를 막아야 합니다. `Toast`
컴포넌트에서 아이콘 렌더링에 `aria-hidden="true"`를 추가해 `message` 텍스트만 읽히도록 수정하세요.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 82acbb41-2a05-40eb-888d-4662b5866780
⛔ Files ignored due to path filters (1)
packages/timo-design-system/src/icons/source/warning-white.svgis excluded by!**/*.svg
📒 Files selected for processing (6)
apps/timo-web/app/home/_containers/TagLimitToastContainer.tsxapps/timo-web/app/home/_containers/TodoLimitToastContainer.tsxapps/timo-web/components/toast/AnimatedToast.tsxpackages/timo-design-system/src/components/index.tspackages/timo-design-system/src/components/toast/Toast.stories.tsxpackages/timo-design-system/src/components/toast/Toast.tsx
…to feat/ui/99-toast-limit-notification
- onClose 콜백을 ref에 저장해 참조 변경과 무관하게 최신 콜백을 호출하도록 했습니다 - 자동 닫힘 타이머 effect의 의존성을 isOpen, duration으로만 제한했습니다
…to feat/ui/99-toast-limit-notification
- TagLimitToastContainer, TodoLimitToastContainer를 app/home/_containers에서 app/[locale]/(main)/home/_containers/toast로 이동했습니다 - 더 이상 사용하지 않는 components/.gitkeep 파일을 제거했습니다
jjangminii
left a comment
There was a problem hiding this comment.
구조 분리가 깔끔하게 잘 됐어요! 디자인 시스템의 Toast는 순수 표시 컴포넌트로만 두고 애니메이션/타이밍 로직은 AnimatedToast로 분리해서, 디자인 시스템이 앱 레이어의 관심사를 전혀 몰라도 되는 구조라 좋네요👍
사소한 코멘트 몇 가지 남겨뒀으니 확인 부탁드려요~
| const [shouldRender, setShouldRender] = useState<boolean>(isOpen); | ||
| const [isVisible, setIsVisible] = useState<boolean>(false); |
There was a problem hiding this comment.
isOpen=true로 props가 들어오면 shouldRender=true로 초기화되는데, isVisible은 항상 false로 시작하고 있어요.
그러면 첫 렌더 시 토스트가 DOM에는 존재하지만(shouldRender=true) 투명한 상태(isVisible=false → opacity-0)로 잠깐 보이다가, requestAnimationFrame 콜백이 실행된 후에야 isVisible=true가 되면서 나타나게 될 것 같아요.
지금 Container들이 isOpen 초기값을 true로 주고 있어서 실제로 이 경로로 실행되고 있는 것 같은데, 의도한 동작이 맞는지 확인 부탁드려요!
There was a problem hiding this comment.
shouldRender가 isVisible 보다 먼저 true가 되어야 페인트되고, 그 다음 프레임에서 isVisible=true로 바뀌면서 css 트랜지션이 걸려야 해서 의도된 동작이 맞습니다!
| export const Toast = ({ message }: ToastProps) => { | ||
| return ( | ||
| <div | ||
| role="status" |
There was a problem hiding this comment.
role="status"는 스크린리더가 현재 읽고 있는 내용을 마친 후에 안내해줘요. 그런데 태그/투두 한도 초과는 사용자가 즉시 알아야 하는 경고 메시지에 가까워서 현재 읽고 있는 내용을 중단하고 바로 안내해주는 role="alert"가 더 적합하다고 생각해요-!
- role="status"를 role="alert"로 변경했습니다 - 태그/투두 한도 초과 등 사용자가 즉시 인지해야 하는 경고 메시지이므로 assertive live region이 적절합니다
…to feat/ui/99-toast-limit-notification
고정 너비(w-122.5)를 제거하여 메시지 길이에 따라 너비가 유동적으로 조정되도록 했습니다.
토스트 컨테이너를 with-time-sidebar 레이아웃 하위로 이동하고, next-intl 기반 영문 번역을 추가했습니다.
yumin-kim2
left a comment
There was a problem hiding this comment.
토스트 생긴 것만 담당하는 부분(Toast)이랑, 언제 뜨고 언제 꺼질지 정하는 부분(AnimatedToast)의 구조를 나눠주신 게 정말 짱짱맨입니다. 이렇게 해두면 나중에 다른 곳에서 이 토스트 갖다 쓸 때 재사용성이 높아지겠네용 수고하셨습니다~~~~~!!
ISSUE 🔗
close #99
What is this PR? 🔍
태그·투두 개수 제한 안내를 위한 Toast UI를 디자인 시스템에 추가하고, timo-web에서 이를 활용하는 애니메이션 래퍼와 컨테이너를 구현했습니다. 이후 리뷰에서 지적된 컴포넌트명 불일치, 영문 메시지 미대응 문제를 해결하고 컨테이너를 with-time-sidebar 레이아웃 구조로 이동했습니다.
배경
next-intl기반으로 영문 번역을 추가하고, 컨테이너를 실제 렌더링 위치인 with-time-sidebar 레이아웃 하위로 이동했습니다.디자인 시스템 (Toast)
WarningWhiteIcon을 포함한Toast컴포넌트와 Storybook 스토리를 추가하고, 고정 너비(w-122.5)를 제거했습니다.Toast는message: ReactNode만 받아 아이콘과 텍스트를 좌우로 배치하는 순수 표시 컴포넌트입니다. 애니메이션·타이밍 로직은 포함하지 않고,role="alert"로 스크린리더에 상태 변경을 알립니다. 너비는 내부 콘텐츠에 맞춰 유동적으로 조정됩니다.timo-web (AnimatedToast, Container, i18n)
Toast를 감싸는AnimatedToast래퍼와TagLimitToastContainer,TodoLimitToastContainer를 추가하고, 컨테이너를 with-time-sidebar 레이아웃 하위로 이동했습니다. 두 컨테이너의 하드코딩된 한국어 문구를next-intl기반 번역으로 교체했습니다.AnimatedToast는isOpen변경에 따라shouldRender/isVisible두 단계 상태로 mount 유지 시간과 transition 클래스를 제어하고,duration(기본 3000ms) 이후onClose를 호출합니다. 두 Container는useTranslations("Toast")로messages/ko.json,messages/en.json의Toast네임스페이스를 조회하며, 강조되는 숫자 부분은t.rich("...", { blue: (chunks) => <span className="text-timo-blue-300">{chunks}</span> })형태로 렌더링합니다. 두 Container 모두 현재isOpen초기값을true로 고정한 스캐폴딩 상태이며, 실제 태그/투두 개수 기반 노출 조건은 아직 연결되어 있지 않습니다.To Reviewers
두 Container는 여전히 실제 태그/투두 개수와 연결되지 않은 스캐폴딩 상태로, 후속 작업에서 카운트 로직과 페이지 연동이 필요합니다.
t.rich의blue태그 렌더링 방식이 다른 다국어 문구에서도 재사용 가능한 패턴인지 확인해 주세요.Screenshot 📷
Test Checklist ✔
pnpm check-types통과pnpm lint통과