Skip to content

[Refactor] SSE 훅을 Provider로 통합, unReadCount 전역상태에서 제거#394

Merged
Chiman2937 merged 3 commits intomainfrom
chiyoung-refactor/notification-state
Feb 24, 2026
Merged

[Refactor] SSE 훅을 Provider로 통합, unReadCount 전역상태에서 제거#394
Chiman2937 merged 3 commits intomainfrom
chiyoung-refactor/notification-state

Conversation

@Chiman2937
Copy link
Member

@Chiman2937 Chiman2937 commented Feb 24, 2026

📝 변경 사항

SSEProvider 통합 및 NotificationProvider 제거 변경사항

브랜치: chiyoung-refactor/notification-state
커밋 범위: e4774f3 ~ e9c48ec (최근 3개 커밋)
변경 파일: 9개 (66 additions, 97 deletions)


커밋 히스토리

커밋 메시지
c200170 fix: useSSEConnect, useSSEEvent를 SSEProvider로 통합
e9c48ec fix: unreadcount는 react query 훅을 직접 호출하도록 수정(전역 상태 저장 x)

1. useSSEConnect + useSSEEvent → SSEProvider 통합

기존에 분리되어 있던 useSSEConnect(연결 관리)와 useSSEEvent(이벤트 처리) 두 훅을 SSEProvider로 통합. NotificationProvider를 완전히 대체.

통합 이유

커스텀 훅은 본질적으로 재사용을 위한 패턴이지만, useSSEConnectuseSSEEvent는 앱 전체에서 단 한 번만 실행되어야 하며 재사용되면 안 되는 로직. 재사용 목적이 아닌 로직을 커스텀 훅으로 관리하는 것은 패턴에 위반되므로, Provider 내부에 종속시켜 단일 실행을 보장하도록 변경.

Before

src/hooks/use-sse/
  ├── index.ts                    ← re-export
  ├── use-sse-connect/index.ts    ← SSE 연결 관리 훅
  └── use-sse-event/index.ts      ← 쿼리 무효화 훅

src/providers/provider-notification/index.tsx  ← Context 기반 Provider

After

src/providers/provider-sse/index.tsx  ← SSE 연결 + 이벤트 처리 + Store 관리 통합

useSSEConnect 훅을 Provider 컴포넌트로 전환하면서 useSSEEvent의 쿼리 무효화 로직(SSE_INVALIDATION_MAP)도 내부에 통합. SSE 관련 로직이 하나의 Provider에 응집됨.

SSEProvider 내부 구조

export const SSEProvider = ({ children }: Props) => {
  // SSE 연결 관리 (기존 useSSEConnect)
  const eventSourceRef = useRef<EventSource | null>(null);
  const queryClient = useQueryClient();
  const { setReceivedData, setHasNewNotification } = useNotificationStore();

  // SSE 이벤트 수신 시 → store 업데이트 + 쿼리 무효화 (기존 useSSEEvent)
  useEffect(() => {
    if (!receivedData) return;
    queryClient.invalidateQueries({ queryKey: notificationKeys.unReadCount() });
    // ... SSE_INVALIDATION_MAP 기반 무효화
  }, [receivedData]);

  // 3초 후 알림 상태 초기화
  useEffect(() => {
    if (!receivedData) return;
    const timer = setTimeout(() => {
      setReceivedData(null);
      setHasNewNotification(false);
    }, 3000);
    return () => clearTimeout(timer);
  }, [receivedData]);

  return <>{children}</>;
};

2. NotificationProvider 삭제 - unReadCount는 React Query 훅 직접 호출

NotificationProvider (Context 기반)를 완전히 삭제. 기존에 Context를 통해 전달하던 unReadCount는 필요한 컴포넌트에서 useGetNotificationUnreadCount 훅을 직접 호출하도록 변경.

변경 이유

unReadCount는 서버 상태이므로 전역 상태(Context/Store)에 저장하지 않고, React Query 훅을 직접 호출하여 관리하는 것이 상태 관리 원칙에 부합.

상태 유형 관리 방식
unReadCount 서버 상태 React Query (useGetNotificationUnreadCount 직접 호출)
receivedData 클라이언트 상태 Zustand (useNotificationStore)
hasNewNotification 클라이언트 상태 Zustand (useNotificationStore)

컴포넌트별 변경

// Before - Context를 통해 가져옴
const { unReadCount, receivedNewNotification } = useNotification();

// After - 서버 상태는 훅 직접 호출, 클라이언트 상태는 store
const { unReadCount } = useGetNotificationUnreadCount();
const { hasNewNotification } = useNotificationStore();
컴포넌트 Before After
cow-bell (헤더 알림 벨) useNotification() useGetNotificationUnreadCount() + useNotificationStore()
notification-header useNotification() useGetNotificationUnreadCount()

