본문 바로가기
Network

프론트엔드가 백엔드와 소통하는 법 (feat. Fetch API / Axios 비교)

by growingTangerine 2023. 6. 13.

프리온보딩 지원 과제로 todo 앱을 만들던 중, 기술에 대한 고민 없이 단순히 fetch 로 네트워크 요청을 보내게 되었다.

그러던 중, POST 요청의 body에 담아서 보내야 하는 데이터 타입이 헷갈렸고, 네트워크 탭의 에러메세지를 보며 디버깅하는데에 시간을 꽤 쓰게 되었다. 

 

그래서 fetch API, axios 라이브러리 등 이미 배웠지만 까먹고 헷갈리는 네트워크 요청에 대해 한번 공부해서 정리해보고자 한다!

이 둘을 비교하고, 어떤 상황에서 어떤 기술을 선택해야 하는지의 기준을 정리해볼 것이다. 

 

Fetch API / Axios는 무엇인가?

공통점 - promise 기반의 HTTP 클라이언트이다. 네트워크 요청을 이행했을 떼, resolve / reject 할 수 있는 promise가 반환된다.

차이점

 - Fetch API: 브라우저 내장

 - Axios: 라이브러리 -> 패키지 매니저를 통해 설치해야 사용 가능함. 

 

Fetch API / Axios 기능 비교

-  Fetch API 문법

fetch(url, options)
// 첫번째 인자는 리소스의 URL, 두번째 인자는 요청의 설정 옵션 객체

// options
{ method: 'GET',
  headers: {
  	"Content-Type": "application/json",
    }
  body: JSON.stringify({}),
  }
  
  // 위와 같이 options는 요청에 따라 커스텀 설정하여 보내주면 된다.

 

- Axios 문법

axios(url, options)

// http 메서드를 붙일 수 있음.
axios.get(url, options)

// options 객체 커스텀하기
axios(url, {
	method: 'get',
    headers: {},
    data: {}
    })

 

문법은 기본 구조는 유사하나, 약간의 차이가 있다.

 

 

응답 처리 시 어떤 차이가 있는지를 살펴보자. 

 

- JSON 데이터 처리

fetch API

fetch(url)
	.then((response) => response.json())
    	.then(console.log);

fetch()는 .then() 메서드에 처리된 promise를 반환한다. 이때, 아직 우리가 필요한 JSON 형태의 데이터가 아니므로 .json() 메서드를 호출해야 한다. 그러면 JSON 형식의 데이터로 resolve 된 또 다른 promise가 반환된다. 따라서 일반적으로 fetch 요청은 이렇게 두개의 .then() 호출을 하게 된다. 

 

Axios

axios.get(url).then((response) => console.log(response.data));

Axios를 사용하면 응답 데이터를 기본적으로 JSON 타입으로 사용할 수 있다. 응답 데이터는 언제나 응답 객체의 data 프로퍼티에서 사용할 수 있다. 

 

- 자동 문자열 변환

Axios

POST 메서드로 JS 객체를 전송하면 Axios가 자동으로 데이터를 문자열로 변환해준다. 

const todo = {
	title: "A new todo",
    completed: false,
    }
    
 
 axios
 	.post(url, {
    	headers: {},
        data: todo,
        }
    )
    .then(console.log)

위의 코드처럼, 별다른 코드 없이 request body로 보내고자 하는 JS 객체를 data에 담아서 전달해주면 된다. 

또한, Axios의 기본 Content-Type은 application/json이다. 

이에 대한 응답 데이터는 response.data에 담아서 온다. 

 

Fetch API

JSON.stringify()를 사용하여 객체를 문자열로 변환한 후, body에 담아서 보내주어야 한다. 

fetch(url, {
method: "POST",
headers: {
	"Content-Type": "application/json",
    },
body: JSON.stringify(todo),
	}
)
	.then((response) => response.json())
    .then((data) => console.log(data));

또한, Fetch API를 사용하면 명시적으로 Content-Type을 application/json으로 설정해주어야 한다

 

 

- 에러 처리

공통점: Fetch, Axios 모두 resolve / reject된 promise를 반환한다. reject 시, .catch()를 사용하여 에러를 처리할 수 있다. 

 

Axios 에러 처리

axios
.get(url)
    .then((response) => console.log(response.data))
    .catch((err) => {
    console.log(err.message);
    })

promise의 상태코드가 200번대가 아니면, reject 한다. 에러 객체에 response 또는 request 프로퍼티가 포함되어 있는지를 확인하여 에러에 대한 자세한 정보를 얻을 수 있다. 

 

.catch((err) => {
// 에러 처리
if (err.response) {
// 요청이 이루어졌고, 서버가 응답한 경우
	const { status, config } = err.response;
    
    if(status === 404) {
    	console.log(`${config.url} not found`);
    }
    
    if(status === 500) {
    	console.log("Server error");
    }
    
    }
    
    else if (err.request) {
    //요청은 이루어졌으나, 서버 응답이 없는 경우
    console.log("Error", err.message);
    } else { 
    //그 외 다른 에러
    console.log("Error", err.message); 
    }
    
    });

