티스토리 뷰

Javascript

AbortController 란 무엇일까

주섬이 2025. 3. 10. 18:40
반응형

AbortController 

자바스크립트에서 비동기 작업을 중단할 수 있도록 도와주는 API이다. (특히 fetch() 요청 중단)

Promise는 원래 취소 기능이 없지만, AbortController로 특정 요청을 강제로 취소할 수 있다.

 

 

기본 개념

AbortController는 비동기 작업을 제어하기 위한 signal 객체를 제공한다.

controller.abort()를 호출하면 해당 signal을 감지하는 모든 요청이 즉시 취소된다.

const controller = new AbortController(); 
const { signal } = controller; 

fetch("https://jsonplaceholder.typicode.com/todos/1", { signal }) 
.then((response) => response.json()) 
.then((data) => console.log("📌 데이터:", data)) 
.catch((error) => { 
    if (error.name === "AbortError") { 
    	console.log("🚨 요청이 취소되었습니다."); 
    } else { 
    	console.error("❌ 오류 발생:", error); 
    } 
}); 

// 1초 후 요청 취소 
setTimeout(() => { 
	controller.abort(); // 요청 강제 취소 
}, 1000);

 

controller.abort()가 호출되면 fetch() 요청은 즉시 중단되고, catch()에서 AbortError를 감지할 수 있다.
즉, fetch 요청이 완료되지 않았더라도 중간에 취소할 수 있다.

 

 

여러개 요청 한번에 취소

한 번의 abort() 호출로 모든 요청을 동시에 중단할 수 있다

const controller = new AbortController(); 
const { signal } = controller; 

const fetchData = (url) => fetch(url, { signal })
.then((res) => res.json()); 

Promise.all([ 
    fetchData("https://jsonplaceholder.typicode.com/todos/1"), 
    fetchData("https://jsonplaceholder.typicode.com/todos/2"), 
]) 
.then((data) => console.log("📌 데이터:", data)) 
.catch((error) => { 
    if (error.name === "AbortError") { 
    	console.log("🚨 모든 요청이 취소되었습니다."); 
    } else { 
    	console.error("❌ 오류 발생:", error); 
    } 
}); 

// 1초 후 모든 요청 취소 
setTimeout(() => { controller.abort(); }, 1000);

 

AbortController를 일반 Promise에 적용

기본적으로 Promise는 취소할 수 없지만, AbortSignal을 활용하면 수동으로 취소 가능하다.

 

 

일반 Promise에 적용

const controller = new AbortController(); 
const { signal } = controller;

const asyncTask = new Promise((resolve, reject) => { 
    const timeout = setTimeout(() => { 
        resolve("✅ 작업 완료!"); 
    }, 5000); 
	
    // `abort` 이벤트가 발생하면 작업 취소 
    signal.addEventListener("abort", () => { 
    	clearTimeout(timeout); 
		reject(new Error("❌ 작업이 취소되었습니다.")); 
	}); 
}); 

asyncTask 
.then(console.log) 
.catch(console.error); 

// 2초 후 강제 취소 
setTimeout(() => { controller.abort(); }, 2000);

 

controller.abort()가 호출되면 timeout이 중단되면서 reject()가 실행된다.

즉, AbortController는 fetch() 요청을 제어하는 데 최적화되어 있지만, 일반 Promise를 취소하려면 추가 처리가 필요하다.

 

 

AbortController의 한계

기능 설명
✅ fetch() 요청 취소 가능 signal을 사용하여 요청을 중단할 수 있음
✅ 여러 개의 요청을 동시에 취소 가능 같은 signal을 여러 개의 요청에 전달
❌ 기본 Promise는 자동 취소 불가 직접 signal.addEventListener("abort")로 처리해야 함
❌ setTimeout() 같은 작업은 기본적으로 취소 안됨 clearTimeout()을 활용해야 함

 

 


 

 

Q. fetch 요청 취소 원리가 궁금해

A. controller.abort()가 호출되면 fetch() 요청이 즉시 중단되고, catch()에서 AbortError를 감지하는 이유

 

fetch() 요청 취소 원리

AbortController는 비동기 요청을 취소할 수 있는 API이며, controller.abort()를 호출하면 연결된 모든 요청이 즉시 중단된다.
이때 fetch()는 정상적으로 reject되며, catch()에서 AbortError를 감지할 수 있다.

const controller = new AbortController(); // AbortController 인스턴스 생성 
const signal = controller.signal; // signal을 fetch에 연결 

fetch('https://jsonplaceholder.typicode.com/todos/1', { signal }) 
.then(response => response.json()) 
.then(data => console.log('응답 데이터:', data)) 
.catch(error => { 
    if (error.name === 'AbortError') { 
    	console.log('⛔ 요청이 중단되었습니다:', error); 
    } else { 
    	console.log('⚠️ 다른 오류 발생:', error); 
    } 
}); 

