eatthefrog
자바스크립트) 비동기함수 with 별코딩 본문
목차
1. 동기 프로그래밍 VS 비동기 프로그래밍
2. 비공기 콜백
3. Promise 완벽 정리
4. Promise.all + allSettled + any + race
5. Aync & Await
1. 동기 프로그래밍 VS 비동기 프로그래밍
동기 VS 비동기
- 동기적 수행 : 순서대로 한번에 하나씩 (with 자바스크립트 싱글 스레드)
- 비동기적 수행: 동시에 실행 (with Web APIs <- 브라우저:멀티 스레드)
Blocking
동기 프로그래밍에서 오래 걸리는 작업이 수행될때 뒤에 작업들이 밀리는 것
비동기 프로그래밍(non-blocking)
- 동기 프로그래밍에서 발생하는 Blocking 문제를 해결할 수 있는 대안이다. 현재 실행 중인 테스크가 완료되지 않더라도, 다음 작업을 바로 실행할 수 있어 블로킹이 발생하지 않는(non-blocking)장점이 있다.
- 그러나 작업이 언제 끝날지 예측할 수 없어서 작업의 실행 순서를 보장하기 어렵다는 단점도 존재한다. 그래서 이전 작업이 끝난 뒤에 실행되도록 보장하기 위해서, 콜백 안에 콜백을 넣는 방식을 사용하닥 콜백지옥을 만나게 된다.
2. 비공기 콜백
콜백 함수


- 함수는 매개변수로 다양한 데이터를 받아올 수 있다. 이때 함수도 받을 수 있는데 이 함수를 콜백함수로 말한다. 즉, 콜백 함수는 다른 함수에 인자로 전달되는 함수다.
- 콜백함수의 실행 조건은, 콜백함수를 매개변수로 받은 메인 함수에 구현사항에 따라서 결정된다. 그래서 이 콜백함수를 필요에 따라 즉시 실행할 수도 있고, 나중에 실행할 수도 있다.
- 일반적으로도 아주 유용하지만, 비동기를 처리하는데도 많이 사용된다.
비동기 콜백(비동기 작업 완료 후 ,, 후처리)
비동기 콜백 예시

콜백 지옥 ( 콜백 기반 비동기 처리 방식)

순차적인 비동기 흐름 보장하기
- login -> 먼저 로그인 후
- addToCart -> 로그인한 사용자만 장바구니에 담을 수 있으므로, login 실행문 안쪽으로 들어감
- makePayment -> 장바구니에 담은 다음에야 결제를 진행할 수 있으므로, addToCart 실행문 안쪽으로 들어감
실행문 바깥에 쓰면?
: 비동기 함수는 setTimeout으로 나중에 실행도기 때문에, 아래처럼 작성하면 순서 보장이 안된다.

addToCart와 makePayment가 이전 함수 실행문 안으로 들어가 이유는?
: 이전 작업이 끝난 뒤에 실행되도록 보장하기 위해서, 콜백 안에 콜백을 넣는 방식 (콜백 지옥)을 사용한 것이다.
3. Promise 완벽 정리
Promise: 비동기 처리에 사용되는 자바스크립트 객체로 비동기 작업이 맞이할 성공 혹은 실패를 나타낸다.


콜백 지옥 형태의 코드를 Promise 버전으로 리팩토링하기
Promise 기반 함수로 변경하고, then() 체이닝으로 비동기 흐름 구현하기
- 들여쓰기 깔끔해짐
- 가독성 높아짐
- 에러 처리를 .catch() 하나로 통합 가능


콜백 지옥 형태의 코드를 async/await 버전으로 리팩토링하기
- 중첩 줄어듦(가독성 향상)
- 흐름이 순차적으로 읽힘
- 에러 헨들링이 명확함 (try/catch)


new Promie 생성하기
- const promise = new Promise((resolve,reject)=>{})
- (resolve, reject)는 Promise 생성자 함수에 전달되는 콜백 함수의 매개변수이며, const promise는 이 콜백 함수의 결과로 생성된 Promise 객체이다.
✔️ (resolve, reject):
→ resolve: 작업이 성공했을 때 호출 (값을 전달할 수 있음)
→ reject: 작업이 실패했을 때 호출 (에러를 전달할 수 있음)
✔️const promise:
→ Promise 생성자에서 만들어진 "결과를 담는 객체"
→ 나중에 .then(), .catch()로 처리 가능