response, request 유무를 기준으로 조건문을 분기하여 에러를 추적해볼 수 있다. 

 

첫번째로, err.response는 클라이언트가 200번대를 벗어나는 상태코드를 가진 에러 응답을 받은 것을 의미한다.

두번째로, response 없이 err.request만 있는 경우, 요청은 보내졌으나 클라이언트가 응답을 받지 못한 것을 의미한다. 

마지막으로, request, response 모두 없는 경우는 요청을 하는 과정에서 오류가 발생한 것을 의미한다. 

 

이렇게 에러를 추적하여 디버깅할 수 있다. 

 

Fetch API 에러 처리

Fetch는 404 에러나 다른 HTTP 에러 응답을 받았다고 해서 promise를 reject 하지 않는다!!! 

reject 하는 경우는 네트워크 장애가 발생한 경우만 해당한다. 

따라서, .then 절을 사용해 수동으로 HTTP 에러를 추적해주어야 한다. 

 

fetch(url)
.then((response) => {
	if(!response.ok) {
    	throw new Error(
        `This is an HTTP error: The status is ${response.status}`
        );
        }
    return response.json();
    })
    .then(console.log)
    .catch((err) => {
    console.log(err.message);
    });

(내가 todo 앱을 만들면서 fetch 요청을 작성할 때, 꽤나 많은 시간을 소요한 부분이다. fetch API를 자세히 알고 나니 더 깔끔하고 조건을 명확하게 구분한 코드가 가능하구나..!)

 

우선, 응답 블록에서 응답의 ok 상태가 false일 경우 .catch 블록에서 에러메세지를 보여줄 수 있도록 커스텀 에러를 발생시킨다.

(fetch API는 응답 객체에 status(상태코드), ok(boolean) 을 가지고 있다)

 

 

- 응답 시간 초과 / 요청 취소

각각의 HTTP 클라이언트에서 HTTP 요청이 시간 초과될 경우 어떻게 처리하는지를 비교해보자. 

 

Axios

timeout 속성을 설정 객체에 추가하여 요청이 종료될 때까지의 시간을 밀리초로 지정할 수 있다. 

axios
.get(url, {
	timeout: 4000, // 기본 설정은 0
    })
    .then((response) => console.log(response.data))
    .catch((err) => console.log(err.message))
    };

Fetch API

fetch를 통한 요청을 취소하기 위해서는 AbortController 인터페이스를 사용할 수 있다. 

controller 객체를 생성하고, signal 객체와 abort() 메서드를 이용한다. 

 

const controller = new AbortController();
const signal = controller.signal;
setTimeout(() => controller.abort(), 4000);

fetch(url, {
signal: signal,
})
	.then((response) => response.json())
    .then(console.log)
    .catch((err) => {
    console.log(err.message);
    }));

signal 객체를 설정 옵션을 통해 fetch()에 넘긴다. 이렇게 하면 4초 이내에 응답이 없을 경우 abort 메서드가 호출되고 fetch 요청이 종료된다. 

 

- 성능

Fetch API와 Axios 모두 promise 기반이므로 성능 문제는 일으키지 않을 것이라고 한다.

(그러나 fetch 가 axios보다 살짝 더 빠르다)

 

결론

- 결국은 취향 차이? 나의 경우에는 Fetch API를 사용하여 todo 앱을 만들면서 API에 응답 status code와 response가 매우 잘 정리되어 있어서, fetch 에서 조건문으로 분기하여 에러 핸들링 하는 것도 수월했다. 그러나 JSON.stringify()로 항상 직렬화 해주어야 하는 것은 번거로웠고, 에러 메세지를 로그 찍는 것도 조금 까다롭게 느껴졌다. 둘 다 써보면서 차이점을 잘 인지하고 있으면 좋을 듯하다. 

 

앞으로 시도해 볼 것

1. Fetch API를 사용한 todo 앱을 Fetch API에 대한 더 깊은 이해를 바탕으로 더 깔끔하고 명확한 코드로 리팩토링 해보고자 한다. (완료)

2. 그 다음에는 axios 요청으로 리팩토링 해보고, 성능 차이를 비교해볼 것이다. (https://www.measurethat.net/ 사이트 이용해보기)

 

 

 

레퍼런스

- https://www.meticulous.ai/blog/fetch-vs-axios

(원문)

 

Axios vs Fetch | Which is Best for Beginners? | Meticulous AI

Learn how to catch UI bugs without writing or maintaining UI tests Try it outTry it out

www.meticulous.ai

https://velog.io/@eunbinn/Axios-vs-Fetch

(번역본)

 

[번역] 입문자를 위한 Axios vs Fetch

Axios와 Fetch API에 대해 소개하고 비교하는 글입니다. 제목에서도 알 수 있듯이 설치방법 부터 간단한 성능 비교까지 기본적인 내용들을 다루고 있습니다.

velog.io

 

'Network' 카테고리의 다른 글

네트워크 02 AJAX  (0) 2022.12.06
네트워크 01 기초 / HTTP  (1) 2022.12.04