thumbnail
AbortController로 비동기 작업 제어하기(feat. Javascript 작동 원리)
Oct 12, 2024

React 애플리케이션을 개발하다 보면, 서버와 통신하거나 대용량 데이터를 처리하는 등 다양한 상황에서 비동기 작업을 다루게 된다. 비동기 작업에서 메모리 누수나 성능 저하와 같은 문제를 방지하기 위해서는 적절한 관리가 필요한데, 이때 사용할 수 있는 도구인 AbortController에 대해 알아보고 React에서 어떻게 활용할 수 있는지 살펴보자.


먼저 비동기 작업이 JavaScript에서 어떤 방식으로 이루어지는지 가볍게 짚고 넘어가 보려 한다.

JavaScript 작동 원리

JavaScript가 대표적인 싱글 스레드(Single Thread) 언어라는 것은 많이 들어봤을 것이다. 싱글 스레드는 프로그램이나 프로세스가 한 번에 하나의 작업만을 수행할 수 있다는 것을 의미한다. 바꿔 말하면 동시에 여러 작업을 처리하지 못하고 모든 작업이 순차적으로 처리된다는 것인데, 이러한 실행 흐름을 동기(Synchronous)라고 한다.

JS 작동 원리

위 그림은 브라우저에서 JavaScript가 작동하는 방식을 도식화한 것이다. 자바스크립트 엔진 내부에는 Heap과 Call Stack이 존재하고 브라우저는 Web APIs, Callback Queue, Event Loop 기능을 제공한다.


각각의 기능에 대해 간단하게 설명하자면,

자바스크립트 엔진 (JavaScript Engine)

  • 사람이 작성한 JS 코드를 컴퓨터가 이해할 수 있는 기계어로 변환시켜 실행하는 프로그램

Memory Heap

  • 함수나 변수를 정의할 때 해당 데이터가 저장되는 공간
  • 메모리 할당, 해체 작업이 이루어지는 곳

Call Stack

  • 자바스크립트 엔진이 실행 중인 함수 호출 기능을 추적할 수 있도록 하는 자료구조
  • 싱글 스레드 = 하나의 call stack만 있음 = 한 번에 하나의 작업만 가능

WebAPIs

  • 브라우저나 Node.js 환경에서 제공하는 비동기 기능

Callback queue

  • 비동기 작업이 완료된 후, 호출할 콜백 함수들이 대기하는 공간

Event Loop

  • call stack이 비어 있을 때, callback queue에 대기 중인 콜백 함수를 call stack에 추가하는 메커니즘

동기 작업은 기본적으로 자바스크립트 엔진 내 call stack에서 이루어진다. 함수가 호출되면 stack의 맨 위에 추가되고 완료되면 제거되는 LIFO(Last-In-First-Out) 방식을 따른다.

동기

비동기 작업

JavaScript에서 변수를 선언하거나 함수를 호출하는 등의 대부분의 일반적인 작업은 동기적으로 처리된다. 순차적으로 실행되는 로직은 직관적이고 이해하기 쉽지만, 막상 실제 애플리케이션은 단순하지만은 않다. 네트워크 요청이나 대용량 파일 처리 등 시간이 오래 걸리는 작업들이 동기적으로 처리된다면 어떨까?


예를 들어, API 요청을 동기적으로 처리한다고 가정해 보자. JavaScript 인터프리터는 해당 요청을 call stack에 추가하고 서버로부터 응답을 받을 때까지 다른 작업을 진행할 수 없다.

요청이 처리되는 동안 버튼 클릭이나 텍스트 입력 등 모든 사용자 인터랙션이 중단되어 사용자는 웹 페이지가 멈춘 것처럼 느껴질 것이다. 이는 UX를 크게 저하하는 요인이 된다. 여러 API를 동시에 요청해야 하는 상황일 때는 전체 처리 시간이 크게 증가하여 성능 저하와 병목 현상을 불러오게 될 수도 있다.


이러한 문제들을 해결하기 위해 웹 브라우저(혹은 Node.js)와 같은 런타임 환경은 비동기 API를 지원한다. 비동기 작업 처리는 백그라운드에서 실행되기 때문에 call stack을 차단하지 않고 동시에 다른 작업을 진행할 수 있다.

