axiosInstance를 사용하면 GET, POST, PUT, DELETE와 같은 모든 API 요청을 중앙에서 관리할 수 있으며, 토큰 만료 처리와 같은 공통 로직을 한 곳에서 처리할 수 있습니다.
axiosInstance를 사용하는 이유
- 중앙 집중식 관리: 모든 API 호출이 한 곳에서 관리되므로 코드의 중복을 줄이고 유지보수가 용이해집니다.
- 자동 토큰 갱신: 응답 인터셉터에서 토큰 만료를 감지하고, 자동으로 갱신한 후 요청을 재시도할 수 있습니다.
- 에러 처리 일관성: 모든 API 요청에 대해 일관된 에러 처리 로직을 적용할 수 있습니다.
API 요청 인터셉터에서 세션과 토큰 연동
axiosInstance가 제대로 작동하려면 요청 인터셉터에서 getSession을 사용하여 현재 세션의 accessToken을 가져와 요청에 포함시켜야 합니다.
/utils/axiosInstance .js
import axios from 'axios'; import { signIn, getSession } from 'next-auth/react'; // Axios 인스턴스 생성 const axiosInstance = axios.create({ baseURL: 'http://localhost:5000/api', // 백엔드 서버의 API URL }); // 요청 인터셉터: 요청을 보내기 전에 토큰을 포함 axiosInstance.interceptors.request.use( async (config) => { const session = await getSession(); // NextAuth로부터 현재 세션 가져오기 if (session?.accessToken) { config.headers['Authorization'] = `Bearer ${session.accessToken}`; // Access Token 설정 } return config; }, (error) => Promise.reject(error) ); // 응답 인터셉터: 토큰이 만료되면 갱신하고 재시도 axiosInstance.interceptors.response.use( (response) => response, async (error) => { const originalRequest = error.config; if (error.response.status === 403 && !originalRequest._retry) { originalRequest._retry = true; // 무한 루프 방지 설정 const session = await signIn('credentials', { redirect: false, refresh: true }); // 토큰 갱신 요청 if (session?.accessToken) { originalRequest.headers['Authorization'] = `Bearer ${session.accessToken}`; // 새로운 Access Token으로 재설정 return axiosInstance(originalRequest); // 원래 요청 재시도 } } return Promise.reject(error); } ); export default axiosInstance;
axiosInstance로 API 요청하는 예제
다음은 axiosInstance를 사용하여 GET, POST, PUT, DELETE 작업을 수행하는 예제입니다.
1. GET 요청 예제
게시판에서 모든 게시글을 가져오는 예제입니다.
// /pages/board/index.js 'use client'; import { useEffect, useState } from 'react'; import axiosInstance from '@/utils/axios'; export default function BoardList() { const [posts, setPosts] = useState([]); useEffect(() => { async function fetchPosts() { try { const response = await axiosInstance.get('/posts'); // axiosInstance로 GET 요청 setPosts(response.data); } catch (error) { console.error('게시글을 가져오는 중 오류 발생:', error.message); } } fetchPosts(); }, []); return ( <div> <h1>게시글 목록</h1> <ul> {posts.map((post) => ( <li key={post.id}>{post.title}</li> ))} </ul> </div> ); }
2. POST 요청 예제
게시글 작성 요청을 보내는 예제입니다.
// /pages/board/write.js 'use client'; import { useState } from 'react'; import axiosInstance from '@/utils/axios'; export default function WritePost() { const [title, setTitle] = useState(''); const [content, setContent] = useState(''); const handleSubmit = async (e) => { e.preventDefault(); try { const response = await axiosInstance.post('/posts', { // axiosInstance로 POST 요청 title, content, }); alert('게시글이 작성되었습니다!'); } catch (error) { alert('게시글 작성에 실패했습니다: ' + error.message); } }; return ( <div> <h1>게시글 작성</h1> <form onSubmit={handleSubmit}> <input type="text" placeholder="제목" value={title} onChange={(e) => setTitle(e.target.value)} /> <textarea placeholder="내용" value={content} onChange={(e) => setContent(e.target.value)} /> <button type="submit">글쓰기</button> </form> </div> ); }
3. PUT 요청 예제
특정 게시글을 업데이트하는 예제입니다.
// /pages/board/edit/[id].js 'use client'; import { useState, useEffect } from 'react'; import axiosInstance from '@/utils/axios'; import { useRouter } from 'next/router'; export default function EditPost({ postId }) { const [title, setTitle] = useState(''); const [content, setContent] = useState(''); const router = useRouter(); useEffect(() => { // 기존 게시글 데이터 가져오기 async function fetchPost() { try { const response = await axiosInstance.get(`/posts/${postId}`); setTitle(response.data.title); setContent(response.data.content); } catch (error) { console.error('게시글을 가져오는 중 오류 발생:', error.message); } } fetchPost(); }, [postId]); const handleSubmit = async (e) => { e.preventDefault(); try { await axiosInstance.put(`/posts/${postId}`, { // axiosInstance로 PUT 요청 title, content, }); alert('게시글이 수정되었습니다!'); router.push('/board'); } catch (error) { alert('게시글 수정에 실패했습니다: ' + error.message); } }; return ( <div> <h1>게시글 수정</h1> <form onSubmit={handleSubmit}> <input type="text" placeholder="제목" value={title} onChange={(e) => setTitle(e.target.value)} /> <textarea placeholder="내용" value={content} onChange={(e) => setContent(e.target.value)} /> <button type="submit">수정하기</button> </form> </div> ); }
4. DELETE 요청 예제
특정 게시글을 삭제하는 예제입니다.
// /pages/board/[id].js 'use client'; import { useRouter } from 'next/router'; import axiosInstance from '@/utils/axios'; export default function PostDetail({ postId }) { const router = useRouter(); const handleDelete = async () => { try { await axiosInstance.delete(`/posts/${postId}`); // axiosInstance로 DELETE 요청 alert('게시글이 삭제되었습니다.'); router.push('/board'); } catch (error) { alert('게시글 삭제에 실패했습니다: ' + error.message); } }; return ( <div> <h1>게시글 상세 보기</h1> {/* 게시글 내용 표시 로직 */} <button onClick={handleDelete}>삭제하기</button> </div> ); }
결론
- 모든 API 요청(GET, POST, PUT, DELETE)을 axiosInstance를 사용하여 중앙에서 관리하는 것이 좋습니다.
- 이렇게 하면 토큰 만료, 인증, 에러 처리 등 공통 로직을 일관되게 관리할 수 있습니다.
- axiosInstance를 사용하면 요청 전 토큰을 설정하고, 응답에서 토큰 만료를 자동으로 처리하여 보안과 유지보수성을 크게 향상시킬 수 있습니다.
이 방식으로 모든 API 요청을 처리하면 인증 관련 문제를 최소화하고, 유지보수하기 쉬운 코드베이스를 유지할 수 있습니다.
댓글 ( 0)
댓글 남기기