.then(), .catch(), .finally()


프로미스 체이닝 (콜백 지옥)
- 콜백 지옥은 단순한 비동기 흐름이지만 코드가 복잡해지고 관리가 어렵다.
- Promise 체이닝은 가독성과 유지보수성 모두 뛰어나며, async/await로 확장도 쉬워 현재는 표준처럼 사용된다.


콜백 지옥
- 각 작업이 이전 작업의 콜백안에 중첩
- 예: login → addToCart → makePayment → reviewProduct → getCoupon
Promise
- 각 작업을 .then()으로 순차적으로 연결
- 에러는 .catch()로 한번에 처리 가능
Promise와 fetch API를 활용한 비동기 요청


fetch에서의 파싱
파싱: 문자열 형태의 데이터를 컴퓨터가 이해할 수 있는 구조로 분석하고 변환하는 과정이다.
fetch(url)
.then((response) => response.json()) // 파싱하는 부분!
- response.json()은 응답 내용을 JSON 형태로 파싱해서 자바스크립트 객체로 바꿔주는 함수다.
- 이걸 해야 .then(data)에서 data.name, data.age 이런 식으로 접근할 수 있다.
4. Promise.all + allSettled + any + race
Promise.all() : 순차적 X(getName()끝나고, getTodo()를 실행), 병렬적으로 시행하며 const pormise에 배열 형태로 결과 값을 전달한다.Promise.all() 함수는 주어진 promise를 실행하다가 reject 되는 것이 하나라도 있으면 즉시 실행을 멈춥니다.

