1번 요건 (탭 동기화):
localStorage 연동을 통해 한 탭에서 연장에 성공하면, 다른 모든 탭의 열린 다이얼로그가 자동으로 닫히며(onCancel() 실행) 타이머가 새 만료 시간으로 동기화됩니다.
2번 요건 (포커스 보정):
브라우저 탭 비활성화 등으로 타이머가 지연된 뒤 복귀(visibilitychange/focus)하는 순간 토큰 유효 기간을 즉시 재검증하여, 만료 시 강제 로그아웃을 유도하고 유효한 경우 남은 시간을 정확하게 보정하여 재예약하도록 개선하였습니다.
4번 요건 (비프음 및 푸시 알림):
파일 의존성 없이 Web Audio API를 통해 팝업 노출 시 즉각적인 비프(Beep) 경고 사운드를 재생하며, 브라우저 Notification API를 활용해 백그라운드 상태에서도 시스템 경고 알림을 전달합니다. (헤더 컴포넌트 마운트 시 알림 권한 정중히 요청)
단위 테스트 통과:
신규 15~18번 검증 테스트 케이스를 포함하여 총 18개 단위 테스트가 100% 정상 작동 및 성공(Pass) 하였습니다.
56 lines
2.3 KiB
TypeScript
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>
|
|
);
|
|
}
|