logo

DowanKim

5. 리액트에서 쿠키는 어떻게 사용할까

2025년 9월 14일

이게머니

기존 카카오로그인 기능에서, 임시로 로컬스토리지에 저장을 하는 것으로 설정해 두었습니다.

하지만 로그인 토큰을 로컬스토리지에 저장을 하는 것은 굉장히 보안적으로 위험한 상황이 될 수 있습니다. 저희는 사전에 쿠키에 저장하기로 회의를 진행했습니다.

쿠키를 적용하는 방법으로는 , react-cookie 라이브러리를 사용하였습니다.

그리고 기본적인 쿠키 유틸함수 파일을 만들었습니다.

1. 쿠키 기본 세팅

import { useCookies } from 'react-cookie'; /** * 쿠키 설정 옵션 타입 */ export interface CookieOptions { path?: string; maxAge?: number; secure?: boolean; sameSite?: 'strict' | 'lax' | 'none'; } /** * 기본 쿠키 옵션 */ const DEFAULT_COOKIE_OPTIONS: CookieOptions = { path: '/', maxAge: 7 * 24 * 60 * 60, // 7일 secure: true, sameSite: 'strict', }; /** * 쿠키 설정 훅 * @param cookieNames 관리할 쿠키 이름들 * @returns 쿠키 객체와 설정 함수 */ export const useCookieManager = (cookieNames: string[]) => { const [cookies, setCookie] = useCookies(cookieNames); /** * 쿠키 설정 * @param name 쿠키 이름 * @param value 쿠키 값 * @param options 쿠키 옵션 */ const setCookieValue = (name: string, value: string, options?: Partial<CookieOptions>) => { setCookie(name, value, { ...DEFAULT_COOKIE_OPTIONS, ...options, }); }; /** * 쿠키 삭제 * @param name 쿠키 이름 */ const removeCookie = (name: string) => { setCookie(name, '', { path: '/', maxAge: 0, }); }; return { cookies, setCookie: setCookieValue, removeCookie, }; }; /** * 토큰 관련 쿠키 관리 훅 */ export const useTokenCookies = () => { const { cookies, setCookie, removeCookie } = useCookieManager(['access_token', 'refresh_token']); /** * 액세스 토큰 설정 * @param token 토큰 값 * @param days 만료일 (기본값: 7일) */ const setAccessToken = (token: string, days: number = 7) => { setCookie('access_token', token, { maxAge: days * 24 * 60 * 60, }); }; /** * 리프레시 토큰 설정 * @param token 토큰 값 * @param days 만료일 (기본값: 30일) */ const setRefreshToken = (token: string, days: number = 30) => { setCookie('refresh_token', token, { maxAge: days * 24 * 60 * 60, }); }; /** * 액세스 토큰 삭제 */ const removeAccessToken = () => { removeCookie('access_token'); }; /** * 리프레시 토큰 삭제 */ const removeRefreshToken = () => { removeCookie('refresh_token'); }; /** * 모든 토큰 삭제 */ const clearTokens = () => { removeAccessToken(); removeRefreshToken(); }; return { accessToken: cookies.access_token, refreshToken: cookies.refresh_token, setAccessToken, setRefreshToken, removeAccessToken, removeRefreshToken, clearTokens, }; };

지난 멘토님의 리뷰를 토대로 주석을 JSDoc 스타일로 작성하여 다른 팀원이 쉽게 이해할 수 있게 진행하였습니다.

기본 쿠키 옵션으로는, 경로, 만료시간, https만 전송, CSRF 공격 방지 설정을 해 두었습니다.

1. 자동 Bearer 토큰 헤더 추가

api.interceptors.request.use( (config) => { const cookies = document.cookie.split(';'); const accessTokenCookie = cookies.find((cookie) => cookie.trim().startsWith('access_token=')); if (accessTokenCookie) { const accessToken = accessTokenCookie.split('=')[1]; if (accessToken) { config.headers.Authorization = `Bearer ${accessToken}`; } } return config; }, (error) => { return Promise.reject(error); }, );

accessTokenCookie가 있으면, 자동으로 헤더에 이를 포함시킵니다.

2. 토큰 자동 갱신

/** * 401 에러 처리 함수 */ export const handle401Error = async ( originalRequest: InternalAxiosRequestConfig, ): Promise<unknown> => { // 토큰 갱신 시도 const newAccessToken = await refreshAccessToken(); if (newAccessToken) { // 새로운 토큰으로 원래 요청 재시도 originalRequest.headers.Authorization = `Bearer ${newAccessToken}`; return api(originalRequest); } else { // 토큰 갱신 실패 시 로그아웃 handleLogout(); return Promise.reject(new Error('토큰 갱신 실패')); } };

토큰을 자동갱신하고, 갱신 실패시 로그아웃하는 로직을 구현하였습니다

3. 인증 상태 관리

import { useState, useEffect } from 'react'; import { useTokenCookies } from '@/utils/cookie'; import { useNavigate } from 'react-router-dom'; export const useAuth = () => { const { accessToken, refreshToken, clearTokens } = useTokenCookies(); const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false); const [isLoading, setIsLoading] = useState<boolean>(true); const navigate = useNavigate(); useEffect(() => { setIsAuthenticated(!!accessToken); setIsLoading(false); }, [accessToken]); const logout = () => { clearTokens(); localStorage.removeItem('userId'); setIsAuthenticated(false); navigate('/login'); }; const checkAuth = () => { return !!accessToken; }; return { isAuthenticated, isLoading, accessToken, refreshToken, logout, checkAuth, }; };

결과

추후 api 통신에는, 토큰이 자동으로 헤더에 추가가 될 것입니다.