🏗️ Axios 없이 구축한 Next.js 전용 네트워크 레이어: customFetch 설계기
·
13 views
·
5 min read
회고록fetch
프론트엔드 아키텍처에서 API 통신은 단순히 데이터를 가져오는 행위를 넘어, 불확실한 네트워크 환경 속에서 데이터의 무결성을 보장하고 통신의 가용성을 주도적으로 통제하는 과정이다. Next.js 공식 문서 역시 서버 측 호출 시 fetch 사용을 권장하며, 프레임워크의 핵심 예제들 또한 fetch를 표준으로 삼고 있다. muroom 프로젝트는 이러한 기술적 지향점에 발맞추어 기성 라이브러리인 Axios에 의존하지 않고, 브라우저와 Node.js 표준인 Fetch API를 래핑한 customFetch 를 직접 설계하여 통신의 신뢰도를 높였다.
설계 원칙: 표준에 대한 신뢰와 제어권의 내재화
서버와 클라이언트의 일관성: Next.js 15 환경은 서버(RSC)와 클라이언트 간의 경계가 유연하다. 별도의 어댑터 없이 양쪽 환경에서 네이티브하게 동작하는 fetch는 환경에 구애받지 않는 최적의 선택지였다.
Minimalism & Control: 프로젝트에 꼭 필요한 기능(Timeout, Interceptor, GA Logging)만 선별하여 담고자 했다. 불필요한 의존성을 제거하고 네트워크 생명주기 전체를 직접 통제하는 것이 시스템 안정성 측면에서 더 효율적이라 판단했다.
📸 범용 라이브러리의 복잡한 내부 구조와 대비되는, Native Fetch 기반의 직관적인 customFetch 흐름도
구현: muroom 프로젝트의 통신 기반, customFetch
설계 원칙을 실현하기 위해 구현된 customFetch는 단순한 래퍼를 넘어 타임아웃 제어, 환경별 주소 분기, 표준화된 에러 처리 로직을 내재화했다.
📦 customFetch 전체 코드 보기
TYPESCRIPT
import { BE_BASE_URL } from '@/config/constants';import { ApiRequestError, type ApiResponse } from '@/types/api';import
백엔드 서버가 간헐적으로 응답 지연(Pending) 상태에 빠질 때, 유저가 무한 로딩을 겪지 않도록 AbortSignal.timeout을 도입했다.
경험적 최적화: 요청 후 2초가 지나도 응답이 없으면 연결을 강제 중단한다. 펜딩된 요청을 끊고 즉시 재시도했을 때 서버가 즉각 응답하는 사례가 빈번함을 확인하고 이를 로직에 반영했다.
설계 철학: 2초는 유저의 인내심이 바닥나기 전의 마지노선이자, 불필요한 대기로 브라우저 자원을 낭비하지 않게 하는 안전장치다.
📸 (좌) 네트워크 탭에서 확인되는 다수의 'Canceled' 요청 상태 / (우) customFetch 내부 로직에 의해 출력된 타임아웃 중단 로그
TYPESCRIPT
// customFetch 내부의 타임아웃 및 세션 유지 설정const { timeout = 2000, ...restOptions } = options;const mergedOptions: CustomRequestInit = { ...restOptions, credentials: restOptions.credentials || 'include', signal: restOptions.signal || AbortSignal.timeout(timeout), };
🌐 하이브리드 환경을 고려한 엔드포인트 분기
Next.js의 App Router는 요청 주체에 따라 Base URL 전략이 달라야 한다. 이를 위해 실행 환경에 따른 자동 분기 로직을 구현했다.
Endpoint Branching: 서버 사이드(RSC)에서는 내부망 주소(BE_BASE_URL)를, 클라이언트 사이드에서는 상대 경로를 사용한다.
API Proxy: Kakao Map API 등 외부 API 통신 시 발생하는 CORS 이슈를 해결하기 위해 Next.js API Routes를 프록시로 활용하여 통신의 안정성을 확보했다.
🛡️ 에러 핸들링: 감추지 않고 수집하는 시스템
에러를 단순한 장애 상황으로 치부하지 않고, 서비스 결함을 파악하기 위한 데이터로 자산화했다.
GA Event Tracking: 공통 error.tsx에서 예외 발생 시 구글 애널리틱스(GA)로 상세 로그를 전송한다. 대시보드 반영 지연과 상관없이 네트워크 페이로드 분석을 통해 데이터 송출의 무결성을 상시 검증한다.
절대적 뷰 통제: ErrorTemplate은 useResponsiveLayout과 결합하여 404/500 에러 시 모바일과 웹 환경에 각각 최적화된 복구 경로(이전으로, 홈으로)를 제공한다.
📸 커스텀 500 에러 UI 렌더링과 동시에 GA4 'collect' 신호(204 Status)가 발생하는 실제 화면
📈 타입 안정성: 제네릭을 통한 DX(개발자 경험) 극대화
단순히 데이터를 가져오는 것을 넘어, 가져온 데이터가 어떤 형태인지 코드가 스스로 이해하게 만드는 것이 핵심이다. 성공 응답 데이터 구조인 ApiResponse<T>에 제네릭을 적용하여 비즈니스 로직에서의 타입 추론을 완벽하게 지원하도록 설계했다.
Generic <T> 활용: 호출부에서 기대하는 데이터 타입을 직접 주입함으로써, 정의되지 않은 프로퍼티에 접근하려는 시도를 컴파일 단계에서 차단한다.
인텔리센스(IntelliSense) 활용: 백엔드 API 명세를 미리 타입에 주입시켜 프론트엔드 코드에 즉시 반영되는 환경을 구축하여, 조금이나마 개발 생산력을 높힌다.
📸 responseData. 입력 시 정의된 인터페이스 필드들이 즉시 자동 완성되는 인텔리센스 화면
🏁 마치며: 실무적 제어권 확보의 중요성
customFetch를 직접 구현해 본 과정은 네트워크의 불확실성을 코드로 직접 다뤄보며 시스템의 예측 가능성을 높이는 유익한 경험이었다.
외부 라이브러리에 전적으로 의존하기보다 표준 API를 활용해 프로젝트 성격에 맞는 최적의 구조를 고민해 본 덕분에, 더 가볍고 관리하기 쉬운 독자적인 통신 계층을 구축할 수 있었다. 결국 안정적인 개발 환경을 바탕으로 사용자에게 더 매끄러운 경험을 제공하는 것, 이것이 이번 작업을 통해 얻은 가장 실질적인 경험이이었다.