Skip to content

Comments

[feat] 지원자 상태 변경 SSE 실시간 동기화 구현#1062

Merged
lepitaaar merged 12 commits intodevelop-fefrom
feature/#798-sync-applicant-content-MOA-296
Jan 19, 2026
Merged

[feat] 지원자 상태 변경 SSE 실시간 동기화 구현#1062
lepitaaar merged 12 commits intodevelop-fefrom
feature/#798-sync-applicant-content-MOA-296

Conversation

@lepitaaar
Copy link
Contributor

@lepitaaar lepitaaar commented Jan 18, 2026

#️⃣ 연관된 이슈

#798

📝 작업 내용

SSE(Server-Sent Events)를 활용한 지원자 상태 실시간 동기화 기능 구현

2026-01-18.00.07.59.mov

지원자 상태 변경 시 실시간으로 UI에 반영되도록 SSE 연결 기능을 구현했습니다.

주요 변경사항

  1. eventsource 라이브러리 도입

    • 브라우저 네이티브 EventSource는 커스텀 헤더(Authorization)를 지원하지 않아, eventsource 라이브러리(v4.1.0)를 도입
    • 커스텀 fetch 옵션을 통해 인증 토큰 전송 가능
  2. SSE 연결 함수 구현 (createApplicantSSE)

    • club.ts에 SSE 연결 생성 함수 추가
    • 자동 재연결 로직 포함 (2초 딜레이)
    • applicant-status-changed 이벤트 리스닝
  3. AdminClubContext 내 SSE 연결 핸들링

    • Context 레벨에서 SSE 연결 관리
    • 지원자 상태 변경 시 자동으로 UI 업데이트
  4. SSE 타입 정의

    • ApplicantSSECallbacks, ApplicantStatusEvent 타입 추가

🫡 참고사항

  • 네이티브 EventSource는 커스텀 헤더를 지원하지 않아 eventsource 라이브러리를 사용했습니다
  • SSE 연결 끊김 시 2초 후 자동 재연결됩니다
  • 백엔드에서 applicant-status-changed 이벤트를 발송하면 프론트엔드에서 해당 지원자 정보가 자동 갱신됩니다

Summary by CodeRabbit

  • 새로운 기능

    • 지원자 상태 및 메모가 페이지 새로고침 없이 실시간으로 반영됩니다.
    • 관리 화면에서 실시간 업데이트 구독을 켜고 끌 수 있는 제어(신청폼 ID 설정)가 추가되었습니다.
  • 기타

    • SSE(EventSource) 지원이 전반적으로 추가/향상되어 더 넓은 환경에서 실시간 알림이 가능해졌습니다.
    • 실시간 연결 오류 시 오류 콜백 호출 및 연결 정리(해제) 처리 로직이 추가되었습니다.

✏️ Tip: You can customize this high-level summary in your review settings.

@lepitaaar lepitaaar self-assigned this Jan 18, 2026
@lepitaaar lepitaaar added ✨ Feature 기능 개발 💻 FE Frontend labels Jan 18, 2026
@vercel
Copy link

vercel bot commented Jan 18, 2026

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

Project Deployment Review Updated (UTC)
moadong Ready Ready Preview, Comment Jan 19, 2026 4:55am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 18, 2026

Warning

.coderabbit.yaml has a parsing error

The CodeRabbit configuration file in this repository has a parsing error and default settings were used instead. Please fix the error(s) in the configuration file. You can initialize chat with CodeRabbit to get help with the configuration file.

💥 Parsing errors (1)
Validation error: Invalid regex pattern for base branch. Received: "**" at "reviews.auto_review.base_branches[0]"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

프론트엔드에 Server-Sent Events(SSE) 기능을 추가: 런타임 의존성 eventsource를 도입하고, createApplicantSSE SSE 클라이언트와 관련 타입을 추가했으며 AdminClubContext에 applicationFormId 상태와 SSE 구독/정리 로직을 연동하고 Applicants 탭 및 상세 페이지에서 이를 사용하도록 변경했습니다.

