Software Framework/Express.js

[Express.js] Promise (callback) | study book

Bull_ 2024. 8. 26. 09:24

callback

자바스크립트는 런타임에서 싱글 스레드로 동작한다. 싱글 스레드로 동작한다는 것은 한 번에 하나의 작업만 처리할 수 있다는 뜻이다. 싱글 스레드로 동작하지만 콜백, 프로미스, 어싱크 어웨잇 방법을 사용하면 비동기 처리가 가능하다. 자바스크립트외 에도 비동기를 구현하기 위해 사용되는 가장 일반적인 방안으로 콜백이 있다. 인자로 함수를 전달하여 특정 상황이 발생할 때 호출하는 방식이다.

const DB = [];

function register(user) {
  return saveDB(user, (user) => sendEmail(user, getResult));
}

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: "abc@test.com",
  password: "1234",
  name: "elly",
});
console.log(result);

js에서 콜백을 사용하는 일반적인 방식이다. 인자에 callback으로 함수를 받아서 넘겨주는 로직이 형성된다. register가 호출되면 saveDB -> sendEmail -> getResult 순으로 비동기 처리가 된다. saveDB의 인자로 senEmail 콜백을 넣고 다시 sendEmail의 인자로 getResult 콜백을 넣은 것이다. 다시 해석하면 register 호출 -> saveDB가 끝나면 sendEmail 호출 -> sendEmail이 끝나면 getResult 호출.

 

지금은 3단계지만 10,20,30단계 등 많은 콜백이 호출될수록 코드는 점점 복잡해질 것이다. 이를 해결하기 위해Promise가 등장한다.

Promise

Promise 는 2015년 ES6 버전에 도입되었다. 비동기를 실행해주는 구문으로 사용한다. 사용시 new 키워드를 통해 인스턴스를 생성하여 반환하여 사용한다.

const DB = [];

function register(user) {
  return saveDB(user)
    .then(sendEmail)
    .then(getResult)
    .catch((error) => new Error(error))
    .finally(() => console.log("완료"));
}

function saveDB(user) {
  const oldDBSize = DB.length + 1;
  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) => resolve(`success register ${user.name}`));
}

const result = register({
  email: "abc@test.com",
  password: "1234",
  name: "elly",
});
result.then(console.log);

// const user = {
//   email: "abc@test.com",
//   password: "1234",
//   name: "elly",
// };
// const allResult = Promise.all([saveDB(user), sendEmail(user), getResult(user)]);
// allResult.then(console.log);

resolve : 콜백이 생성한 인스턴스의 값이 유효하면 이행한다.
reject : 콜백이 생성한 인스턴의 값이 유효하지 않으면 거절한다.

function register(user) {
  return saveDB(user)
    .then(sendEmail)
    .then(getResult)
    .catch((error) => new Error(error))
    .finally(() => console.log("완료"));
}

다시 register 함수를 살펴보면 더 직관적임을 확인할 수 있다. saveDB에서 생성한 인스턴스가 resolve라면 then 함수를 통해 계속 이행한다. 그리고 비동기 요청도 중 에러가 발생하면 catch 를 통해서 에러를 확인할 수 있다.

마지막으로 주석부분은 Promise.all을 사용함으로써 register 함수 없이 비동기로 콜백을 연속 호출하는 것을 구현해놓은 상태다. allResult.then(console.log) 에 콜백으로 콘솔 출력함수를 넣어줌으로써 반환된 값들을 리스트로 출력해준다.

async await

async await은 가장 최근에 도입된 비동기 처리 방식이다.

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();

setTimeout을 통해서 1초의 지연을 두었다. 그리고 for문에서 0부터 9까지 순회하는 반복문을 만들어 await으로 비동기 호출을 하였다. 즉 1번 함수실행이 끝나야 2번함수가 호출될 수 있는 것이다. 말 그대로 기다린다는 뜻이다.

 

이를 더 유연하게 사용하기 위해 api 호출하는 함수를 보자.

const axios = require("axios");
const url =
  "https://raw.githubusercontent.com/wapj/jsbackend/main/movieinfo.json";

async function getMovieList() {
  try {
    const result = await axios.get(url);
    const { data } = result;
    if (!data.articleList || data.articleList.size == 0) {
      throw new Error("데이터 없음");
    }

    const results = data.articleList.map((article, idx) => {
      return { title: article.title, rank: idx + 1 };
    });

    for (let movieInfo of results) {
      console.log(`[${movieInfo.rank}위] ${movieInfo.title}`);
    }
  } catch (err) {
    console.log("에러 발생");
    console.error(err);
  }
}

getMovieList();

처음 await axios.get 을 통해서 데이터가 다 가져와질 때까지 기다린다. 만약 json을 받아오는 함수를 호출했지만 호출만하고 모두 불러와졌는지 검사하지 않는다면 아래 코드에서 에러가 날 것이다. await을 통해서 이를 막을 수 있다.

참고 자료

https://product.kyobobook.co.kr/detail/S000201457949

 

Node.js 백엔드 개발자 되기 | 박승규 - 교보문고

Node.js 백엔드 개발자 되기 | ★ 자바스크립트 Node.js 백엔드 개발자가 되고 싶다면 ★ HTML/CSS/자바스크립트 그다음에 꼭 보세요 실력을 갖춘 개발자로 성장하려면 시작이 중요합니다. 그래서 이

product.kyobobook.co.kr