로고로고

JWT를 활용한 로그인 기능 구현 (Next.js)

2025년 7월 2일

위리브 프로젝트 - 로그인 기능 구현을 블로그 글로 정리

 

  • 사용 기술 스택
    • Next.js (Page Router)
      • 프로젝트에 페이지 라우터를 사용한 적이 거의 없어 선택
    • TypeScript
    • React Hook Form + Zod
    • Axios
    • Zustand

 

 

  • JWT 로그인 흐름 설계
    1. 로그인 요청
    2. 서버에서 JWT 발급
    3. 프론트엔드가 토큰 저장 (쿠키)
    4. 이후 요청 시 헤더에 토큰 포함
    5. 서버에서 토큰 검증
    6. 사용자 인증 완료

 

 

  • 로그인 요청
    • 로그인에 성공하면 백엔드에서 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("/");
};