feat: 채팅 소켓 토큰 만료 재연결 및 끊김 안내 개선#293
Open
khkim6040 wants to merge 10 commits into
Open
Conversation
웹소켓 연결 시점에 액세스 토큰이 만료되면 서버가 accessTokenExpired 이벤트를 내려보낸다(paxi-popo-nest-api #154). 기존에는 이 이벤트의 핸들러가 TODO로 비어 있어, 토큰이 URL 쿼리에 박힌 채 자동 재연결이 만료된 토큰을 그대로 재사용하는 문제가 있었다. - socket-factory: onAccessTokenExpired 콜백을 추가해 소켓 소유자에게 재연결을 위임 - NewChatScreen: 이벤트 수신 시 기존 소켓 정리 → refreshAccessToken으로 토큰 갱신 → 새 토큰으로 소켓 재생성. 중복 진입(isReauthingRef) 및 갱신 반복 실패(reauthCountRef, 최대 2회) 가드 추가 - 정상 연결되면 갱신 카운터 초기화
NewChatScreen에 인라인돼 있던 토큰 만료 → 갱신 → 소켓 재생성 오케스트레이션을 socket-reauth.ts의 순수 함수로 추출한다. 의존성을 주입받게 해 컴포넌트 렌더 없이 단위 테스트할 수 있도록 한다. 동작은 동일하며, 중복 진입/반복 실패 가드를 ReauthGuard 객체 하나로 통합했다.
- socket-factory: accessTokenExpired 이벤트가 onAccessTokenExpired로 위임되는지 등 이벤트 배선 검증 (기존 미커버 영역) - socket-reauth: 정리→갱신→재생성 순서, 중복 호출 무시, maxAttempts 도달 시 중단, 갱신/재생성 실패 시 가드 해제 검증
reconnectAttempt 변경에 의존하던 useEffect([reconnectAttempt]) 재조회는 '재연결 성공 시 0으로 리셋되는 부수효과'에 기대는 비직관적 구조였다. 재조회를 재연결 성공 콜백(onSocketConnected)에서 명시적으로 호출하도록 옮긴다. 최초 연결은 useFocusEffect 초기 조회와 중복되므로 hasConnectedOnceRef 가드로 건너뛴다. (HANDOFF #4)
reconnectionDelay 5000 고정으로 장애 시 5초마다 무한 재시도하던 것을, reconnectionDelayMax(30s)를 추가해 시도마다 지연이 늘어나도록 한다. 서버 부하를 줄이고 일시 장애 회복 여지를 준다. (HANDOFF #5)
position:absolute; top:60 하드코딩은 헤더 높이/노치 환경에서 위치가 어긋날 수 있었다. 헤더 바로 아래 일반 흐름에 배치해 환경에 무관하게 정렬되도록 한다. (HANDOFF #5)
npm run pc(pre-commit) 실행 시 Prettier가 정리한 줄바꿈 반영.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR improves the resilience and UX of the Paxi chat socket connection in NewChatScreen by adding an explicit “token expired → refresh → recreate socket” flow, simplifying disconnect messaging, and making reconnection recovery (missed-data refetch) more explicit and testable.
Changes:
- Add
socket-reauthorchestration to refresh tokens and recreate the socket whenaccessTokenExpiredis received, with guards against duplicate entry and infinite loops. - Update
socketFactoryto support anonAccessTokenExpiredcallback and enable exponential reconnection backoff (capped). - Refactor
NewChatScreenreconnection UX/state and add Jest unit tests for the new socket factory/reauth behaviors.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
src/utils/socket-reauth.ts |
New token-refresh + socket recreation orchestrator with guard state and attempt cap. |
src/utils/socket-factory.ts |
Adds accessTokenExpired delegation hook and reconnection backoff cap. |
src/utils/__tests__/socket-reauth.test.ts |
Unit tests covering ordering, dedupe, caps, and failure paths for reauth orchestration. |
src/utils/__tests__/socket-factory.test.ts |
Unit tests validating event wiring and callback delegation in the socket factory. |
src/screens/paxi/NewChatScreen.tsx |
Integrates reauth flow and refactors disconnect UI/state + explicit refetch on reconnect. |
src/constants/socket-events.ts |
Updates documentation for ACCESS_TOKEN_EXPIRED event usage. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
275
to
279
| useCallback(() => { | ||
| // 포커스마다 새 소켓을 생성하므로 첫 connect를 최초 연결로 취급한다. | ||
| hasConnectedOnceRef.current = false; | ||
| getRoomInfo(); | ||
| getMyInfo(); |
Comment on lines
+51
to
+54
| if (guard.reauthCount >= maxAttempts) { | ||
| console.error('토큰 갱신 반복 실패 — 재연결 중단'); | ||
| deps.releaseSocket(); | ||
| return; |
이전 세션의 socketConnected/hasDisconnected가 남아, 화면 재진입 시 실제로 끊긴 적 없어도 끊김 배너가 뜨거나 입력창이 잘못 활성화되는 문제를 해소한다. 포커스 진입 시 초기 connect 전까지 두 상태를 false로 초기화한다.
reconnectWithFreshToken이 maxAttempts 상한으로 조기 반환할 때 releaseSocket()만 호출했다. releaseSocket은 리스너 제거 후 disconnect하므로 disconnect 이벤트가 발생하지 않아 UI가 연결됨으로 남을 수 있다. 상한 도달 경로에서도 setSocketConnected(false)를 호출하도록 하고 해당 동작을 검증하는 단위 테스트를 추가한다.
Comment on lines
+184
to
+190
| const onAccessTokenExpired = () => | ||
| reconnectWithFreshToken(reauthGuardRef.current, { | ||
| releaseSocket: releaseCurrentSocket, | ||
| refreshAccessToken, | ||
| recreateSocket: initSocket, | ||
| setSocketConnected, | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#️⃣ 연관된 이슈
X (서버: PoApper/paxi-popo-nest-api#154 와 세트로 동작)
📝 작업 내용
Paxi 채팅방(
NewChatScreen) 소켓 연결의 토큰 만료 복구와 끊김 안내를 개선합니다.src/screens/paxi/HANDOFF.md"채팅 소켓 재연결 안내 개선" 섹션의 #1~#5 중 보안(토큰 query string 제거)을 제외한 전부를 처리했습니다.기존
auth에 박혀, 자동 재연결이 만료된 토큰을 그대로 재사용 → 5초마다 영원히 실패.accessTokenExpired핸들러는 TODO만 있고 미구현.disconnect/error양쪽에서 증가하는 끊김 누적 수일 뿐 실제 재연결 시도 횟수가 아니며, 숫자가 커질수록 불안만 유발.useEffect([reconnectAttempt])의 "0으로 리셋되는 부수효과"에 의존하는 비직관적 구조.top: 60하드코딩.작업 후
accessTokenExpired수신 시refreshAccessToken()으로 토큰을 갱신하고 새 토큰으로 소켓을 재생성. 오케스트레이션은socket-reauth.ts(reconnectWithFreshToken)로 분리하고 중복 진입/반복 실패(최대 2회) 가드 추가. 갱신 실패 시 기존 로그아웃 흐름에 위임.reconnectAttempt(number) →hasDisconnected(boolean), 문구 "연결이 끊어졌습니다. 재연결 중…"으로 변경(이중 카운팅 문제 제거).onSocketConnected에서 직접 재조회.hasConnectedOnceRef로 최초 연결의 중복 조회 방지.reconnectionDelayMax(30s) 추가. 배너 레이아웃: 절대 위치 → 헤더 아래 레이아웃 흐름 배치.✅ 테스트 여부 체크(환경 명시) & 스크린샷
💬 리뷰 요구사항 (선택)
accessTokenExpired이벤트 emit)과 세트로 머지되어야 실효성이 있습니다. 서버 측 머지 일정과 맞춰주세요.auth.token만으로 인증을 수용하는지 확인이 필요해 이번 범위에서 제외했습니다.