Changes

Cohort / File(s) 변경 사항 요약
의존성
frontend/package.json
런타임 의존성 eventsource (^4.1.0) 추가
SSE API 계층
frontend/src/apis/club.ts
`createApplicantSSE(applicationFormId, callbacks): EventSource
타입 정의
frontend/src/types/applicants.ts
ApplicantStatusEvent, ApplicantSSECallbacks 인터페이스 추가 (applicantId, status, memo, timestamp, clubId, applicationFormId, onStatusChange, onError)
컨텍스트 변경
frontend/src/context/AdminClubContext.tsx
applicationFormId 상태 및 setApplicationFormId 노출; SSE 구독/정리 useEffect 추가; applicantsData를 status/memo 변경에 따라 불변 업데이트
탭 연동
frontend/src/pages/AdminPage/tabs/ApplicantsTab/*
ApplicantsTab에서 setApplicationFormId(formId) 호출 및 컴포넌트 언마운트 시 null로 정리
Detail 페이지 조정
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx
effect 의존성에 applicant?.status, applicant?.memo 추가 (필드 변경 시 재실행 보장)

Sequence Diagram(s)

sequenceDiagram
    participant User as 사용자
    participant ApplicantsTab as ApplicantsTab
    participant AdminClubContext as AdminClubContext
    participant API as createApplicantSSE
    participant Server as 서버(SSE 엔드포인트)
    participant ApplicantDetail as ApplicantDetailPage

    User->>ApplicantsTab: 지원자 탭 방문
    ApplicantsTab->>AdminClubContext: setApplicationFormId(formId)
    AdminClubContext->>API: createApplicantSSE(formId, callbacks)
    API->>Server: EventSource 연결 (Bearer 토큰, credentials)
    Server-->>API: SSE 연결 수립

    loop 상태 변경 발생
        Server-->>API: applicant-status-changed 이벤트 (JSON)
        API->>AdminClubContext: callbacks.onStatusChange(event)
        AdminClubContext->>AdminClubContext: applicantsData 업데이트 (status/memo)
        AdminClubContext-->>ApplicantsTab: 컨텍스트 업데이트 전파
        AdminClubContext-->>ApplicantDetail: 컨텍스트 업데이트 전파
        ApplicantDetail->>ApplicantDetail: status/memo 변경 감지 및 UI 갱신
    end

    User->>ApplicantsTab: 탭 이탈
    ApplicantsTab->>AdminClubContext: setApplicationFormId(null)
    AdminClubContext->>API: EventSource 연결 종료
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

📬 API

Suggested reviewers

  • Zepelown
  • seongwon030
  • oesnuj
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목이 변경 사항의 주요 내용인 SSE 실시간 동기화 구현을 명확하게 반영하고 있습니다.
Linked Issues check ✅ Passed PR이 MOA-296의 모든 주요 요구사항을 충족합니다: SSE 이벤트 연결 구현, 모집 상태 실시간 업데이트, 메모 필드 업데이트가 모두 포함되어 있습니다.
Out of Scope Changes check ✅ Passed 모든 변경 사항이 SSE 실시간 동기화 구현이라는 범위 내에 있으며, MOA-296의 요구사항과 관련된 변경만 포함되어 있습니다.
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 docstrings

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.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@frontend/src/apis/club.ts`:
- Around line 88-134: The createApplicantSSE function currently captures
accessToken once and returns the original EventSource while its onerror
reconnects to a new instance (causing leaked/uncloseable sources, stale tokens,
and unlimited retries); fix by not performing automatic internal reconnect with
a new EventSource that the caller doesn't hold—instead change
createApplicantSSE(connect) to create and return a single EventSource instance
and remove its internal onerror reconnection, or accept a tokenProvider callback
and a reconnect policy so reconnection is controlled by the caller; specifically
update createApplicantSSE / connect / eventSource to (1) obtain Authorization
via a provided tokenProvider() at connect time (not closed-over localStorage
value), (2) stop replacing the returned eventSource from inside source.onerror
(or if you keep internal reconnect, ensure eventSource is updated and previous
source closed and returned to caller), and (3) implement a retry
limit/exponential backoff (MAX_RETRIES) so onerror does not retry indefinitely.
- Around line 92-93: The code captures accessToken once at the top of
createApplicantSSE which causes stale tokens to be used for long-lived SSE
connections; instead, inside the fetch callback used to open/reconnect the SSE
(i.e., the fetch/eventSource initiation logic within createApplicantSSE), call
localStorage.getItem('accessToken') each time before making the request and set
the Authorization header from that fresh value, handling the null case there
(e.g., abort/retry or throw) rather than returning early from the outer
function; remove the top-level accessToken capture and any early return that
uses it so token lookup is dynamic per connection attempt.
🧹 Nitpick comments (1)
frontend/src/pages/AdminPage/tabs/ApplicantsTab/ApplicantDetailPage/ApplicantDetailPage.tsx (1)

