[PROlog] Access Token을 zustand에서 관리 + 토큰 유효성 검사

2024. 4. 8. 17:28·Project/PROlog

(※ 이 내용은 아직 정확하게 구현되는지 검증되지 않았습니다! 백엔드와 소통없이 프론트에서 독단적으로 작성한 것이니 참고만 해주세요!!!)

 

로그인을 구현할 때,

1. 단순히 백엔드한테 구글로부터 받은 인가코드를 보내면

2. 받은 토큰을 localStorage에 저장하는 방식으로 구현했었다.

 

하지만 이 방식은 보안을 고려했을 때 좋은 방법이 아니다. localStorage에 토큰을 저장하는 것은 권장되는 방법이 아니다.

 

따라서 아래와 같은 방법으로 수정하려 한다.

 

  1. 로그인을 하면 서버는 클라이언트한테 Access Token과 Refresh Token을 받음
  2. Access Token은 전역 변수로 관리(Zustand 활용), RefreshToken은 httpOnly 쿠키로 서버로 부터 받음(이 때 서버와 클라이언트가 도메인이 같다면, 따로 조치 필요x)
  3. 클라이언트가 새로고침이나 재렌더링이 될 때마다 Access Token의 유효성을 확인하는 요청을 보냄
  4. 클라이언트는 Refresh Token을 서버로 전송
  5. 서버에서 Refresh Token의 유효성을 검증하고 새로운 Access Token을 클라이언트로 전송
  6. 로그아웃 시 클라이언트는 Token을 제거

 

Access Tokend을 zustand를 통한 전역 변수로 관리하게 되면, 새로고침이나 재렌더링이 되면 변수에 저장되있던 Access Token이 사라지게 된다.

-> 따라서 최상위 컴포넌트인 App.tsx에서 새로고침(재렌더링)이 될 때마다 토큰 유효성 검증을 보내는 코드를 작성해야한다.

 


useStore.ts

import { create } from 'zustand';

export interface State {
  accessToken: string;
  setAccessToken: (token: string) => void;
  logOut: () => void;
}

const useStore = create<State>((set) => ({
  
  // access Token
  accessToken: '',

  // access Token 저장
  setAccessToken: (token: string) => set({ accessToken: token }),

  // 로그아웃
  logOut: () => set({ accessToken: '' }),
}));

export default useStore;

 

zustand를 활용하여 access Token을 전역변수로 관리하기 위하여 위와 같이 코드를 작성했다.

setAccessToken을 통하여 accessToken을 저장하고, logOut이 되면 accessToken을 다시 빈 문자열로 바꾸어준다.

 


App.tsx

import Router from './Router';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import useStore from './store/useStore';
import { useEffect, useState } from 'react';
import { createTheme, ThemeProvider } from '@mui/material/styles';
import axios from 'axios';
import Loading from './pages/Loading';

const queryClient = new QueryClient();

function App() {
  // refresh Token을 주고 받기 위한 설정
  axios.defaults.withCredentials = true;

  const { logOut, setAccessToken } = useStore();

  // 토큰 유효성 검증하는 동안 로딩 컴포넌트를 띄우기 위한 상태
  const [isLoading, setIsLoading] = useState(true);

  // 토큰 유효성 검증
  useEffect(() => {
    const validateAccessToken = async () => {
      try {
        // httpOnly 쿠키에 있던 refresh Token이 서버로 전송
        // 그리고 응답으로 새로운 Access Token을 받는다
        const response = await axios.get('endpoint');

        // 새로 받아온 AccessToken 저장
        const newAccessToken = response.data.accessToken;
        setAccessToken(newAccessToken);
      } catch (error) {
        // 유효성 검증 실패 시 로그아웃
        logOut();
      } finally {
        // 로딩 컴포넌트 끄기
        setIsLoading(false);
      }
    };

    validateAccessToken();
  }, [setAccessToken, logOut]);

  // 로딩 중이면 로딩 컴포넌트 표시
  if (isLoading) {
    return <Loading />;
  }

  return (
    <QueryClientProvider client={queryClient}>
      <ThemeProvider theme={theme}>
        <Router />
      </ThemeProvider>
    </QueryClientProvider>
  );
}

export default App;

 

axios.defaults.withCredentials = true 구문을 추가해놓으면 HttpOnly 쿠키에 있는 refresh Token이 자동으로 서버가 전송이 된다고 해서 위 구문을 추가했다.(실제로 되는지는 테스트해야 겠지만...)

 

그리고 refresh Token을 전송하면

 

1. refresh Token 유효성 검증에 성공했을 시

-> 서버로 부터 새로운 access Token을 받고 zustand 전역 변수로 저장한다.

 

2. refresh Token 유효성 검증에 실패했을 시

-> 로그아웃한다.

 

그리고 유효성 검증을 하는 동안 로딩화면을 띄어놓는다.

 


GoogleLogin.tsx

import { useMutation } from '@tanstack/react-query';
import axios from 'axios';
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import Loading from './Loading';
import useStore from '../store/useStore';

const GoogleLogin = () => {
  const navigate = useNavigate();
  const setAccessToken = useStore((state) => state.setAccessToken);

  // 회원가입된 유저일 때: 게시판 페이지로 이동
  const handleBoard = () => {
    navigate('/board');
    window.location.reload();
  };

  // 회원가입을 해야하는 유저일 때: 회원가입 페이지로 이동
  const handleRegister = () => {
    navigate('/register/1');
    window.location.reload();
  };

  // 현재 url에서 code 추출
  const params = new URLSearchParams(window.location.search);
  const code = params.get('code');

  // code 백엔드로 보내기 토큰 받기
  const loginMutation = useMutation({
    mutationFn: async (code: string) => {
      // 명세서보고 url 수정할 것
      const response = await axios.post('url', { code });

      return response.data;
    },

    onSuccess: (res) => {
      // 백엔드와 소통해서 res.data.accessToken이 맞는지 확인 할 것
      // access Token을 zustand 라이브러리를 통하여 전역 관리
      setAccessToken(res.data.accessToken);

      // 회원가입된 유저이냐의 여부에 따라 이동하는 페이지 결정(백엔드와 소통할 것)
      res.data.isExistingMember ? handleBoard() : handleRegister();
    },
    onError: (error) => {
      console.log(error);
    },
  });

  useEffect(() => {
    if (code) {
      loginMutation.mutate(code);
    } else {
      console.log('로그인 재시도하세요.');
    }
  }, [code]);

  return <Loading />;
};

export default GoogleLogin;

 

원래는 Access Token을 localStorage에 저장했었는데, setAccessToken 함수를 활용하여 받은 Access Token을 Zustand 전역 변수로 저장했다(refresh Token은 따로 건들 필요가 없어보인다.)


일단 위와 같이 코드를 수정했다. 잘 되는지는 실제로 백엔드와 협업해서 작동해봐야 확인할 수 있다(잘 됐으면 좋겠다 ㅠ)

 

그리고 잘 되면 네이버 로그인도 추가해보려한다. 그리고 로그인 여부에 따라 글쓰기, 수정, 삭제 등 버튼의 표시 여부도 바꾸는 것도 코딩을 해야한다. 계속 해보자!

 

저작자표시 비영리 변경금지 (새창열림)

'Project > PROlog' 카테고리의 다른 글

[PROlog] Material UI 사용하기(+Tailwind CSS 다크모드 적용)  (1) 2024.03.29
[PROlog] 인트로 페이지 변경 + Quill editor 도입  (0) 2024.03.02
[PROlog] CRUD 게시판 만들기 프로젝트 시작  (0) 2024.02.29
'Project/PROlog' 카테고리의 다른 글
  • [PROlog] Material UI 사용하기(+Tailwind CSS 다크모드 적용)
  • [PROlog] 인트로 페이지 변경 + Quill editor 도입
  • [PROlog] CRUD 게시판 만들기 프로젝트 시작
퀵차분
퀵차분
웹 프론트엔드 개발자를 꿈꾸고 있습니다 :)
  • 퀵차분
    QC's Devlog
    퀵차분
  • 전체
    오늘
    어제
    • 분류 전체보기 (165)
      • Frontend (28)
        • HTML, CSS (7)
        • Javascript (3)
        • React (11)
        • Typescript (2)
        • Next.js (4)
      • Node.js (3)
      • Study (40)
        • Modern JS Deep Dive (13)
        • SQL (1)
        • Network (1)
        • 프롬프트 엔지니어링 (4)
        • 인공지능 (9)
        • 시스템프로그래밍 (11)
        • 선형대수학 (1)
      • Intern (4)
      • KUIT (20)
      • Algorithm (48)
        • Baekjoon(C++) (26)
        • Programmers(JavaScript) (22)
      • 우아한테크코스(프리코스) (4)
      • Project (7)
        • PROlog (4)
        • Nomadcoder (2)
      • 생각 (4)
      • Event (7)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    자바스크립트
    백준
    타입스크립트
    next.js
    HTML
    typescript
    리액트
    티스토리챌린지
    프로그래머스 자바스크립트
    프롬프트 엔지니어링
    javascript
    음악추천
    알고리즘
    KUIT
    시스템프로그래밍
    프로그래머스
    react
    프론트엔드
    오블완
    인공지능
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
퀵차분
[PROlog] Access Token을 zustand에서 관리 + 토큰 유효성 검사
상단으로

티스토리툴바