import { useEffect } from "react";
import { useRouter } from "next/router";
import { toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
toast.configure();
const checkAuth = async () => {
const token = localStorage.getItem("token");
if (!token) return false;
try {
const response = await fetch("/api/auth/validate", {
method: "POST",
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${token}` },
});
if (!response.ok) throw new Error("Invalid token");
return true;
} catch (error) {
return false;
}
};
export default function ProtectedPage() {
const router = useRouter();
useEffect(() => {
(async () => {
const isValid = await checkAuth();
if (!isValid) {
toast.error("세션이 만료되었습니다. 다시 로그인 해주세요.");
localStorage.removeItem("token");
router.push("/logout");
}
})();
}, [router]);
return <div>보호된 페이지</div>;
}
설명
1. localStorage에서 토큰을 가져옵니다.
2. /api/auth/validate로 토큰 검증 요청을 보냅니다.
3. 토큰이 유효하지 않으면:
• react-toastify를 사용해 메시지를 표시합니다.
• localStorage에서 토큰을 삭제합니다.
• /logout 페이지로 이동합니다.
추가 작업
• /api/auth/validate 엔드포인트에서 토큰 검증 로직을 구현해야 합니다.
• 로그아웃 페이지(/logout 또는 /login 등)를 만들어 사용자에게 안내 메시지를 표시할 수 있습니다.
이 코드가 원하는 동작을 수행하도록 필요한 부분을 맞춰 수정하면 됩니다! 😊
import { useEffect } from "react";
import { useRouter } from "next/router";
export default function Logout() {
const router = useRouter();
useEffect(() => {
// 로그아웃 처리 (예: 토큰 삭제)
localStorage.removeItem("token");
// 로그인 페이지로 리다이렉트
router.push("/login");
}, [router]);
return <p>로그아웃 중입니다...</p>;
}
import { useEffect } from "react";
import { useRouter } from "next/router";
import { ToastContainer, toast } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
function MyApp({ Component, pageProps }: any) {
const router = useRouter();
useEffect(() => {
const checkToken = async () => {
const token = localStorage.getItem("token");
if (!token) {
// 토큰이 없으면 토스트 메시지 표시 후 로그아웃 페이지로 이동
toast.error("토큰이 유효하지 않습니다. 다시 로그인해주세요.", {
position: "top-right",
autoClose: 3000,
});
setTimeout(() => {
router.push("/logout"); // 로그아웃 페이지로 이동
}, 3000);
} else {
// 토큰 유효성 검증 로직 (예: API 호출)
const isValid = await validateToken(token);
if (!isValid) {
toast.error("토큰이 만료되었습니다. 다시 로그인해주세요.", {
position: "top-right",
autoClose: 3000,
});
setTimeout(() => {
router.push("/logout");
}, 3000);
}
}
};
checkToken();
}, [router]);
const validateToken = async (token: string) => {
try {
// 예시: 토큰 검증 API 호출
const response = await fetch("/api/validate-token", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
});
if (response.ok) {
const data = await response.json();
return data.isValid; // API 응답에 따라 유효성 판단
}
return false;
} catch (error) {
console.error("토큰 검증 중 오류 발생:", error);
return false;
}
};
return (
<>
<ToastContainer />
<Component {...pageProps} />
</>
);
}
export default MyApp;
import { Toaster } from 'react-hot-toast';
import { AuthProvider } from '../context/AuthContext';
import '../styles/globals.css';
function MyApp({ Component, pageProps }) {
return (
<AuthProvider>
<Component {...pageProps} />
<Toaster position="top-center" />
</AuthProvider>
);
}
export default MyApp;
import { createContext, useContext, useEffect, useState } from 'react';
import { useRouter } from 'next/router';
import Cookies from 'js-cookie';
import toast from 'react-hot-toast';
const AuthContext = createContext();
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const router = useRouter();
useEffect(() => {
// 페이지 로드 시 토큰 유효성 체크
checkUserAuthentication();
}, []);
const checkUserAuthentication = async () => {
const token = Cookies.get('token');
if (!token) {
setLoading(false);
return;
}
try {
// 서버에 토큰 유효성 검증 요청
const response = await fetch('/api/auth/validate-token', {
headers: {
Authorization: `Bearer ${token}`
}
});
if (!response.ok) {
throw new Error('Invalid token');
}
const data = await response.json();
setUser(data.user);
} catch (error) {
handleInvalidToken();
} finally {
setLoading(false);
}
};
const handleInvalidToken = () => {
toast.error('인증이 만료되었습니다. 다시 로그인해주세요.');
logout();
};
const logout = () => {
Cookies.remove('token');
setUser(null);
router.push('/login');
};
return (
<AuthContext.Provider value={{ user, loading, logout, handleInvalidToken }}>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
return useContext(AuthContext);
}
import axios from 'axios';
import Cookies from 'js-cookie';
import toast from 'react-hot-toast';
import { useRouter } from 'next/router';
const api = axios.create({
baseURL: '/api',
headers: {
'Content-Type': 'application/json',
}
});
// 요청 인터셉터 설정
api.interceptors.request.use(config => {
const token = Cookies.get('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// 응답 인터셉터 설정
api.interceptors.response.use(
response => response,
error => {
// 401 에러 (인증 실패) 처리
if (error.response && error.response.status === 401) {
toast.error('인증이 만료되었습니다. 다시 로그인해주세요.');
// 토큰 제거
Cookies.remove('token');
// 로그인 페이지로 리디렉션
// Next.js에서는 인터셉터 내부에서 바로 router.push를 사용할 수 없기 때문에
// 약간의 지연 후 리디렉션
setTimeout(() => {
window.location.href = '/login';
}, 1500);
}
return Promise.reject(error);
}
);
export default api;