최근 QA 테스트 진행 중, QA 팀원분의 다음과 같은 요청이 있었다.
api를 사용하는 컴포넌트의 네트워크 실패/지연 케이스를 재현해보고 싶은데 방법이 없을까요?
로컬 환경에서는 이를 쉽게 재현할 수 있겠지만, 개발 내용을 공유하기 위해선 개발 서버로 배포가 필요했고 특정 api만을 원할때마다 실패 혹은 지연을 일으킬 수 있는 방법이 떠오르지 않아 당시엔 '그건 좀 힘들 것 같아요 ㅜㅜ' 라고 답변을 드렸다.
이후, 같은 질문을 받는다면 좀 더 괜찮은 답변을 드리고 싶었고 몇가지 방법을 찾아냈다.
- Chrome 개발자 도구 > 네트워크 탭 > fetch/XHR 탭 > 원하는 api 요청 block
- Chorme 개발자 도구 > 설정 > 제한 > 커스텀 네트워크 속도/지연 프로필 설정 (네트워크 탭에서 제공하는 preset 네트워크 설정인 slow 3g, fast 3g가 충분히 느리지 않다고 생각들때 사용)
위 방법 외에도 javascript 에서도 웹 요청을 programatically 하게 중단시킬 수 있는 AbortController 라는 interface를 사용하는 방법이 있다.
Interface
interface AbortController {
constructor();
// AbortSignal 타입의 signal 객체
[SameObject] readonly attribute AbortSignal signal;
// signal과 연관된 DOM 요청을 취소시킨다
// reason이 undefined 라면 DOMException 타입의 "AbortError" 로 초기화
undefined abort(optional any reason);
};
// EventTarget interface를 상속한다
interface AbortSignal : EventTarget {
// 이미 aborted 된 signal을 반환한다.
[NewObject] static AbortSignal abort(optional any reason);
// 매개변수로 넘겨준 시간 후에 abort 가 발생하는 AbortSignal 객체를 반환한다
[Exposed=(Window,Worker), NewObject] static AbortSignal timeout([EnforceRange] unsigned long long milliseconds);
// aborted 유무 판단 flag
readonly attribute boolean aborted;
// signal's abort reason
readonly attribute any reason;
// signal이 aborted 됐으면 reason을 throw 한다.
undefined throwIfAborted();
// event type이 abort인 onabort event handler를 가진다
attribute EventHandler onabort;
};
Usage
fetch/axios 중단 시키기 (axios는 v0.22 부터 지원가능하다고 한다)
// ref: https://developer.mozilla.org/ko/docs/Web/API/AbortSignal
const controller = new AbortController();
const signal = controller.signal;
const downloadBtn = document.querySelector('.download');
const abortBtn = document.querySelector('.abort');
downloadBtn.addEventListener('click', fetchVideo);
abortBtn.addEventListener('click', function() {
controller.abort();
console.log('Download aborted');
});
function fetchVideo() {
...
// with fetch
fetch(url, {signal}).then(function(response) {
...
}).catch(function(e) {
reports.textContent = 'Download error: ' + e.message;
})
...
// with axios
axios.get(url, {signal}).then(function(response) {
...
});
...
}
timeout 발생시키는 signal 생성 (꽤 최신 브라우저들만 이를 지원한다)
// https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout
try {
const res = await fetch(url, { signal: AbortSignal.timeout(5000) });
const result = await res.blob();
// …
} catch (err) {
if (err.name === "TimeoutError") {
console.error("Timeout: It took more than 5 seconds to get the result!");
} else if (err.name === "AbortError") {
console.error("Fetch aborted by user action (browser stop button, closing tab, etc.");
} else if (err.name === "TypeError") {
console.error("AbortSignal.timeout() method is not supported");
} else {
// A network error, or some other problem.
console.error(`Error: type: ${err.name}, message: ${err.message}`);
}
}
AbortSignal interface를 signal로 받는 요청에도 사용 가능하다.
fs readFile 메서드 abort 하기
// ref: https://blog.logrocket.com/complete-guide-abortcontroller-node-js/
const fs = require("node:fs");
const controller = new AbortController();
const { signal } = controller;
fs.readFile("data.txt", { signal, encoding: "utf8" }, (error, data) => {
if (error) {
if (error.name === "AbortError") {
console.log("Read file process aborted");
} else {
console.error(error);
}
return;
}
console.log(data);
});
controller.abort();
AbortSignal interface는 내부적으로 event type이 abort인 onabort event handler를 가지고 있기 때문에
abortable 한 api를 커스텀하게 만들수도 있다.
// ref: https://blog.logrocket.com/complete-guide-abortcontroller-node-js/
const customAbortableApi = (options: {signal?: AbortSignal}) => {
const { signal } = options;
if (signal?.aborted === true) {
throw new Error(signal.reason);
}
const abortEventListener = () => {
// Abort API from here
};
if (signal) {
signal.addEventListener("abort", abortEventListener, { once: true });
}
try {
// Run some asynchronous code
if (signal?.aborted === true) {
throw new Error(signal.reason);
}
// Run more asynchronous code
} finally {
if (signal) {
signal.removeEventListener("abort", abortEventListener);
}
}
};
참고
https://developer.mozilla.org/ko/docs/Web/API/AbortSignal
https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal/timeout
https://dom.spec.whatwg.org/#interface-abortcontroller
https://stackoverflow.com/questions/38329209/how-to-cancel-abort-ajax-request-in-axios
https://blog.logrocket.com/complete-guide-abortcontroller-node-js/
'Study' 카테고리의 다른 글
OpenSource Contribute 후기 (2) | 2023.11.20 |
---|---|
[Next.js] Data Fetching APIs 비교 분석 (getStaticProps, getStaticPaths, getServersideProps, getInitialProps) (0) | 2022.08.20 |