API 요청을 취소하는 방법: AbortController로 백엔드 API, LLM 요청 중단하기

soleil_lucy·2026년 3월 15일

문제 상황

개인 프로젝트에서 유튜브 영상 URL을 입력하면 영상 속 레시피를 분석해 재료와 조리 과정을 구조화된 데이터로 추출하는 기능을 구현했습니다.

사용자가 유튜브 링크를 입력하면 해당 영상을 분석하고 레시피 정보를 추출하기 위해 LLM API를 호출하는 구조였습니다.

하지만 이 과정에서 응답을 받기까지 약 20~30초 정도의 시간이 소요되기도 했습니다.

테스트를 진행하다 보니 다음과 같은 상황이 종종 발생했습니다.

  • 분석 시간이 예상보다 길어져 중간에 요청을 중단하고 싶을 때
  • 유튜브 링크를 잘못 입력해 요청을 취소하고 다시 시도하고 싶을 때
  • 새로운 링크로 다시 분석을 요청하고 싶을 때

하지만 한 번 보낸 API 요청은 기본적으로 클라이언트에서 쉽게 취소할 수 없었습니다.

이 경우 더 이상 필요하지 않은 요청도 계속 서버에서 처리될 수 있었습니다. 특히 LLM 기반 분석 작업은 응답 시간이 길어질 수 있기 때문에, 사용자가 요청을 중단할 수 있는 UX가 중요하다고 느꼈습니다.

그래서 진행 중인 API 요청을 사용자가 직접 중단할 수 있는 방법을 찾게 되었습니다.

해결 방법: AbortController

문제를 해결하기 위해 LLM API 요청을 취소할 수 있는 방법을 찾아보았습니다.

조사해보니 JavaScript에서는 AbortController라는 Web API를 사용해 진행 중인 비동기 작업을 중단할 수 있다는 것을 알게 되었습니다.

AbortControllerfetch와 같은 네트워크 요청에 취소 신호를 전달하여 요청을 중단할 수 있도록 해주는 인터페이스입니다.

이를 활용하면 다음과 같은 상황에서 API 요청을 취소할 수 있습니다.

  • 사용자가 페이지를 이탈했을 때
  • 새로운 요청이 발생했을 때 이전 요청 취소
  • 사용자가 직접 “취소” 버튼을 눌렀을 때

이 기능을 활용하면 불필요한 네트워크 요청을 줄이고 사용자 경험을 개선할 수 있습니다.

AbortController란?

AbortController는 진행 중인 비동기 작업을 중단할 수 있도록 해주는 Web API입니다. 대표적으로 fetch와 같은 HTTP 요청을 취소할 때 많이 사용됩니다.

일반적으로 브라우저에서 API 요청을 보내면, 요청이 시작된 이후에는 클라이언트에서 이를 직접 취소하기 어렵습니다. 하지만 AbortController를 사용하면 진행 중인 요청을 명시적으로 중단(abort)할 수 있습니다.

AbortController는 크게 두 가지 요소로 구성됩니다.

  • AbortController: 요청 취소를 제어하는 컨트롤러
  • AbortSignal: 취소 신호를 전달하는 객체

먼저 AbortController 인스턴스를 생성한 뒤, 해당 객체의 signal을 API 요청에 전달합니다. 이후 필요할 때 abort() 메서드를 호출하면 요청이 중단됩니다.

const controller = new AbortController();

fetch("/api/data", {
  signal: controller.signal,
});

// 요청 취소
controller.abort();

abort()가 호출되면 해당 요청은 중단되고, fetch의 Promise는 AbortError와 함께 reject 됩니다.

이러한 방식으로 AbortController는 네트워크 요청, 스트림 처리, 응답 데이터 소비 등의 비동기 작업을 중간에 취소할 수 있는 메커니즘을 제공합니다.

스트림 처리와 응답 데이터 소비란 무엇일까?

AbortController는 단순히 HTTP 요청 자체만 취소하는 것이 아니라, 요청 이후에 이어지는 데이터 처리 과정도 함께 중단할 수 있습니다.

예를 들어 fetch 요청이 완료된 뒤에도 다음과 같은 작업이 이어질 수 있습니다.

1️⃣ 스트림 처리

일부 API는 데이터를 한 번에 보내지 않고 조각(chunk) 단위로 나누어 전송합니다. 대표적인 예가 LLM 응답 스트리밍입니다.

예를 들어 AI 응답이 다음과 같이 순차적으로 도착할 수 있습니다.

오늘은
바스크 치즈케이크
레시피를
알려드리겠습니다.

이런 경우 클라이언트는 데이터를 스트림으로 계속 읽어들이게 됩니다.

만약 사용자가 중간에 “응답 중단” 버튼을 누르면 AbortController를 통해 진행 중인 스트림 읽기를 중단할 수 있습니다.

2️⃣ 응답 데이터 소비

fetch로 받은 응답은 보통 다음과 같은 메서드를 통해 데이터로 변환하는 과정이 필요합니다.

