icon
8장 : 비동기 처리 및 데이터 페칭

axios 라이브러리 소개


프론트엔드 개발에서 매우 널리 사용되는 HTTP 클라이언트 라이브러리인 Axios에 대해 알아보겠습니다.

fetch API는 자바스크립트 표준으로 강력하지만, 몇 가지 단점(예: 에러 처리의 번거로움, JSON 변환 수동 처리, 요청 취소의 복잡성)이 있습니다. Axios는 이러한 fetch의 단점을 보완하고, 더 많은 기능을 제공하여 개발 편의성을 크게 향상시킵니다.


Axios란 무엇인가?

Axios는 브라우저와 Node.js 환경에서 모두 사용할 수 있는 Promise 기반의 HTTP 클라이언트 라이브러리입니다. 즉, 서버와의 통신(API 호출)을 더 쉽고 편리하게 할 수 있도록 도와주는 도구입니다.

주요 특징 및 장점

  • Promise 기반: async/await와 함께 사용하여 비동기 코드를 매우 간결하게 작성할 수 있습니다.
  • 간편한 API: fetch보다 더 직관적이고 사용하기 쉬운 API를 제공합니다.
  • 자동 JSON 변환: 응답 데이터를 자동으로 JSON으로 파싱해주므로 response.json()을 별도로 호출할 필요가 없습니다.
  • 에러 처리 용이: HTTP 상태 코드 4xx, 5xx 등의 오류 응답도 .catch() 블록에서 쉽게 처리할 수 있습니다. (기본 fetch는 200번대 응답이 아니어도 .then()으로 넘어감)
  • 요청/응답 인터셉터: 요청이 보내지기 전이나 응답이 오기 전에 공통 로직(예: 헤더 추가, 에러 로깅)을 처리할 수 있는 기능을 제공합니다.
  • 요청 취소 기능: CancelToken (레거시) 또는 AbortController를 사용하여 요청을 쉽게 취소할 수 있습니다.
  • 요청 타임아웃 설정: 특정 시간 내에 응답이 없으면 요청을 자동으로 취소하는 기능을 제공합니다.
  • 업로드 진행률 추적: 파일 업로드 시 진행률을 모니터링할 수 있습니다.

Axios 설치

Axios를 사용하려면 먼저 프로젝트에 설치해야 합니다.

npm install axios
# 또는
yarn add axios

Axios의 기본 사용법

설치 후에는 다음과 같이 Axios를 임포트하여 사용할 수 있습니다.

import axios from 'axios';

GET 요청

데이터를 조회할 때 사용합니다.

// 모든 게시물 가져오기
axios.get('https://jsonplaceholder.typicode.com/posts')
  .then(response => {
    console.log('게시물 목록:', response.data); // response.data에 실제 데이터가 들어있음
  })
  .catch(error => {
    console.error('게시물 가져오기 오류:', error);
  });

// 특정 ID의 게시물 가져오기
axios.get('https://jsonplaceholder.typicode.com/posts/1')
  .then(response => {
    console.log('단일 게시물:', response.data);
  })
  .catch(error => {
    console.error('단일 게시물 가져오기 오류:', error);
  });

POST 요청

새로운 데이터를 생성할 때 사용합니다. 두 번째 인자로 전송할 데이터를 객체 형태로 전달합니다.

axios.post('https://jsonplaceholder.typicode.com/posts', {
  title: '새로운 게시물',
  body: '이것은 Axios로 작성된 새 게시물입니다.',
  userId: 1,
})
.then(response => {
  console.log('게시물 생성 성공:', response.data);
})
.catch(error => {
  console.error('게시물 생성 오류:', error);
});

PUT/PATCH 요청

기존 데이터를 업데이트할 때 사용합니다.

  • PUT: 리소스의 전체를 교체할 때 (모든 필드 전송)
  • PATCH: 리소스의 일부만 업데이트할 때 (일부 필드 전송)
// PUT 요청 (전체 업데이트)
axios.put('https://jsonplaceholder.typicode.com/posts/1', {
  id: 1, // PUT은 보통 ID도 함께 보냄
  title: '업데이트된 제목',
  body: '내용도 완전히 바뀌었습니다.',
  userId: 1,
})
.then(response => {
  console.log('게시물 PUT 업데이트 성공:', response.data);
})
.catch(error => {
  console.error('게시물 PUT 업데이트 오류:', error);
});

// PATCH 요청 (부분 업데이트)
axios.patch('https://jsonplaceholder.typicode.com/posts/1', {
  title: '부분적으로 업데이트된 제목',
})
.then(response => {
  console.log('게시물 PATCH 업데이트 성공:', response.data);
})
.catch(error => {
  console.error('게시물 PATCH 업데이트 오류:', error);
});

DELETE 요청

데이터를 삭제할 때 사용합니다.

