fetch와 axios 차이
프론트엔드에서 API를 호출할 때 fetch와 axios를 자주 만난다.
둘 다 HTTP 요청을 보내고 응답을 받는 데 사용하지만, 같은 도구는 아니다.fetch는 브라우저에 내장된 Web API이고, axios는 별도로 설치해서 사용하는 HTTP 클라이언트 라이브러리다.
초보자 입장에서는 “둘 중 무엇을 써야 하는지”가 먼저 궁금할 수 있다.
간단한 요청은 fetch만으로 충분한 경우가 많고, 공통 설정과 에러 처리와 timeout 같은 편의 기능이 필요하면 axios가 편하다.
fetch의 역할
fetch는 리소스를 가져오기 위한 브라우저 내장 API다.
MDN 문서 기준으로 Fetch API는 네트워크를 포함한 리소스 요청을 위한 인터페이스를 제공한다.
별도 패키지를 설치하지 않아도 브라우저와 Node.js의 최신 런타임에서 사용할 수 있다.
const response = await fetch('/api/users')
const data = await response.json()fetch는 Promise를 반환한다.
응답을 받으면 Response 객체가 오고, 본문을 JSON으로 읽으려면 response.json()을 따로 호출해야 한다.
이 흐름은 조금 길어 보이지만 표준 API라는 장점이 있다.
브라우저 기본 기능만으로 HTTP 요청을 다룰 수 있고, 추가 의존성이 생기지 않는다.
axios의 역할
axios는 Promise 기반 HTTP 클라이언트 라이브러리다.
사용하려면 프로젝트에 패키지를 설치해야 한다.
npm install axios설치 후에는 아래처럼 사용할 수 있다.
import axios from 'axios'
const response = await axios.get('/api/users')
const data = response.dataaxios는 응답 본문을 response.data에 담아준다.
JSON 응답을 다룰 때 response.json()을 직접 호출하지 않아도 되는 점이 초보자에게는 더 직관적으로 느껴질 수 있다.
설치 여부
가장 먼저 다른 점은 설치 여부다.fetch는 내장 API라서 설치가 필요 없다.
await fetch('/api/users')axios는 외부 패키지라서 의존성에 추가해야 한다.
npm install axiosimport axios from 'axios'프로젝트에 의존성을 하나 더 추가하는 것이 부담이 아니라면 axios를 써도 된다.
반대로 단순한 요청 몇 개만 필요하다면 fetch로 시작하는 편이 가볍다.
의존성 위치가 헷갈린다면 dependencies와 devDependencies 차이를 같이 보면 좋다.
JSON 처리 차이
fetch는 응답을 받은 뒤 본문을 직접 읽어야 한다.
const response = await fetch('/api/users')
const data = await response.json()여기서 response.json()도 비동기 작업이다.
응답 헤더를 받은 것과 본문을 JSON으로 파싱하는 것은 별도 단계라고 생각하면 된다.
axios는 JSON 응답을 data에 넣어준다.
const response = await axios.get('/api/users')
const data = response.data그래서 같은 GET 요청이라면 axios 코드가 더 짧아 보이는 경우가 많다.
다만 짧다는 이유만으로 항상 axios를 고를 필요는 없다.
프로젝트가 이미 fetch 기반으로 통일되어 있다면 그 흐름을 유지하는 것도 좋은 선택이다.
HTTP 에러 처리 차이
가장 중요한 차이는 HTTP 에러 처리다.fetch는 서버가 404나 500 같은 상태 코드를 반환해도 네트워크 요청 자체가 성공했다면 Promise를 reject하지 않는다.
MDN 문서 기준으로 fetch()는 서버가 HTTP 에러 상태로 응답해도 Response로 resolve될 수 있다.
그래서 fetch에서는 response.ok를 직접 확인하는 코드가 필요하다.response.ok는 상태 코드가 200부터 299 사이인지 알려주는 boolean 값이다.
const response = await fetch('/api/users')
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`)
}
const data = await response.json()axios는 기본적으로 2xx 범위를 벗어난 응답을 에러로 처리한다.
axios 문서도 validateStatus 기본 조건을 통해 어떤 상태 코드를 reject할지 정할 수 있다고 설명한다.
try {
const response = await axios.get('/api/users')
console.log(response.data)
} catch (error) {
console.error(error)
}이 차이를 모르고 fetch를 쓰면 404 응답인데도 catch로 가지 않는 상황을 만날 수 있다.
API 호출 코드에서 에러 처리가 중요하다면 이 차이를 꼭 기억해야 한다.
timeout 처리
axios는 요청 config에 timeout을 넣을 수 있다.
const response = await axios.get('/api/users', {
timeout: 5000,
})axios 문서도 production에서는 timeout을 설정하라고 안내한다.
요청이 무한히 기다리는 상황을 줄일 수 있기 때문이다.
fetch에는 axios처럼 단순한 timeout 옵션이 바로 있는 것은 아니다.
대신 AbortController를 사용해 요청을 취소하는 방식으로 timeout을 구현한다.
const controller = new AbortController()
const timeoutId = setTimeout(() => controller.abort(), 5000)
try {
const response = await fetch('/api/users', {
signal: controller.signal,
})
const data = await response.json()
} finally {
clearTimeout(timeoutId)
}가능은 하지만 axios보다 코드가 길다.
요청 timeout을 여러 곳에서 반복해서 다뤄야 한다면 axios나 별도 fetch wrapper를 만드는 편이 편하다.
공통 설정과 interceptor
서비스가 커질수록 API 요청에는 공통 설정이 많아진다.
base URL, 인증 토큰, 공통 헤더, 에러 로깅, refresh token 처리 같은 것들이다.
axios는 instance와 interceptor를 제공해서 이런 공통 처리를 모으기 쉽다.
const api = axios.create({
baseURL: 'https://api.example.com',
timeout: 5000,
})
api.interceptors.request.use((config) => {
config.headers.Authorization = `Bearer ${token}`
return config
})fetch도 wrapper 함수를 만들면 비슷한 구조를 만들 수 있다.
async function apiFetch(path: string, init?: RequestInit) {
const response = await fetch(`https://api.example.com${path}`, {
...init,
headers: {
Authorization: `Bearer ${token}`,
...init?.headers,
},
})
if (!response.ok) {
throw new Error(`HTTP error: ${response.status}`)
}
return response.json()
}작은 프로젝트라면 wrapper로 충분하다.
요청 전후 처리가 많고 팀에서 axios 패턴에 익숙하다면 axios가 더 편할 수 있다.
취소 처리
fetch는 AbortController와 AbortSignal을 사용해 요청을 취소한다.
브라우저 표준 API라서 fetch와 잘 맞는다.
const controller = new AbortController()
fetch('/api/users', {
signal: controller.signal,
})
controller.abort()axios도 AbortController 기반 취소를 지원한다.
const controller = new AbortController()
axios.get('/api/users', {
signal: controller.signal,
})
controller.abort()요즘 코드는 두 방식 모두 AbortController로 이해하면 된다.
과거 axios 예시에서 보이던 CancelToken은 새 코드에서 먼저 선택할 방식은 아니다.
선택 기준
단순한 API 호출이라면 fetch로 충분하다.
설치가 필요 없고, 표준 API라서 의존성이 늘어나지 않는다.
React, Next.js, 브라우저 기본 기능을 익히는 단계라면 fetch를 먼저 이해하는 것이 좋다.
요청 설정이 많고 에러 처리를 한 곳에 모으고 싶다면 axios가 편하다.response.data, timeout, instance, interceptor 같은 기능은 실무 코드에서 자주 도움이 된다.
팀 프로젝트에서는 기술 자체보다 일관성이 더 중요하다.
이미 fetch wrapper가 잘 만들어져 있다면 굳이 axios를 추가하지 않아도 된다.
이미 axios instance와 interceptor 중심으로 구성되어 있다면 그 패턴을 따르는 편이 좋다.
Nuxt fetch와의 구분
예전 Nuxt 2에는 fetch라는 이름의 hook이 있었다.
이 글에서 말하는 fetch는 브라우저와 런타임에서 HTTP 요청을 보내는 Fetch API다.
이름은 같지만 역할이 다르다.
Nuxt 2의 fetch hook과 lifecycle 차이는 Fetch API와 created hook에서 API 호출 시 차이에 따로 정리해두었다.
새 글을 읽을 때는 두 개념을 섞지 않는 편이 좋다.
정리
fetch는 브라우저 내장 Web API다.
설치가 필요 없고 표준에 가깝지만, JSON 처리와 HTTP 에러 처리를 직접 챙겨야 한다.
axios는 별도 패키지로 사용하는 HTTP 클라이언트다.response.data, 기본 에러 처리, timeout, instance, interceptor 같은 편의 기능이 있다.
작고 단순한 요청은 fetch로 충분하다.
공통 설정과 에러 처리가 많아지면 axios나 잘 설계된 fetch wrapper가 필요해진다.
참고 자료