const response = await fetch("/api/v1/recipe/1");
const data = await response.json();

여기서 response.json()은 응답 데이터를 파싱하는 비동기 작업입니다. 만약 응답 데이터가 매우 크거나 처리 시간이 길다면, 이 과정 역시 AbortController로 중단할 수 있습니다.

언제 사용하는가?

AbortController는 사용자 인터랙션이 빠르게 변하는 상황에서 특히 유용합니다. 대표적으로 다음과 같은 경우에 사용됩니다.

1. 이전 API 요청을 취소해야 할 때

검색창 자동완성이나 필터 기능처럼 사용자가 빠르게 입력을 변경하는 경우, 이전 요청의 결과는 더 이상 필요하지 않을 수 있습니다.

예를 들어 사용자가 "cheese"를 검색하는 과정에서 다음과 같은 요청이 연속적으로 발생할 수 있습니다.

입력: c
GET /api/v1/search?query=c

입력: ch
GET /api/v1/search?query=ch

입력: che
GET /api/v1/search?query=che

입력: chee
GET /api/v1/search?query=chee

입력: chees
GET /api/v1/search?query=chees

입력: cheese
GET /api/v1/search?query=cheese

이때 이전 요청이 취소되지 않으면 불필요한 요청이 계속 서버로 전송됩니다. 또한 응답 순서가 뒤바뀌면서 오래 걸린 요청의 결과가 UI를 덮어쓰는 문제도 발생할 수 있습니다.

이런 경우 새 요청을 보내기 전에 이전 요청을 abort하여 문제를 방지할 수 있습니다.

2. 사용자가 요청을 직접 취소할 수 있도록 할 때

API 응답 시간이 긴 작업에서는 사용자가 작업을 중단할 수 있는 UX가 필요합니다.

응답이 오래 걸리는 작업의 경우 사용자는 결과를 기다리다가 중간에 작업을 취소하고 싶을 수 있습니다. 예를 들어 요청 시간이 예상보다 길어지거나, 더 이상 해당 작업이 필요하지 않다고 판단하는 상황이 있을 수 있습니다.

이때 사용자가 요청을 중단할 수 있는 방법이 없다면 불필요한 요청이 계속 서버에서 처리될 수 있습니다. 따라서 사용자에게 요청을 취소할 수 있는 인터페이스를 제공하는 것이 중요합니다.

특히 AI / LLM 응답은 생성 과정이 길어질 수 있기 때문에 사용자가 응답을 기다리다가 중간에 생성을 중단하고 싶어하는 상황이 자주 발생합니다.

예를 들어 다음과 같은 상황입니다.

  • 파일 업로드
  • 대용량 데이터 분석
  • AI/LLM 응답 생성

이러한 기능에서는 응답 시간이 길어질 수 있기 때문에, 사용자가 “취소” 버튼을 통해 진행 중인 작업을 중단할 수 있도록 하는 UX가 자주 사용됩니다.

3. 페이지 이동이나 컴포넌트 언마운트 시

사용자가 페이지를 떠났는데도 이전 요청이 계속 실행되는 경우가 있습니다. 이 경우 다음과 같은 문제가 발생할 수 있습니다.

  • 불필요한 네트워크 요청
  • 메모리 사용 증가
  • 이미 사라진 화면을 업데이트 하려는 오류

추가 설명: 메모리 사용 증가

API 요청이 완료되지 않은 상태에서 페이지를 떠나더라도, 해당 요청과 관련된 Promise, 콜백, 응답 데이터 처리 로직은 메모리에 남아 계속 실행될 수 있습니다.

예를 들어 사용자가 어떤 페이지에서 데이터를 요청한 뒤 곧바로 다른 페이지로 이동했다고 가정해 보겠습니다.

useEffect(() => {
  fetch("/api/v1/recipe/1")
    .then(res => res.json())
    .then(data => {
      setData(data);
    });
}, []);

이때 네트워크 요청은 여전히 진행 중입니다.

만약 이런 요청이 반복적으로 발생하면 불필요한 비동기 작업이 계속 쌓이게 되고, 브라우저 메모리 사용량이 증가할 수 있습니다.

특히 다음과 같은 상황에서 문제가 더 커질 수 있습니다.

  • 사용자가 페이지를 빠르게 이동하는 경우
  • 검색 입력처럼 요청이 자주 발생하는 경우
  • 대용량 데이터를 처리하는 API인 경우

따라서 더 이상 필요하지 않은 요청은 AbortController로 중단하여 불필요한 리소스 사용을 줄이는 것이 좋습니다.

추가 설명: 이미 사라진 화면을 업데이트 하려는 오류

페이지를 떠났거나 컴포넌트가 사라진 뒤에도 API 요청이 완료되면, 더 이상 존재하지 않는 화면을 업데이트하려는 코드가 실행될 수 있습니다.

예를 들어 다음과 같은 코드가 있다고 가정해 보겠습니다.

