JavaScript의 AbortController를 사용하여 fetch 요청, 타이머 등과 같은 비동기 작업을 효과적으로 취소하는 방법을 배우고 더 깔끔하고 성능이 뛰어난 코드를 확보하세요.
JavaScript AbortController: 비동기 작업 취소 마스터하기
최신 웹 개발에서 비동기 작업은 어디에나 존재합니다. API에서 데이터를 가져오고, 타이머를 설정하고, 사용자 상호 작용을 처리하는 작업은 독립적으로 실행되고 잠재적으로 장시간 지속되는 코드를 포함하는 경우가 많습니다. 그러나 완료되기 전에 이러한 작업을 취소해야 하는 시나리오가 있습니다. 바로 이럴 때 JavaScript의 AbortController
인터페이스가 도움이 됩니다. DOM 작업 및 기타 비동기 작업에 취소 요청을 알리는 깔끔하고 효율적인 방법을 제공합니다.
취소의 필요성 이해
기술적인 세부 사항을 살펴보기 전에 비동기 작업 취소가 왜 중요한지 이해해 보겠습니다. 다음과 같은 일반적인 시나리오를 고려해 보세요.
- 사용자 탐색: 사용자가 검색 쿼리를 시작하여 API 요청을 트리거합니다. 요청이 완료되기 전에 다른 페이지로 빠르게 이동하면 원래 요청은 관련성이 없어지며 불필요한 네트워크 트래픽과 잠재적인 부작용을 방지하기 위해 취소해야 합니다.
- 제한 시간 관리: 비동기 작업에 대한 제한 시간을 설정합니다. 제한 시간이 만료되기 전에 작업이 완료되면 중복 코드 실행을 방지하기 위해 제한 시간을 취소해야 합니다.
- 컴포넌트 언마운트: React 또는 Vue.js와 같은 프론트엔드 프레임워크에서 컴포넌트는 종종 비동기 요청을 합니다. 컴포넌트가 언마운트되면 해당 컴포넌트와 관련된 진행 중인 요청은 메모리 누수와 언마운트된 컴포넌트 업데이트로 인한 오류를 방지하기 위해 취소해야 합니다.
- 리소스 제약: 리소스가 제한된 환경(예: 모바일 장치, 임베디드 시스템)에서 불필요한 작업을 취소하면 귀중한 리소스를 확보하고 성능을 향상시킬 수 있습니다. 예를 들어 사용자가 페이지의 해당 섹션을 지나 스크롤하면 큰 이미지 다운로드를 취소합니다.
AbortController 및 AbortSignal 소개
AbortController
인터페이스는 비동기 작업 취소 문제를 해결하기 위해 설계되었습니다. 두 가지 주요 구성 요소로 구성됩니다.
- AbortController: 이 객체는 취소 신호를 관리합니다. 취소 요청을 알리는 데 사용되는 단일 메서드인
abort()
가 있습니다. - AbortSignal: 이 객체는 작업이 중단되어야 함을 나타내는 신호를 나타냅니다.
AbortController
와 연결되어 있으며 취소 가능해야 하는 비동기 작업에 전달됩니다.
기본 사용법: Fetch 요청 취소
fetch
요청을 취소하는 간단한 예제부터 시작하겠습니다.
const controller = new AbortController();
const signal = controller.signal;
fetch('https://api.example.com/data', { signal })
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log('Data:', data);
})
.catch(error => {
if (error.name === 'AbortError') {
console.log('Fetch aborted');
} else {
console.error('Fetch error:', error);
}
});
// To cancel the fetch request:
controller.abort();
설명:
AbortController
인스턴스를 만듭니다.controller
에서 연결된AbortSignal
을 가져옵니다.signal
을fetch
옵션에 전달합니다.- 요청을 취소해야 하는 경우
controller.abort()
를 호출합니다. .catch()
블록에서 오류가AbortError
인지 확인합니다. 그렇다면 요청이 취소된 것입니다.
AbortError 처리
controller.abort()
가 호출되면 fetch
요청이 AbortError
와 함께 거부됩니다. 코드에서 이 오류를 적절하게 처리하는 것이 중요합니다. 그렇지 않으면 처리되지 않은 프로미스 거부 및 예기치 않은 동작이 발생할 수 있습니다.
다음은 오류 처리가 포함된 더욱 강력한 예제입니다.
const controller = new AbortController();
const signal = controller.signal;
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data', { signal });
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
console.log('Data:', data);
return data;
} catch (error) {
if (error.name === 'AbortError') {
console.log('Fetch aborted');
return null; // Or throw the error to be handled further up
} else {
console.error('Fetch error:', error);
throw error; // Re-throw the error to be handled further up
}
}
}
fetchData();
// To cancel the fetch request:
controller.abort();
AbortError 처리 모범 사례:
- 오류 이름 확인: 올바른 오류 유형을 처리하고 있는지 확인하려면 항상
error.name === 'AbortError'
인지 확인하세요. - 기본값 반환 또는 다시 throw: 애플리케이션의 논리에 따라 기본값(예:
null
)을 반환하거나 호출 스택에서 더 높이 처리되도록 오류를 다시 throw할 수 있습니다. - 리소스 정리: 비동기 작업이 리소스(예: 타이머, 이벤트 리스너)를 할당한 경우
AbortError
처리기에서 정리합니다.
AbortSignal을 사용하여 타이머 취소
AbortSignal
은 setTimeout
또는 setInterval
로 생성된 타이머를 취소하는 데 사용할 수도 있습니다. 기본 제공 타이머 기능은 AbortSignal
을 직접 지원하지 않으므로 약간 더 수동적인 작업이 필요합니다. abort 신호를 수신하고 트리거될 때 타이머를 지우는 사용자 지정 함수를 만들어야 합니다.
function cancellableTimeout(callback, delay, signal) {
let timeoutId;
const timeoutPromise = new Promise((resolve, reject) => {
timeoutId = setTimeout(() => {
resolve(callback());
}, delay);
signal.addEventListener('abort', () => {
clearTimeout(timeoutId);
reject(new Error('Timeout Aborted'));
});
});
return timeoutPromise;
}
const controller = new AbortController();
const signal = controller.signal;
cancellableTimeout(() => {
console.log('Timeout executed');
}, 2000, signal)
.then(() => console.log("Timeout finished successfully"))
.catch(err => console.log(err));
// To cancel the timeout:
controller.abort();
설명:
cancellableTimeout
함수는 콜백, 지연 및AbortSignal
을 인수로 사용합니다.setTimeout
을 설정하고 시간 초과 ID를 저장합니다.abort
이벤트를 수신하는AbortSignal
에 이벤트 리스너를 추가합니다.abort
이벤트가 트리거되면 이벤트 리스너가 시간 초과를 지우고 프로미스를 거부합니다.
이벤트 리스너 취소
타이머와 유사하게 AbortSignal
을 사용하여 이벤트 리스너를 취소할 수 있습니다. 이는 언마운트되는 컴포넌트와 연결된 이벤트 리스너를 제거하려는 경우 특히 유용합니다.
const controller = new AbortController();
const signal = controller.signal;
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log('Button clicked!');
}, { signal });
// To cancel the event listener:
controller.abort();
설명:
signal
을addEventListener
메서드에 옵션으로 전달합니다.controller.abort()
가 호출되면 이벤트 리스너가 자동으로 제거됩니다.
React 컴포넌트의 AbortController
React에서 AbortController
를 사용하여 컴포넌트가 언마운트될 때 비동기 작업을 취소할 수 있습니다. 이는 메모리 누수와 언마운트된 컴포넌트 업데이트로 인한 오류를 방지하는 데 필수적입니다. 다음은 useEffect
후크를 사용하는 예입니다.
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [data, setData] = useState(null);
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data', { signal });
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const data = await response.json();
setData(data);
} catch (error) {
if (error.name === 'AbortError') {
console.log('Fetch aborted');
} else {
console.error('Fetch error:', error);
}
}
}
fetchData();
return () => {
controller.abort(); // Cancel the fetch request when the component unmounts
};
}, []); // Empty dependency array ensures this effect runs only once on mount
return (
{data ? (
Data: {JSON.stringify(data)}
) : (
Loading...
)}
);
}
export default MyComponent;
설명:
useEffect
후크 내에서AbortController
를 만듭니다.signal
을fetch
요청에 전달합니다.useEffect
후크에서 정리 함수를 반환합니다. 이 함수는 컴포넌트가 언마운트될 때 호출됩니다.- 정리 함수 내에서
controller.abort()
를 호출하여 fetch 요청을 취소합니다.
고급 사용 사례
AbortSignal 연결
경우에 따라 여러 AbortSignal
을 함께 연결할 수 있습니다. 예를 들어 자식 컴포넌트에서 작업을 취소해야 하는 상위 컴포넌트가 있을 수 있습니다. 새 AbortController
를 만들고 해당 신호를 상위 및 자식 컴포넌트에 모두 전달하여 이를 달성할 수 있습니다.
타사 라이브러리와 함께 AbortController 사용
AbortSignal
을 직접 지원하지 않는 타사 라이브러리를 사용하는 경우 라이브러리의 취소 메커니즘과 함께 작동하도록 코드를 조정해야 할 수 있습니다. 여기에는 AbortSignal
을 처리하는 자체 함수에서 라이브러리의 비동기 함수를 래핑하는 작업이 포함될 수 있습니다.
AbortController 사용의 이점
- 향상된 성능: 불필요한 작업을 취소하면 네트워크 트래픽, CPU 사용량 및 메모리 소비를 줄여 특히 리소스가 제한된 장치에서 성능을 향상시킬 수 있습니다.
- 더 깔끔한 코드:
AbortController
는 취소를 관리하는 표준화되고 우아한 방법을 제공하여 코드를 더 읽기 쉽고 유지 관리하기 쉽게 만듭니다. - 메모리 누수 방지: 언마운트된 컴포넌트와 관련된 비동기 작업을 취소하면 메모리 누수와 언마운트된 컴포넌트 업데이트로 인한 오류를 방지할 수 있습니다.
- 더 나은 사용자 경험: 관련 없는 요청을 취소하면 오래된 정보가 표시되지 않고 인식되는 대기 시간이 줄어들어 사용자 경험을 향상시킬 수 있습니다.
브라우저 호환성
AbortController
는 Chrome, Firefox, Safari 및 Edge를 포함한 최신 브라우저에서 널리 지원됩니다. 최신 정보는 MDN Web Docs에서 호환성 표를 확인할 수 있습니다.
Polyfill
AbortController
를 기본적으로 지원하지 않는 이전 브라우저의 경우 Polyfill을 사용할 수 있습니다. Polyfill은 이전 브라우저에서 최신 기능의 기능을 제공하는 코드 조각입니다. 온라인에서 사용할 수 있는 여러 AbortController
Polyfill이 있습니다.
결론
AbortController
인터페이스는 JavaScript에서 비동기 작업을 관리하는 강력한 도구입니다. AbortController
를 사용하면 취소를 정상적으로 처리하는 더 깔끔하고 성능이 뛰어나며 강력한 코드를 작성할 수 있습니다. API에서 데이터를 가져오거나, 타이머를 설정하거나, 이벤트 리스너를 관리하는 등 AbortController
는 웹 애플리케이션의 전반적인 품질을 향상시키는 데 도움이 될 수 있습니다.