프로그래밍은 동기 프로그래밍, 비동기 프로그래밍으로 나눌 수 있다.
동기 프로그래밍이란 작업을 차례대로 진행하면서 작업의 실행이 완료될때까지 중단될 수 없다.
따라서 모든 작업은 이전 작업의 실행이 완료될 때까지 기다려야 한다.
반면 비동기 프로그래밍에서는 임의의 순서로 또는 동시에 작업이 진행될 수 있다.
자바스크립트는 단일 작업 특성을 갖고 있기 때문에 실행이 오래 걸리는 작업(setTimeOut, 파일 불러오기 등)에서는 다음 작업의 실행이 지연되는 blocking 현상이 발생할 수 있다. 이를 해결하기 위해서는 비동기 처리가 필요하다.
예를 들어, 동기로 프로그램이 작동한다면 짜장면 배달부가 한 집에 배달을 한 뒤, 해당 고객이 짜장면을 다 먹고 난 뒤 그릇을 회수해야 다음 배달을 갈 수 있게 된다는 것이다. 현대적인 웹페이지에가 동기적으로만 작동한다면, 페이지의 최상단 또는 최하단부터 하나씩 로딩될 것이며, 페이지를 로딩하는 순간에 다른 명령어도 사용할 수 없을 것이다. 또한 로딩이 오래 걸리는 작업을 만난다면 뒤의 작업들은 하염없이 기다릴 수밖에 없어 사용자의 입장에서 불편할 수 밖에 없다.
이제 자바스크립트에서 비동기 처리를 하는 방법을 알아보겠다.
콜백(Callback)
콜백 함수는 현재 코드의 실행 결과를 받지 않고 이후 코드를 수행하는 기법을 말한다.
콜백은 실행 가능한 함수를 인자로 전달하여 특정 상황이 발생했을 때 해당 함수를 호출하는 방법이다.
예) 회원가입 하기
회원가입 API 호출 시 -> 데이터베이스에 저장 -> 이메일 전송 -> 성공 메시지 띄우기
const DB = [];
function register(user) {
return saveDB(user, function (user) { //콜백
return sendEmail(user, function (user) { //콜백
return getResult(user); //콜백
});
});
}
function saveDB(user, callback) {
DB.push(user);
console.log(`save ${user.name} to DB`);
return callback(user);
}
function sendEmail(user, callback) {
console.log(`email to ${user.email}`);
return callback(user);
}
function getResult(user) {
return `success register ${user.name}`;
}
const result = register({email: "andy@test.com", password: "1234", name: "andy"});
console.log(result);
콜백 지옥(Callback Hell)
콜백이 많아진다면, 가독성이 떨어지는 코드가 발생한다.
Promise 객체
'이 코드는 미래의 어느 시점에 실행될 거야' 라고 약속하는 객체이다.
이행, 거절, 실행 세 가지 상태를 가질 수 있고 new 연산자로 인스턴스를 생성할 수 있다.
Promise는 처음에는 함수가 실행되기를 기다리는 pending상태였다가, 함수가 잘 작동이 되면 resolve, 잘 작동되지 않으면 reject를 반환한다.
1. resolve(value): 작업이 성공적으로 끝난 경우
프로미스를 실행한 곳의 then으로 돌아간다.
2. reject(value): 작업이 실패한 경우
catch 문으로 넘어간다.
예시) 위에서 콜백으로 작성한 회원가입 코드를 promise 객체로 작성한 예시
const DB = [];
function saveDB(user) {
const oldDBSize = DB.length;
DB.push(user);
console.log(`save ${user.name} to DB`);
return new Promise((resolve, reject) => {
if(DB.length > oldDBSize) {
resolve(user);
} else {
reject(new Error("Save DB Error!"));
}
});
}
function sendEmail(user) {
console.log(`email to ${user.email}`);
return new Promise((resolve) => {
resolve(user);
});
}
function getResult(user) {
return new Promise((resolve, reject) => {
resolve(`success register ${user.name}`);
})
}
function registerByPromise(user) {
const result = saveDB(user).then(sendEmail).then(getResult);
console.log(result);
return result;
}
const myUser = {email: "andy@test.com", password: "1234", name: "andy"};
const result = registerByPromise(myUser);
result.then(console.log);
Async/Await
callback과 promise보다 비동기 실행의 작업을 좀 더 유연하고 '일반함수'적으로 실행하도록 하기 위해 제안된 객체
비동기 작업을 수행하지만, 겉보기에는 일반함수 같아 좀 더 사용하기 편하다.
async function 함수명() {
await 비동기처리_메서드명();
}
async로 '이 함수에서 비동기 처리를 하겠다'라는 것을 보여주고, await로 비동기처리를 할 메서드를 실행한다.
async function myName() {
return "Andy";
}
async function showName() {
const name = await myName();
console.log(name);
}
console.log(myName());
// console.log(showName());
function waitOneSecond(msg) {
return new Promise((resolve, _) => {
setTimeout(() => resolve(`${msg}`), 1000);
});
}
async function countOneToTen() {
for (let x of [...Array(10).keys()]) {
let result = await waitOneSecond(`${x + 1}초 대기중...`);
console.log(result);
}
console.log("완료");
}
countOneToTen();
await는 promise객체인 myName() 함수의 실행이 끝나기를 기다린다. 출력 결과서 pending은 console.log(myname)의 결과값이고, await의 결과로 andy가 출력된다.
countOneToTen에서는 waitOneSecond함수가 끝나기를 기다리고 끝나면 return값으로 promise객체를 반환한다.
참고 서적
- Node.js 백엔드 개발자 되기(최승규)
'Node.js' 카테고리의 다른 글
[Node.js] HTTP/1.1 GET, POST 요청 처리하기(2) - Client(Feat. Axios) (0) | 2023.12.04 |
---|---|
[Node.js]HTTP/1.1 GET, POST 요청 처리하기(1) - Server (0) | 2023.11.29 |