useEffect(() => {
  fetch("/api/v1/recipe/1")
    .then(res => res.json())
    .then(data => {
      setData(data);
    });
}, []);

만약 사용자가 API 응답이 오기 전에 다른 페이지로 이동하면, 해당 컴포넌트는 이미 언마운트된 상태가 됩니다.

그런데 요청이 늦게 완료되어 setData(data); 코드가 실행됩니다.

이때 React에서는 이미 사라진 컴포넌트의 상태를 업데이트 하려는 문제가 발생합니다.

정리

요청을 취소하지 않으면 다음과 같은 문제가 발생할 수 있습니다.

  • 더 이상 필요 없는 비동기 작업이 계속 실행되어 리소스를 낭비할 수 있음
  • 이미 사라진 컴포넌트를 업데이트하려는 코드가 실행되어 경고나 버그가 발생할 수 있음

따라서 페이지 이동이나 컴포넌트 언마운트 시 진행 중인 요청을 정리하는 것이 중요합니다.

컴포넌트 언마운트?

사용자가 페이지를 이동하면 해당 화면을 구성하던 요소들은 화면에서 제거됩니다.
React에서는 이러한 상태를 ‘컴포넌트가 언마운트 되었다’고 표현합니다.

이러한 문제를 방지하기 위해 진행 중인 비동기 요청을 중단할 수 있는 방법이 필요합니다. JavaScript에서는 AbortController를 사용해 이러한 요청을 취소할 수 있습니다.

4. LLM/스트리밍 응답을 중단할 때

최근에는 AI API 호출을 중단하기 위해서도 AbortController가 많이 사용됩니다.

LLM 응답은 다음과 같은 특징이 있습니다.

  • 응답 시간이 길다
  • 스트리밍 방식으로 데이터를 받는다
  • 사용자가 중간에 취소하고 싶을 수 있다

이때 AbortController를 사용하면 진행 중인 AI 응답을 즉시 중단할 수 있어 사용자 경험을 개선할 수 있습니다.

실제로 사용해보기

앞에서 살펴본 것처럼 AbortController를 사용하면 진행 중인 API 요청을 중단할 수 있습니다.

이번에는 실제로 AbortController를 사용해 LLM API 요청을 취소하는 방법을 살펴보겠습니다.

1. AbortController 생성하기

AbortController 인스턴스를 생성합니다.

const controller = new AbortController();

AbortController 객체는 요청을 취소하는 역할을 하며, 여기서 생성된 signal을 API 요청에 전달해 취소 신호를 보낼 수 있습니다.

2. API 요청에 signal 전달하기

fetch 요청을 보낼 때 signal을 함께 전달합니다.

const controller = new AbortController();

fetch("/api/v1/recipes/extract", {
  method: "POST",
  body: JSON.stringify({ url: youtubeUrl }),
  signal: controller.signal,
});

3. 요청 중단하기

요청을 중단하려면 AbortControllerabort() 메서드를 호출하면 됩니다.

controller.abort();

이 메서드가 호출되면 진행 중인 fetch 요청이 즉시 취소됩니다.

4. 요청 취소 에러 처리하기

요청이 취소되면 fetchAbortError를 발생시킵니다. 따라서 이를 구분해 처리하는 것이 좋습니다.

try {
  const response = await fetch("/api/v1/recipes/extract", {
    method: "POST",
    body: JSON.stringify({ url: youtubeUrl }),
    signal: controller.signal,
  });

  const data = await response.json();
} catch (error) {
  if (error.name === "AbortError") {
    console.log("요청이 취소되었습니다.");
  } else {
    console.error(error);
  }
}

이렇게 AbortController를 사용하면 사용자가 원할 때 진행 중인 API 요청을 중단할 수 있습니다.

프론트엔드에서 AbortController로 요청을 취소하면 서버에서는 요청의 abort signal을 통해 클라이언트 연결이 종료된 것을 감지할 수 있으며, 이를 활용해 진행 중인 작업을 중단할 수 있습니다.

이를 통해 사용자가 요청을 취소했을 때 네트워크 요청뿐 아니라 서버에서 수행 중이던 크롤링이나 LLM 분석 작업도 함께 중단되어 불필요한 리소스 사용을 줄일 수 있습니다.

알게된 점

이번 글을 통해 AbortController를 사용하면 진행 중인 백엔드 API나 LLM API 요청을 클라이언트에서 중단할 수 있다는 것을 알게 되었습니다.

특히 사용자와의 인터랙션이 빈번하게 발생하는 브라우저 환경에서는, 더 이상 필요하지 않은 요청을 취소함으로써 불필요한 네트워크 요청을 줄일 수 있습니다.

LLM과 같이 응답 시간이 긴 API를 사용하는 경우, 사용자가 요청을 중간에 취소할 수 있는 기능을 제공하는 것이 사용자 경험을 개선하는 데 도움이 된다는 점도 확인할 수 있었습니다.

참고 자료

AbortController | MDN Docs

profile
여행과 책을 좋아하는 개발자입니다.

0개의 댓글