type FetchWithAbortResult =
  | { response: Response; abort: () => void }
  | { error: NetworkError | HttpError }

class NetworkError extends Error {
  constructor(message: string) {
    super(message)
    this.name = 'NetworkError'
  }
}

class HttpError extends Error {
  constructor(public status: number, message?: string) {
    super(message || `HTTP error: ${status}`)
    this.name = 'HttpError'
  }
}

async function fetchWithAbortController(
  url: string,
  options: RequestInit = {}
): Promise<FetchWithAbortResult> {
  const controller = new AbortController()
  const { signal } = controller

  try {
    const response = await fetch(url, { ...options, signal })

    if (!response.ok) {
      throw new HttpError(response.status)
    }

    return {
      response,
      abort: () => controller.abort(),
    }
  } catch (error) {
    if (error.name === 'AbortError') {
      return {
        error: new NetworkError(),
      }
    }

    const e =
      error instanceof HttpError ? error : new NetworkError(error.message)

    return {
      error: e,
    }
  }
}
  • API 요청 시 발생할 수 있는 오류 문제의 필요성
  • AbortController 사용과 사용자 정의 에러 클래스를 통한 에러 관리
  • HTTP 요청과 네트워크 오류를 안정적으로 처리
#378

Footnotes

  1. XMLHttpRequest가 완전히 성공할때까지 정보를 주기적으로 호출하는 함수

  2. fetch 에서는 ReadableStream, Response 조합으로 구현 가능

  3. axios 에서는 onUploadProgress 콜백(progressEvent)으로 구현

#52
  • 조건에 따라서 브라우저에서 자동으로 발생1
  • OPTIONS 메서드로 요청
  • cloudfront에 OPTIONS 메서드 설정이 안되어 있다면 실패2

Footnotes

  1. 단순 요청(Simple requests)

  2. CloudFront 배포의 캐시 동작이 HTTP 요청에 대한 OPTIONS 메서드를 허용함

#53
const formData = new FormData();
const single = document.querySelector('input[type="file"]');
const multiple = document.querySelector('input[type="file"][multiple]');

formData.append('single', single.files[0]);

[...multiple].forEach((file, index) => {
  formData.append(`multiple_${index}`, file);
})

fetch('https://example.com/posts', {
  method: 'POST',
  body: formData,
})
#55

이미지 다운로드 구현. 이미지 응답값을 buffer로 변환해서 파일쓰기로 저장한다.

const fs = require('fs')
const util = require('util')
const fetch = require('node-fetch')

const writeFile = util.promisify(fs.writeFile)
const mkdir = util.promisify(fs.mkdir)

const FOLDER_PATH = 'FOLDER_PATH'

async function download({ url }) {
  const response = await fetch(url)
  const buffer = Buffer.from(await response.arrayBuffer())

  if (!fs.existsSync(FOLDER_PATH)) {
    await mkdir(FOLDER_PATH)
  }

  await writeFile(url, buffer)
}
#59

서비스워커로 fetch를 감지해서 해당 기능을 구현한다는 내용. 개인적으로는 mock은 간단하게 구현 가능할 것 같은데 이미 같은 기능의 잘 만들어진 라이브러리들이 있으니까 아이디어 정도로 생각하면 될 것 같다.

addEventListener('fetch', e => { 
  // e.request
  // e.respondWith
})
#70