Skip to content

feat: 알림 SSE 실시간 반영과 견적 요청 UX 개선 - #101

Merged
youngmis merged 4 commits into
devfrom
feature/contents
Aug 10, 2026
Merged

feat: 알림 SSE 실시간 반영과 견적 요청 UX 개선#101
youngmis merged 4 commits into
devfrom
feature/contents

Conversation

@youngmis

@youngmis youngmis commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

📋 작업 내용

  1. 알림 미읽음 뱃지를 SSE로 갱신
  2. 프론트 알림 메세지 조합 파일 수정
  3. 견적 요청 화면 주소 선택창에서 주소가 긴 경우 박스 안에서 말줄임 처리ㅣ

🔥 변경 사항

  • package.json / package-lock.json: pre-commit용 eslint-plugin-storybook 의존성을 추가했습니다.
  • NotificationTrigger.tsx: 알림 벨에서 SSE 구독 훅을 호출해 미읽음 뱃지가 실시간으로 갱신되게 했습니다.
  • notificationMessages.ts: 아직 post 로직이 없는 알림 타입 문구를 정리하고, 실제 쓰는 타입 위주로 템플릿을 맞췄습니다.
  • EstimateRequestForm.tsx: 견적 요청 성공 시 /estimates/requests로 이동하도록 했고, 긴 출발지·도착지 주소는 박스 크기를 유지한 채 앞부분만 보이도록 말줄임 처리했습니다.
  • useNotificationSse.ts: 로그인 상태에서 알림 SSE를 구독하고, 이벤트에 따라 unread/list 캐시를 갱신·재연결하는 훅을 추가했습니다.
  • notificationSse.ts: Authorization을 넣을 수 있는 fetch 기반 SSE 클라이언트(파싱·스트림 읽기)를 추가했습니다.
  • apiRoutes.ts: SSE 구독 경로 SSE_SUBSCRIBE를 추가했습니다.
  • notification.ts: 알림 타입을 notificationMessages.ts에 있는 타입과 동기화했습니다.

✅ 체크리스트

  • 로컬에서 정상 동작을 확인했습니다.
  • 기존 기능에 영향을 주지 않는지 확인했습니다.
  • 불필요한 console.log를 제거했습니다.
  • lint를 통과했습니다.
  • README 또는 문서를 수정했습니다. (필요 시)
  • API 명세와 일치하는지 확인했습니다.

📷 스크린샷 (선택)


🔗 관련 이슈

Closes #


💬 To Reviewer

Summary by CodeRabbit

  • 새 기능

    • 로그인 상태에서 새 알림과 읽지 않은 알림 수가 실시간으로 반영됩니다.
    • 알림 목록이 자동으로 갱신되어 최신 내용을 확인할 수 있습니다.
    • 알림 연결이 일시적으로 끊겨도 자동으로 재연결됩니다.
  • 개선

    • 견적 요청 완료 후 성공 안내 대신 보낸 견적 요청 목록으로 바로 이동합니다.
    • 긴 지역명이 화면 너비를 넘지 않도록 말줄임 처리했습니다.
    • 더 이상 사용하지 않는 알림 유형과 문구를 정리했습니다.

미읽음 뱃지를 SSE로 갱신하고, 견적 요청 성공 시 보낸 견적 목록으로
이동하며 긴 주소는 박스 안에서 말줄임 처리한다.
pre-commit용 eslint-plugin-storybook도 추가한다.

Co-authored-by: Cursor <cursoragent@cursor.com>
@vercel

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
moving-frontend Error Error Aug 10, 2026 9:00am
moving-frontend-p2ol Error Error Aug 10, 2026 9:00am

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@youngmis, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 18 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 943f2635-9291-4181-9fcf-4c3ef5e308c9

📥 Commits

Reviewing files that changed from the base of the PR and between e5fcad8 and 2513917.

📒 Files selected for processing (10)
  • src/components/common/Header/notification/NotificationTrigger.tsx
  • src/components/estimate/request/EstimateRequestForm.tsx
  • src/components/landing/LandingBottomBanner.tsx
  • src/components/landing/LandingFeatureCompare.tsx
  • src/components/landing/LandingFeatureRequest.tsx
  • src/components/landing/LandingHero.tsx
  • src/components/landing/LandingMoveTypes.tsx
  • src/hooks/notifications/useNotificationSse.ts
  • src/lib/api/notificationSse.ts
  • src/lib/constants/apiRoutes.ts
