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 요청과 네트워크 오류를 안정적으로 처리
Footnotes
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,
})
이미지 다운로드 구현. 이미지 응답값을 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)
}
서비스워커로 fetch를 감지해서 해당 기능을 구현한다는 내용. 개인적으로는 mock은 간단하게 구현 가능할 것 같은데 이미 같은 기능의 잘 만들어진 라이브러리들이 있으니까 아이디어 정도로 생각하면 될 것 같다.
addEventListener('fetch', e => {
// e.request
// e.respondWith
})