본문 바로가기
JAVASCRIPT

비동기 프로그래밍

by escaper101 2020. 11. 28.

JavaScript는 싱글 스레드 언어이다. 한번에 하나의 작업만 수행할 수 있다는 의미이다. 그렇다면 JavaScript 언어로 시간이 오래걸리는 작업을 수행하기 위해서는 어떻게 해야 할까? 오랜 시간이 걸리는 하나의 작업이 JavaScript가 가진 단일 스레드를 막고(블로킹)하게 될 것이다. 

 

이런 블로킹 현상이 일어나는 것을 방지하고 JavaScript로 비동기 프로그래밍을 하는 방법에 대해 알아보자. 

크게 세 가지 방법이 있다. 

1. 콜백함수 사용

2. 프로미스 사용

3. Async/await 사용 

 

하나 하나씩 살펴보자. 

1. 콜백함수: 콜백함수를 통해 비동기 작업을 처리하는 방식. 필요한 비동기 작업에 따라 코드가 다음과 같은 모양이 될 수 있다. 이때, 이렇게 옆으로 누운 피라미드 형태를 띄는 중첩 콜백 함수 패턴을 콜백 헬이라고 한다. 

보기와 같이 각 콜백 함수 별 나름의 로직이 추가되고 때에 따라 예외 처리를 위한 함수가 추가된다면 코드의 가독성이 매우 떨어질 수 있다. 이러한 문제를 해결하기 위해 ES6에 도입된 프로미스를 사용할 수 있다. 

first(function(resultFromFirst) {
  second(resultFromFirst, function(resultFromSecond) {
    third(resultFromSecond, function(resultFromThird) {
      fourth(resultFromThird, function(resultFromFourth) {
        fifth(resultFromFourth, function(resultFromFifth) {
          sixth(resultFromFifth, function(resultFromSixth) {
            // some logic
          })
        }) 
      })
    })
  })
});

 

2. 프로미스: ES6에 새로 도입된 JavaScript 문법으로 비동기 작업을 처리할 때 사용할 수 있다. 

프로미스는 상태에 따라 크게 pending과 settled로 나뉜다. pending은 비동기 작업이 실행되기 전 상태를 말하고 settled는 비동기 작업이 실행된 후 상태를 뜻한다. 프로미스가 Settled 상태이면 작업 결과에 따라 역시 두 개의 상태를 가질 수 있다. 작업이 잘 완료되어 약속한(promise) 값을 받는데 성공한 경우 fulfilled 상태가, 작업 중 오류가 발생하여 값을 받는데 실패하면 rejected 상태로 구분된다.

다음은 프로미스를 이용한 비동기 작업을 처리하는 코드이다. 예시에서는 레시피 정보를 받아오는 API를 사용한다고 가정했다. 프로미스를 통해 받은 값에는 .then() 메서드를 통해 접근할 수 있고 에러가 발생한 경우는 .catch() 블록 안에서 처리하면 된다. 이때, 예시에서도 알 수 있듯이, .then() 블록을 통해 받은 프로미스 값을 리턴하는 경우 또다른 .then() 블록에서 사용할 수 있다. 이렇게 프로미스 체인을 통해 비동기 작업 결과를 사용한 또 다른 비동기 작업을 처리할 수 있다. 프로미스 체인을 사용하여 여러개의 비동기 작업 시 그 중 하나라도 오류가 발생하는 경우 catch() 블록에 오류가 잡히게된다. 콜백 함수 예시에서 살펴본 코드와 비교할 때 가독성이 한층 증가한 것을 확인할 수 있다. 

const getRecipeData = function(menu) {
  fetch(`www.nameofsomewebpage/${menu}`)
  .then(recipe => {
    // get author information of recipe
    return getAuthor(recipe.author);
  })
  .then(author => {
    // get all recipies written by the author
    return getAllRecipies(author);
  })
  .then(recipes => {
    // some other logic...
  })
  .catch(error => {
    console.log(error);
  })
}

마지막으로 async/await 문법을 사용하여 비동기 작업을 처리하는 방법을 알아보자. 위의 예시와 동일한 작업을 수행하는 코드를 async와 await 문법을 사용하는 코드로 변경하였다. 이때, 비동기 작업을 처리할 함수 앞에 반드시 async 키워드를 붙어주고 프로미스 값을 리턴하는 함수 앞에 await 키워드를 붙여주어야 한다. 두 개의 키워드가 필요한 위치에서 사용될 때 제대로 동작하니 주의하자. try 구문 안에서 프로미스 요청 값을 사용한 작업을 처리하고 catch 블록 안에 에러 핸들링 코드를 작성해주면 된다. 이 문법을 사용하면 비동기 작업을 마치 동기 작업인 것 처럼 코드를 작성할 수 있다. 위에서 아래로 코드를 읽어내려가며 작업 흐름을 파악할 수 있기 때문에 가독성이 증가한다. 

async function getRecipeData(menu) {
  try {
    const recipe = await fetch(`www.nameofsomewebpage/${menu}`);
    const author = await getAuthor(recipe.author);
    const allRecipes = await getAllRecipies(author);

    return allRecipies
    
  } catch(error) {
    console.log(error);
  }
}

 

이번 포스팅을 통해 싱글 스레드 언어인 JavaScript로 비동기 작업을 처리하는 방법에 대해 살펴보았다. 

'JAVASCRIPT' 카테고리의 다른 글

Runtime 과 Call Stack  (0) 2020.11.28
생성자 함수와 Prototype  (0) 2020.11.17
Closure의 이해  (0) 2020.11.17
this 키워드  (0) 2020.11.16
Hoisting과 Temporal Dead Zone (TDZ)  (0) 2020.11.16