📝 Walkthrough

Walkthrough

알림 SSE 구독과 재연결, 이벤트별 캐시 갱신을 추가했습니다. 알림 유형과 메시지 템플릿을 정리했습니다. 견적 요청 성공 후 이동 경로와 지역 표시 레이아웃을 변경했습니다. 개발 의존성 버전도 업데이트했습니다.

Changes

실시간 알림

Layer / File(s) Summary
SSE 연결 및 이벤트 파싱
src/lib/constants/apiRoutes.ts, src/lib/api/notificationSse.ts
알림 SSE 경로와 Bearer 인증 fetch 구독을 추가했습니다. 스트림 청크에서 event: 및 여러 data: 행을 파싱합니다.
SSE 인증, 재연결 및 캐시 갱신
src/hooks/notifications/useNotificationSse.ts, src/components/common/Header/notification/NotificationTrigger.tsx
NotificationTrigger에서 SSE 훅을 실행합니다. 인증 갱신, 401 처리, 지수 백오프 재연결, AbortController 정리를 추가했습니다. 알림 이벤트에 따라 미읽음 수와 목록 캐시를 갱신합니다.
알림 유형 및 메시지 계약
src/types/notification.ts, src/components/common/Header/notification/notificationMessages.ts
6개 알림 유형과 해당 메시지 템플릿을 제거했습니다. 남은 템플릿에 설명 주석을 추가했습니다.

견적 요청 화면

Layer / File(s) Summary
견적 요청 완료 흐름 및 레이아웃
src/components/estimate/request/EstimateRequestForm.tsx
요청 성공 시 토스트 대신 보낸 견적 요청 목록으로 이동합니다. 지역 주소 텍스트에 축소 및 말줄임 스타일을 적용했습니다. 대기 중인 견적 링크 JSX를 정리했습니다.

개발 의존성

Layer / File(s) Summary
Storybook ESLint 플러그인 버전 업데이트
package.json
eslint-plugin-storybook 버전 범위를 ^10.5.5에서 ^10.5.7로 변경했습니다.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant 사용자
  participant NotificationTrigger
  participant useNotificationSse
  participant NotificationSseApi
  participant 알림서버
  사용자->>NotificationTrigger: 헤더 렌더링
  NotificationTrigger->>useNotificationSse: SSE 연결 시작
  useNotificationSse->>NotificationSseApi: accessToken과 함께 구독
  NotificationSseApi->>알림서버: GET /notifications/sse/subscribe
  알림서버-->>NotificationSseApi: notification 이벤트 전송
  NotificationSseApi-->>useNotificationSse: 이벤트명과 data 전달
  useNotificationSse-->>NotificationTrigger: 미읽음 수 및 목록 캐시 갱신
Loading

Possibly related PRs

Suggested labels: ✨ feat

Suggested reviewers: 9g-g9, obebe-creator

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 알림 SSE 실시간 갱신과 견적 요청 UX 개선이라는 주요 변경 사항을 정확하고 간결하게 설명합니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch feature/contents
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/contents

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 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 `@src/hooks/notifications/useNotificationSse.ts`:
- Around line 99-100: In the normal-completion path after
subscribeNotificationSse returns, await sleep(INITIAL_RETRY_DELAY_MS,
abortController.signal) before starting the next connection, while retaining the
retryDelayMs reset.

In `@src/lib/api/notificationSse.ts`:
- Around line 36-38: Update the SSE error handling around response validation to
throw a structured error that preserves response.status as a dedicated status
property, then update the useNotificationSse error branch to detect
authentication failures via error.status === 401 instead of parsing
error.message. Preserve existing behavior for other HTTP statuses.
🪄 Autofix

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: CHILL

Plan: Pro Plus

Run ID: d374266b-3a02-4dac-b4f9-4e710d6475a4

📥 Commits

