개요
setInterval(), setTimeout()을 이용하여 세션 타임아웃을 구현하는 경우 Page Visibility API로 인해 예상된 결과가 출력되지 않을 수 있습니다.
이를 해결하기 위해 visibilitychange 이벤트를 이용하여 세션 타임아웃을 동기화하는 방법을 공유하고자 합니다.
setInterval() 함수를 통해 세션 타임아웃 기능을 구현하곤 합니다.
세션 타임아웃을 5분으로 설정된 화면에서 사용자는 3분 동안 브라우저를 최소화하였습니다.
다시 화면을 활성화하였을 때 예상된 2분과 상이한 결과를 출력되게 됩니다.
다음과 같은 현상은 브라우저 비활성화(탭 이동, 최소화 등)하였을 때 Page Visibility API가 브라우저 리소스를 절약함에 발생하게 됩니다.
이를 해결하기 위해 visibilitychange 이벤트를 이용하여 세션 타임아웃을 동기화하는 방법을 공유하고자 합니다.
Page Visibility API
Page Visibility API는 브라우저가 최소화, 탭 이동과 같이 비활성화가 될 때 리소스를 절약하도록 성능을 제한합니다.
예를 들어, 세션 타임아웃이 5분으로 설정된 화면에서 3분 동안 브라우저를 최소화한 후 최대화하였을 때 남은 시간은 2분으로 출력되어야 합니다.
그러나 성능 제한으로 인해 예상과 상이한 결과를 출력하게 됩니다.
Page Visibility API 동작 테스트
하단에 작성된 코드에서 setInterval() 함수를 통해 1초마다 현재 시간을 출력하도록 구현하였습니다.
setInterval(() => {
console.log(new Date())
}, 1000)
약 2분동안 브라우저를 최소화 한 후 활성화하였을 때 다음과 같이 로그를 출력하게 됩니다.
Sun Oct 06 2024 13:51:34 GMT+0900 (한국 표준시)
Sun Oct 06 2024 13:51:35 GMT+0900 (한국 표준시) // +1s
Sun Oct 06 2024 13:51:36 GMT+0900 (한국 표준시) // +1s
Sun Oct 06 2024 13:52:29 GMT+0900 (한국 표준시) // +53s
Sun Oct 06 2024 13:53:29 GMT+0900 (한국 표준시) // +60s
Sun Oct 06 2024 13:53:53 GMT+0900 (한국 표준시) // +34s
Sun Oct 06 2024 13:53:53 GMT+0900 (한국 표준시) // +0s
Sun Oct 06 2024 13:53:54 GMT+0900 (한국 표준시) // +1s
Sun Oct 06 2024 13:53:55 GMT+0900 (한국 표준시) // +1s
이를 통해 브라우저 비활성화 상태에서는 setInterval() 동작을 제한한다는 것을 확인할 수 있습니다.
해당 로그는 Chrome을 기준으로 작성되었으며 브라우저마다 리소스 절약 동작 방식이 상이하니 참고바랍니다.
visibilitychange 이벤트를 통해 세션 타임아웃 동기화하기
세션 타임아웃 동기화 구조는 다음과 같습니다.
A. 시간 관련 정보는 모두 timestamp로 관리된다고 가정합니다.
B. API가 호출될 때 서버 시간을 로컬에 저장합니다.
C. 브라우저 비활성화 상태에서 활성화되는 경우 서버 시간을 조회합니다.
C. 남은 세션 타임아웃 시간 - (A - B) 와 같이 계산한다.
const ONE_SECOND = 1000 // 1초
const SESSION_TIMEOUT = 5 * 60 * ONE_SECOND // 5분
// API가 호출된 경우 서버 시간을 저장했다고 가정
// 300000은 임의의 시간임을 참고
localStorage.setItem('098f6bcd4621d373cade4e832627b4f6', '300000')
// 세션 타임아웃을 5분으로 설정
let remainSessionTimeout = SESSION_TIMEOUT
// 타이머 설정
setInterval(() => {
// 1초마다 1초씩 감소
remainSessionTimeout -= ONE_SECOND
console.log('Timer:', remainSessionTimeout)
}, ONE_SECOND)
function getSession() {
return new Promise((resolve, reject) => {
setTimeout(() => {
// API를 마지막으로 호출한지 3분이 지났다고 가정
resolve({ serverTime: 480000 })
})
})
}
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
return
}
// 브라우저 비활성화 상태에서 활성화로 변경되는 경우 다음 조건문을 실행
getSession().then((result) => {
const localSessionServerTime = localStorage.getItem(
'098f6bcd4621d373cade4e832627b4f6'
)
remainSessionTimeout -= result.serverTime - Number(localSessionServerTime)
console.log('visibilitychange event:', remainSessionTimeout)
})
})
다음과 같이 사용한다면 브라우저가 리소스를 절약하여 세션 타임아웃이 동기화되지 않는 부분을 해결할 수 있습니다.
참고
https://developer.mozilla.org/ko/docs/Web/API/Page_Visibility_API
https://developer.mozilla.org/ko/docs/Web/API/Document/visibilitychange_event
https://developer.mozilla.org/ko/docs/Web/API/Window/setInterval
https://developer.mozilla.org/ko/docs/Web/API/Window/localStorage
'JavaScript' 카테고리의 다른 글
[JavaScript] 숫자 타입의 정수와 실수의 관계 (0) | 2022.08.26 |
---|---|
[JavaScript] 변수 선언과 값 할당의 메모리 구조 (0) | 2022.08.26 |
[JavaScript] 호이스팅(hosting)의 이해 (0) | 2022.08.26 |
[JavaScript] 배열 내 특정 값 찾기 - find(), includes() ,Set 객체의 has() (0) | 2022.07.19 |
[JavaScript] Set 객체를 통해 배열 중복 제거 (0) | 2022.07.19 |