Next.js 카카오 로그인 구현하기 (w spring) (3) 토큰 삭제 훅 리팩토링하기

moreas·2023년 5월 26일
0

팀 프로젝트

목록 보기
12/14

어떤 글을 쓸 거냐면

타이머를 활용한 토큰 삭제 훅에서 새로고침을 하면 타이머가 초기화되는 문제점 해결하는 과정을 기록했다.

서론

저번에 토큰 삭제 hook 생성을 하고 일정 시간이 지나면 액세스 토큰을 삭제하여 자동 로그아웃 처리를 하도록 만들었다. 임의로 타이머를 1분으로 설정해두고 테스트를 해봤을 땐 제대로 로그아웃이 되었고 별다른 문제가 없다고 생각했는데 문제가 한둘이 아니었다. ^^;

해당 로직의 문제점

  1. 새로고침하면 타이머가 초기화가 되어 실제 토큰이 만료될 때까지 자동 로그아웃을 적용하지 못한다.
  2. 타이머는 새로고침할 때마다 다시 처음부터 카운트되므로, 제 시간에 제거되지 못한 토큰은 결국 만료된 토큰으로 남아있어 페이지에서 로그인 관련 에러가 난다. 그렇다면 이 로직은 쓸모가 없는 것이다..!

본론

새로고침을 해도 타이머 초기화되지 않도록 설정하기

  • 그렇다면 새로고침을 해도 타이머가 초기화되지 않도록 로직을 구현하면 된다고 판단했다.
  • 로컬 스토리지를 사용하여 현재 타이머의 시간을 저장하기로 했다.
  • 타이머를 저장하기 위하여 useRef를 사용했다. 이유는 현재 저장하고 싶은 값을 리렌더링없이 저장할 수 있고 DOM을 참조할 수 있다는 점에서 useState보다는 useRef를 사용하는 게 맞지 않을까? 싶었기 때문이다.

로컬 스토리지에 시간 저장하기

  • useEffect 훅으로 accessToken과 startTimeRef의 변경을 감지하여 localStorage에 시작 시간과 남은 시간을 저장한다.
    그랬더니 소수점 세 자리까지 로컬 스토리지에 저장된다는 걸 깨달았다!
  • Math.floor를 통하여 정수를 반환하도록 로직을 수정했다.

      localStorage.setItem(
        "startTime",
        Math.floor(startTimeRef.current).toString()
      );
      localStorage.setItem(
        "remainingTime",
        Math.floor(remainingTimeRef.current).toString()
      );

콜백 함수 정의

  • startTimeRefremainingTimeRef는 useRef 훅을 사용하여 각각 시작 시간과 남은 시간을 저장하기 위해 사용했다.
  • useEffect 훅 내부에서 컴포넌트가 마운트되거나 accessToken이 변경될 때 실행되는 콜백 함수를 정의했다.
  • 함수가 실행된다면 액세스 토큰을 삭제하고 토큰 관련 리덕스를 초기화한 뒤, 라우터를 통해 홈페이지로 이동하는 deleteToken 함수를 작성한다.
  • accessToken이 존재하는 경우 해당 로직을 실행하고 토큰이 존재하지 않는 경우, 타이머를 초기화, 시작 시간과 남은 시간을 초기화, localStorage에서 해당 값을 제거한다. (이 부분에서 챗 지피티의 도움을 좀 받았다 고마워요..)
    - 현재 시간을 가져와 시작 시간과의 차이를 계산하여 경과한 시간을 구한다.
    - localStorage에서 가져온 남은 시간에서 경과한 시간을 뺀 값을 남은 시간으로 설정한다.
    - 남은 시간이 양수인 경우, 해당 시간이 경과한 후 deleteToken 함수를 실행하는 타이머를 실행하도록 한다.
    - 남은 시간이 음수인 경우, deleteToken 함수를 즉시 실행하여 액세스 토큰을 삭제한다.
 const deleteToken = (): void => {
      cookie.remove("accessToken", { path: "/" });
      dispatch(resetLog());
      router.replace("/");
    };

    const waitTime = 24 * 60 * 60 * 1000; // 원하는 시간

    if (accessToken) {
      const storedStartTime = localStorage.getItem("startTime");
      const storedRemainingTime = localStorage.getItem("remainingTime");
      const currentTime = Date.now();
      const elapsedTime = storedStartTime
        ? currentTime - parseInt(storedStartTime)
        : 0;
      const remainingTime = storedRemainingTime
        ? parseInt(storedRemainingTime) - elapsedTime
        : waitTime;

      if (remainingTime > 0) {
        timerRef.current = setTimeout(deleteToken, remainingTime);
        startTimeRef.current = currentTime;
        remainingTimeRef.current = remainingTime;
      } else {
        deleteToken();
      }
    } else {
      clearTimeout(timerRef.current!);
      startTimeRef.current = null;
      remainingTimeRef.current = 0;
      localStorage.removeItem("startTime");
      localStorage.removeItem("remainingTime");
    }
  • 이제 콘솔을 통하여 타이머가 제대로 동작하는지 확인하는 과정을 거쳐야 했다.
  • useEffect를 더 사용하여 토큰이 있는 경우에 setInterval을 사용하여 1초마다 남은 시간을 감소시키고 해당 값을 콘솔에 띄우도록 로직을 작성하였다.
  useEffect(() => {
    if (accessToken) {
      const interval = setInterval(() => {
        const remainingSeconds = Math.floor(remainingTimeRef.current / 1000);
        console.log("Remaining time:", remainingSeconds, "seconds");
        remainingTimeRef.current -= 1000;
      }, 1000);

      return (): void => {
        clearInterval(interval);
      };
    }
  }, [accessToken]);

  • 해당 로직을 사용하는 헤더 컴포넌트에 임포트하였다.

