Node.js는 왜 비동기인가?
Node.js는 단일 스레드(Single Thread) 기반 런타임입니다.
이는 기본적으로 한 번에 하나의 JS 코드만 실행된다는 말인데요.
그런데도 동시에 많은 요청을 처리할 수 있는 이유는 바로 비동기(Asynchronous) + 논블로킹(Non-blocking) I/O 이기 때문이라고 할 수 있습니다.
우선, 동기와 비동기에 대해 알아보겠습니다.
동기 vs 비동기
- 동기 (Synchronous)
: 코드가 순서대로 실행되고, 이전 작업이 끝날 때까지 다음 작업이 기다리는 방식
const data = fs.readFileSync('file.txt');
console.log(data);
- 파일 읽는 동안 프로그램은 멈춤
- 다른 요청 처리 불가 👉 블로킹(작업이 끝날 때까지 이벤트 루프가 멈춤)
- 비동기 (Asynchronous)
작업을 맡겨두고, 끝나면 나중에 알려달라는 방식 -> 실행되는 코드가 기다려야 하는 시간이 생김
ex) setTimeOut(), setIntervel(), query()
fs.readFile('file.txt', (err, data) => {
console.log(data);
});
- 파일 읽는 동안 다른 코드 실행 가능
- 완료되면 콜백 실행 👉 논블로킹(작업은 OS가, Node는 계속 다른 일)
Node.js의 이벤트 루프
Call Stack
↓
Web API (또는 libuv)
↓
Callback Queue
↓
Event Loop
동작 흐름
JS 코드 실행 (Call Stack)
비동기 작업 발견
OS(libuv)에 맡김
완료되면 Callback Queue에 넣음
Call Stack이 비면 Event Loop가 콜백 실행
비동기 발생 -> 비동기 처리
여기서 제가 말씀드리는 발생과 처리는 당연하게도 다른 이야기인데요,
일단 이전 시간이 오래 걸리면, 안 기다려주고 다음 코드를 무작정 실행하는 비동기가 발생을 하면,
이를 '동기처럼' 순서를 맞춰 이전 코드들의 시간을 다 기다려주고 실행하기 위해, 비동기를 처리해줄 수 있다는 것입니다.
1. 콜백
: 완료 시점에 실행될 함수를 전달
- 제어권을 Node에게 넘김
- 완료되면 이벤트 루프가 콜백을 실행
- 중첩되면 콜백 지옥이 발생할 수 있음
fs.readFile('a.txt', (err, data) => {
console.log(data);
});
2. Promise (reseolve, reject)
: 비동기 작업의 상태를 객체로 모델링
const p = new Promise((resolve, reject) => {
resolve("성공");
});
resolve -> 성공
reject -> 실패
3. then & catch : Promise의 사용 문법
//Promise "객체" : 무조건 약속을 지키는 친구
let promise = new Promise((resolve, reject)=>{
//executor : 친구가 해야할 일
setTimeout(() => resolve('성공!'), 3000); //3초 후에 resolve라는 콜백함수 호출하면서 '성공!'이라는 결과 전달
//resolve(결과) : executor가 성공 했을 때
//reject(에러) : 실패했을 때
//할 일 다하면 무조건 콜백함수 resolve() or reject() 둘 중 하나 호출
}); //친구 소환, 매개변수로 함수를 받음
// promise의 기본 메소드 : promise가 일 다 하고 (= 약속 다 지키고) 호출해야하는 함수
promise.then(
function(result){
console.log(result); // 성공! 출력
}, //첫번째 매개변수 : resolve이 호출됐을 때 실행할 함수
function(error){} //두번째 매개변수 : reject이 호출됐을 때 실행할 함수
);
Promise 체이닝
let promise = new Promise((resolve, reject)=>{
setTimeout(() => resolve('성공'), 3000);
}).then(
function(result){
console.log(result);
return result + "!!!!!";
},
function(error){}
).then(
function(result){
console.log(result);
return result + "?????";
},
function(error){}
).then(
function(result){
console.log(result);
},
function(error){}
);
return으로 다음 then을 실행하도록 할 수 있고, return을 하지 않으면 그 이후의 then에 undefined가 전달되어 항상 새로운 Promise를 반환하게 됩니다.

4. ES2017 promise를 쉽게 쓰기 위한 방법 => async & await
async-await : Promise를 좀 더 간결하게 사용할 수 있도록 도와주는 문법 -> 즉 비동기 처리가 쉽다
- async
- async 함수 : async function f() {}
- async 함수는 항상 Promise 객체를 반환한다.
- 만약 반환값이 Promise가 아니라면, 반환값을 Promise.resolve()로 감싸서 반환한다.
async function f() {}
// = 동일 =
function f() {
return new Promise(...)
}
async function f() {
return 7;
}
f().then(
function(result){
console.log("promise resolve : ", result);
},
function(error){
console.log("promise reject : ", error);
}
);
- wait
- async-await : Promise를 좀 더 간결하게 사용할 수 있도록 도와주는 문법 -> 즉 비동기 처리가 쉽다
- await : async 함수 안에서만 동작, Promise 객체.then() 메소드를 좀 더 쉽게 사용할 수 있도록
- async 의 두번째 기능 : Promise 객체가 일이 끝날 때까지 기다릴 수 있는 공간을 제공
async function f() {
let promise = new Promise((resolve, reject)=>{
setTimeout(() => resolve('성공'), 3000);
});
let result = await promise; // await : Promise 객체가 resolve될 때까지 기다렸다가, resolve된 값을 result에 할당
console.log(result);
}
f();
직렬 실행
: Promise가 끝난 후 다음 Promise를 생성
// async-await : Promise를 좀 더 간결하게 사용할 수 있도록 도와주는 문법 -> 즉 비동기 처리가 쉽다
// await : async 함수 안에서만 동작
// Promise 객체.then() 메소드를 좀 더 쉽게 사용할 수 있도록
// async 의 두번째 기능 : Promise 객체가 일이 끝날 때까지 기다릴 수 있는 공간을 제공
async function f() {
// promise 객체 한개당 => query 하나당 => await 하나씩
let promise1 = new Promise((resolve, reject)=>{
setTimeout(() => resolve('첫번째 쿼리'), 3000);
});
let result1 = await promise1;
// promise 객체가 일 다 할때까지 기다려줌
console.log(result1);
let promise2 = new Promise((resolve, reject)=>{
setTimeout(() => resolve('두번째 쿼리 with ' + result1 ), 3000);
});
let result2 = await promise2;
console.log(result2);
let promise3 = new Promise((resolve, reject)=>{
setTimeout(() => resolve('세번째 쿼리 with ' + result2 ), 3000);
});
let result3 = await promise3;
console.log(result3);
}
f();
'Devcourse' 카테고리의 다른 글
| [Trello 클론 해보기] Firebase로 로그인/로그아웃 구현 (0) | 2026.04.13 |
|---|---|
| [BookShop 프로젝트] CartItem (0) | 2026.03.03 |
| [BookShop 프로젝트] - 도서목록 조회 페이지 구현 - MySQL 시간 범위 구하기, DB 페이징(paging) (0) | 2026.02.16 |
| Express-generator 프로젝트 구조 (0) | 2026.02.13 |
| <데브코스> 2026-02-10 (0) | 2026.02.10 |