[JS] 타이머 이벤트 설정하기 (24년 리팩토링 진행)

Yeong·2023년 11월 26일
0

로그인 후 사용자에게서 5분간 아무런 이벤트가 발생하지않을 시 자동로그아웃 되는 기능을 만들어보았다.

나의 경우에는 클릭이벤트로 감지를 하였다.

  1. 로그인 시 타이머시작 (0초부터 ++1 하는 방식)
  2. 클릭 이벤트 발생 시 타이머 초기화
  3. 클릭 이벤트 미발생시엔 maxCount에 다다를시 로그아웃 로직 실행
<template>
  <div>
    <main />
  </div>
</template>

<script>
import { mapMutations, mapState } from 'vuex';

export default {
  name: 'Main',
  data() {
    return {
      timerCount: 0,
      maxCount: 300, // 5분 
      timerInterval: null,
    };
  },
  mounted() {
    this.startTimer();
    document.addEventListener('mousedown', this.handleMouseClick);
  },
  beforeDestroy() {
    clearInterval(this.timerInterval);
    document.removeEventListener('mousedown', this.handleMouseClick);
  },
  methods: {
    // 타이머 5분 카운트 시작
    startTimer() {
      this.timerInterval = setInterval(() => {
        if (this.timerCount < this.maxCount) {
          this.timerCount++;
        } else {
          clearInterval(this.timerInterval);
          alert('5분간 미사용으로 인한 로그아웃');
          this.$router.push('/login');
        }
      }, 1000); // 1초마다 타이머 증가
    },
    
    // 리셋 타이머
    resetTimer() {
      clearInterval(this.timerInterval);
      this.timerCount = 0;
    },
    handleMouseClick() {
      this.resetTimer(); // 클릭 이벤트 발생 시 타이머를 초기화하고 0초부터 다시 시작
      this.startTimer();
    },
  },
};
</script>

메인페이지 내에 이와 같이 작성했다.
이게 효율적으로 짠건지는 의문이 들긴하지만.. 구현은 잘 되었다.