결론

  • 페이지를 새로고침 하여도 타이머가 초기화되지 않고 일정 시간이 지난 후 자동 로그아웃이 되는 걸 확인하였다.

  • 결론적으로 이런 과정 없이 백엔드 api를 통하여 토큰 만료 여부에 관해 통신을 하고, 리프레시 토큰을 활용하여 토큰을 재발급 api를 사용하여 silent하게 로그인을 유지하는 로직을 완성할 수 있다.

  • 그러나 해당 에러를 직면했을 때는 이미 기능 구현과 팀 프로젝트가 완료된 상태고 배포도 완료된 상태라 프론트엔드단에서 이 문제를 해결할 수 있는 방법을 생각하다보니,, 일정 시간이 지나면 로그아웃을 하도록 수정하는 방법이 최선이라고 판단했다.

  • 로그인 과정은 정말.. 생각할수록 쉽지 않다고 느꼈다. 개발을 처음 시작해본 입장으로 로그인 과정을 제대로 해내지 못해서 정말 아쉽다. 부족한 경험 대비 너무 많은 기능 구현을 맡게 되어서 시간 안에 해내는 데만 급급했다. 난이도 있는 순으로 기능을 나열하여 팀원들과 조금만 더 상의하고 파트를 분배했다면 로그인 구현에 집중할 수 있었을 거라는 아쉬움이 있다. ㅠㅠ

  • 백엔드와의 커뮤니케이션도 아쉬웠다. 팀플이 처음이고 서로의 로직을 구현하는 데에만 열중하다보니 놓치고 가는 부분이 있어서 그 간극을 조금만 해소할 수 있었다면 배포 전에 기능을 다 완성할 수 있었을 텐데 나의 부족함을 체감했다. 로그인 전반에 대해 경험을 해봤거나 프론트나 백 둘 다 지식이 있는 사람이 한 명이라도 있다면 로그인 로직을 다룰 때 좀 더 유리하다고 생각한다.

  • 이런 어려움을 경험했으니 다음 기회에서는 더 잘 할 수 있을 거라고 확신한다..!

세 줄 요약

  1. 첫 팀플에 첫 로그인 기능을 맡았다면 신중하게 고민해보자. 😢
  2. 적어도 로그인 경험이 있는 사람과 페어를 이루어서 작업하는 것이 효율적이다.
  3. 불가능을 빠르게 판단하여 대안을 내는 것도 하나의 능력이다..!
profile
Everything is connected 🐶 좀 더 나은 개발을 위해

0개의 댓글