세션 타이머, 왜 setTimeout은 안 될까? 웹 워커로 해결하는 법

김유현·2025년 3월 2일
3
post-thumbnail

현재 진행중인 프로젝트에서 보안상의 이유로
일정 시간 동안 사용자가 아무런 활동을 하지 않으면 자동 로그아웃이 필요함.
기존에 setTimeout을 사용해서 일정 시간이 지나면 로그아웃하는 방식으로 구현되었으나,
브라우저 탭이 비활성화되거나 시스템이 절전 모드로 들어가면 setTimeout의 정확도가 떨어지는 문제가 있음.

이를 해결하기 위해서 개선된 방식인 웹 워커를 활용한 경험을 정리해서 공유드리고자 합니다.


1. 웹 워커(Web Worker)란?

  • 웹 워커는 메인 스레드와 별도로 백그라운드에서 동작하는 JS 실행 환경
  • 타이머를 웹 워커에서 실행하면 브라우저 탭의 상태와 관계없이 일정한 주기로 실행 가능
  • 참조: [MDN Web Docs] Web Workers API

1.1 JS는 싱글 스레드 언어라고 알고있었는데?

네, 맞습니다. JS는 싱글 스레드 언어이지만, JS가 돌아가고 있는 엔진은 브라우저에 속해있습니다.
브라우저는 멀티 스레딩을 지원하고 있고, 웹 워커가 해당 멀티 스레딩 중 백그라운드 스레드에 속해 있습니다.

그러므로 Workers API 는 JS에서 지원하는 기능이 아닌, Web API의 기능입니다.
최신 브라우저는 Web Worker 기능을 지원한다고 볼 수 있습니다.

출처: TCP School

1.2 웹 워커 사용법

  • Angular 17 기준으로 설명됩니다.

1.2.1 Angular CLI로 웹 워커 번들링

  • Angular CLI는 worker 옵션을 통해 웹 워커를 번들링하도록 지원합니다.
  • 아래 Angular CLI를 사용하면 자동으로 웹 워커 파일과 설정을 생성할 수 있습니다.
    • ng generate web-worker /shared/workers/session-timer
  • 위 명령은 다음을 수행합니다
    • src/app/shared/workers/session-timer.worker.ts 파일 생성.
    • Angular CLI 설정 파일(angular.json)에 워커를 번들링하도록 자동 추가.
    • tsconfig.worker.json 파일 생성: TypeScript가 웹 워커를 인식하도록 설정
  • 폴더 구조:
    src/
    ├── app/
    │   ├── shared/
    │   │   ├── workers/
    │   │   │   ├── session-timer.worker.ts
    │   │   │   └── another.worker.ts
    │   │   └── services/
    │   │       ├── session-timeout.service.ts
    │   ├── components/
    │   └── app.module.ts
    ├── assets/
    ├── angular.json
    ├── tsconfig.app.json
    ├── tsconfig.worker.json

2. setTimeout을 통해 구현하려던 세션 타이머 서비스 코드

2.1 setTimeout 세션 타이머의 결함

  • 단순히 setTimeout을 사용해서 일정 시간이 지나면 로그아웃하는 방식
    • 하지만 브라우저 탭이 비활성화되거나 시스템이 절전 모드로 들어가면 setTimeout의 정확도가 떨어지는 문제가 있음.

3. 웹 워커를 사용한 세션 타이머 관리 코드

  • 필요한 파일:
    • session-timer.worker.ts
    • session-timeout.service.ts

3.1.1 웹 워커 파일 코드(session-timer.worker.ts)

  • 메인 스레드에서 일정 시간(타임아웃 시간)을 전달하면, 웹 워커가 해당 시간이 지나면 'session_timeout' 메시지를 반환함.
  • 타이머를 이용해 1초마다 확인(check)하면서, 타임아웃이 되었는지 체크하는 구조.
  • addEventListener('message', ...) (메시지 리스너 등록)
    • 메인 스레드에서 postMessage()로 데이터를 보내면 이 리스너가 실행됨.
    • { data }는 메인 스레드에서 넘긴 데이터 (타임아웃 값).
    • 이 웹 워커는 메인 스레드에서 서비스 코드를 통해 타임아웃 지속 시간(밀리초 단위)을 넘겨받음.

3.1.2 Angular 서비스에서 워커 불러오기(session-timeout.service.ts)

  • new URL('../../workers/session-timer.worker', import.meta.url)을 사용하여 웹 워커 파일을 동적으로 불러옴.
    • import.meta.url은 현재 모듈의 URL을 가져오는 방식.
    • type: 'module'은 웹 워커를 ES 모듈로 로드할 수 있도록 설정.
  • this.worker.postMessage(timeoutDuration);
    • 웹 워커에게 타임아웃 값 전달. session-timer.worker.ts파일의 이벤트 리스너에서 { data }로 사용됨.
  • this.worker.onmessage = ({ data }) => { ... }
    • 웹 워커가 postMessage()로 보낸 메시지를 수신함.
    • 만약 session_timeout 메시지가 도착하면 onTimeout()을 호출하여 세션 만료 처리를 수행.

3.1.3 컴포넌트에서 서비스 사용(app.component.ts)

3.2 웹 워커의 한계 및 단점

웹 워커는 메인 스레드의 영향을 받지 않고 백그라운드에서 안정적인 타이머 처리를 가능하게 해주는 강력한 도구이지만 몇 가지 단점도 존재합니다.

  • 브라우저 지원 범위
    최신 브라우저에서는 대부분 지원하지만 구형 브라우저나 일부 제한된 환경(내장 브라우저 등)에서는 호환성 문제가 발생할 수 있습니다.

  • DOM 접근 불가
    웹 워커는 백그라운드 스레드에서 동작하기 때문에 DOM을 직접 조작할 수 없습니다. 이는 UI 업데이트가 필요한 로직에서는 메인 스레드와의 메시지 통신을 거쳐야 하므로 구조가 복잡해질 수 있습니다.

  • 디버깅이 상대적으로 어려움
    워커는 별도 스레드에서 실행되므로 콘솔 로그가 메인 스레드와 분리되어 나오고 디버깅 도구에서도 별도로 확인해야 하기에 디버깅이 다소 불편할 수 있습니다.

  • 리소스 관리 필요
    웹 워커는 명시적으로 종료하지 않으면 계속 실행되므로 불필요한 리소스 점유를 방지하기 위해 terminate() 등을 통해 수동으로 종료 관리를 해주어야 합니다.

4. 결론

  • 기존 setTimeout 방식은 브라우저 탭이 비활성화되거나 시스템이 절전 모드로 들어가면 정확도가 떨어지는 문제가 있었습니다. 이를 해결하기 위해 웹 워커를 활용해 독립적인 스레드에서 타이머를 실행하는 방식으로 개선했습니다.

  • 웹 워커를 사용하면 메인 스레드가 차단되지 않고 브라우저 환경에 관계없이 일정한 주기로 타이머를 유지할 수 있습니다.

  • 그 결과 세션 타임아웃 감지를 더 안정적이고 정확하게 수행할 수 있었고 웹 애플리케이션의 보안성을 강화할 수 있었습니다.

profile
FRONTEND DEVELOPER

0개의 댓글