🌐 웹개발

Fetch API

페치 API

Fetch API는 브라우저 내장 HTTP 클라이언트로, Promise 기반의 현대적 네트워크 요청을 지원합니다. XMLHttpRequest를 대체하며 async/await와 함께 사용하면 깔끔한 비동기 코드를 작성할 수 있습니다.

📖 상세 설명

Fetch API는 2015년 모던 브라우저에 도입된 네트워크 요청 인터페이스입니다. 기존 XMLHttpRequest(XHR)의 복잡한 콜백 패턴을 Promise 기반으로 개선하여, 비동기 HTTP 통신을 더 직관적으로 처리할 수 있게 해줍니다. 현재 모든 모던 브라우저와 Node.js 18+ 버전에서 네이티브로 지원됩니다.

fetch() 함수는 Request 객체 또는 URL을 인자로 받아 Response 객체를 반환하는 Promise를 생성합니다. Response 객체는 응답 본문을 다양한 형태(JSON, text, blob, arrayBuffer)로 파싱하는 메서드를 제공합니다. 스트림 기반 처리도 지원하여 대용량 파일 다운로드나 실시간 데이터 처리에도 활용됩니다.

Fetch API의 핵심 특징은 기본적으로 HTTP 오류(4xx, 5xx)를 예외로 던지지 않는다는 점입니다. 네트워크 장애만 reject되며, HTTP 상태 코드는 response.ok나 response.status로 직접 확인해야 합니다. 이러한 설계는 응답 본문에 포함된 에러 메시지를 읽을 수 있게 해주지만, 명시적인 상태 체크가 필수입니다.

AbortController와 함께 사용하면 진행 중인 요청을 취소할 수 있어, 사용자가 페이지를 떠나거나 검색어가 변경될 때 불필요한 요청을 중단할 수 있습니다. 또한 credentials, mode, cache 등의 옵션으로 쿠키 전송, CORS 동작, 캐시 정책을 세밀하게 제어할 수 있습니다.

💻 코드 예제

// 기본 GET 요청
const response = await fetch('/api/users');
if (!response.ok) {
  throw new Error(`HTTP error! status: ${response.status}`);
}
const users = await response.json();

// POST 요청 with JSON body
const newUser = await fetch('/api/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ name: '김철수', email: 'kim@example.com' }),
});

// 인증 토큰과 함께 요청
const authResponse = await fetch('/api/profile', {
  headers: {
    'Authorization': `Bearer ${accessToken}`,
  },
  credentials: 'include', // 쿠키 포함
});

// AbortController로 요청 취소
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);

try {
  const data = await fetch('/api/slow-endpoint', {
    signal: controller.signal,
  });
  clearTimeout(timeoutId);
} catch (error) {
  if (error.name === 'AbortError') {
    console.log('요청이 타임아웃되었습니다');
  }
}

// 파일 업로드 with FormData
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('description', '프로필 이미지');

await fetch('/api/upload', {
  method: 'POST',
  body: formData, // Content-Type 자동 설정
});

// 재사용 가능한 fetch wrapper
async function api(endpoint, options = {}) {
  const response = await fetch(`/api${endpoint}`, {
    ...options,
    headers: {
      'Content-Type': 'application/json',
      ...options.headers,
    },
  });

  if (!response.ok) {
    const error = await response.json().catch(() => ({}));
    throw new Error(error.message || `HTTP ${response.status}`);
  }

  return response.json();
}

🗣️ 실무 대화 예시

👨‍💻 주니어 개발자
"fetch로 API 호출했는데 에러 처리가 제대로 안 되는 것 같아요. try-catch로 감쌌는데 404 에러가 catch로 안 가요."
👩‍💻 시니어 개발자
"fetch는 네트워크 에러만 reject해요. HTTP 에러는 response.ok로 체크해야 해요. 패턴은 'if (!response.ok) throw new Error(...)' 로 명시적으로 던져주세요. 아예 axios처럼 자동으로 에러 던지는 wrapper 함수를 만들어두면 편해요."
👨‍💼 기술 면접관
"fetch와 axios의 차이점과 각각 어떤 상황에서 선택하시겠어요?"
👨‍💻 지원자
"fetch는 브라우저 네이티브라 번들 사이즈가 0이고, 대부분의 경우 충분합니다. 다만 axios는 인터셉터, 자동 에러 처리, 요청 취소가 더 편리하고, IE 지원도 됩니다. 저는 작은 프로젝트는 fetch wrapper를 만들어 쓰고, 복잡한 API 레이어가 필요하면 axios를 선택합니다."

⚠️ 주의사항

🔗 관련 용어

📚 더 배우기