/Development/JavaScript

비동기 처리의 핵심, Promise , async / await 정리


현대 JavaScript 개발에서 가장 중요한 개념 중 하나는 비동기(Asynchronous) 처리입니다.
브라우저는 사용자의 클릭 이벤트를 처리하고,
서버와 통신하며,
이미지를 다운로드하고,
애니메이션을 렌더링하는 등 수많은 작업을 동시에 수행해야 합니다.

만약 모든 작업을 순차적으로만 처리한다면
웹 페이지는 매우 느려지고 사용자는 불편함을 느끼게 됩니다.
이러한 문제를 해결하기 위해 JavaScript는 비동기 실행 모델을 채택하였으며,
그 중심에는 Promise 라는 객체가 존재합니다.

Promise는 미래에 완료될 작업의 결과를 나타내는 객체입니다.
즉시 결과를 반환하지 못하는 작업이라도
나중에 성공 또는 실패 결과를 전달할 수 있도록 설계되었습니다.

대표적으로 API 요청,
파일 읽기,
데이터베이스 조회,
사용자 위치 정보 조회 등의 기능에서 Promise가 사용됩니다.

Promise를 이해하면 단순히 문법을 익히는 수준을 넘어
JavaScript 런타임의 동작 원리를 더욱 깊이 이해할 수 있습니다.

Callback 방식이 가진 구조적 문제점

Promise가 등장하기 이전에는 Callback 함수가 비동기 처리를 담당했습니다.
작업이 끝난 후 실행할 함수를 인자로 전달하는 방식입니다.

초기에는 간단하고 직관적으로 보였지만
비동기 작업이 많아질수록 코드 구조가 급격히 복잡해지는 문제가 발생했습니다.

예를 들어 로그인 요청 이후 사용자 정보를 조회하고,
그 다음 게시물 목록을 불러오고,
마지막으로 댓글 데이터를 요청하는 상황을 가정해볼 수 있습니다.

각 작업이 이전 작업의 결과를 필요로 한다면
콜백 함수 내부에 또 다른 콜백을 작성해야 합니다.

이러한 형태는 흔히 Callback Hell이라고 불립니다.
코드 들여쓰기가 계속 증가하고
에러 처리가 분산되며
유지보수가 어려워지는 문제가 발생합니다.

대규모 프로젝트에서는 이러한 구조가 심각한 생산성 저하를 유발할 수 있습니다.

Promise의 세 가지 상태

Promise 객체는 항상 특정 상태(State)를 가집니다.
이 상태를 이해하는 것은 Promise 동작 원리를 파악하는 데 매우 중요합니다.

첫 번째 상태는 Pending 입니다.
작업이 아직 완료되지 않은 대기 상태를 의미합니다.

두 번째 상태는 Fulfilled 입니다.
비동기 작업이 성공적으로 완료되어 결과값을 반환한 상태입니다.

세 번째 상태는 Rejected 입니다.
작업 수행 중 오류가 발생하여 실패한 상태를 의미합니다.

중요한 점은 Promise 상태가 한번 결정되면 변경되지 않는다는 것입니다.
Fulfilled가 된 Promise는 다시 Pending 상태로 돌아갈 수 없으며,
Rejected 상태 또한 마찬가지입니다.

이러한 불변성 덕분에 비동기 흐름을 예측 가능하게 관리할 수 있습니다.

then과 catch를 활용한 체이닝 기법

Promise의 가장 큰 장점은 체이닝(Chaining)이 가능하다는 점입니다.

then() 메서드는 작업 성공 시 실행할 로직을 정의하며,
새로운 Promise를 반환하기 때문에 연속적으로 연결할 수 있습니다.

이를 통해 여러 비동기 작업을 순차적으로 실행하면서도
코드 가독성을 유지할 수 있습니다.

또한 catch() 메서드를 사용하면
체인 과정에서 발생하는 오류를 한 곳에서 처리할 수 있습니다.

기존 Callback 방식에서는 각 단계마다 개별적인 오류 처리가 필요했지만
Promise는 중앙 집중형 에러 핸들링이 가능합니다.

이러한 구조는 유지보수성을 크게 향상시키며
실제 서비스 개발 환경에서 매우 큰 장점으로 작용합니다.

특히 서버 API 호출이 빈번한 SPA 환경에서는
Promise 체이닝이 거의 필수적으로 사용됩니다.

Async/Await가 등장한 이유

Promise는 Callback Hell 문제를 상당 부분 해결했지만
복잡한 비즈니스 로직에서는 여전히 체인이 길어질 수 있었습니다.

이를 개선하기 위해 ECMAScript 2017에서
asyncawait 문법이 추가되었습니다.

이 문법은 Promise 기반으로 동작하지만
마치 동기 코드처럼 작성할 수 있도록 도와줍니다.

덕분에 코드 흐름을 자연스럽게 읽을 수 있으며
디버깅 또한 훨씬 쉬워졌습니다.

현재 대부분의 React, Vue, Next.js 프로젝트에서도
비동기 처리를 위해 Async/Await 패턴을 기본적으로 사용하고 있습니다.

하지만 내부적으로는 결국 Promise가 동작하기 때문에
근본 원리를 이해하기 위해서는 Promise 학습이 반드시 필요합니다.

실무에서 Promise를 알아야 하는 이유

실제 웹 개발에서는 거의 모든 외부 데이터가 비동기적으로 처리됩니다.
회원 로그인,
상품 조회,
주문 처리,
결제 요청,
실시간 채팅 기능까지 대부분 Promise 기반으로 구현됩니다.

또한 브라우저 기본 API인 Fetch API 역시 Promise를 반환합니다.

현대 프레임워크와 라이브러리들도 Promise를 중심으로 설계되어 있기 때문에
이를 제대로 이해하지 못하면 프로젝트 구조를 파악하기 어려워집니다.

성능 최적화 측면에서도 Promise는 중요합니다.
여러 비동기 작업을 병렬로 처리하는 Promise.all() 같은 기능을 활용하면
전체 응답 시간을 단축할 수 있습니다.

결국 Promise는 단순한 문법 요소가 아니라
현대 JavaScript 생태계를 구성하는 핵심 기반 기술이라고 볼 수 있습니다.

Summary

Promise

• JavaScript 비동기 처리의 핵심 객체.
• Pending, Fulfilled, Rejected 상태를 가짐.
• Callback Hell 문제를 해결하기 위해 등장함.
• then과 catch를 통해 체이닝 및 에러 처리가 가능함.

Async/Await

• Promise를 더욱 직관적으로 사용할 수 있게 만든 문법.
• 동기 코드처럼 읽히기 때문에 가독성이 뛰어남.
• React, Vue, Next.js 등 현대 프론트엔드 환경에서 널리 사용됨.
• 내부적으로는 Promise 기반으로 동작함.


블로그 목록