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
문법과 함께 사용하면 비동기 코드를 동기 코드처럼 깔끔하게 작성할 수 있습니다.
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.js
에 AxiosExample
컴포넌트를 추가하여 테스트해 보세요.
Axios 인스턴스 (Instance)
여러 API 요청에서 공통적으로 적용해야 할 설정(예: 기본 URL, 헤더, 타임아웃)이 있다면, Axios 인스턴스를 생성하여 관리할 수 있습니다. 이는 코드 중복을 줄이고 유지보수성을 높입니다.
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 인스턴스 사용 예시
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
와 동일하게 적용됩니다.
// `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 로 간편하게 지원 |
타임아웃 | 수동 구현 필요 (setTimeout 과 Promise.race ) | 기본 지원 |
진행률 추적 | 복잡 | 기본 지원 |
번들 사이즈 | 작음 (0KB) | 비교적 큼 (약 10-15KB gzip) |
결론적으로, 소규모 프로젝트나 간단한 요청에는 fetch
API도 충분히 좋지만, 실제 프로덕션 환경의 대규모 애플리케이션에서는 Axios가 제공하는 풍부한 기능과 편리한 에러 처리, 인터셉터 기능 등이 개발 생산성과 유지보수성에 큰 이점을 제공합니다. 따라서 대부분의 리액트 프로젝트에서는 Axios를 사용하는 것을 강력히 권장합니다.
"Axios 라이브러리 소개"는 여기까지입니다. 이 장에서는 Axios가 무엇인지, 왜 필요한지, 그리고 fetch
API와 비교했을 때 어떤 장점들이 있는지 알아보았습니다. GET, POST, PUT, DELETE와 같은 기본적인 HTTP 요청을 async/await
와 함께 사용하는 방법과, Axios 인스턴스를 생성하여 공통 설정을 관리하는 방법까지 학습했습니다.
이제 여러분은 리액트 애플리케이션에서 가장 널리 사용되는 데이터 페칭 도구인 Axios를 능숙하게 다룰 수 있게 되었습니다.