Files
frontend/apps/cs/app/(main)/(with-base-layout)/test-session/page.tsx
T
changwng e93a83163e [cs frontend] 세션 체크 로직 핵심 구현 내역
1번 요건 (탭 동기화):
                        localStorage 연동을 통해 한 탭에서 연장에 성공하면, 다른 모든 탭의 열린 다이얼로그가 자동으로 닫히며(onCancel() 실행) 타이머가 새 만료 시간으로 동기화됩니다.
                        2번 요건 (포커스 보정):
                        브라우저 탭 비활성화 등으로 타이머가 지연된 뒤 복귀(visibilitychange/focus)하는 순간 토큰 유효 기간을 즉시 재검증하여, 만료 시 강제 로그아웃을 유도하고 유효한 경우 남은 시간을 정확하게 보정하여 재예약하도록 개선하였습니다.
                        4번 요건 (비프음 및 푸시 알림):
                        파일 의존성 없이 Web Audio API를 통해 팝업 노출 시 즉각적인 비프(Beep) 경고 사운드를 재생하며, 브라우저 Notification API를 활용해 백그라운드 상태에서도 시스템 경고 알림을 전달합니다. (헤더 컴포넌트 마운트 시 알림 권한 정중히 요청)
                        단위 테스트 통과:
                        신규 15~18번 검증 테스트 케이스를 포함하여 총 18개 단위 테스트가 100% 정상 작동 및 성공(Pass) 하였습니다.
2026-07-10 15:45:03 +09:00

56 lines
2.3 KiB
TypeScript

"use client";
import { useSessionExtendConfirm } from "../../../../../../packages/common/components/src/lib/use-session-extend-confirm";
import { useState, useRef } from "react";
export default function TestSessionPage() {
const [refreshCalled, setRefreshCalled] = useState(false);
const [declineCalled, setDeclineCalled] = useState(false);
const [timeoutCalled, setTimeoutCalled] = useState(false);
// 클로저 트랩 회피를 위한 Ref 선언
const refreshCalledRef = useRef(false);
const declineCalledRef = useRef(false);
const timeoutCalledRef = useRef(false);
useSessionExtendConfirm({
enabled: true,
getAccessToken: () => {
// Ref 참조를 통해 최신의 갱신 상태를 즉각 반영하여 클로저 락 회피
if (refreshCalledRef.current || declineCalledRef.current || timeoutCalledRef.current) {
// 리프레시/거절/만료 완료 후에는 만료 시간이 아주 충분히 남은 가상 토큰 반환 (1000초 남음)
const payload = btoa(JSON.stringify({ exp: Math.floor(Date.now() / 1000) + 1000 }));
return `header.${payload}.signature`;
}
// 최초 실행 시에는 5분 버퍼(300초) 진입까지 정확히 2초가 남은 가상 토큰 반환 (302초 남음)
const payload = btoa(JSON.stringify({ exp: Math.floor(Date.now() / 1000) + 302 }));
return `header.${payload}.signature`;
},
silentRefresh: async () => {
refreshCalledRef.current = true;
setRefreshCalled(true);
},
declineSessionExtend: () => {
declineCalledRef.current = true;
setDeclineCalled(true);
},
onTimeout: () => {
timeoutCalledRef.current = true;
setTimeoutCalled(true);
},
message: "로그인을 연장하시겠습니까?",
});
return (
<div style={{ padding: "50px", textAlign: "center" }}>
<h1>Session Extend Confirm Test Page</h1>
<p>이 페이지는 useSessionExtendConfirm 훅 E2E 테스팅을 위한 격리 라우트입니다.</p>
<div style={{ marginTop: "20px" }}>
{refreshCalled && <p data-testid="refresh-status">Refresh Successful</p>}
{declineCalled && <p data-testid="decline-status">Decline Successful</p>}
{timeoutCalled && <p data-testid="timeout-status">Timeout Successful</p>}
</div>
</div>
);
}