60-65: 의존성 배열의 중복 항목

applicant?.statusapplicant?.memo는 중복된 의존성입니다. Context에서 SSE 이벤트 수신 시 applicant 객체를 불변하게 업데이트하므로 ({ ...applicant, status, memo }), applicant 참조 자체가 변경됩니다. 따라서 applicant만 의존성으로 두어도 동일하게 동작합니다.

♻️ 의존성 배열 단순화 제안
   useEffect(() => {
     if (applicant) {
       setAppMemo(applicant.memo);
       setApplicantStatus(mapStatusToGroup(applicant.status).status);
     }
-  }, [applicant, applicant?.status, applicant?.memo]);
+  }, [applicant]);

Copy link
Member

@seongwon030 seongwon030 left a comment

Choose a reason for hiding this comment

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

고생하셨습니다 리뷰 단 거 확인 부탁드려요

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@frontend/src/context/AdminClubContext.tsx`:
- Around line 74-100: The onError handler inside the sseConnect setup can
schedule multiple reconnect timeouts if errors fire repeatedly; modify the
onError in createApplicantSSE (used by sseConnect in the useEffect) to clear any
existing reconnectTimeoutRef.current before calling setTimeout so only one
reconnect timer is active; ensure reconnectTimeoutRef is set to the new timeout
ID and left cleared on cleanup (you’ll touch eventSourceRef,
reconnectTimeoutRef, sseConnect, createApplicantSSE and the onError callback to
implement this).
- Around line 38-43: The ref type for reconnect timeout uses NodeJS.Timeout
which conflicts with browser DOM types; update the reconnectTimeoutRef
declaration to use a DOM-safe timeout type by replacing NodeJS.Timeout with
ReturnType<typeof setTimeout> (i.e. change the type used in the
reconnectTimeoutRef useRef call), keeping the EventSource ref and
applicationFormId state unchanged so the symbol reconnectTimeoutRef is the only
edit required.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@frontend/src/apis/club.ts`:
- Around line 94-97: The connect function currently returns null silently when
localStorage.getItem('accessToken') is falsy; instead, invoke the SSE error
handling so the upper-level reconnection/login flow can act. Modify connect to
detect missing accessToken and call the existing onError handler (e.g.,
onError(new Error('Missing accessToken')) or similar) with a clear Error before
returning null, referencing the connect function, accessToken variable, and
EventSource/onError handler so the reconnection logic is triggered rather than
silently stopping.

Copy link
Member

@seongwon030 seongwon030 left a comment

Choose a reason for hiding this comment

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

확인했습니다~

@lepitaaar lepitaaar merged commit 2cf875c into develop-fe Jan 19, 2026
3 checks passed
@lepitaaar lepitaaar deleted the feature/#798-sync-applicant-content-MOA-296 branch January 22, 2026 06:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💻 FE Frontend ✨ Feature 기능 개발

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants