Skip to content

Notification Flow

l2juhan edited this page Apr 18, 2026 · 2 revisions

μ•Œλ¦Ό κΈ°λŠ₯ ν”Œλ‘œμš° (Notification Flow)

인앱 μ•Œλ¦Ό, ν‘Έμ‹œ μ•Œλ¦Ό, μ•Œλ¦Ό 섀정에 λŒ€ν•œ 전체 ν”Œλ‘œμš°λ₯Ό μ„€λͺ…ν•©λ‹ˆλ‹€.


1. κ°œμš”

ν•­λͺ© λ‚΄μš©
λŒ€μƒ μ‚¬μš©μž WORKER, EMPLOYER 곡톡
인앱 μ•Œλ¦Ό SSE μ‹€μ‹œκ°„ ꡬ독 + Header 벨 μ•„μ΄μ½˜ 뱃지
ν‘Έμ‹œ μ•Œλ¦Ό FCM 토큰 기반 (μ•± λ°±κ·ΈλΌμš΄λ“œ/μ’…λ£Œ μ‹œ)
API λͺ¨λ“ˆ src/api/notification/

2. μ•Œλ¦Ό μ‹œμŠ€ν…œ ꡬ쑰

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      μ•Œλ¦Ό μ‹œμŠ€ν…œ ꡬ쑰                             β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                 β”‚
β”‚  [μ•± Foreground]                                                β”‚
β”‚      β”‚                                                          β”‚
β”‚      β”œβ”€β†’ SSE ꡬ독 (/api/notifications/stream)                   β”‚
β”‚      β”‚     β†’ μƒˆ μ•Œλ¦Ό μˆ˜μ‹  μ‹œ unreadCount 증가                    β”‚
β”‚      β”‚     β†’ Header 벨 μ•„μ΄μ½˜μ— 숫자 뱃지 ν‘œμ‹œ (1~9, 10+β†’"9+") β”‚
β”‚      β”‚                                                          β”‚
β”‚      └─→ 벨 μ•„μ΄μ½˜ 클릭 β†’ NotificationPopup (졜근 5개)          β”‚
β”‚            β†’ "전체 보기" β†’ NotificationScreen                   β”‚
β”‚                                                                 β”‚
β”‚  [μ•± Background/μ’…λ£Œ]                                            β”‚
β”‚      β”‚                                                          β”‚
β”‚      β”œβ”€β†’ FCM ν‘Έμ‹œ μ•Œλ¦Ό (OS μ•Œλ¦Όμ„Όν„°μ— ν‘œμ‹œ)                      β”‚
β”‚      β”‚                                                          β”‚
β”‚      └─→ μ•Œλ¦Ό νƒ­ β†’ λ”₯링크 λ„€λΉ„κ²Œμ΄μ…˜ (actionType 기반)           β”‚
β”‚                                                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

3. SSE μ‹€μ‹œκ°„ μ•Œλ¦Ό ꡬ독

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    SSE μ‹€μ‹œκ°„ ꡬ독 ν”Œλ‘œμš°                          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                 β”‚
β”‚  [둜그인 성곡]                                                   β”‚
β”‚      β”‚                                                          β”‚
β”‚      β–Ό                                                          β”‚
β”‚  useNotificationStream() ν™œμ„±ν™”                                  β”‚
β”‚      β”‚                                                          β”‚
β”‚      β”œβ”€β†’ GET /api/notifications/unread-count                     β”‚
β”‚      β”‚     β†’ 초기 읽지 μ•Šμ€ μ•Œλ¦Ό 수 β†’ notificationStore μ €μž₯     β”‚
β”‚      β”‚                                                          β”‚
β”‚      └─→ SSE μ—°κ²° (/api/notifications/stream)                   β”‚
β”‚            β†’ XMLHttpRequest 기반 (React Native ν˜Έν™˜)             β”‚
β”‚            β†’ μ—°κ²° λŠκΉ€ μ‹œ 5초 ν›„ μžλ™ μž¬μ—°κ²°                     β”‚
β”‚            β†’ μƒˆ 이벀트 μˆ˜μ‹  μ‹œ incrementUnreadCount()             β”‚
β”‚                                                                 β”‚
β”‚  [λ‘œκ·Έμ•„μ›ƒ/μ–Έλ§ˆμš΄νŠΈ]                                              β”‚
β”‚      β”‚                                                          β”‚
β”‚      β–Ό                                                          β”‚
β”‚  SSE μ—°κ²° ν•΄μ œ                                                   β”‚
β”‚                                                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

notificationStore (Zustand)

interface NotificationStoreState {
  unreadCount: number;
  setUnreadCount: (count: number) => void;
  incrementUnreadCount: () => void;
  decrementUnreadCount: () => void;
}

4. FCM 토큰 등둝/μ‚­μ œ

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    FCM 토큰 관리 ν”Œλ‘œμš°                            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                 β”‚
β”‚  [둜그인 성곡]                                                   β”‚
β”‚      β”‚                                                          β”‚
β”‚      β–Ό                                                          β”‚
β”‚  useFcmToken() ν™œμ„±ν™”                                            β”‚
β”‚      β”‚                                                          β”‚
β”‚      β–Ό                                                          β”‚
β”‚  GET /api/settings/me β†’ pushEnabled 쑰회 (2026-03-09 λ³€κ²½)       β”‚
β”‚      β”‚                                                          β”‚
β”‚      β”œβ”€β†’ pushEnabled: false β†’ 토큰 λ“±λ‘ν•˜μ§€ μ•ŠμŒ                 β”‚
β”‚      β”‚                                                          β”‚
β”‚      └─→ pushEnabled: true β†’ registerPushToken()                β”‚
β”‚              β”‚                                                  β”‚
β”‚              β–Ό                                                  β”‚
β”‚          expo-notifications둜 Expo Push Token λ°œκΈ‰               β”‚
β”‚          (λ””λ°”μ΄μŠ€λ§ˆλ‹€ κ³ μœ ν•œ 토큰)                                β”‚
β”‚              β”‚                                                  β”‚
β”‚              β–Ό                                                  β”‚
β”‚          POST /api/notifications/fcm-token                       β”‚
β”‚          { token: "ExponentPushToken[xxx]", deviceInfo: "ios 18" }β”‚
β”‚              β”‚                                                  β”‚
β”‚              β–Ό                                                  β”‚
β”‚          AsyncStorage에 토큰 μ €μž₯ (λ‘œκ·Έμ•„μ›ƒ μ‹œ μ‚­μ œμš©)            β”‚
β”‚                                                                 β”‚
β”‚  [λ‘œκ·Έμ•„μ›ƒ]                                                      β”‚
β”‚      β”‚                                                          β”‚
β”‚      β–Ό                                                          β”‚
β”‚  unregisterPushToken()                                           β”‚
β”‚      β”‚                                                          β”‚
β”‚      β–Ό                                                          β”‚
β”‚  DELETE /api/notifications/fcm-token { token: "μ €μž₯된 토큰" }     β”‚
β”‚      β”‚                                                          β”‚
β”‚      β–Ό                                                          β”‚
β”‚  AsyncStorageμ—μ„œ 토큰 제거                                      β”‚
β”‚      β”‚                                                          β”‚
β”‚      β–Ό                                                          β”‚
β”‚  logout() μ‹€ν–‰ (auth 토큰 μ‚­μ œ, μΏ ν‚€ μ‚­μ œ)                       β”‚
β”‚                                                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

5. ν‘Έμ‹œ μ•Œλ¦Ό λ”₯링크 λ„€λΉ„κ²Œμ΄μ…˜

μ•Œλ¦Όμ„ νƒ­ν•˜λ©΄ actionType에 따라 ν•΄λ‹Ή ν™”λ©΄μœΌλ‘œ μ΄λ™ν•©λ‹ˆλ‹€.

actionType β†’ ν™”λ©΄ λ§€ν•‘

actionType 근둜자 고용주
VIEW_WORK_RECORD WorkerHomeMain (μ£Όκ°„ μΊ˜λ¦°λ”) EmployerHomeMain (일간 μΊ˜λ¦°λ”)
VIEW_CORRECTION_REQUEST SentRequests (보낸 μš”μ²­) EmployerReceivedRequests (받은 μš”μ²­)
VIEW_PENDING_APPROVAL Notifications EmployerReceivedRequests
VIEW_SALARY WorkerMonthlyCalendar Notifications
VIEW_PAYMENT_MANAGEMENT Notifications RemittanceManage (μ†‘κΈˆκ΄€λ¦¬)
VIEW_WORKPLACE_INVITATION WorkplaceManage WorkerManage (직원관리)
VIEW_NOTICE Notifications Notifications
NONE Notifications Notifications

λ”₯링크 처리 방식

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    λ”₯링크 λ„€λΉ„κ²Œμ΄μ…˜ ν”Œλ‘œμš°                         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                 β”‚
β”‚  [μ•± μ’…λ£Œ μƒνƒœμ—μ„œ μ•Œλ¦Ό νƒ­]                                       β”‚
β”‚      β”‚                                                          β”‚
β”‚      β–Ό                                                          β”‚
β”‚  getLastNotificationResponseAsync()                              β”‚
β”‚  β†’ λ§ˆμ§€λ§‰ μ•Œλ¦Ό 응닡 확인 β†’ actionType μΆ”μΆœ β†’ navigate()          β”‚
β”‚                                                                 β”‚
β”‚  [μ•± λ°±κ·ΈλΌμš΄λ“œμ—μ„œ μ•Œλ¦Ό νƒ­]                                       β”‚
β”‚      β”‚                                                          β”‚
β”‚      β–Ό                                                          β”‚
β”‚  addNotificationResponseReceivedListener()                       β”‚
β”‚  β†’ μ•Œλ¦Ό 응닡 이벀트 μˆ˜μ‹  β†’ actionType μΆ”μΆœ β†’ navigate()          β”‚
β”‚                                                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

6. μ•Œλ¦Ό 상세 ν™”λ©΄ (NotificationScreen)

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    μ•Œλ¦Ό 상세 ν™”λ©΄ ꡬ쑰                             β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                 β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  β”‚
β”‚  β”‚ Header: [←]  "μ•Œλ¦Ό"  [전체 읽음]          β”‚                  β”‚
β”‚  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€                  β”‚
β”‚  β”‚ Filter Chips                              β”‚                  β”‚
β”‚  β”‚  [전체]  [읽지 μ•Šμ€ μ•Œλ¦Ό N]               β”‚                  β”‚
β”‚  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€                  β”‚
β”‚  β”‚ NotificationItem (FlatList, pull-refresh) β”‚                  β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚                  β”‚
β”‚  β”‚  β”‚ [πŸ””] 근무 일정이 λ³€κ²½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.  Γ—β”‚  β”‚                  β”‚
β”‚  β”‚  β”‚      3λΆ„ μ „                         β”‚  β”‚                  β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚                  β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚                  β”‚
β”‚  β”‚  β”‚ [πŸ“‹] μ •μ • μš”μ²­μ΄ μŠΉμΈλ˜μ—ˆμŠ΅λ‹ˆλ‹€.  Γ—β”‚  β”‚                  β”‚
β”‚  β”‚  β”‚      1μ‹œκ°„ μ „                       β”‚  β”‚                  β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚                  β”‚
β”‚  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€                  β”‚
β”‚  β”‚ Pagination (ν•˜λ‹¨ κ³ μ •)                    β”‚                  β”‚
β”‚  β”‚  < [1] [2] [3] ... [10] >                 β”‚                  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  β”‚
β”‚                                                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

μ„œλ²„μ‚¬μ΄λ“œ νŽ˜μ΄μ§€λ„€μ΄μ…˜

GET /api/notifications?page=0&size=10&is_read=false
β†’ { content: [...], totalPages: 5, totalElements: 42 }

7. μ•Œλ¦Ό μ„€μ • ν™”λ©΄ (NotificationSettingsScreen)

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    μ•Œλ¦Ό μ„€μ • ν™”λ©΄ ꡬ쑰                             β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                 β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  β”‚
β”‚  β”‚ Header: [←]  "μ•Œλ¦Ό μ„€μ •"                  β”‚                  β”‚
β”‚  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€                  β”‚
β”‚  β”‚                                           β”‚                  β”‚
β”‚  β”‚  ν‘Έμ‹œ μ•Œλ¦Ό                    [Toggle ●]  β”‚                  β”‚
β”‚  β”‚  앱을 μ‚¬μš©ν•˜μ§€ μ•Šμ„ λ•Œλ„                   β”‚                  β”‚
β”‚  β”‚  μ•Œλ¦Όμ„ λ°›μŠ΅λ‹ˆλ‹€.                          β”‚                  β”‚
β”‚  β”‚  ─────────────────────────                β”‚                  β”‚
β”‚  β”‚  λ””λ°”μ΄μŠ€ μ•Œλ¦Ό μ„€μ • μ—΄κΈ°      πŸ”—           β”‚                  β”‚
β”‚  β”‚  ─────────────────────────                β”‚                  β”‚
β”‚  β”‚  λ””λ°”μ΄μŠ€ μ„€μ •μ—μ„œ μ•Œλ¦Ό κΆŒν•œμ„ 끄면         β”‚                  β”‚
β”‚  β”‚  ν‘Έμ‹œ μ•Œλ¦Όμ΄ μ•± λ‚΄ μ„€μ •κ³Ό 관계없이          β”‚                  β”‚
β”‚  β”‚  μˆ˜μ‹ λ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.                         β”‚                  β”‚
β”‚  β”‚                                           β”‚                  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  β”‚
β”‚                                                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

ν† κΈ€ λ™μž‘ (2026-03-09 λ³€κ²½, PR #41)

기쑴의 AsyncStorage 둜컬 μ €μž₯ λ°©μ‹μ—μ„œ μ„œλ²„ API 동기화 λ°©μ‹μœΌλ‘œ 변경됐닀.

[ν™”λ©΄ μ§„μž…]
  β†’ GET /api/settings/me β†’ { pushEnabled } λ‘œλ“œ β†’ ν† κΈ€ μ΄ˆκΈ°κ°’ μ„€μ •

[ν† κΈ€ OFF β†’ ON]:
  β†’ λ””λ°”μ΄μŠ€ μ•Œλ¦Ό κΆŒν•œ 확인
  β†’ κΆŒν•œ OFF β†’ Alert "λ””λ°”μ΄μŠ€ μ„€μ •μ—μ„œ μ•Œλ¦Όμ„ ν—ˆμš©ν•΄μ£Όμ„Έμš”" + μ„€μ • 이동
  β†’ κΆŒν•œ ON β†’ PUT /api/settings/me { pushEnabled: true }
           β†’ 성곡 μ‹œ registerPushToken() 호좜

[ν† κΈ€ ON β†’ OFF]:
  β†’ PUT /api/settings/me { pushEnabled: false }
  β†’ 성곡 μ‹œ unregisterPushToken() 호좜
ꡬ뢄 이전 (둜컬) ν˜„μž¬ (μ„œλ²„)
μ €μž₯μ†Œ AsyncStorage (PUSH_ENABLED_KEY) μ„œλ²„ DB (/api/settings/me)
닀쀑 λ””λ°”μ΄μŠ€ λ””λ°”μ΄μŠ€λ³„ λ”°λ‘œ 관리 계정 λ‹¨μœ„λ‘œ 톡일
ν›… 연동 useFcmTokenκ°€ AsyncStorage 쑰회 useFcmTokenκ°€ GET /api/settings/me 쑰회
κ΄€λ ¨ 파일 src/api/settings/{index,types}.ts μ‹ κ·œ 생성 β€”

8. API μ—”λ“œν¬μΈνŠΈ μš”μ•½

인앱 μ•Œλ¦Ό

λ©”μ„œλ“œ μ—”λ“œν¬μΈνŠΈ μ„€λͺ…
GET /api/notifications μ•Œλ¦Ό λͺ©λ‘ 쑰회 (page, size, is_read)
GET /api/notifications/unread-count 읽지 μ•Šμ€ μ•Œλ¦Ό 수
GET /api/notifications/stream SSE μ‹€μ‹œκ°„ μ•Œλ¦Ό ꡬ독
PUT /api/notifications/{id}/read μ•Œλ¦Ό 읽음 처리
PUT /api/notifications/read-all 전체 읽음 처리
DELETE /api/notifications/{id} μ•Œλ¦Ό μ‚­μ œ

FCM 토큰

λ©”μ„œλ“œ μ—”λ“œν¬μΈνŠΈ μ„€λͺ…
POST /api/notifications/fcm-token FCM 토큰 등둝
DELETE /api/notifications/fcm-token FCM 토큰 μ‚­μ œ

μ•Œλ¦Ό μ„€μ • (2026-03-09 μ‹ κ·œ, PR #41)

λ©”μ„œλ“œ μ—”λ“œν¬μΈνŠΈ μ„€λͺ…
GET /api/settings/me μ‚¬μš©μž μ•Œλ¦Ό μ„€μ • 쑰회 (pushEnabled λ“±)
PUT /api/settings/me μ‚¬μš©μž μ•Œλ¦Ό μ„€μ • κ°±μ‹ 

9. κ΄€λ ¨ 파일

λΆ„λ₯˜ 파일 μ„€λͺ…
API api/notification/types.ts μ•Œλ¦Ό νƒ€μž… μ •μ˜ (NotificationType, ActionType, PagedResponse λ“±)
API api/notification/index.ts μ•Œλ¦Ό API ν•¨μˆ˜ 7개
Store stores/notificationStore.ts unreadCount μ „μ—­ μƒνƒœ
Hook hooks/common/useNotifications.ts μ•Œλ¦Ό ν™”λ©΄ 데이터 관리
Hook hooks/common/useNotificationStream.ts SSE ꡬ독 + 뱃지 μ—…λ°μ΄νŠΈ
Hook hooks/common/useFcmToken.ts 둜그인 μ‹œ FCM 토큰 μžλ™ 등둝
Hook hooks/common/useNotificationNavigation.ts ν‘Έμ‹œ νƒ­ β†’ λ”₯링크
Util utils/sse.ts XMLHttpRequest 기반 SSE ν΄λΌμ΄μ–ΈνŠΈ
Util utils/pushToken.ts 토큰 λ°œκΈ‰/등둝/μ‚­μ œ μœ ν‹Έ
Screen screens/common/NotificationScreen.tsx μ•Œλ¦Ό 상세 ν™”λ©΄
Screen screens/common/NotificationSettingsScreen.tsx μ•Œλ¦Ό μ„€μ • ν™”λ©΄
Component components/common/notification/NotificationPopup.tsx Header μ•Œλ¦Ό 팝μ—