Reviewing files that changed from the base of the PR and between 1ea1be1 and e5fcad8.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json, !**/package-lock.json
📒 Files selected for processing (8)
  • package.json
  • src/components/common/Header/notification/NotificationTrigger.tsx
  • src/components/common/Header/notification/notificationMessages.ts
  • src/components/estimate/request/EstimateRequestForm.tsx
  • src/hooks/notifications/useNotificationSse.ts
  • src/lib/api/notificationSse.ts
  • src/lib/constants/apiRoutes.ts
  • src/types/notification.ts

Comment thread src/hooks/notifications/useNotificationSse.ts Outdated
Comment thread src/lib/api/notificationSse.ts

@yooseohyeon yooseohyeon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

작업하느라 수고하셨습니다! 알림 실시간 반영과 견적 요청에서 주소가 긴 경우 ui 등 문제가 잘 해결된 것 같습니다!

다만 견적 요청 성공 시 잠시 안내 화면이 보였다가 (empty state(트럭 이미지+안내문구), cta 버튼) 보낸 견적 요청 페이지로 이동하는데, 바로 이동하는 것이 목적이라면 견적 요청 성공 후에는 안내 화면 자체가 보이지 않도록 수정이 필요해보입니다!

또한 이건 제 실수일수도 있지만 dev 브랜치의 많은 수정 사항들이 누락된 것으로 보여서 한 번 확인 부탁드립니다!

신영미 and others added 2 commits August 10, 2026 17:48
Keep notification SSE subscription and pathname-close behavior together.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

@juengseulki juengseulki left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📋 PR 리뷰

전체 변경사항 확인했습니다!

이번 PR은 알림 미읽음 뱃지를 SSE로 실시간 반영하는 흐름, 알림 메시지 타입/템플릿 정리, 견적 요청 완료 후 이동 및 긴 주소 말줄임 UX 개선을 중심으로 확인했습니다.

👍 좋았던 점

  • 로그인 상태에서만 useNotificationSse()를 구독하도록 하고, 인증 scope가 준비된 뒤 연결을 시작하도록 한 점이 좋았습니다.
  • 브라우저 기본 EventSource 대신 Authorization 헤더를 넣을 수 있는 fetch 기반 SSE 클라이언트를 별도로 구현한 방향이 현재 인증 구조와 잘 맞습니다.
  • 장시간 유지되는 SSE 연결에 기존 fetchInstance를 재사용하지 않고 별도 fetch를 사용해 타임아웃 영향을 받지 않도록 한 점이 좋았습니다.
  • SSE 이벤트 수신 시 unread count 캐시를 즉시 증가시키면서도 이후 invalidate를 수행해 서버 상태와 다시 동기화하도록 한 점이 안정적입니다.
  • 알림 목록 캐시도 함께 invalidate해 벨 뱃지만 바뀌고 목록은 이전 상태로 남는 문제를 방지했습니다.
  • SSE 연결 종료/오류 시 1초부터 최대 30초까지 backoff를 적용해 재연결 요청이 과도하게 몰리지 않도록 한 점이 좋았습니다.
  • 컴포넌트 unmount 또는 인증 scope 변경 시 AbortController로 기존 스트림과 retry 대기를 함께 종료하도록 처리한 점도 잘 구성되어 있습니다.
  • 401 발생 시 access token refresh를 시도하고 이후 재연결하도록 기존 인증 흐름을 재사용한 점이 좋았습니다.
  • SSE parser에서 event:, data:, 빈 줄 기반 flush, heartbeat comment(:)를 구분해 처리하고 있어 기본적인 SSE 메시지 형식에 맞게 구현되어 있습니다.
  • 견적 요청 성공 후 /estimates/requests로 바로 이동하도록 변경하면서, ACTIVE 캐시가 먼저 갱신되어 기존 활성 견적 안내 화면이 잠깐 노출되는 문제까지 별도 성공 상태 UI로 막은 점이 세심합니다.
  • 긴 주소에 min-w-0, overflow-hidden, truncate를 함께 적용하고 title={value}도 추가해 박스 레이아웃을 유지하면서 전체 주소 확인 방법도 남겨둔 점이 좋았습니다.

🔍 확인 및 제안

1. SSE unread count 증가 후 즉시 invalidate