Promise.allSettled()
- Promise.all() 함수는 주어진 promise를 실행하다가 reject 되는 것이 하나라도 있으면 즉시 실행을 멈춘다. 이때 실행이 빨린것의 결과를추출하기 때문에 어떤 비동기 작업에서 에러가 발생했는지 쉽게 알기 어렵다. 이때 사용할 수 있는것이 Promise.allSettled()이다.
- Promise.allSettled() 메서드는 주어진 모든 프로미스를 이행하거나 거부한 후, 각 프로미스에 대한 결과를 나타내는 객체 배열을 반환한다. 일반적으로 서로의 성공 여부에 관련 없는 여러 비동기 작업을 수행해야 하거나, 항상 각 프로미스의 실행 결과를 알고 싶을 때 사용하다.
- 그에 비해, Promise.all()이 반환한 프로미스는 서로 연관된 작업을 수행하거나, 하나라도 거부 당했을 때 즉시 거부하고 싶을 때 적합하다.
Promise.any()
가장 먼저 resolve가 된 promise의 값을 반환한다(한개). 특이한 점은 입력의 모든 프로미스가 reject되면 최종적으로 이 메서드도 거부 되며, 거부된 사유가 배열이 포함된 AggregateError('All prmise were rejected) 가 반환된다.
Promise.race()
전달해준 Promise들끼리 경주를 시켜서, resolve와 reject관계없이, 가장 빨리 완료된 Promise의 결과값을 반환한다.
5. Async & Await
async function : 함수 이름 앞에 async 키워드를 붙이면 항상 promise를 반환합니다. 만약 async 함수의 반환값이 명시적으로 promise가 아니라면 암묵적으로 promise로 감싸진다.
- name: 함수의 이름
- param: 함수에게 전달되기 위한 인자의 이름
- statements: 함수 본문을 구성하는 내용. await 메커니즘이 사용될 수 있다.
async function name([param[, param[, ... param]]]) {
statements
}
await: 후처리!
: 여러 promise의 동작을 동기스럽게(순차적으로) 사용할 수 있게 해준다. Promise chaining이 구조화된 callback과 유사한 것처럼 aync/await 또한 제네레이터(generator)와 프로미스(promise)를 묶는것과 유사하다.
function networkRequest() {
return new Promise((resolve) => {
setTimeout(() => {
console.log('데이터를 받아왔습니다.');
resolve('서버1');
}, 2000);
});
}
// Case1: await X
// 결과: 유저, 데이터를 받아왔습니다.
async function getUser() {
networkRequest(); // 비동기 요청 시작, 기다리지 않음
return '유저'; // 바로 반환됨
}
// Case2: await O
// 결과: 데이터를 받아왔습니다, 유저
async function getUser() {
await networkRequest(); // 2초 동안 기다림
return '유저';
}
const user = getUser();
user.then((name) => console.log(name));
Note: await 키워드는 async 함수에서만 유효하다는 것을 기억하십시오. async 함수의 본문 외부에서 사용하면 SyntaxError가 발생합니다.
async/await VS Promise chaining
: aync&await을 사용한 버전이 훨씬 가독성이 좋다.
// function networkRequest() {..} 생략
// async & await
async function getUser() {
await networkRequest();
await networkRequest();
return '유저';
}
// promise chaining
function getUser() {
return networkRequest()
.then(()=>{
return networkRequest();
})
.then(()=>{
return "유저"
})
}
const user = getUser();
user.then((name) => console.log(name));
async/await 활용하기: 비동기 작업을 처리하는 코드를, 순차적으로 보기 편하게 작성할 수 있다.
// function networkRequest() {..} 생략
async function getUser(){
await networkRequest();
return '유저';
}
async function getTodo() {
await newtorkRequest();
return ['공부하기','운동하기'];
}
async funtion getData() {
const user = await getUser(); //2초 기다림
const todo = auait getTodo(); //2초 기다림
console.log(`${user}님 ${todo}를 하세요`); //총 4초 기다리고: '유저님 운동하고, 법먹기를 사에ㅛ
}
getData()
aync&await 에러 처리하기: try, catch
// function networkRequest() {..} 생략
// getUser에서 에러가 발생하도록 처리
async function getUser(){
throw new Error('에러가 발생했어요!'); //✅에러가 발생하도록 처리함
await networkRequest();
return '유저';
}
async function getTodo() {
await newtorkRequest();
return ['공부하기','운동하기'];
}
async funtion getData() {
let user; // 상태 변할 수 있으니까 const 말고 let에 담기
try
user = await getUser();
} catch (error) {
console.log(error.message);
}
const todo = await getTodo(); //2초 기다림
console.log(`${user}님 ${todo}를 하세요`); // 에러가 발생했어요!, (2초 후) undefined님 공부하고, 운동하기를 하세요.
}
getData()
fetchAPI를 비동기적으로(async/await) 받아오기
fetch API
- fetch()는 브라우저 내장 Web API이며, 서버에 네트워크 요청을 보낼 수 있게 해준다.
- 호출하면 Promise 객체를 반환하므로, 비동기 처리에 사용된다.
- 주로 서버에서 데이터를 받아올 때 사용된다. (예: REST API 호출).
- 📦 fetch()는 기본적으로 GET 요청이며, POST, PUT, DELETE 등의 메서드는 options 객체를 추가해야 한다.
async/await과 함께 사용하는 이유
- fetch() 자체가 Promise를 반환하므로, await를 사용해 응답이 도착할 때까지 기다릴 수 있다. (후처리!)
- fetch()의 응답은 Response 객체이고, 이걸 다시 .json() 등의 메서드를 호출해서 실제 데이터를 추출(파싱)해야 한다.
- response.json()도 비동기(Promise)이므로 await을 한 번 더 사용해야 실제 데이터를 얻을 수 있다.
async fucntion fetchData() {
// 1. 서버에 요청 보내고 응답을 기다림
const response = await fetch('url')
//2. 응답 내용을 JSON 데이터로 파싱함
const data = await response.json();
// 3. 실제 데이터 출력
console.log(data);
}
fetchData();
'프론트엔드 노트' 카테고리의 다른 글
| 타입스크립트 제네릭 (0) | 2025.07.08 |
|---|---|
| 타입스크립트 기초 (1) | 2025.07.08 |
| Skyscanner) Task 1: Create a Backpack React web app (진행중) (0) | 2025.05.16 |
| Expo 빌드 & App Store 배포하기(2) (0) | 2025.04.30 |
| Expo 빌드 & App Store 배포하기(1) (0) | 2025.04.29 |