[React] 세션 기반 인증과 토큰 기반 인증 구현하기
1. 세션 vs 토큰 기반 인증
1.1 세션 기반 인증
- 서버가 세션을 생성하고 관리한다.
- 클라이언트는 서버에서 발급받은 세션 ID를 쿠키에 저장하고, 이후 요청 시 이 쿠키를 통해 세션 정보를 자동으로 전달한다.
- 서버는 세션 정보를 메모리나 데이터베이스에 저장하여 클라이언트의 요청을 인증한다.
1.2 토큰 기반 인증
- 클라이언트가 인증을 받은 후, 서버는 JWT(JSON Web Token)와 같은 토큰을 발급한다.
- 클라이언트는 이 토큰을 로컬에 저장하고, 이후 요청 시 HTTP 헤더에 포함시켜 서버에 전송한다.
- 서버는 이 토큰을 검증하여 클라이언트를 인증한다.
정리!
- 세션 기반 인증: 쿠키 사용, 서버 저장
- 토큰 기반 인증: 스토리지 사용, 서버 저장x
2. 세션 기반 인증
2.1 Axios 설정
- 세션 기반 인증에서는
withCredentials: true
를 설정하여 쿠키를 자동으로 포함시킬 수 있도록 한다. - 또한, 클라이언트 측에서 직접 세션 ID를 관리할 필요가 없으며, 브라우저가 세션 쿠키를 자동으로 포함시키게 된다.
// src/api/auth.js
import axios from "axios";
const authAxios = axios.create({
baseURL: process.env.REACT_APP_SERVER_URL, // 환경 변수에서 기본 URL을 설정
withCredentials: true, // 쿠키를 자동으로 포함시키기 위한 설정
});
// 요청 인터셉터 추가 (세션 기반 인증에서는 필요 없음)
// 요청 인터셉터는 토큰 기반 인증과 달리 사용되지 않음
authAxios.interceptors.request.use(
(config) => {
// 세션 기반 인증에서는 쿠키를 자동으로 처리하므로 토큰을 수동으로 추가할 필요 없음
return config;
},
(error) => {
console.error("에러가 발생하였습니다. 문의해주세요.", error);
return Promise.reject({ state: "ERROR", message: error.message });
}
);
// 회원 가입
export const register = async (data) => {
return await authAxios.post(`/members/new`, data);
};
// 로그인
export const login = async (data) => {
return await authAxios.post(`/login`, data);
};
// 로그아웃
export const logout = async () => {
return await authAxios.get(`/logout`);
};
// 프로필 조회
export const getProfile = async () => {
return await authAxios.get(`/profile`);
};
// 프로필 닉네임 변경
export const updateNickname = async (newNickname) => {
return await authAxios.post(`/profile/updateNickname`, { newNickname });
};
// 타 사용자 프로필 조회
export const getMembersProfile = async (memberId) => {
return await authAxios.get(`/members/${memberId}/profile`);
};
2.2 인증 제공자 (AuthProvider.jsx)
세션 기반 인증에서는 클라이언트 측에서 세션 ID를 직접 관리할 필요가 없으므로, 세션을 관리하는 것은 서버의 역할이다.
따라서 클라이언트는 로그인 상태를 확인하기 위해 서버에 요청을 보내고, 서버가 세션 정보를 반환하게 된다.
// src/context/AuthContext.jsx
import { getProfile } from "api/auth";
import { createContext, useContext, useEffect, useState } from "react";
const AuthContext = createContext();
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const isSignIn = !!user; // 로그인 상태 확인
useEffect(() => {
const fetchUser = async () => {
try {
const response = await getProfile();
setUser(response.data.member);
} catch (error) {
setUser(null); // 인증 실패 시 사용자 정보 초기화
} finally {
setLoading(false);
}
};
fetchUser();
}, []);
if (loading) {
return <div>Loading...</div>;
}
return (
<AuthContext.Provider value={{ user, setUser, isSignIn }}>
{children}
</AuthContext.Provider>
);
};
export const useAuth = () => useContext(AuthContext);
3. 토큰 기반 인증
3.1 Axios 설정
토큰 기반 인증에서는 요청 시
Authorization header
에 토큰을 포함시켜야 하며, 이 토큰은 클라이언트에서 로컬 스토리지 또는 다른 저장소에 저장한다.
withCredentials는 필요 없다.
// src/api/auth.js
import axios from "axios";
const authAxios = axios.create({
baseURL: process.env.REACT_APP_SERVER_URL, // 환경 변수에서 기본 URL을 설정
});
// 요청 인터셉터 추가
authAxios.interceptors.request.use(
(config) => {
const token = localStorage.getItem("authToken"); // 로컬 스토리지에서 토큰 가져오기
if (token) {
config.headers.Authorization = `Bearer ${token}`; // 헤더에 토큰 추가
}
return config;
},
(error) => {
console.error("에러가 발생하였습니다. 문의해주세요.", error);
return Promise.reject({ state: "ERROR", message: error.message });
}
);
// 회원 가입
export const register = async (data) => {
return await authAxios.post(`/members/new`, data);
};
// 로그인
export const login = async (data) => {
const response = await authAxios.post(`/login`, data);
const token = response.data.accessToken; // 응답에서 토큰 추출
localStorage.setItem("authToken", token); // 로컬 스토리지에 토큰 저장
return response;
};
// 로그아웃
export const logout = async () => {
localStorage.removeItem("authToken"); // 로컬 스토리지에서 토큰 제거
return await authAxios.get(`/logout`);
};
// 프로필 조회
export const getProfile = async () => {
return await authAxios.get(`/profile`);
};
// 프로필 닉네임 변경
export const updateNickname = async (newNickname) => {
return await authAxios.post(`/profile/updateNickname`, { newNickname });
};
// 타 사용자 프로필 조회
export const getMembersProfile = async (memberId) => {
return await authAxios.get(`/members/${memberId}/profile`);
};
3.2 인증 제공자 (AuthProvider.jsx)
토큰 기반 인증에서는 클라이언트 측에서 로그인 상태를 확인하고, 토큰을 로컬 스토리지에서 읽어와서 사용한다.
// src/context/AuthContext.jsx
import { getUser } from "api/auth";
import { createContext, useContext, useEffect, useState } from "react";
const AuthContext = createContext();
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const isSignIn = !!user;
useEffect(() => {
const fetchUser = async () => {
// 토큰이 있는 경우에만 사용자 정보를 가져오도록 설정
const token = localStorage.getItem("authToken");
if (token) {
try {
const response = await getUser();
setUser(response.data);
} catch (error) {
setUser(null);
} finally {
setLoading(false);
}
} else {
setLoading(false);
}
};
fetchUser();
}, []);
if (loading) {
return <div>Loading...</div>;
}
return (
<AuthContext.Provider value={{ user, setUser, isSignIn }}>
{children}
</AuthContext.Provider>
);
};
export const useAuth = () => useContext(AuthContext);
댓글남기기