feat: 토스트(Sonner) 컴포넌트 개발 및 프로젝트 전반 적용 (#81)#82
Conversation
alert() 호출 및 단순 알림용 openAlert/openError를 showToast/showErrorToast로 전환. 모임 설정, 즐겨찾기, 초대 링크 복사, 멤버 관리, 주제 CRUD, 책 등록 등 11개 파일 20곳의 사용자 피드백을 토스트로 통일. onClose 콜백으로 navigate하는 모달과 openConfirm은 기존 유지.
WalkthroughSonner 기반 토스트 알림 시스템을 추가하고, 기존 alert/modal 기반 사용자 피드백을 프로젝트 전반(컴포넌트·페이지)에 걸쳐 toast로 마이그레이션했습니다. 패키지 의존성과 Sonner 래퍼·유틸이 추가되고 App에 Toaster가 등록되었습니다. (≤50단어) Changes
Sequence Diagram(s)sequenceDiagram
participant Component as UI Component/Page
participant ToastLib as src/shared/lib/toast
participant Toaster as Sonner (Toaster)
Component->>+ToastLib: showToast("작업 완료")
ToastLib->>+Toaster: toast("작업 완료", options)
Toaster-->>-ToastLib: rendered
ToastLib-->>-Component: (no response)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~18 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 5 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Tip Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord. 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: 3
🤖 Fix all issues with AI agents
In `@src/pages/ComponentGuide/ComponentGuidePage.tsx`:
- Around line 1990-1998: The guide text states "Error: ... 4초 후 자동 닫힘" but the
actual implementation uses DURATION = 3000 in showErrorToast; update the source
of truth by either changing the DURATION constant used by showErrorToast in
toast.ts to 4000 (so showErrorToast uses 4000ms) or update the guide text in
ComponentGuidePage (the Showcase block) to say 3초; reference showErrorToast and
the DURATION constant in toast.ts when making the change so the documentation
and implementation match.
- Around line 1931-2001: Prettier flagged formatting issues in the ToastSection
component; run your formatter (e.g., `prettier --write`) across the changed
files or format the ToastSection function and its JSX (references: ToastSection,
showToast, showErrorToast, Showcase, Button) so the JSX/strings match project
Prettier rules, then re-stage and commit the formatted changes.
In `@src/shared/lib/toast.ts`:
- Line 3: The error toast currently uses the global DURATION (3000ms) but design
spec requires errors to last 4000ms; update the toast logic so showErrorToast
(or whichever function dispatches error toasts) uses a 4000ms duration instead
of DURATION—either introduce a new constant ERROR_DURATION = 4000 and use that
in showErrorToast, or pass 4000 as the duration argument when creating the error
toast, leaving DURATION (3000) for default toasts.
🧹 Nitpick comments (8)
package.json (1)
61-61:next-themes는 불필요한 의존성입니다.이 프로젝트는 Vite + React Router 기반이며,
src/shared/ui/Sonner.tsx에서next-themes를 사용하지 않습니다. shadcn CLI가 자동으로 추가한 것으로 보이나, 실제로 사용되지 않는 런타임 의존성은 번들 사이즈에 영향을 줄 수 있으므로 제거하는 것이 좋습니다.#!/bin/bash # next-themes가 실제로 사용되는지 확인 rg -n "next-themes" --type=ts --type=tsx -g '!node_modules' -g '!package.json' -g '!pnpm-lock.yaml' rg -n "useTheme" --type=ts --type=tsx -g '!node_modules'src/shared/ui/Sonner.tsx (1)
10-10: 에러 아이콘 숨김에' '대신null을 사용하세요.공백 문자는 DOM 텍스트 노드를 생성하지만, 아이콘 래퍼는 여전히 마운트되어 불필요한 공간을 차지합니다. Sonner는
null을 통해 아이콘을 완전히 제거하는 것을 지원합니다.icons={{ error: null }}src/pages/Gatherings/GatheringSettingPage.tsx (2)
111-113:openError→showErrorToast전환 누락 가능성모임 삭제 실패 시
openError를 사용하고 있지만onClose콜백(navigate 등)이 없습니다. 같은 파일 내 다른 에러 핸들러(Line 92, 126, 141, 163)는 모두showErrorToast로 전환된 상태입니다.PR 전환 기준(
openError+ onClose 없음 → 토스트)에 따르면 여기도 토스트가 적절해 보입니다.제안
onError: () => { - openError('오류', '모임 삭제에 실패했습니다.') + showErrorToast('모임 삭제에 실패했습니다.') },
40-40:openError제거 가능 여부 확인Line 112의
openError를 토스트로 전환하면,openError는 Line 75(navigate 콜백 포함)에서만 사용됩니다. 전환하지 않더라도 현재 사용은 문제없으나, 전환 시 destructuring에서openError제거 가능 여부를 함께 검토해 주세요.src/pages/Meetings/MeetingDetailPage.tsx (1)
67-78: 에러 발생 시 토스트 다중 표시 & 후속 처리 부재3개 쿼리가 동시에 실패하면 토스트 3개가 한꺼번에 뜹니다(sonner 스펙상 최대 3개). UX 상 큰 문제는 아니지만, 주석 처리된
navigate(Line 77)가 암시하듯 에러 시 페이지 이탈 등의 후속 처리가 빠져 있습니다.의도적이라면 주석을 제거하고, 추후 처리 예정이라면 TODO로 명시해 두는 게 좋겠습니다.
src/pages/Books/BookListPage.tsx (1)
244-249: 성공은 토스트, 실패는openConfirm— 일관성 검토성공 시
showToast를 사용하면서, 실패 시에는openConfirm(확인 버튼만 있는 모달)을 사용하고 있습니다.사용자 확인이 반드시 필요한 경우가 아니라면
showErrorToast('책 등록에 실패했습니다.')로 통일하는 것이 이번 PR의 전환 기준과 일관됩니다.src/pages/Gatherings/CreateGatheringPage.tsx (1)
44-46:openError→showErrorToast전환 누락 가능성모임 생성 실패 시
openError를 사용하지만onClose콜백이 없습니다.GatheringSettingPage의 유사 패턴(수정/승인/거절/내보내기 실패)은 모두showErrorToast로 전환된 상태입니다.의도적으로 모달을 유지한 것이라면 괜찮지만, 전환 기준상 토스트 대상으로 보입니다.
src/pages/Gatherings/GatheringDetailPage.tsx (1)
57-67:handleInviteClick의존성 배열에gathering객체 전체 참조.
gathering객체 전체를 deps에 넣으면 서버 refetch마다 콜백이 재생성됩니다. 실제로 사용하는 값은gathering?.invitationLink뿐이므로, 불필요한 재생성을 줄일 수 있습니다.다만 현재 구조에서 실질적 성능 영향은 미미하므로 참고 수준입니다.
♻️ 의존성 최적화 제안
+ const invitationLink = gathering?.invitationLink + const handleInviteClick = useCallback(async () => { - if (!gathering?.invitationLink) return + if (!invitationLink) return try { - const inviteUrl = `${window.location.origin}/invite/${gathering.invitationLink}` + const inviteUrl = `${window.location.origin}/invite/${invitationLink}` await navigator.clipboard.writeText(inviteUrl) showToast('초대 링크가 복사되었습니다.') } catch { showErrorToast('링크 복사에 실패했습니다.') } - }, [gathering]) + }, [invitationLink])
🚀 풀 리퀘스트 제안
📋 작업 내용
sonner 기반 토스트 컴포넌트를 개발하고, 프로젝트 전반의
alert()/openAlert()/openError()단순 알림을 토스트로 전환했습니다.🔧 변경 사항
토스트 컴포넌트 개발
<Sonner />컴포넌트 (src/shared/ui/Sonner.tsx)showToast()/showErrorToast()유틸 함수 (src/shared/lib/toast.ts)w-fit max-w-[360px])도메인별 토스트 전환 (11개 파일, 20곳)
Header.tsxalert('준비중입니다.')→showToastGatheringDetailPage.tsxshowErrorToast, 초대 링크 복사 →showToast/showErrorToastGatheringListPage.tsxshowErrorToastGatheringSettingPage.tsxshowToast/showErrorToastCreateGatheringPage.tsxshowToast/showErrorToast구현MeetingDetailPage.tsxalert()3건 →showErrorToastMeetingDetailButton.tsxalert()→showToastTopicCard.tsxshowToast, 좋아요 에러 →showErrorToastTopicCreatePage.tsxshowToastBookListPage.tsxshowToast추가ProfileImagePicker.tsxshowErrorToast전환 기준
alert()→ 무조건 토스트openAlert()/openError()onClose 콜백 없는 단순 알림 → 토스트openAlert()/openError()+ onClose로 navigate → 모달 유지openConfirm()→ 모달 유지📄 기타
Mutation Hook 토스트 적용 현황 (후속 작업)
현재 컴포넌트 레벨에서 토스트를 적용했으며, mutation hook 내부 토스트는 별도 작업으로 진행 예정입니다.
useCreateGatheringuseDeleteGatheringuseUpdateGatheringuseHandleJoinRequestuseRemoveMemberuseToggleFavoriteuseCreateMeetinguseUpdateMeetinguseJoinMeetinguseCancelJoinMeetinguseDeleteMeetinguseConfirmMeetinguseRejectMeetinguseCreateTopicuseDeleteTopicuseLikeTopicuseConfirmTopicsuseCreateBookuseDeleteBookuseCreateBookRecorduseUpdateBookRecorduseDeleteBookRecorduseCreateBookReviewuseOnboardinguseUpdateNicknameuseUpdateProfileImageuseDeleteProfileImageuseDeleteUseruseLogoutuseDeleteMyPreOpinionAnswercloses #81
Summary by CodeRabbit
새로운 기능
의존성