모든 인기 fetch() 래퍼의 숨겨진 문제점: 우리는 무엇을 놓치고 있을까?
2025-03-11 22:24:18목차
- 문제의 근원: 잘못된 fetch() 사용 사례
- 오류 핸들링: try..catch의 남용
- 인기 있는 fetch() 래퍼의 비효율성
- ky와 직접 구현의 비교
- 개선된 fetch() 사용법: dr-fetch 소개
- 결론: 표준 fetch()로 돌아가기
- 추가 참고자료
문제의 근원: 잘못된 fetch() 사용 사례
JavaScript 개발자라면 누구나 HTTP 요청을 할 때 한 번쯤 fetch() 함수를 사용해보았을 것입니다. 그와 같은 기본적인 사용법에도 불구하고, 많은 개발자들이 axios 또는 ky와 같은 래퍼를 사용해 왔습니다. 하지만 이러한 선택이 정말로 효과적일까요? 실제로는 그렇지 않습니다. 잘못된 조사에 의하면, 많은 fetch() 래퍼들이 비효율적인 try..catch 구조를 사용하고 있으며, 이는 불필요한 성능 저하를 가져옵니다.
오류 핸들링: try..catch의 남용
많은 개발자들이 오류를 처리하기 위해 try..catch 구문을 사용합니다. 하지만 이는 단순한 조건 분기를 위해 사용하는 것은 잘못된 관행입니다. 예를 들어, 특정 변수가 false인지 확인하는 상황에서, 굳이 에러를 throw하고 이를 캐치하는 방식보다는 단순히 if 조건문으로 처리하는 것이 훨씬 효과적입니다. 디버깅 및 성능 면에서 try..catch는 상당한 비용을 발생시킵니다.
인기 있는 fetch() 래퍼의 비효율성
axios나 ky 같은 라이브러리들은 대부분 비동기 요청이 실패할 경우 예외를 던지는 방식으로 동작합니다. 이는 불필요한 try..catch의 사용을 유도하며, 코드의 복잡성을 증가시킵니다. 더욱이, 이러한 방식은 성능을 최대 40%까지 감소시킬 수 있습니다. 특히, 비즈니스 로직에서 예외를 통해 정교한 흐름 제어를 하는 것이 아닌 단순 패턴의 요청이 대부분인 경우, 이는 반드시 재고해야 할 문제입니다.
ky와 직접 구현의 비교
예를 들어, ky 라이브러리를 사용하여 JWT 인증이 필요한 요청을 보낼 때의 예제 코드를 살펴봅시다. ky는 인터셉터 패턴을 제공하지만, 이로 인해 추가적인 API 학습 곡선이 필요합니다. 한편, JavaScript의 기본 fetch()를 사용하여 동일한 기능을 구현하는 것은 덜 복잡하고, 코드의 가독성을 높입니다.
// ky 사용 예제
import ky from 'ky';
export const api = ky.extend({
hooks: {
beforeRequest: [
request => {
request.headers.set('Authorization', `Bearer ${getToken()}`);
}
]
}
});
// 직접 구현
export function myFetch(url, options) {
options.headers = { ...options.headers, Authorization: `Bearer ${getToken()}` };
return fetch(url, options);
}
위 코드 예시를 통해 알 수 있듯이, ky를 사용하는 것은 오히려 더 많은 코드와 복잡성을 초래합니다.
개선된 fetch() 사용법: dr-fetch 소개
기존 문제점을 해결하기 위한 솔루션으로, dr-fetch라는 라이브러리가 소개되었습니다. 이 라이브러리는 단순한 fetch() 함수의 확장을 통해 개선된 HTTP 요청을 제공합니다. 특히, HTTP 응답 코드에 기반한 타입체킹 기능을 제공하여 TypeScript 사용 시에 유용합니다.
// fetcher.ts
import { DrFetch, setHeaders } from 'dr-fetch';
function myFetch(url: string, init?: RequestInit) {
init ??= {};
setHeaders(init, {
Accept: 'application/json',
Authorization: `Bearer ${getToken()}`,
});
return fetch(url, init);
}
export default new DrFetch(myFetch);
// 이 라이브러리를 사용하여 요청을 보낼 수 있습니다.
import fetcher from './fetcher.js';
const response = await fetcher
.for<200, MyData[]>()
.for<401, undefined>()
.for<400, ValidationError[]>()
.fetch('/api/data/?sort=desc');
if (response.status === 200) {
console.log('Data: %o', response.body.map(x => x.id));
} else if (response.status === 400) {
console.log('Errors: %s', response.body.map(error => error.message).join('\n'));
} else if (response.status === 401) {
deleteSavedToken();
}
결론: 표준 fetch()로 돌아가기
대규모 애플리케이션이 아닌 경우, 기본 fetch() API로도 충분히 높은 성능과 직관적인 코드를 작성할 수 있습니다. 다양한 라이브러리가 제공하는 부가 기능들이 필요하다면, fetch()를 기반으로 필요한 기능을 테스트하고, 가능한 경우 표준 API를 활용하는 것이 장기적으로 프로젝트의 유지보수와 성능에 더 유리할 것입니다.
추가 참고자료
아래는 추가로 참고할만한 자료들입니다: