개인의 기록
  • 소개
  • 프로젝트
  • 글
  • 링크

© 2026 newgirok

← 글 목록

Axios — HTTP 요청을 처리하는 클라이언트 라이브러리

2026년 4월 14일
HTTPAPIAxiosReact

Axios는 브라우저와 Node.js 환경 모두를 지원하는 Promise 기반 HTTP 클라이언트입니다. 내장 fetch에 비해 요청·응답 인터셉터, 자동 JSON 직렬화·역직렬화, 오류 처리 등 편의 기능을 기본으로 제공합니다.

npm install axios

1. Instance

Instance: baseURL, timeout, 공통 헤더 등을 미리 설정해 두고 재사용하는 Axios 객체

동일한 API 서버에 여러 요청을 보낼 때, 매 요청마다 baseURL을 반복하는 대신 인스턴스에 한 번 설정합니다.

// src/api/client.ts
import axios from "axios";

const client = axios.create({
  baseURL: "https://api.example.com",
  timeout: 10000,
  headers: {
    "Content-Type": "application/json",
  },
});

export default client;
// 사용 시 — baseURL이 자동으로 앞에 붙음
import client from "./client";

const response = await client.get("/users"); // GET https://api.example.com/users

인스턴스를 기능별로 분리하는 것도 가능합니다. 예를 들어 인증이 필요한 API용 인스턴스와 공개 API용 인스턴스를 별도로 만들 수 있습니다.


2. Request

Axios는 각 HTTP 메서드에 대응하는 단축 메서드를 제공합니다.

// GET — 데이터 조회
const { data: users } = await client.get("/users");
const { data: user } = await client.get("/users/1", {
  params: { include: "profile" }, // ?include=profile 쿼리스트링으로 변환
});

// POST — 데이터 생성
const { data: newUser } = await client.post("/users", {
  name: "홍길동",
  email: "hong@example.com",
});

// PUT — 데이터 전체 교체
await client.put("/users/1", { name: "홍길동", email: "new@example.com" });

// PATCH — 데이터 일부 수정
await client.patch("/users/1", { name: "홍길순" });

// DELETE — 데이터 삭제
await client.delete("/users/1");

params 옵션은 객체를 URL 쿼리스트링으로 자동 변환합니다.


3. Response

Axios는 서버 응답을 다음 구조로 래핑합니다.

interface AxiosResponse<T> {
  data: T;          // 서버가 응답한 실제 데이터 (JSON 자동 파싱)
  status: number;   // HTTP 상태 코드 (200, 201, 404 등)
  statusText: string; // 상태 메시지 ("OK", "Not Found" 등)
  headers: object;  // 응답 헤더
  config: object;   // 요청 시 사용된 설정
  request: object;  // 실제 요청 객체
}
const response = await client.get<User>("/users/1");

console.log(response.data);       // User 객체
console.log(response.status);     // 200
console.log(response.headers["content-type"]); // "application/json"

구조 분해 할당으로 data만 바로 꺼내는 패턴이 자주 쓰입니다.

const { data } = await client.get<User[]>("/users");

4. Header

Header: 요청과 응답에 포함되는 메타데이터로, 인증 토큰·콘텐츠 타입·캐시 정책 등을 전달하는 방법

헤더는 인스턴스 기본값, 메서드별 기본값, 요청별 설정 세 단계로 우선순위가 결정됩니다.

// 1. 인스턴스 생성 시 기본 헤더 설정
const client = axios.create({
  headers: { "Content-Type": "application/json" },
});

// 2. 특정 메서드의 기본 헤더 설정
client.defaults.headers.post["X-Custom-Header"] = "value";

// 3. 요청별 헤더 설정 (가장 높은 우선순위)
await client.get("/secure", {
  headers: { Authorization: `Bearer ${token}` },
});

Authorization 헤더처럼 매 요청에 공통으로 붙어야 하는 헤더는 인터셉터로 자동화하는 것이 일반적입니다.


5. Config

각 요청에 다양한 옵션을 객체 형태로 전달할 수 있습니다.

const config: AxiosRequestConfig = {
  params: { page: 1, limit: 20 },     // URL 쿼리스트링
  headers: { Authorization: "Bearer ..." }, // 요청 헤더
  timeout: 5000,                        // 타임아웃(ms)
  responseType: "blob",                 // 응답 타입 (json/blob/arraybuffer 등)
  signal: abortController.signal,       // 요청 취소 시그널
  onUploadProgress: (e) => {            // 업로드 진행률 콜백
    console.log(`${Math.round(e.progress! * 100)}%`);
  },
};

await client.post("/files", formData, config);

인스턴스의 기본 설정(defaults)과 요청별 config가 병합되며, 요청별 설정이 우선합니다.


6. Interceptor

Interceptor: 요청이 전송되기 전 또는 응답이 반환되기 전에 가로채 공통 처리를 삽입하는 미들웨어

인터셉터는 인증 토큰 자동 삽입, 에러 코드 공통 처리, 로딩 상태 관리 등에 사용됩니다.

// 요청 인터셉터 — 토큰 자동 삽입
client.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem("accessToken");
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => Promise.reject(error)
);

// 응답 인터셉터 — 토큰 만료 처리
client.interceptors.response.use(
  (response) => response,
  async (error) => {
    const originalRequest = error.config;

    if (error.response?.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true;
      const newToken = await refreshAccessToken();
      originalRequest.headers.Authorization = `Bearer ${newToken}`;
      return client(originalRequest); // 원래 요청 재시도
    }

    return Promise.reject(error);
  }
);

인터셉터는 등록한 순서대로 실행됩니다. 더 이상 필요 없을 때는 eject로 제거합니다.

const id = client.interceptors.request.use(handler);
client.interceptors.request.eject(id);

7. Timeout

Timeout: 지정한 시간 내에 서버 응답이 오지 않으면 자동으로 요청을 중단하고 에러를 발생시키는 설정

타임아웃은 인스턴스 기본값 또는 요청별로 설정합니다.

// 인스턴스에 기본 타임아웃 설정
const client = axios.create({
  baseURL: "https://api.example.com",
  timeout: 10000, // 10초
});

// 요청별 타임아웃 오버라이드
await client.get("/slow-endpoint", { timeout: 30000 }); // 30초

// 타임아웃 에러 처리
try {
  await client.get("/data");
} catch (error) {
  if (axios.isAxiosError(error) && error.code === "ECONNABORTED") {
    console.error("요청 시간이 초과되었습니다.");
  }
}

타임아웃 에러의 code는 ECONNABORTED입니다.


8. Cancel Request

Cancel Request: 이미 전송된 요청을 중간에 취소하는 방법, 주로 AbortController를 사용

컴포넌트 언마운트 또는 사용자가 검색어를 빠르게 변경할 때 이전 요청이 완료되기 전에 취소하여 불필요한 처리를 줄입니다.

import { useEffect, useState } from "react";

function SearchResults({ query }: { query: string }) {
  const [results, setResults] = useState([]);

  useEffect(() => {
    const controller = new AbortController();

    const fetchData = async () => {
      try {
        const { data } = await client.get("/search", {
          params: { q: query },
          signal: controller.signal,
        });
        setResults(data);
      } catch (error) {
        if (axios.isCancel(error)) {
          // 취소된 요청은 에러로 처리하지 않음
          return;
        }
        console.error(error);
      }
    };

    fetchData();

    return () => {
      // 컴포넌트 언마운트 또는 query 변경 시 이전 요청 취소
      controller.abort();
    };
  }, [query]);

  return <ul>{results.map((r) => <li>{r}</li>)}</ul>;
}

axios.isCancel로 취소로 인한 에러인지 판별합니다.


9. Error Handling

Error Handling: Axios 에러 객체의 구조를 파악하고 HTTP 상태 코드 및 상황별로 분기하여 처리하는 방법

Axios는 4xx·5xx 응답을 자동으로 에러로 처리합니다. axios.isAxiosError로 Axios 에러인지 판별하고, error.response에서 서버가 반환한 상세 정보를 읽습니다.

try {
  const { data } = await client.get("/users/999");
} catch (error) {
  if (axios.isAxiosError(error)) {
    if (error.response) {
      // 서버가 응답을 반환했으나 2xx 범위 밖
      const { status, data } = error.response;

      switch (status) {
        case 400:
          console.error("잘못된 요청:", data.message);
          break;
        case 401:
          console.error("인증이 필요합니다.");
          break;
        case 403:
          console.error("접근 권한이 없습니다.");
          break;
        case 404:
          console.error("리소스를 찾을 수 없습니다.");
          break;
        case 500:
          console.error("서버 오류가 발생했습니다.");
          break;
        default:
          console.error(`알 수 없는 오류: ${status}`);
      }
    } else if (error.request) {
      // 요청은 전송되었으나 응답 없음 (네트워크 오류)
      console.error("서버에 연결할 수 없습니다.");
    } else {
      // 요청 설정 중 오류
      console.error("요청 설정 오류:", error.message);
    }
  }
}

공통 에러 처리는 응답 인터셉터에 두고, 컴포넌트에서는 비즈니스 로직에 맞는 처리만 담당하도록 분리하는 것이 유지보수에 유리합니다.

다음 글 →ESLint — 코드 품질을 검사하는 정적 분석 도구