notification 이벤트에서 unread count를 먼저 +1 한 뒤 바로 동일 Query를 invalidate하고 있습니다.

즉시 UI 반영과 서버 기준 보정을 모두 챙기려는 의도는 이해되고 현재 구조에서도 문제는 없어 보입니다.

다만 이벤트가 매우 자주 들어오는 경우에는 매 이벤트마다 unread count/list refetch가 발생할 수 있으므로,
향후 알림 빈도가 높아진다면 debounce 또는 이벤트 payload를 이용한 직접 캐시 갱신 범위를 넓히는 방향도 검토할 수 있을 것 같습니다.

현재 규모에서는 blocker는 아닙니다.

2. 401 이후 refresh 성공 시 재연결 흐름

401 발생 시 ensureAccessTokenRefreshed()를 실행하고 다음 loop에서 새 access token을 다시 읽는 구조라 흐름은 정상적으로 보입니다.

refresh 실패 시 연결 루프를 종료하도록 한 것도 로그인 만료 상황에서 불필요한 재연결을 막는 방향으로 적절합니다.

3. 알림 타입 제거 범위

NotificationType에서 일부 기존 타입을 제거하고 notificationMessages.ts와 맞춘 부분은 확인했습니다.

이 타입이 프론트 전체에서 "현재 실제로 수신 가능한 백엔드 NotificationType" 자체를 표현하는 타입이라면,
아직 post 로직이 없다는 이유로 enum 값을 제거하는 것은 이후 서버에서 해당 타입이 내려왔을 때 파싱/처리 범위를 좁힐 수 있습니다.

반대로 이 타입이 정말 "현재 알림 패널에서 템플릿을 제공하는 타입"만 표현하는 용도라면 지금 정리 방향도 가능합니다.

현재 주석도 알림 패널 문구 템플릿이 정의된 타입이라고 명시되어 있어 의도는 이해되지만,
백엔드 NotificationType과 동일 이름을 쓰고 있어 역할이 헷갈릴 수 있으므로 타입 이름을 조금 더 명확히 분리하는 것도 고려할 수 있을 것 같습니다.

4. PR 범위 외 Landing 파일 변경 확인

이번 PR 설명에는 SSE/알림/견적 요청 UX가 주요 범위인데 변경 파일 목록에는 여러 Landing 컴포넌트도 포함되어 있습니다.

기능상 문제가 있다는 의미는 아니지만,
PR에 의도적으로 포함된 변경인지 한 번 확인하면 좋을 것 같습니다.

수고하셨습니다! 👍

Comment thread src/hooks/notifications/useNotificationSse.ts
Comment thread src/types/notification.ts
@youngmis
youngmis merged commit bc7c47a into dev Aug 10, 2026
2 of 4 checks passed
@juengseulki
juengseulki requested a review from wndnjs2037 August 14, 2026 06:04
return;
}

retryDelayMs = Math.min(retryDelayMs * 2, MAX_RETRY_DELAY_MS);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

로직을 보면 정상 종료됐을 때는 L105에서 retryDelayMs를 1초로 리셋하는데, 이 catch 경로에서는 계속 2배씩만 늘어나고 다시 리셋되는 곳이 없는 것 같아요. 이러면 재연결 실패가 몇 번 쌓여서 delay가 30초까지 커진 다음, 오랜만에 연결이 한 번 끊긴 경우에도 계속 30초 지연이 유지되는 문제가 있을 수 있어요. 연결이 다시 정상적으로 붙었을 때 delay를 초기화하는 지점이 따로 있는지 확인해보시면 좋을 것 같습니다.

@wndnjs2037

wndnjs2037 commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator

알림 실시간 반영부터 견적 요청 UX까지 범위가 넓은데도 꼼꼼하게 마무리해주신 것 같습니다.
알림 목록 캐시를 LIST_SCOPE 단위로 한 번에 다시 불러오도록 처리해주셔서, page·limit이 붙은 키까지 한 번에 걸리도록 잘 구현하신 것 같아요. 페이지네이션이 늘어나도 invalidate 코드를 다시 손댈 필요가 없는 구조라 좋은 것 같습니다.
고생하셨습니다 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants