Fetch vs Axios: 리액트 API 데이터 연동 완벽 가이드
혹시 최고급 가전제품과 멋진 인테리어로 집을 꾸며놓고, 정작 냉장고는 텅 비워둔 채 한숨 쉰 적 있으신가요? 지난 12편까지 우리가 만든 리액트(React) 애플리케이션이 딱 이런 상태입니다. 컴포넌트라는 튼튼한 벽돌로 화면을 짓고, 전역 상태 관리(Context API, Zustand)를 통해 집안의 전력망까지 완벽하게 구축했습니다. 하지만 이 아름다운 웹 공간에 '진짜 살아있는 데이터'가 없다면 그저 빈 껍데기에 불과합니다.
지난 편 말미에 리액트 라우터(Router)를 통한 페이지 이동을 예고해 드렸는데요. 빈 방을 돌아다니는 것보다, 각 방을 채울 유용한 정보들을 외부에서 배달받는 방법을 아는 것이 먼저라는 생각이 들었습니다. 그래서 오늘은 웹 애플리케이션에 생명력을 불어넣는 '백엔드와의 소통 기술, API 데이터 연동'에 대해 깊이 있게 파헤쳐보겠습니다. 프론트엔드 개발의 양대 산맥인 Fetch와 Axios, 과연 실무에서는 어떤 것을, 왜 선택해야 할까요?
API와 비동기 통신: 프론트엔드의 배달 주문 시스템
프론트엔드 개발자가 백엔드 서버와 데이터를 주고받는 과정을 이해하려면, 식당의 '배달 주문 시스템'을 떠올려보시면 쉽습니다. 우리는 고객(프론트엔드)이고, 요리를 만드는 주방은 백엔드 서버입니다. 고객이 주방에 직접 들어가 요리를 꺼내올 수는 없죠. 이때 필요한 것이 바로 메뉴판과 배달원 역할을 하는 API(Application Programming Interface)입니다.
여기서 아주 중요한 개념인 비동기 통신(Asynchronous)이 등장합니다. 만약 우리가 피자를 주문하고(API 요청), 피자가 도착할 때까지 현관문 앞에서 아무것도 안 하고 꼼짝없이 서 있어야 한다면 어떨까요? 엄청난 시간 낭비일 것입니다. 이것이 '동기(Synchronous)' 방식입니다.
반면, '비동기' 방식은 피자를 주문해 놓고 우리는 소파에 앉아 넷플릭스를 보거나 다른 집안일을 하는 것입니다. 피자가 도착했다는 초인종이 울리면(응답 완료), 그때 피자를 받아 식탁에 세팅(화면 렌더링)하면 됩니다. 웹 브라우저가 서버에 데이터를 요청해 놓고, 데이터가 오는 동안 화면이 멈추지 않고 다른 작업(UI 애니메이션, 스크롤 등)을 계속할 수 있게 해주는 핵심 기술이 바로 비동기 통신입니다.
브라우저의 기본기: Fetch API로 첫 주문하기
과거에는 서버와 통신하기 위해 jQuery의 Ajax나 복잡한 XMLHttpRequest를 사용해야 했습니다. 하지만 모던 자바스크립트(ES6+) 시대가 열리면서, 브라우저에 기본적으로 내장된 Fetch API가 등장했습니다. Fetch는 별도의 라이브러리 설치 없이 즉시 사용할 수 있다는 것이 가장 큰 장점입니다.
Fetch API의 기본 사용법
Fetch를 사용해 데이터를 가져오는 기본 구조는 다음과 같습니다. 현대적인 자바스크립트 문법인 async/await를 활용하면 코드를 훨씬 직관적으로 작성할 수 있습니다.
// Fetch를 이용한 데이터 요청 예시
async function getUserData() {
try {
// 1단계: 서버에 요청을 보내고 응답을 기다림
const response = await fetch('https://api.example.com/users');
// 2단계: 응답받은 데이터를 JSON 형태로 변환
const data = await response.json();
console.log(data);
} catch (error) {
console.error('데이터를 불러오는데 실패했습니다.', error);
}
}
코드를 보면 두 번의 await가 사용된 것을 알 수 있습니다. 첫 번째는 서버에 연결하여 응답 헤더(포장지)를 받는 과정이고, 두 번째 .json()은 실제 데이터(내용물)를 자바스크립트가 읽을 수 있도록 포장을 뜯는 과정입니다. Fetch는 이렇게 수동으로 JSON 변환 과정을 거쳐야 한다는 특징이 있습니다.
Fetch의 치명적인 함정: 에러 처리
Fetch를 사용할 때 초보자들이 가장 많이 겪는 함정이 있습니다. 바로 HTTP 에러(404 Not Found, 500 Server Error)를 기본적으로 에러(catch)로 인식하지 않는다는 점입니다. Fetch는 오직 인터넷 연결이 끊기거나 네트워크 자체에 문제가 생겼을 때만 에러를 발생시킵니다. 서버가 "그런 데이터 없는데요?(404)"라고 정상적으로 응답했다면, Fetch는 이를 성공적인 통신으로 간주해버립니다. 따라서 response.ok 속성을 사용해 개발자가 직접 에러를 걸러내야 하는 번거로움이 있습니다.
프론트엔드 개발자의 최애 무기: Axios 완벽 해부
Fetch의 여러 가지 불편함을 해결하고, 더 강력한 기능으로 무장한 외부 라이브러리가 바로 Axios입니다. 현재 실무 프론트엔드 프로젝트, 특히 리액트나 뷰(Vue) 생태계에서 가장 압도적으로 많이 사용되는 HTTP 통신 라이브러리입니다. (npm install axios 명령어로 설치 후 사용합니다.)
Axios가 Fetch보다 사랑받는 3가지 이유
- 자동 JSON 변환: Fetch처럼 귀찮게
.json()을 호출할 필요가 없습니다. Axios는 응답 데이터를 자동으로 JSON 객체로 변환하여response.data에 예쁘게 담아줍니다. - 직관적인 에러 처리: 400번대(클라이언트 에러)나 500번대(서버 에러) HTTP 상태 코드가 떨어지면, Axios는 이를 즉각적으로 에러로 판단하고
catch블록으로 넘겨줍니다. 개발자는 진짜 에러 처리에만 집중할 수 있습니다. - 인터셉터(Interceptor) 기능: Axios의 꽃이라 불리는 기능입니다. 요청을 보내기 직전이나, 응답을 받기 직전에 데이터를 가로채서 특정 작업을 수행할 수 있습니다.
보안 요원 역할을 하는 '인터셉터(Interceptor)'
인터셉터는 아파트 단지 입구에 있는 '보안 요원'과 같습니다. 예를 들어, 로그인이 필요한 서비스에서는 매번 API를 요청할 때마다 사용자 인증 토큰(JWT)을 함께 보내야 합니다. 이걸 매번 코드에 수동으로 적어준다면 엄청난 중복이 발생하겠죠? 인터셉터 기능을 설정해두면, 나가는 모든 요청의 헤더에 자동으로 토큰을 붙여줍니다. 반대로 서버에서 토큰이 만료되었다는 응답이 오면, 화면에 에러를 띄우기 전에 인터셉터가 몰래 토큰을 갱신하고 원래 하려던 요청을 다시 보내는 마법 같은 처리도 가능합니다.
// Axios 기본 사용법 예시
async function getUserData() {
try {
// json 변환 과정 없이 바로 data 추출 가능
const response = await axios.get('https://api.example.com/users');
console.log(response.data);
} catch (error) {
// 404, 500 에러도 이곳에서 자동으로 잡힘
console.error('에러 발생!', error.response.status);
}
}
실전 비교: Fetch vs Axios, 나의 선택은?
그렇다면 무조건 Axios를 사용하는 것이 정답일까요? 상황에 따라 적절한 도구를 선택하는 것이 진정한 전문가의 자세입니다. 한눈에 보기 쉽게 두 기술을 비교해 보겠습니다.
| 비교 항목 | Fetch API | Axios |
|---|---|---|
| 설치 여부 | 내장 API (설치 불필요) | 외부 라이브러리 (npm 설치 필요) |
| JSON 변환 | 수동 (.json() 사용) | 자동 (response.data) |
| 에러 처리 | 네트워크 에러만 catch (수동 HTTP 에러 처리) | HTTP 에러 자동 catch |
| 주요 기능 | 기본적인 비동기 통신 | 인터셉터, 요청 취소, 타임아웃 설정 등 |
| 추천 상황 | 토이 프로젝트, 바닐라 JS, 가벼운 통신 | 실무/기업용 프로젝트, 복잡한 인증/에러 처리 |
결론적으로 말씀드리면: 외부 라이브러리 의존성을 최소화하고 싶은 가벼운 프로젝트나 바닐라 자바스크립트 환경이라면 Fetch를 추천합니다. 하지만 인증 토큰 관리가 필요하고, API 요청이 빈번하며, 체계적인 에러 핸들링이 필수적인 엔터프라이즈급 리액트 프로젝트라면 주저 없이 Axios를 선택하시기 바랍니다.
React 환경에서 API 통신 시 주의할 점
이제 11편에서 배운 리액트 훅(Hooks)과 오늘 배운 API 통신을 결합해 볼 차례입니다. 리액트 컴포넌트에서 데이터를 언제, 어떻게 불러와야 할까요? 바로 useEffect 훅을 사용해야 합니다.
컴포넌트가 화면에 처음 그려질 때(Mount), useEffect 안에서 Axios를 통해 백엔드에 데이터를 요청합니다. 그리고 데이터가 도착하면 useState를 이용해 상태를 업데이트하고, 변경된 상태에 따라 화면이 다시 그려지게(Re-render) 만드는 것이 리액트 데이터 연동의 기본 흐름입니다.
이때 사용자 경험(UX)을 위해 절대 잊지 말아야 할 세 가지 상태 관리가 있습니다.
1. Loading (로딩 중): 데이터를 기다리는 동안 빈 화면 대신 '스피너'나 '스켈레톤 UI'를 보여주어야 합니다.
2. Data (성공): 성공적으로 받아온 데이터를 화면에 렌더링합니다.
3. Error (실패): 통신에 실패했을 때 사용자가 당황하지 않도록 친절한 에러 메시지와 '다시 시도' 버튼을 제공해야 합니다.
마무리하며
오늘은 텅 빈 프론트엔드 애플리케이션에 진짜 데이터를 채워 넣는 방법, Fetch와 Axios를 활용한 API 연동에 대해 알아보았습니다. 백엔드와 원활하게 소통하는 방법을 익혔으니, 이제 우리의 웹 공간은 매일매일 새로운 정보로 가득 찰 것입니다.
데이터를 성공적으로 불러왔으니, 이제 정말로 이 데이터들을 들고 여러 방(페이지)을 자유롭게 넘나들 준비가 끝났습니다. 다음 14편에서는 하나의 페이지(SPA) 안에서 마법처럼 화면을 전환해 주는 '리액트 라우터(React Router)의 모든 것'에 대해 본격적으로 다뤄보겠습니다. 데이터가 흐르는 동적인 웹을 향한 여정, 다음 편도 기대해 주세요!