axios.delete('https://jsonplaceholder.typicode.com/posts/1')
  .then(response => {
    console.log('게시물 삭제 성공:', response.status); // 200 OK, 204 No Content 등
  })
  .catch(error => {
    console.error('게시물 삭제 오류:', error);
  });

async/await와 함께 사용

Axios는 Promise를 반환하므로, async/await 문법과 함께 사용하면 비동기 코드를 동기 코드처럼 깔끔하게 작성할 수 있습니다.

src/components/AxiosExample.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';

function AxiosExample() {
  const [post, setPost] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchPost = async () => {
      try {
        setLoading(true);
        setError(null);
        // await과 함께 axios.get 사용
        const response = await axios.get('https://jsonplaceholder.typicode.com/posts/1');
        setPost(response.data); // Axios는 응답 데이터를 response.data에 넣어줍니다.
      } catch (err) {
        // Axios는 HTTP 오류 (4xx, 5xx)도 catch 블록에서 처리 가능합니다.
        console.error("Axios 오류 객체:", err);
        if (err.response) {
          // 서버 응답이 있는 경우 (예: 404, 500)
          setError(new Error(`데이터를 불러오지 못했습니다. ${err.response.status} ${err.response.statusText}`));
          console.error("응답 데이터:", err.response.data);
          console.error("응답 상태:", err.response.status);
          console.error("응답 헤더:", err.response.headers);
        } else if (err.request) {
          // 요청이 이루어졌으나 응답을 받지 못한 경우 (예: 네트워크 오류)
          setError(new Error("네트워크 오류가 발생했습니다."));
          console.error("요청 객체:", err.request);
        } else {
          // 요청 설정 중 문제 발생 (예: 잘못된 URL)
          setError(new Error(`예상치 못한 오류: ${err.message}`));
          console.error("오류 메시지:", err.message);
        }
      } finally {
        setLoading(false);
      }
    };

    fetchPost();
  }, []);

  if (loading) {
    return <div style={{ textAlign: 'center', padding: '20px' }}>Axios로 게시물을 불러오는 중...</div>;
  }

  if (error) {
    return <div style={{ textAlign: 'center', padding: '20px', color: 'red' }}>Axios 오류: {error.message}</div>;
  }

  return (
    <div style={{ maxWidth: '600px', margin: '20px auto', padding: '25px', border: '1px solid #ddd', borderRadius: '8px', boxShadow: '0 2px 5px rgba(0,0,0,0.05)', backgroundColor: '#fdfdfd' }}>
      <h2 style={{ textAlign: 'center', color: '#2c3e50', marginBottom: '20px' }}>Axios로 가져온 게시물</h2>
      <h3 style={{ color: '#3498db', marginBottom: '10px' }}>{post.title}</h3>
      <p>{post.body}</p>
    </div>
  );
}

export default AxiosExample;

App.jsAxiosExample 컴포넌트를 추가하여 테스트해 보세요.


Axios 인스턴스 (Instance)

여러 API 요청에서 공통적으로 적용해야 할 설정(예: 기본 URL, 헤더, 타임아웃)이 있다면, Axios 인스턴스를 생성하여 관리할 수 있습니다. 이는 코드 중복을 줄이고 유지보수성을 높입니다.

src/api/jsonPlaceholder.js
import axios from 'axios';

const jsonPlaceholder = axios.create({
  baseURL: 'https://jsonplaceholder.typicode.com', // 모든 요청의 기본 URL
  timeout: 5000, // 5초 동안 응답이 없으면 타임아웃
  headers: {
    'Content-Type': 'application/json',
    // 'Authorization': 'Bearer YOUR_TOKEN' // 인증 토큰 등 공통 헤더
  },
});

export default jsonPlaceholder;

Axios 인스턴스 사용 예시

src/components/AxiosInstanceExample.js
import React, { useState, useEffect } from 'react';
import jsonPlaceholder from '../api/jsonPlaceholder'; // 인스턴스 임포트

function AxiosInstanceExample() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchUsers = async () => {
      try {
        setLoading(true);
        setError(null);
        // 인스턴스를 사용하여 요청
        const response = await jsonPlaceholder.get('/users'); // baseURL이 적용됨
        setUsers(response.data);
      } catch (err) {
        setError(err);
      } finally {
        setLoading(false);
      }
    };

    fetchUsers();
  }, []);

  if (loading) {
    return <div style={{ textAlign: 'center', padding: '20px' }}>Axios 인스턴스로 사용자 목록 불러오는 중...</div>;
  }

  if (error) {
    return <div style={{ textAlign: 'center', padding: '20px', color: 'red' }}>Axios 인스턴스 오류: {error.message}</div>;
  }

  return (
    <div style={{ maxWidth: '800px', margin: '20px auto', padding: '20px', border: '1px solid #ddd', borderRadius: '8px', boxShadow: '0 2px 5px rgba(0,0,0,0.05)' }}>
      <h2 style={{ textAlign: 'center', color: '#2c3e50' }}>Axios 인스턴스 사용 예시 (사용자 목록)</h2>
      <ul style={{ listStyle: 'none', padding: 0 }}>
        {users.map(user => (
          <li
            key={user.id}
            style={{
              padding: '15px',
              marginBottom: '10px',
              border: '1px solid #eee',
              borderRadius: '5px',
              backgroundColor: '#fefefe',
              boxShadow: '0 1px 3px rgba(0,0,0,0.02)',
            }}
          >
            <strong style={{ color: '#3498db' }}>{user.name}</strong> ({user.username}) - {user.email}
          </li>
        ))}
      </ul>
    </div>
  );
}

