React

 

axiosInstance를 사용하면 GET, POST, PUT, DELETE와 같은 모든 API 요청을 중앙에서 관리할 수 있으며, 토큰 만료 처리와 같은 공통 로직을 한 곳에서 처리할 수 있습니다.

 

 

axiosInstance를 사용하는 이유

  1. 중앙 집중식 관리: 모든 API 호출이 한 곳에서 관리되므로 코드의 중복을 줄이고 유지보수가 용이해집니다.
  2. 자동 토큰 갱신: 응답 인터셉터에서 토큰 만료를 감지하고, 자동으로 갱신한 후 요청을 재시도할 수 있습니다.
  3. 에러 처리 일관성: 모든 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 요청을 처리하면 인증 관련 문제를 최소화하고, 유지보수하기 쉬운 코드베이스를 유지할 수 있습니다.

 

 

 

 

 

 

 

 

 

about author

PHRASE

Level 60  라이트

될 수 있는 한 선(善)을 행하고, 무엇보다도 더 자유를 사랑하고, 가령 왕좌 밑에 있을지라도 단연코 진리를 배반하지 않으리! - L. 베토벤 [수기]

댓글 ( 0)

댓글 남기기

작성