axios 라이브러리 소개
Axios로 GET·POST·수정·삭제 요청을 보내고 공통 설정 인스턴스를 만들어 Fetch API와 사용 방식을 비교합니다.
프론트엔드 개발에서 매우 널리 쓰이는 HTTP 클라이언트 라이브러리 Axios를 알아보겠습니다.
fetch는 브라우저에 내장된 표준 API이고 AbortController를 포함해 요청에 필요한 기본 기능을 제공합니다. 다만 HTTP 오류 판정과 JSON 변환을 호출 코드에서 직접 처리해야 합니다.
Axios는 인스턴스 기본값, 요청·응답 인터셉터, timeout, 응답 변환을 한 클라이언트 계층에 모으고 싶을 때 유용합니다. 어느 쪽이 적합한지는 프로젝트의 정책 복잡도와 번들 비용에 따라 결정합니다.
실습 예시는 외부 공개 API 대신 로컬 Mock API(json-server, http://localhost:4000) 기준으로 맞춥니다.
포트 정책은 4000=Mock API, 4100=별도 백엔드/실시간 서버로 분리하면 실습 환경 충돌을 줄일 수 있습니다.
아래 다이어그램은 호출 의도부터 설정 병합, 실제 전송, 응답 처리와 React 상태 커밋까지의 경계를 보여 줍니다.
Axios 요청 파이프라인과 React UI 커밋 경계 — 상태 추적
실제 요청 config 병합/선택 interceptor/HTTP/응답과 최신 UI commit에서 전달 값이 달라진다. strict JSON과 shape 검사는 별도이며 source 전체가 interceptor를 구현한다는 주장을 피한다.
Intent → transport → UI commit
Axios 설정에서 전송·응답·UI 커밋까지
Axios 인스턴스는 호출 의도와 전송 정책 사이의 경계입니다. 설정의 출처와 인터셉터 반환 계약을 분명히 하고, 네트워크 취소와 오래된 UI 커밋 방지를 함께 설계합니다.
-
API 함수가 호출 의도를 표현
getUser(id, { signal })처럼 method·URL·params/data와 취소 신호를 전달합니다. 컴포넌트는 전송 구현보다 필요한 작업을 말합니다. -
설정을 출처별 우선순위로 병합
library defaults<instance.defaults<request config. 뒤의 요청별 값이 앞의 공통값을 덮어쓰므로 예외 정책을 호출 지점에서 명시할 수 있습니다. -
선택한 요청 인터셉터가 config를 보강
인증·추적처럼 여러 요청에 공통인 관심사만 다룹니다. fulfilled handler는 config를 반환하고, 실패를 계속 전달하려면 reject를 반환합니다.
-
어댑터가 실제 HTTP 전송을 수행
브라우저·Node 환경에 맞는 adapter가 최종 config를 전송합니다.
AbortSignal은 이 전송에 연결되어 대기 중인 요청을 실제로 중단합니다. -
응답 인터셉터가 계약을 명시적으로 이어감
성공 handler는 원래
response또는 의도적으로 정규화한 값을 반환합니다. 오류 handler는 복구하지 않는 한Promise.reject(error)로 거절 상태를 보존합니다. -
호출자가 UI 생명주기에 맞춰 커밋
현재 요청만 성공·오류·loading 상태를 갱신합니다. cleanup 시 transport를 취소하고, active flag나 최신 request ID로 늦은 catch·finally의 stale commit도 막습니다.
controller.abort()
Axios 0.22.0부터 지원하는 signal을 통해 진행 중인 요청을 중단합니다. 취소 오류는 실패 안내와 분리합니다.
active 또는 최신 요청 ID
취소가 늦게 관찰되거나 다른 계층이 이미 완료한 Promise라도 이전 요청이 새 화면 상태를 덮어쓰지 못하게 합니다.
인스턴스에는 서버별 base URL과 timeout 같은 안정적인 기본값을 두고, method·params/data·signal처럼 호출마다 달라지는 값은 요청 config에서 명시합니다.
이 예의 조건
- 원도표의 전체 구조와 설명은 위 관계도와 표에서 유지합니다. 아래 인쇄 표에도 원도표의 관계·조건·라벨·설명·주석을 보존합니다.
- 두 관찰 칸의 위치는 고정입니다. 관찰 상태는 해당 순간의 값이며, 동작·이유는 그 조건에서의 판정과 변화 원인입니다. 독립 비교와 실패 분기는 별도 조건으로 읽고 모든 분기를 한 요청의 필수 실행 순서로 이어 읽지 않습니다.
관계도의 보충 설명
| 구분 | 내용 |
|---|---|
| 원도표 설명 | getUser(id, { signal })처럼 method·URL·params/data와 취소 신호를 전달합니다. 컴포넌트는 전송 구현보다 필요한 작업을 말합니다. |
| 원도표 조건·주석 | API 함수가 호출 의도를 표현 getUser(id, { signal })처럼 method·URL·params/data와 취소 신호를 전달합니다. 컴포넌트는 전송 구현보다 필요한 작업을 말합니다. |
| 원도표 설명 | library defaults < instance.defaults < request config. 뒤의 요청별 값이 앞의 공통값을 덮어쓰므로 예외 정책을 호출 지점에서 명시할 수 있습니다. |
| 원도표 조건·주석 | 설정을 출처별 우선순위로 병합 library defaults < instance.defaults < request config. 뒤의 요청별 값이 앞의 공통값을 덮어쓰므로 예외 정책을 호출 지점에서 명시할 수 있습니다. |
| 원도표 설명 | 브라우저·Node 환경에 맞는 adapter가 최종 config를 전송합니다. AbortSignal은 이 전송에 연결되어 대기 중인 요청을 실제로 중단합니다. |
| 원도표 조건·주석 | 어댑터가 실제 HTTP 전송을 수행 브라우저·Node 환경에 맞는 adapter가 최종 config를 전송합니다. AbortSignal은 이 전송에 연결되어 대기 중인 요청을 실제로 중단합니다. |
| 원도표 설명 | 성공 handler는 원래 response 또는 의도적으로 정규화한 값을 반환합니다. 오류 handler는 복구하지 않는 한 Promise.reject(error)로 거절 상태를 보존합니다. |
| 원도표 조건·주석 | 응답 인터셉터가 계약을 명시적으로 이어감 성공 handler는 원래 response 또는 의도적으로 정규화한 값을 반환합니다. 오류 handler는 복구하지 않는 한 Promise.reject(error)로 거절 상태를 보존합니다. |
| 원도표 설명 | Axios 0.22.0부터 지원하는 signal을 통해 진행 중인 요청을 중단합니다. 취소 오류는 실패 안내와 분리합니다. |
| 장면 | 현재 관찰값 | 설명 | 변화 이유 |
|---|---|---|---|
| 요청 config | config: 병합 결과 / interceptor: 등록 시 선택적 보강 | library < instance < request 우선으로 method·URL·params·data를 준비한다 | 구성의 병합과 UI의 state는 다르다 |
| transport와 응답 | response: 값 또는 reject / 검증: JSON/shape 별도 | adapter가 HTTP를 전송하고 설정된 응답 처리를 거친다 | 모든 예제가 interceptor를 구현한다는 전제는 없다 |
| 현재 UI 반영 | gate: 최신성 / UI: 현재 결과 | 현재 요청의 검증된 결과만 data·error·loading에 쓴다 | Axios의 response.data 자체가 게시글 필드의 검증은 아니다 |
Axios 요청 파이프라인과 React UI 커밋 경계 — 상태 추적
실제 요청 config 병합/선택 interceptor/HTTP/응답과 최신 UI commit에서 전달 값이 달라진다. strict JSON과 shape 검사는 별도이며 source 전체가 interceptor를 구현한다는 주장을 피한다.
Intent → transport → UI commit
Axios 설정에서 전송·응답·UI 커밋까지
Axios 인스턴스는 호출 의도와 전송 정책 사이의 경계입니다. 설정의 출처와 인터셉터 반환 계약을 분명히 하고, 네트워크 취소와 오래된 UI 커밋 방지를 함께 설계합니다.
-
API 함수가 호출 의도를 표현
getUser(id, { signal })처럼 method·URL·params/data와 취소 신호를 전달합니다. 컴포넌트는 전송 구현보다 필요한 작업을 말합니다. -
설정을 출처별 우선순위로 병합
library defaults<instance.defaults<request config. 뒤의 요청별 값이 앞의 공통값을 덮어쓰므로 예외 정책을 호출 지점에서 명시할 수 있습니다. -
선택한 요청 인터셉터가 config를 보강
인증·추적처럼 여러 요청에 공통인 관심사만 다룹니다. fulfilled handler는 config를 반환하고, 실패를 계속 전달하려면 reject를 반환합니다.
-
어댑터가 실제 HTTP 전송을 수행
브라우저·Node 환경에 맞는 adapter가 최종 config를 전송합니다.
AbortSignal은 이 전송에 연결되어 대기 중인 요청을 실제로 중단합니다. -
응답 인터셉터가 계약을 명시적으로 이어감
성공 handler는 원래
response또는 의도적으로 정규화한 값을 반환합니다. 오류 handler는 복구하지 않는 한Promise.reject(error)로 거절 상태를 보존합니다. -
호출자가 UI 생명주기에 맞춰 커밋
현재 요청만 성공·오류·loading 상태를 갱신합니다. cleanup 시 transport를 취소하고, active flag나 최신 request ID로 늦은 catch·finally의 stale commit도 막습니다.
controller.abort()
Axios 0.22.0부터 지원하는 signal을 통해 진행 중인 요청을 중단합니다. 취소 오류는 실패 안내와 분리합니다.
active 또는 최신 요청 ID
취소가 늦게 관찰되거나 다른 계층이 이미 완료한 Promise라도 이전 요청이 새 화면 상태를 덮어쓰지 못하게 합니다.
인스턴스에는 서버별 base URL과 timeout 같은 안정적인 기본값을 두고, method·params/data·signal처럼 호출마다 달라지는 값은 요청 config에서 명시합니다.
이 예의 조건
- 원도표의 전체 구조와 설명은 위 관계도와 표에서 유지합니다. 아래 인쇄 표에도 원도표의 관계·조건·라벨·설명·주석을 보존합니다.
- 두 관찰 칸의 위치는 고정입니다. 관찰 상태는 해당 순간의 값이며, 동작·이유는 그 조건에서의 판정과 변화 원인입니다. 독립 비교와 실패 분기는 별도 조건으로 읽고 모든 분기를 한 요청의 필수 실행 순서로 이어 읽지 않습니다.
관계도의 보충 설명
| 구분 | 내용 |
|---|---|
| 원도표 설명 | getUser(id, { signal })처럼 method·URL·params/data와 취소 신호를 전달합니다. 컴포넌트는 전송 구현보다 필요한 작업을 말합니다. |
| 원도표 조건·주석 | API 함수가 호출 의도를 표현 getUser(id, { signal })처럼 method·URL·params/data와 취소 신호를 전달합니다. 컴포넌트는 전송 구현보다 필요한 작업을 말합니다. |
| 원도표 설명 | library defaults < instance.defaults < request config. 뒤의 요청별 값이 앞의 공통값을 덮어쓰므로 예외 정책을 호출 지점에서 명시할 수 있습니다. |
| 원도표 조건·주석 | 설정을 출처별 우선순위로 병합 library defaults < instance.defaults < request config. 뒤의 요청별 값이 앞의 공통값을 덮어쓰므로 예외 정책을 호출 지점에서 명시할 수 있습니다. |
| 원도표 설명 | 브라우저·Node 환경에 맞는 adapter가 최종 config를 전송합니다. AbortSignal은 이 전송에 연결되어 대기 중인 요청을 실제로 중단합니다. |
| 원도표 조건·주석 | 어댑터가 실제 HTTP 전송을 수행 브라우저·Node 환경에 맞는 adapter가 최종 config를 전송합니다. AbortSignal은 이 전송에 연결되어 대기 중인 요청을 실제로 중단합니다. |
| 원도표 설명 | 성공 handler는 원래 response 또는 의도적으로 정규화한 값을 반환합니다. 오류 handler는 복구하지 않는 한 Promise.reject(error)로 거절 상태를 보존합니다. |
| 원도표 조건·주석 | 응답 인터셉터가 계약을 명시적으로 이어감 성공 handler는 원래 response 또는 의도적으로 정규화한 값을 반환합니다. 오류 handler는 복구하지 않는 한 Promise.reject(error)로 거절 상태를 보존합니다. |
| 원도표 설명 | Axios 0.22.0부터 지원하는 signal을 통해 진행 중인 요청을 중단합니다. 취소 오류는 실패 안내와 분리합니다. |
| 장면 | 현재 관찰값 | 설명 | 변화 이유 |
|---|---|---|---|
| 요청 config | config: 병합 결과 / interceptor: 등록 시 선택적 보강 | library < instance < request 우선으로 method·URL·params·data를 준비한다 | 구성의 병합과 UI의 state는 다르다 |
| transport와 응답 | response: 값 또는 reject / 검증: JSON/shape 별도 | adapter가 HTTP를 전송하고 설정된 응답 처리를 거친다 | 모든 예제가 interceptor를 구현한다는 전제는 없다 |
| 현재 UI 반영 | gate: 최신성 / UI: 현재 결과 | 현재 요청의 검증된 결과만 data·error·loading에 쓴다 | Axios의 response.data 자체가 게시글 필드의 검증은 아니다 |
Axios란 무엇인가?
Axios는 브라우저와 Node.js 환경에서 모두 사용할 수 있는 Promise 기반의 HTTP 클라이언트 라이브러리입니다.
즉, 서버와의 통신(API 호출)을 더 쉽고 편리하게 할 수 있도록 도와주는 도구입니다.
주요 특징 및 장점- Promise 기반:
async/await와 함께 사용하여 비동기 코드를 매우 간결하게 작성할 수 있습니다. - 간편한 API:
fetch보다 더 직관적이고 사용하기 쉬운 API를 제공합니다. - 자동 JSON 변환: 응답 데이터를 자동으로 JSON으로 파싱해주므로
response.json()을 별도로 호출할 필요가 없습니다. - 에러 처리 정책: 기본
validateStatus에서는 2xx 밖의 HTTP 응답을 reject합니다. 기준을 바꾸면 같은 상태 코드가 fulfilled 경로로 갈 수 있습니다. 기본fetch는 HTTP 상태와 관계없이 응답 Promise를 resolve하므로response.ok등을 직접 확인합니다. - 요청/응답 인터셉터: adapter가 요청을 보내기 전과 응답·오류가 돌아온 뒤 공통 로직을 적용할 수 있습니다.
- 요청 취소 기능: Axios 0.22.0부터 표준
AbortController의signal을 지원합니다.CancelToken은 deprecated API이므로 새 코드에서는 사용하지 않습니다. - 요청 타임아웃 설정: 특정 시간 내에 응답이 없으면 요청을 자동으로 취소하는 기능을 제공합니다.
- 업로드 진행률 추적: 파일 업로드 시 진행률을 모니터링할 수 있습니다.
Axios 설치
Axios를 사용하려면 먼저 프로젝트에 설치해야 합니다.
npm install axios
# 또는
yarn add axiosAxios의 기본 사용법
설치 후에는 다음과 같이 Axios를 임포트하여 사용할 수 있습니다.
import axios from 'axios';GET 요청
데이터를 조회할 때 사용합니다.
// 모든 게시물 가져오기
axios.get('http://localhost:4000/posts?_limit=10')
.then(response => {
console.log('게시물 목록:', response.data);
})
.catch(error => {
console.error('게시물 가져오기 오류:', error);
});
// 특정 ID의 게시물 가져오기
axios.get('http://localhost:4000/posts/1')
.then(response => {
console.log('단일 게시물:', response.data);
})
.catch(error => {
console.error('단일 게시물 가져오기 오류:', error);
});Mock API 쿼리 버전 확인하기
?_limit=10은 원 예제의 서버 쿼리 계약입니다. 현행 JSON Server v1은 _page와 _per_page 페이지네이션을 설명하며 _limit에서의 변경을 명시합니다. 설치한 서버 버전을 확인하고 응답의 모양까지 함께 바꿔야 합니다. v1 페이지 응답은 게시물 배열 자체가 아니라 페이지 정보와 data 배열을 담은 객체입니다.
axios.get('http://localhost:4000/posts', {
params: { _page: 1, _per_page: 10 },
}).then(response => {
console.log('게시물 목록:', response.data.data);
});뒤 완성 실습은 페이지네이션을 넣지 않은 /posts 배열을 사용합니다. 페이지 응답 객체를 원 PostsPage에 그대로 넘기면 비배열 계약 오류가 나므로, 페이지 단위 UI를 추가할 때는 배열 추출과 페이지 메타데이터도 함께 설계합니다.
POST 요청
새로운 데이터를 생성할 때 사용합니다.
두 번째 인자로 전송할 데이터를 객체 형태로 전달합니다.
axios.post('http://localhost:4000/posts', {
title: '새로운 게시물',
body: '이것은 Axios로 작성된 새 게시물입니다.',
userId: 1,
})
.then(response => {
console.log('게시물 생성 성공:', response.data);
})
.catch(error => {
console.error('게시물 생성 오류:', error);
});PUT/PATCH 요청
기존 데이터를 업데이트할 때 사용합니다.
PUT: 일반적으로 대상 리소스를 요청 표현으로 교체하는 의미로 사용합니다. 필수 필드와 실제 동작은 서버 API 계약을 따라야 합니다.PATCH: 리소스의 일부 변경을 표현합니다. 지원하는 patch 문서 형식은 서버 계약을 확인합니다.
// PUT 요청 (전체 업데이트)
axios.put('http://localhost:4000/posts/1', {
title: '업데이트된 제목',
body: '내용도 완전히 바뀌었습니다.',
userId: 1,
})
.then(response => {
console.log('게시물 PUT 업데이트 성공:', response.data);
})
.catch(error => {
console.error('게시물 PUT 업데이트 오류:', error);
});
// PATCH 요청 (부분 업데이트)
axios.patch('http://localhost:4000/posts/1', {
title: '부분적으로 업데이트된 제목',
})
.then(response => {
console.log('게시물 PATCH 업데이트 성공:', response.data);
})
.catch(error => {
console.error('게시물 PATCH 업데이트 오류:', error);
});DELETE 요청
데이터를 삭제할 때 사용합니다.
axios.delete('http://localhost:4000/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 controller = new AbortController();
let active = true;
const fetchPost = async () => {
try {
setLoading(true);
setError(null);
const response = await axios.get('http://localhost:4000/posts/1', {
signal: controller.signal,
});
if (active) setPost(response.data);
} catch (err) {
if (!active) return;
if (axios.isAxiosError(err) && err.code === 'ERR_CANCELED') return;
if (!axios.isAxiosError(err)) {
setError(new Error('예상하지 못한 오류가 발생했습니다.'));
} else if (err.response) {
setError(new Error(`서버가 ${err.response.status} 상태로 응답했습니다.`));
} else if (err.request) {
const message = ['ECONNABORTED', 'ETIMEDOUT'].includes(err.code)
? '요청 시간이 초과되었습니다.'
: '서버 응답을 받지 못했습니다.';
setError(new Error(message));
} else {
setError(new Error(`요청을 구성하지 못했습니다: ${err.message}`));
}
} finally {
if (active) setLoading(false);
}
};
fetchPost();
return () => {
active = false;
controller.abort();
};
}, []);
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의 catch 블록에서는 오류 객체의 어느 필드가 채워졌는지에 따라 사용자 메시지와 복구 방식을 다르게 잡아야 합니다.
Error evidence · recovery choice
Axios 오류 증거에 따른 분류와 복구
오류 메시지보다 먼저 Axios가 남긴 증거를 봅니다. 취소인지, 서버 응답이 있는지, 요청은 만들어졌지만 응답이 없는지에 따라 사용자 안내와 재시도 가능성이 달라집니다.
-
axios.isAxiosError(error)로 경계를 확인false라면 요청 구성이나 응답 처리 코드 등에서 생긴 예상 밖 오류일 수 있습니다. Axios 전송 오류처럼 필드를 가정하지 말고 별도 오류 경로로 보냅니다. -
error.code === 'ERR_CANCELED'화면 이탈·요청 교체 같은 의도적 취소를 먼저 분리합니다. 보통 오류 배너나 자동 재시도의 대상이 아니며 stale state도 커밋하지 않습니다.
-
error.response가 있으면 서버가 응답status,data,headers를 바탕으로 인증·검증·서버 실패를 구분합니다. 상태 코드만으로 사용자 메시지를 단정하지 말고 API 오류 계약을 읽습니다. -
error.request만 있으면 응답을 받지 못한 요청네트워크·CORS·연결 실패를 조사합니다. timeout은 보통
ECONNABORTED, 설정에 따라ETIMEDOUT코드가 될 수 있으므로code도 함께 확인합니다. -
Axios 오류지만 response와 request가 없으면 설정 단계
잘못된 config처럼 전송 전에 Axios가 만든 오류일 수 있습니다. interceptor가 일반
Error를 던졌다면 첫 분기의 예상 밖 오류 경로로 보내며 이 분기와 섞지 않습니다.
validateStatus가 resolve와 reject를 가른다
기본 설정은 2xx를 성공으로 봅니다. 기준을 바꾸면 같은 HTTP 상태가 fulfilled 응답 인터셉터로 갈 수 있으므로 호출자 오류 정책과 함께 관리합니다.
분류는 자동 재시도 허가가 아니다
멱등성, 사용자 의도, 서버 힌트, 요청 body 재전송 가능성을 확인합니다. 특히 취소·검증 실패·설정 오류는 무조건 재시도하지 않습니다.
응답 인터셉터는 adapter가 상태를 판정한 뒤 실행됩니다. 오류 handler가 값을 반환하면 체인은 복구된 fulfilled 상태가 되므로, 복구 의도가 없으면 reject를 그대로 반환합니다.
HTTP 성공·JSON 변환·게시글 검증의 세 경계
원 AxiosExample은 성공 응답이 게시물 객체라는 전제입니다. Axios가 response.data를 제공한다고 해서 title·body 필드까지 검증하지는 않습니다. 잘못된 JSON에 대한 기본 변환 정책도 항상 throw한다는 보장이 아닙니다. 엄격한 JSON 문법 검사가 필요할 때는 Axios의 변환 설정에 맞춰 responseType: 'json'과 transitional: { silentJSONParsing: false }를 함께 지정하고, 파싱 뒤 필수 필드를 별도로 확인합니다.
혼자 풀기
네 요청은 모두 active인 setup의 요청이라고 가정합니다. 기본 설정의 404, validateStatus: status => status < 500을 둔 404, ERR_CANCELED 취소, 200·잘못된 게시물 모양의 결과를 각각 생각해 보세요. 어느 것이 원 코드의 catch에 들어가며, 사용자에게 정상 게시글이라고 표시해도 될까요?
전송 계약과 화면 계약을 나누어 풀이
기본 404는 reject되어 err.response.status로 서버 오류를 안내합니다. 변경한 validator는 404도 fulfilled로 보내므로 원 코드의 catch가 실행되지 않습니다. 이를 실제 게시글 성공으로 취급할지는 API 계약에 맞는 추가 판정이 필요합니다. 취소는 ERR_CANCELED 검사에서 사용자 오류를 저장하지 않습니다. 200은 기본 HTTP 검사를 통과하지만 필수 필드가 없으면 올바른 게시글 화면을 보장하지 않습니다. 상태 코드·파싱·필드 모양은 각각 확인해야 합니다.
Axios 인스턴스 (Instance)
여러 API 요청에서 공통적으로 적용해야 할 설정(예: 기본 URL, 타임아웃)이 있다면, Axios 인스턴스를 생성하여 관리할 수 있습니다.
이는 코드 중복을 줄이고 유지보수성을 높입니다.
import axios from 'axios';
const mockApi = axios.create({
baseURL: 'http://localhost:4000', // 로컬 mock 서버 기본 URL
timeout: 5000, // 5초 동안 응답이 없으면 타임아웃
});
export default mockApi;import React, { useState, useEffect } from 'react';
import axios from 'axios';
import mockApi from '../api/mockApi'; // 인스턴스 임포트
function AxiosInstanceExample() {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
let active = true;
const fetchUsers = async () => {
try {
setLoading(true);
setError(null);
const response = await mockApi.get('/users', {
signal: controller.signal,
});
if (active) setUsers(Array.isArray(response.data) ? response.data : []);
} catch (err) {
if (!active) return;
if (axios.isAxiosError(err) && err.code === 'ERR_CANCELED') return;
setError(err instanceof Error ? err : new Error('예상하지 못한 오류가 발생했습니다.'));
} finally {
if (active) setLoading(false);
}
};
fetchUsers();
return () => {
active = false;
controller.abort();
};
}, []);
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.firstName} {user.lastName}</strong> ({user.username}) - {user.email}
</li>
))}
</ul>
</div>
);
}
export default AxiosInstanceExample;Axios와 useEffect의 조합
fetch API와 마찬가지로, Axios도 useEffect 훅 내부에서 async/await와 함께 사용하여 컴포넌트 생명주기에 맞춰 데이터를 가져올 수 있습니다.
로딩·성공·실패를 구분하는 원칙은 fetch와 같지만, Axios의 응답과 오류 객체 형태에 맞춰 처리해야 합니다.
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 controller = new AbortController();
let active = true;
const fetchData = async () => {
if (!url) {
setLoading(false);
return;
}
setLoading(true);
setError(null);
try {
const response = await axios.get(url, {
...options,
signal: controller.signal,
});
if (active) setData(response.data);
} catch (err) {
if (!active) return;
if (axios.isAxiosError(err) && err.code === 'ERR_CANCELED') return;
setError(err);
} finally {
if (active) setLoading(false);
}
};
fetchData();
return () => {
active = false;
controller.abort();
};
}, [url, options]);
return { data, loading, error };
};
export default useAxiosFetch;호출자는 options를 컴포넌트 밖 상수로 두거나 useMemo로 안정화해야 합니다. 객체를 JSON.stringify해 의존성으로 쓰면 함수·AbortSignal 같은 값이 빠지고 직렬화 순서에 결합되므로 요청 동일성 판정에 적합하지 않습니다.
범용 조회 훅의 적용 한계
원 useAxiosFetch는 현재 setup의 뒤늦은 쓰기를 막지만 URL별 visible key를 반환하지 않습니다. url이 바뀐 직후 새 Effect가 실행되기 전 렌더에서는 이전 data가 남을 수 있고, url이 없어져도 이전 data·error를 비우지 않습니다. 서로 다른 사용자·게시글의 데이터를 분리해야 한다면 이 단순 훅에 같은 key의 갱신과 새 key의 요청을 구분하는 설계를 더해야 합니다. 앞 절 useFetch의 visibleRequest와 뒤 실습의 activeKey가 그 경계를 보여 줍니다. 취소 signal만 추가하는 것으로 이 표시 정책까지 해결되는 것은 아닙니다.
fetch vs axios 정리
| 특징 | fetch (네이티브 API) | axios (라이브러리) |
|---|---|---|
| 설치 필요 여부 | 브라우저에 내장 | 설치 필요 (npm install axios) |
| 응답 body | 필요에 따라 .json(), .text(), stream reader 등을 호출 | 기본 변환 결과를 response.data로 제공 |
| HTTP 상태 판정 | 4xx·5xx도 응답 Promise는 resolve되므로 response.ok 등을 확인 | 기본 validateStatus는 2xx 밖을 reject하며 기준 변경 가능 |
| 요청/응답 공통 처리 | wrapper를 직접 설계 | 인스턴스와 interceptor API 제공 |
| 요청 취소 | AbortController의 signal | Axios 0.22.0부터 같은 AbortController의 signal 지원 |
| 타임아웃 | 지원 환경의 AbortSignal.timeout() 또는 controller와 timer 조합 | timeout 설정과 AbortSignal을 함께 사용할 수 있음 |
| 진행률 처리 | Streams API 등 환경별 기능 사용 | adapter·런타임이 지원하는 진행 이벤트 사용 |
| 번들 비용 | 애플리케이션 의존성 추가 없음 | 라이브러리와 사용 기능에 따른 비용이 추가되므로 현재 번들 분석으로 확인 |
몇 개의 단순한 요청이라면 내장 fetch와 작은 wrapper가 충분할 수 있습니다. 여러 서버의 기본값, interceptor, timeout과 일관된 오류 객체가 필요하다면 Axios 인스턴스가 중복을 줄일 수 있습니다. 팀의 실제 요구와 번들 분석을 기준으로 선택합니다.
Axios 라이브러리 소개는 여기까지입니다.
이 장에서는 Axios가 무엇인지, fetch와 어떤 계약 차이가 있는지 알아보았습니다.
GET, POST, PUT, DELETE와 같은 기본적인 HTTP 요청을 async/await와 함께 사용하는 방법과, Axios 인스턴스를 생성하여 공통 설정을 관리하는 방법까지 학습했습니다.
Axios를 도입할 때는 편리한 문법보다 설정 우선순위, interceptor의 반환 계약, 취소와 stale commit 방지, 오류 분류가 실제 코드에서 어디에 놓이는지 함께 봐야 합니다.