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;

'아이티 코드' 카테고리의 다른 글

리액트플로우  (0) 2025.03.18
마크다운 내용정리  (0) 2025.02.27
2. Dockerfile 작성 (Next.js 최적화)  (0) 2025.02.22
스트리밍 자료  (0) 2025.02.12
스트리밍  (0) 2025.02.11

+ Recent posts