Skip to content

feat: SSE 양방향 하트비트로 연결 생존 관리 #628

Description

@kanghaeun

구현할 기능

서버는 죽은 클라이언트를 약 17분 뒤에야 인지하고, 중계 뒤에서 끊기면 영영 모른다.
클라이언트는 서버 ping 이 주석이라 스트림 생존을 관측할 수 없다.

양방향 하트비트와 연결 식별자로 양쪽 모두 연결 상태를 정확히 파악한다.

동작

  • connect 이벤트에서 connectionId 수신·보관, 재연결 시 갱신
  • SSE 연결 중 + 포그라운드일 때 30초마다 connectionId 를 실어 하트비트 POST. 백그라운드에선 보내지 않고, 실패해도 재시도하지 않고 다음 주기에 보낸다
  • 401 은 다른 API 와 같이 토큰 갱신 대상 (연결 30분이 토큰 15분보다 길어 연결 중 한 번은 만난다)
  • 409 (NOTIFICATION-002) 응답 시 즉시 재연결 (서버가 이미 연결을 정리한 상태)
  • 서버 heartbeat 이벤트가 60초 이상 결측되면 강제 재연결 (워치독). 임계값은 한 번 밀린 ping 을 끊김으로 오판하지 않도록 두 주기
  • 재연결 전에 토큰이 만료됐으면 refresh 한 뒤 연다. 연결 중 만료는 그 연결을 끊지 않는다

워치독이 별도로 필요한 이유

하트비트 POST 는 "클라이언트가 서버에 요청을 보낼 수 있다"만 증명하고, "서버 → 클라이언트 스트림이 살아있다"는 증명하지 못한다. 서로 다른 연결이기 때문.

스트림만 끊기고 일반 요청은 정상인 경우, POST 는 계속 성공하므로 서버는 정상으로 판단하고 409 도 돌아오지 않는다. 이 상태는 클라이언트가 스트림 유입을 직접 관측해야만 감지되고, 새 번호로 POST 가 오면 서버가 옛 연결을 정리한다.

작업 상세 내용

  • @piki/core 에러 카탈로그에 NOTIFICATION-002 등록 (ERROR_CODE.NOTIFICATION_CONNECTION_NOT_FOUND)
  • ENDPOINTS 에 하트비트 엔드포인트 상수 추가
  • postNotificationHeartbeat 작성 (본문 connectionId). 401 은 clientApi 인터셉터에 위임
  • connect 이벤트 핸들링 추가 : connectionId 수신·보관, 재연결 시작 시 비움
  • SSE 연결 중 + 포그라운드일 때만 30초 간격 전송
  • 409 (NOTIFICATION-002) 응답 시 즉시 재연결. 응답 지연 중 이미 재연결됐으면 무시
  • 409 에러 토스트 억제 : 내부 신호이므로 사용자에게 노출되면 안 된다.
    기본 에러 처리를 타면 30초마다 토스트가 뜰 수 있음
  • 마지막 수신 시각 기록 : heartbeat·notification·silent-sync·connect 모든 수신에 갱신
  • 60초 초과 시 강제 재연결 (워치독)
  • 숨김 시 하트비트 전송 중단, 복귀 시 즉시 하트비트로 서버가 정리한 연결(409) 감지 후 재연결
  • 재연결 시 목록 API 로 놓친 알림 동기화 (기존 로직 유지)

참고

  • apps/web/src/hooks/useNotificationSSE.ts
  • apps/web/src/app/api/notifications/subscribe/route.ts
  • SSE 명세 문서 (수동 관리, 클라이언트용 정본)

Activity

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

Metadata

Metadata

Assignees

Labels

featureNew feature or request

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions