JWT를 활용한 로그인 기능 구현 (Next.js)
2025년 7월 2일
위리브 프로젝트 - 로그인 기능 구현을 블로그 글로 정리
- 사용 기술 스택
- Next.js (Page Router)
- 프로젝트에 페이지 라우터를 사용한 적이 거의 없어 선택
- TypeScript
- React Hook Form + Zod
- Axios
- Zustand
- Next.js (Page Router)
- JWT 로그인 흐름 설계
- 로그인 요청
- 서버에서 JWT 발급
- 프론트엔드가 토큰 저장 (쿠키)
- 이후 요청 시 헤더에 토큰 포함
- 서버에서 토큰 검증
- 사용자 인증 완료
- 로그인 요청
- 로그인에 성공하면 백엔드에서 Set-Cookie 헤더를 통해 JWT를 쿠키에 저장
- httpOnly, secure, sameSite 속성으로 보안 강화
export const postLogin = async (form: LoginForm) => {
const res = await axios.post("/api/login", form, {
withCredentials: true,
});
return res.data;
};
const onSubmit = async (data: LoginForm) => {
try {
await postLogin(data);
toast.success("로그인 성공!");
router.push("/dashboard");
} catch (e) {
toast.error("로그인 실패");
}
};
- 로그인 후 사용자 정보 가져오기
- 쿠키에 저장된 토큰은 클라이언트에서 직접 접근할 수 없지만, hetServerSideProps에서 쿠키를 읽어 토큰을 해석
- ex) jwt.decode() 혹은 jwt.verify()로 사용자 ID, role 정보 추출
export const getServerSideProps: GetServerSideProps = async ({ req }) => {
const cookies = cookie.parse(req.headers.cookie || "");
const token = cookies["access_token"];
if (!token) {
return { redirect: { destination: "/", permanent: false } };
}
const decoded = jwt.verify(token, process.env.JWT_SECRET!) as JwtPayload;
return {
props: { user: decoded },
};
};
- 클라이언트에서 사용자 정보 상태 관리 (Zustand)
- useEffect로 페이지 로드 시 서버에서 전달된 유저 정보를 상태에 저장
interface AuthState {
user: User | null;
setUser: (user: User) => void;
}
export const useAuthStore = create<AuthState>((set) => ({
user: null,
setUser: (user) => set({ user }),
}));
- 로그아웃 처리
export const postLogout = () => {
return axios.post("/api/logout", null, { withCredentials: true });
};
const handleLogout = async () => {
await postLogout();
useAuthStore.getState().setUser(null);
router.push("/");
};