비동기

AbortController

네트워크 요청과 같은 작업은 요청이 필요할 때만 전송되고 처리되는 것이 중요하다. 만약 사용자가 데이터를 요청한 후, 요청이 완료되기 전에 페이지를 이동하거나 앱을 닫는다면 해당 작업은 어떻게 될까?


fetch API와 같은 비동기 작업은 이미 브라우저의 Web API에서 처리되고 있기 때문에 JavaScript 엔진 내 call stack과는 무관하게 계속해서 진행된다. 즉, 네트워크 요청은 브라우저가 관리하는 별도의 작업이므로 페이지를 이동하더라도 계속 작업이 유지될 수 있다는 것이다.

요청이 필요하지 않음에도 계속해서 불필요한 리소스를 낭비하고 페이지 이동 후에 콜백 함수가 실행되어 예상치 못한 동작을 발생시키는 문제를 방지하려면 적절한 타이밍에 중단점을 명시해 주어야 한다.


AbortController API를 사용하면 fetch 요청과 같은 비동기 작업에서 개발자가 필요할 때만 요청이 처리되도록 할 수 있다.

React에서의 활용

AbortController는 signal 속성과 abort() 메서드로 비동기 작업을 제어한다.

signal: 비동기 작업에 전달되어 해당 작업의 상태를 추적

abort(): 비동기 작업을 즉시 중단시키는 메서드


먼저 AbortController 객체와 signal을 생성한다.

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

AbortSignal은 다음과 같은 속성을 가진다.

  • aborted 현재 신호가 중단되었는지를 나타내는 boolean 값. 초깃값은 false이며, 중단 후에는 true로 변경된다.
  • onabort: 신호가 중단될 때 호출할 핸들러를 설정할 수 있다. signal.onabort = () => { console.log('요청이 중단되었습니다.'); };
  • reason: abort() 메서드를 호출할 때 인자로 전달된 값(중단 이유) 반환

fetch 요청에 signal을 전달하여 해당 요청을 취소 가능한 상태로 만든다.

const response = await fetch('https://jsonplaceholder.typicode.com/posts', { signal, });

필요한 시점에 controller.abort()를 호출하면 요청을 취소할 수 있다.

controller.abort();

React에서는 useEffect hook과 함께 사용하면 컴포넌트가 언마운트될 때 자동으로 비동기 작업을 취소할 수 있다.

import { useEffect } from 'react'; function MyComponent() { useEffect(() => { const controller = new AbortController(); const signal = controller.signal; const fetchData = async () => { try { const response = await fetch( 'https://jsonplaceholder.typicode.com/posts', { signal }, ); const data = await response.json(); } catch (err) { if (err.name === 'AbortError') { console.log('요청이 중단되었습니다'); } } }; fetchData(); return () => { controller.abort(); // 컴포넌트 언마운트 시 요청 중단 }; }, []); return <div>Loading...</div>; }

AbortController의 장점

AbortController를 사용하면 아래와 같은 장점이 있다.


표준화된 API

  • W3C에서 정의한 웹 표준의 일부로 Fetch API와 직접적으로 연관되어 설계되었기 때문에 다양한 브라우저 간의 호환성을 보장한다.

메모리 관리

  • 불필요한 네트워크 요청이나 장시간 걸리는 작업을 중단하여 리소스를 절약할 수 있다.
  • 특히 React와 같은 컴포넌트 기반 프레임워크에서 컴포넌트 언마운트 시 진행 중인 작업을 정리하는 데 유용하다.

UX 개선

  • 사용자가 작업을 중단하거나 페이지를 떠날 때 즉시 반응할 수 있어 더 나은 UX를 제공할 수 있다.

코드 가독성 향상

  • 코드가 간결하고 요청이 중단된 이유를 명확하게 확인할 수 있어 코드의 가독성이 향상되고 유지 보수가 쉽다.

References

Table Of Contents
nxnaxx blog © 2022-2024 Powered By Gatsby.