// 2초 후 요청 강제 취소 
setTimeout(() => { controller.abort(); // fetch 요청 중단 }, 2000);

 

  1. fetch()가 실행되고 signal을 전달하여 요청을 추적할 수 있도록 설정한다.
  2. 2초 후 controller.abort()가 호출되면 요청이 강제 중단된다.
  3. fetch()는 reject되며, catch() 블록이 실행된다.
    • error.name === 'AbortError'를 확인하여 요청이 사용자에 의해 취소되었는지 판별 가능함

controller.abort()를 호출하면 fetch()는 강제로 reject되고 catch()에서 AbortError가 발생한다.

즉, fetch() 자체가 실패한게 아니라 '사용자의 강제 중단'에 의해 catch()로 이동된다.

 

 

사용자가 페이지 떠날때 진행 중인 요청 취소

beforeunload가 호출될때 controller.abort()를 요청해 강제 중단시킨다.

const controller = new AbortController(); 
const signal = controller.signal; 

// 페이지 떠날 때 요청 취소 
window.addEventListener('beforeunload', () => controller.abort()); 

fetch('https://example.com/data', { signal }) 
.then(response => response.json()) 
.then(data => console.log(data)) 
.catch(error => { 
    if (error.name === 'AbortError') { 
    	console.log('⛔ 페이지 이동으로 인해 요청이 중단됨'); 
    } 
});

 

 


 

 

Q. fetch()가 AbortController의 취소 기능을 제공하는건가?

A.  fetch() 자체가 abort 기능을 제공하는 것은 아니다.

fetch()는 AbortController가 제공하는 AbortSignal을 사용할 수 있도록 설계되었기에 AbortController의 기능을 활용하는 것이다. 자체적으로 요청을 취소하는 기능을 내장하고 있지 않다.

 

 

fetch()는 AbortController의 기능을 아래와 같이 활용한다.

  1. 기본적으로 Promise는 취소할 수 없다.
  2. 하지만 AbortController는 abort() 메서드를 제공하여 수동으로 AbortSignal을 발생시킬 수 있다.
  3. fetch()는 signal 옵션을 제공하여 AbortSignal을 전달받으면, 요청이 취소될 수 있도록 설계되었다.
  4. 즉, fetch()는 AbortController의 기능을 활용하여 취소 가능하지만, 자체적으로 abort() 메서드를 제공하는 것은 아니다.
// AbortController 인스턴스 생성
const controller = new AbortController();
const signal = controller.signal;

// fetch 요청 실행
fetch('https://jsonplaceholder.typicode.com/todos/1', { signal })
  .then(response => response.json())
  .then(data => console.log('응답 데이터:', data))
  .catch(error => {
    if (error.name === 'AbortError') {
      console.log('⛔ 요청이 중단되었습니다:', error);
    } else {
      console.log('⚠️ 다른 오류 발생:', error);
    }
  });

// 2초 후 요청 강제 취소
setTimeout(() => {
  controller.abort(); // `abort` 이벤트 발생 → fetch 내부에서 감지하여 요청 취소
}, 2000);

 

 

 

 fetch() 내부에서 AbortController.signal을 처리하는 방식 

  1. fetch(url, { signal }) 실행 시, signal을 내부적으로 저장한다.
  2. 네트워크 요청을 시작
  3. controller.abort()가 호출되면 signal이 abort 이벤트를 발생시킨다.
  4. fetch() 내부에서 이 abort 이벤트를 감지하고, 요청을 즉시 중단한다.
  5. Promise가 reject되며, catch()에서 AbortError가 발생한다.

 

 

fetch()는 브라우저 내장 API로 내부 코드가 공개되어있지는 않지만, 유사 동작 코드 예제를 통해 알아볼 수 있다. (챗 지피티 제공)

async function customFetch(url, { signal } = {}) { 
	return new Promise((resolve, reject) => { 
    	// XMLHttpRequest 객체 생성 (fetch 내부 구현을 유사하게 재현) 
        const xhr = new XMLHttpRequest(); 
        
        // 요청 초기화 
        xhr.open('GET', url); 
        
        // 요청 완료 시 
        resolve xhr.onload = () => resolve(xhr.responseText); 
        
        // 요청 실패 시 
        reject xhr.onerror = () => reject(new Error('네트워크 오류 발생')); 
        
        // 🚀 **AbortController가 `abort` 이벤트를 발생시키면, 요청을 취소** 
        if (signal) { 
        	signal.addEventListener('abort', () => { 
            	xhr.abort(); // 요청 취소 
                
                // `fetch`와 동일한 방식으로 reject 
                reject(new DOMException('요청이 중단되었습니다.', 'AbortError')); 
            }); 
        } 
        
        // 요청 전송 
        xhr.send(); 
    }); 
}

 

 


 

 

 

Q. 혹시 fetch로 페이지에서 비동기 요청 중에 페이지를 떠나게되면 실제 우리 눈으로 브라우저를 볼순 없지만, fetch가 계속 되고 있어? 나는 페이지를 떠나게되면 자동으로 요청이 취소될 줄 알았는데?

A. 페이지를 떠나도 fetch 요청은 브라우저에서 계속 진행되며, 자동으로 취소되지 않는다.

즉, 브라우저 백그라운드에서 요청이 유지되고 응답을 받으려고 한다.

 

왜 fetch 요청이 계속 유지될까?

브라우저는 네트워크 요청을 별도의 Web APIs에서 관리하기 때문에 요청이 유지된다.

Web APIs는 자바스크립트에 의해 동작하는 것이 아니기때문에, fetch가 호출된 후 자바스크립트가 실행을 멈추더라도 요청을 유지하게된다.

또한 페이지가 닫히거나 이동하더라도 네트워크 요청은 독립적으로 네트워크 계층에서 처리되는 요청이므로 브라우저가 직접 끊지 않는다.

그렇기 때문에 페이지에서 자바스크립트의 컨텍스트가 사라지더라도 네트워크 요청은 별도로 유지된다. 다만 브라우저가 이를 최적화하여 너무 오래된 요청은 취소할 수 있다. 

따라서 사용자 행동에 의해(페이지를 닫거나 이동) 불필요한 네트워크 요청을 방지하려면 직접 취소 로직을 구현할 수 있다.

반응형
최근에 올라온 글
최근에 달린 댓글
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
Total
Today
Yesterday