24.04 기준으로 해당 타이머에 버그사항을 발견했다. 크롬이나 사파리 등 웹 안에서 동일한 도메인웹사이트를 여러개 띄울때(중복탭을 이용해서) 하나의 탭에서만 타이머가 작동하고, 백그라운드에 있는 활성화되어있지않은 탭은 타이머가 멈춰있는 것이었다. 수없이 QA를 돌려보다가 어떤때는 되고 어떤 때에는 안되어서 보니 저렇게 시나리오가 나왔다. 그 원인을 찾아보니 크롬 브라우저에서는 사용자가 현재 보고 있지 않은 탭의 웹 사이트에서 자바스크립트 함수가 실행되는 빈도를 제한한다는 기사를 확인했다.(https://www.itworld.co.kr/news/103916#csidx4eb5cd320d50e39a31f8ea4b5db62cf
https://www.itworld.co.kr/news/103916)

그래서 타이머함수가 작동하지않았던 것 !!

어떻게 수정을 해야할까 고민을 하다가, 현재 시간과 탭이 열려있던 마지막 시간을 기준으로 5분 이상 차이가 나면 로그아웃 시키는 방향으로 수정했다.

기존에 data에 담아두었던 timerCount, maxCount 변수는 사용하지않아지웠고, timerInterval만 commonStore로 전역으로 쓸 수 있게끔 남겨두었다.

// pages 내 index.vue

<script>
import { mapMutations, mapState } from 'vuex';

export default {
  name: 'MainLayout',
  computed: {
    ...mapState('common', ['isLoading', 'timerInterval', 'modal']),
  },

  mounted() {
    this.getNowTime();
    // 기존 로직
    this.START_TIMER();
    document.addEventListener('mousedown', this.handleMouseClick);
  },
  beforeDestroy() {
    this.RESET_TIMER();
    document.removeEventListener('mousedown', this.handleMouseClick);
  },
  methods: {
    ...mapMutations('dashBoard', ['CLEARINTERVAL_FUNCTION']),
    ...mapMutations('common', [
      'START_TIMER',
      'RESET_TIMER',
      'SET_STANDARD_DATE',
    ]),
    ...mapMutations('auth', ['REMOVE_TOKEN']),
    async handleMouseClick() {
      if (this.timerInterval !== null) {
        await this.getNowTime(); // 클릭 이벤트 발생 시, 클릭한 시간으로 업데이트
        await this.RESET_TIMER();
        await this.START_TIMER();
      }
    },
    async getNowTime() {
      const nowDate = new Date();
      this.SET_STANDARD_DATE(nowDate);
    },
  },
};
</script>

index.vue에서는 commonStore에 있는 메서드들을 불러와서 사용토록 했다.

START_TIMER(state) {
      state.timerInterval = setInterval(() => {
        const nowDate = new Date();
        const saveDate = new Date(state.standardDate);

        const differenceMillis = Math.abs(nowDate - saveDate);
        const differenceSeconds = Math.floor(differenceMillis / 1000);

        switch (true) {
          // 5분을 넘지않았을 경우
          case differenceSeconds < state.maxCount:
            // console.log('return', differenceSeconds);
            break;
          // 5분 초과~6분 미만일 경우
          case differenceSeconds >= state.maxCount &&
            differenceSeconds <= state.maxCount + state.modalMaxCount:
            // 로그아웃 안내 모달창을 띄우고, 로그아웃_오토 로직 실행
            clearInterval(state.timerInterval);
            store.commit('auth/LOGOUT_AUTO');
            break;
          // 최종 6분 경과 후
          case differenceSeconds >= state.maxCount + state.modalMaxCount:
          default:
            store.commit('auth/LOGOUT_MINUTE');
            break;
        }
      }, 1000);
    },
    RESET_TIMER(state) {
      clearInterval(state.timerInterval);
      state.timerInterval = null;
    },
  1. 컴포넌트단에서 처음에 mounted 될 때에 methods의START_TIMER를 호출하는데 가장 먼저 getNowTime 함수를 통해서 클릭한 시간으로 업데이트 시킨다. 그 뒤에 SET_STANDARD_DATE 뮤테이션을 통하여 store 내 standardDate에 업데이트된 시간을 저장한다.

  2. START_TIMER()가 호출되면 store 단의 mutation에서 해당 로직이 진행된다. 또 한번 현재 시간을 받아오고, saveDate라는 변수에 받아왔던 현재 시간을 저장한다.(상태관리를 위해 mutation에 작성하였다)

✨✨ 여기서부터가 핵심이 되는 로직인데 현재시간에서 기존 클릭했던 시간의 차이를 구하는 것이다!!!!
(발생한 지 얼마나 지났는지 계산)

현재 밀리초 단위로 계산이되기 때문에, Math.floor를 사용하여 초단위로 받아왔다.

  1. 프로젝트의 경우 5분 미만으로 비활성화였을경우, 5분-6분 사이경우, 6분 이상일 경우로 3가지로 케이스가 나뉘었다.
  • differenceSeconds가 스토어의 state에 저장해둔 maxCount(300초) 미만일 경우엔 break;
    그리고 마우스 클릭이벤트를 통하여 새롭게 클릭된 시간이 업데이트되고, 타이머를 한차례 리셋한 다음에 타이머 시작 로직이 한번더 수행된다.
  • 5분 초과 6분 미만일경우엔 로그아웃 안내 모달창을 띄웠다.
  • 그리고 6분이 넘었을 경우엔 로그아웃 시키는 로직

📎 결과적으로 지금까지 여러탭에서 테스트를 진행했을 때 동일하게 로그아웃 로직이 수행된다. 탭이 비활성화되어있을 때 자바스크립트 로직이 동작하지않는다는 사실은 새로 깨달았다.. 큰 배움이 되었다.
그리고 마지막 업데이트 시각과 현재 시간을 비교하여 로직을 수행하는 것 말고도 또 좋은 방안이 있을 것 같아 차차 고민을 해보아야 겠다는 생각도 했다!

0개의 댓글