3. providers.tsx Provider 교체

src/app/providers.tsx에서 NotificationProviderSSEProvider로 교체.

// Before
<AuthProvider hasRefreshToken={hasRefreshToken}>
  <NotificationProvider>
    ...
  </NotificationProvider>
</AuthProvider>

// After
<AuthProvider hasRefreshToken={hasRefreshToken}>
  <SSEProvider>
    ...
  </SSEProvider>
</AuthProvider>

삭제된 파일 목록

파일 사유
src/hooks/use-sse/index.ts SSEProvider로 통합
src/hooks/use-sse/use-sse-event/index.ts SSEProvider 내부로 통합
src/providers/provider-notification/index.tsx SSEProvider + 직접 훅 호출로 대체

변경된 파일 목록

파일 변경 유형
src/hooks/use-sse/use-sse-connect/index.tssrc/providers/provider-sse/index.tsx 이동 및 통합 (훅 → Provider, useSSEEvent 로직 병합)
src/providers/index.ts 수정 (NotificationProvider → SSEProvider)
src/app/providers.tsx 수정 (NotificationProvider → SSEProvider)
src/components/layout/header/cow-bell/index.tsx 수정 (useNotification → useNotificationStore + 훅 직접 호출)
src/components/pages/notification/notification-header/index.tsx 수정 (useNotification → 훅 직접 호출)

🔗 관련 이슈

Closes #


🧪 테스트 방법

  • 수동 테스트 검증(로컬 환경)
  • 유닛 테스트 검증
  • 통합 테스트 검증

📸 스크린샷 (선택)


📋 체크리스트

  • 관련 문서를 업데이트했습니다 (필요한 경우)
  • 테스트를 추가/수정했습니다 (필요한 경우)
  • Breaking change가 있다면 명시했습니다

💬 추가 코멘트


CodeRabbit Review는 자동으로 실행되지 않습니다.

Review를 실행하려면 comment에 아래와 같이 작성해주세요

@coderabbitai review

Summary by CodeRabbit

릴리스 노트

  • Refactor
    • 알림 상태 관리 시스템을 재구조화했습니다. 기존 알림 제공자 기반 구조에서 더 효율적인 상태 관리 체계로 전환되었으며, 사용자 경험에는 영향을 주지 않습니다.

@Chiman2937
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 24, 2026

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

워크스루

알림 시스템을 React Context 기반 후크에서 Zustand 저장소 및 SSE 컴포넌트 기반 아키텍처로 재구성합니다. NotificationProvider를 SSEProvider로 교체하고, 관련 후크 및 쿼리 무효화 로직을 업데이트하여 저장소 기반 상태 관리로 전환합니다.

변경사항

Cohort / File(s) 요약
Provider 구조 재편성
src/app/providers.tsx, src/providers/index.ts
NotificationProvider를 SSEProvider로 교체하고 공개 내보내기 업데이트.
알림 상태 관리 패턴 변경
src/components/layout/header/cow-bell/index.tsx, src/components/pages/notification/notification-header/index.tsx
useNotification에서 useNotificationStore 및 useGetNotificationUnreadCount로 마이그레이션하여 저장소 기반 상태 사용.
SSE 후크 정리
src/hooks/use-sse/index.ts, src/hooks/use-sse/use-sse-event/index.ts
useSSEConnect 및 useSSEEvent 내보내기 제거 및 use-sse-event 파일 삭제.
알림 Provider 제거
src/providers/provider-notification/index.tsx
Context 기반 NotificationProvider 및 useNotification 후크 전체 제거 (35줄).
SSE Provider 재구현
src/providers/provider-sse/index.tsx
useSSEConnect 후크를 SSEProvider 컴포넌트로 교체하고 저장소 기반 상태 관리 및 쿼리 무효화 로직 추가.

예상 코드 리뷰 난이도

🎯 4 (복잡) | ⏱️ ~50분

관련된 가능성 있는 PR들

제안된 레이블

Ready For Review!

제안된 리뷰어

  • wooktori

시 🐰

Context의 우산을 걷어내고 ✨
저장소의 빛이 비치네
SSE는 흘러흘러 무효화되고
알림의 종은 더 맑게 울려 🔔
새로운 구조로 춤을 춘다

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed PR 제목은 주요 변경사항을 명확하고 간결하게 요약하고 있습니다. SSE 훅을 Provider로 통합하고 unReadCount를 전역 상태에서 제거한다는 핵심 내용을 정확히 전달합니다.
Description check ✅ Passed PR 설명은 템플릿 구조를 따르고 있으며, 변경사항에 대한 상세한 설명, 커밋 히스토리, Before/After 코드 예시, 변경된 파일 목록 등을 포함하고 있습니다. 테스트 방법과 체크리스트 섹션도 포함되어 있습니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chiyoung-refactor/notification-state

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 and usage tips.