export default AxiosInstanceExample;

Axios와 useEffect의 조합

fetch API와 마찬가지로, Axios도 useEffect 훅 내부에서 async/await와 함께 사용하여 컴포넌트 생명주기에 맞춰 데이터를 가져올 수 있습니다. 로딩 상태와 에러 처리 패턴은 fetch와 동일하게 적용됩니다.

src/hooks/useAxiosFetch.js
// `useFetch` 커스텀 훅을 Axios 기반으로 변경 (src/hooks/useAxiosFetch.js)
import { useState, useEffect } from 'react';
import axios from 'axios';

const useAxiosFetch = (url, options = {}) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const source = axios.CancelToken.source(); // 요청 취소를 위한 CancelToken 생성
    // 또는 const controller = new AbortController(); const signal = controller.signal; (Axios 0.27.0+ 부터 지원)

    const fetchData = async () => {
      setLoading(true);
      setError(null);
      try {
        const response = await axios.get(url, {
          cancelToken: source.token, // 요청 취소 토큰 전달
          // signal: controller.signal, // AbortController 사용 시
          ...options
        });
        setData(response.data);
      } catch (err) {
        if (axios.isCancel(err)) { // 요청 취소로 인한 에러인지 확인
          console.log('Request canceled:', err.message);
        } else {
          setError(err);
        }
      } finally {
        setLoading(false);
      }
    };

    if (url) {
      fetchData();
    } else {
      setLoading(false);
    }

    // 클린업 함수: 컴포넌트 언마운트 또는 의존성 변경 시 요청 취소
    return () => {
      source.cancel('Operation canceled by the user.'); // 요청 취소 메시지
      // controller.abort(); // AbortController 사용 시
    };
  }, [url, JSON.stringify(options)]); // options 객체는 문자열화하여 의존성 안정화

  return { data, loading, error };
};

export default useAxiosFetch;

위 예시에서 options 객체를 JSON.stringify하는 이유는, 객체가 의존성 배열에 직접 포함될 경우 참조가 매번 달라져 불필요한 재실행을 유발할 수 있기 때문입니다. 더 나은 방법은 useMemo 등으로 options 객체를 안정화하는 것입니다.


fetch vs axios 정리

특징fetch (네이티브 API)axios (라이브러리)
설치 필요 여부필요 없음 (내장)설치 필요 (npm install axios)
Promise 기반
JSON 변환.json() 메서드 호출 필요 (수동)자동 변환 (response.data)
에러 처리네트워크 오류만 .catch()로 처리, HTTP 오류는 .then()에서 response.ok 확인 필요HTTP 오류 (4xx, 5xx) 포함 모든 에러를 .catch()로 처리
요청/응답 인터셉터없음강력한 지원
요청 취소AbortController 필요 (복잡)CancelToken 또는 AbortController로 간편하게 지원
타임아웃수동 구현 필요 (setTimeoutPromise.race)기본 지원
진행률 추적복잡기본 지원
번들 사이즈작음 (0KB)비교적 큼 (약 10-15KB gzip)

결론적으로, 소규모 프로젝트나 간단한 요청에는 fetch API도 충분히 좋지만, 실제 프로덕션 환경의 대규모 애플리케이션에서는 Axios가 제공하는 풍부한 기능과 편리한 에러 처리, 인터셉터 기능 등이 개발 생산성과 유지보수성에 큰 이점을 제공합니다. 따라서 대부분의 리액트 프로젝트에서는 Axios를 사용하는 것을 강력히 권장합니다.


"Axios 라이브러리 소개"는 여기까지입니다. 이 장에서는 Axios가 무엇인지, 왜 필요한지, 그리고 fetch API와 비교했을 때 어떤 장점들이 있는지 알아보았습니다. GET, POST, PUT, DELETE와 같은 기본적인 HTTP 요청을 async/await와 함께 사용하는 방법과, Axios 인스턴스를 생성하여 공통 설정을 관리하는 방법까지 학습했습니다.

이제 여러분은 리액트 애플리케이션에서 가장 널리 사용되는 데이터 페칭 도구인 Axios를 능숙하게 다룰 수 있게 되었습니다.