@github-actions
Copy link

github-actions bot commented Feb 24, 2026

📊 Coverage Report

Status Build Log Updated (UTC)
✅ Ready View Build 2026-02-24 07:50:29

📉 #394main에 병합하면 coverage가 0.33% 감소합니다.

Coverage 요약

@@             Coverage Diff             @@
##             main     #394       +/-   ##
===========================================
- Coverage   35.69%   35.36%    -0.33%     
===========================================
  Files         267      264        -3     
  Lines       12136    12104       -32     
  Branches      469      474        +5     
===========================================
- Hits         4332     4280       -52     
+ Misses       7804     7824       +20     

영향받은 파일

파일 Coverage 변화
/home/runner/work/WeGo_FrontEnd/WeGo_FrontEnd/src/hooks/use-notification/index.ts 0.00% (-100.00%) ⬇️
/home/runner/work/WeGo_FrontEnd/WeGo_FrontEnd/src/hooks/use-notification/use-notification-get-list/index.ts 0.00% (-46.66%) ⬇️
/home/runner/work/WeGo_FrontEnd/WeGo_FrontEnd/src/hooks/use-notification/use-notification-get-unread-count/index.ts 0.00% (-30.43%) ⬇️
/home/runner/work/WeGo_FrontEnd/WeGo_FrontEnd/src/hooks/use-notification/use-notification-update-read/index.ts 0.00% (-40.00%) ⬇️
/home/runner/work/WeGo_FrontEnd/WeGo_FrontEnd/src/hooks/use-notification/use-notification-update-read-all/index.ts 0.00% (-40.00%) ⬇️
/home/runner/work/WeGo_FrontEnd/WeGo_FrontEnd/src/providers/provider-sse/index.tsx 19.87% (+19.87%) ⬆️

@github-actions
Copy link

github-actions bot commented Feb 24, 2026

🎭 Playwright Report

E2E Test가 성공적으로 완료되었습니다.

Test 요약 내용을 확인해주세요.

Status Build Log Updated (UTC)
✅ Ready View Build 2026-02-24 07:51:50

📊 Test Summary

  • ✅ Passed: 3
  • ❌ Failed: 0
  • ⏱️ Duration: 36.6s

📜 Test Details

✅ Passed Tests (3)
  • profile.test.ts (3)
    • [chromium] 존재하지 않는 프로필 페이지로 접속 시 404 redirect 되는 지 테스트
    • [firefox] 존재하지 않는 프로필 페이지로 접속 시 404 redirect 되는 지 테스트
    • [webkit] 존재하지 않는 프로필 페이지로 접속 시 404 redirect 되는 지 테스트

@github-actions
Copy link

github-actions bot commented Feb 24, 2026

🎨 Storybook Report

변경 사항이 없습니다

모든 Story가 이전 빌드와 동일합니다.

Status Storybook Build Log Updated (UTC)
✅ Unchanged View Storybook View Build 2026-02-24 07:52:12

@github-actions
Copy link

github-actions bot commented Feb 24, 2026

🚀 PR Preview Report

Build가 성공적으로 완료되었습니다.

Preview에서 변경사항을 확인하세요.

Status Preview Build Log Updated (UTC)
✅ Ready Visit Preview View Logs 2026-02-24 07:51:35

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 24, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Chiman2937
Copy link
Member Author

@coderabbitai 리뷰 내용 없나요?

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 24, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read the main refactored SSE provider
cat src/providers/provider-sse/index.tsx

Length of output: 4809


🏁 Script executed:

#!/bin/bash
# Read the cow-bell component that consumes the new hooks
cat src/components/layout/header/cow-bell/index.tsx

Length of output: 1164


🏁 Script executed:

#!/bin/bash
# Read the notification header component
cat src/components/pages/notification/notification-header/index.tsx

Length of output: 1477


🏁 Script executed:

#!/bin/bash
# Read the notification store to understand client state management
cat src/stores/use-notification-store/index.ts

Length of output: 74


🏁 Script executed:

#!/bin/bash
# Check the useGetNotificationUnreadCount hook implementation
cat src/hooks/use-notification/use-get-notification-unread-count/index.ts

Length of output: 101


🏁 Script executed:

#!/bin/bash
# Find the notification store file
fd -t f "use-notification-store" --full-path

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Find the unread count hook file
fd -t f "use-get-notification-unread-count" --full-path

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Check the stores index to understand exports
cat src/stores/index.ts

Length of output: 109


🏁 Script executed:

#!/bin/bash
# Check the use-notification hooks index
cat src/hooks/use-notification/index.ts

Length of output: 318


🏁 Script executed:

#!/bin/bash
# Read the notification store
cat src/stores/useNotificationStore.ts

Length of output: 66


🏁 Script executed:

#!/bin/bash
# Read the unread count hook
cat src/hooks/use-notification/use-notification-get-unread-count/index.ts

Length of output: 641


🏁 Script executed:

#!/bin/bash
# Check if there are any type definitions for NotificationItem
rg -n "type NotificationItem|interface NotificationItem" --type=ts

Length of output: 72


🏁 Script executed:

#!/bin/bash
# Look at the PR diff to understand what was changed in the SSE provider
git diff e4774f3..e9c48ec -- src/providers/provider-sse/index.tsx

Length of output: 5178


🏁 Script executed:

#!/bin/bash
# Find all store files
find src/stores -type f -name "*.ts" -o -name "*.tsx"

Length of output: 93


🏁 Script executed:

#!/bin/bash
# Read all files in stores directory
cat src/stores/useNotificationStore.ts 2>/dev/null || cat src/stores/useNotificationStore.tsx 2>/dev/null || ls -la src/stores/

Length of output: 300


🏁 Script executed:

#!/bin/bash
# Search for useNotificationStore definition
rg -n "useNotificationStore" --type=ts --type=tsx -A 10

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Read the notification store with correct path
cat src/stores/useNotificationStore/index.ts

Length of output: 579


🏁 Script executed:

#!/bin/bash
# Check what was the old implementation before refactoring
git diff e4774f3..e9c48ec -- src/hooks/use-sse/

Length of output: 5573


🏁 Script executed:

#!/bin/bash
# Check the old notification provider that was removed
git show e4774f3:src/providers/provider-notification/index.tsx

Length of output: 1078


🏁 Script executed:

#!/bin/bash
# Search for setHasNewNotification and setReceivedData usage
rg -n "setHasNewNotification|setReceivedData" -C 2

Length of output: 2577


@Chiman2937 네, 리뷰 드리겠습니다!

전반적으로 SSE 로직을 하나의 Provider로 통합하고 서버 상태와 클라이언트 상태를 명확하게 분리한 점은 좋은 리팩토링입니다. 다만 몇 가지 개선할 부분이 있습니다:

주요 이슈

1. 의존성 배열 문제 (src/providers/provider-sse/index.tsx, Line 158)

}, [receivedData, setReceivedData]);

Zustand의 setter 함수(setReceivedData, setHasNewNotification)는 stable하므로 의존성 배열에 포함할 필요가 없습니다.

수정 제안:

}, [receivedData]);

2. 함수 정의 위치 및 메모이제이션

connect, disconnect, reconnect, setupSSEConnection 함수들이 컴포넌트 본문에 직접 정의되어 있어 매 렌더링마다 재생성됩니다. 이들이 클로저로 외부 값들을 캡처하고 있는데, 의존성 관리가 명확하지 않습니다.

수정 제안:

const setupSSEConnection = useCallback((token: string) => {
  // ... existing code
}, [setReceivedData, setHasNewNotification]);

const reconnect = useCallback(async () => {
  // ... existing code
}, [setupSSEConnection]);

// 또는 connect/disconnect도 useCallback으로 감싸거나
// 모든 로직을 useEffect 내부로 이동

3. 순환 참조 패턴

Line 38에서 store의 receivedData를 구독하고, Line 111에서 같은 store에 데이터를 설정한 후, Line 135-145의 useEffect에서 다시 이 데이터를 읽어 처리하는 구조입니다. SSEProvider 내부에서 완결되는 로직이므로 굳이 전역 store를 거칠 필요가 있는지 재검토가 필요합니다.

제안:

  • receivedData는 Provider 내부 로컬 state로 관리하고
  • hasNewNotification만 전역 store로 관리하는 것이 더 명확할 수 있습니다

4. 하드코딩된 타임아웃

Line 155의 3초 timeout이 하드코딩되어 있습니다. 상수로 분리하는 것이 좋습니다:

const NOTIFICATION_DISPLAY_DURATION = 3000;

const timer = setTimeout(() => {
  setReceivedData(null);
  setHasNewNotification(false);
}, NOTIFICATION_DISPLAY_DURATION);

긍정적인 부분

✅ 서버 상태(unReadCount)와 클라이언트 상태(hasNewNotification)의 명확한 분리
✅ SSE 연결/이벤트 처리 로직의 단일 책임 원칙 준수
✅ Context 중첩 제거로 Provider 구조 단순화
✅ 쿼리 무효화 맵(SSE_INVALIDATION_MAP)의 명확한 정의

이러한 개선사항들을 반영하면 더 견고한 코드가 될 것 같습니다. 추가 질문 있으시면 말씀해주세요!

@Chiman2937 Chiman2937 merged commit b00d240 into main Feb 24, 2026
7 checks passed
@Chiman2937 Chiman2937 deleted the chiyoung-refactor/notification-state branch February 24, 2026 07:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant