[패스트캠퍼스] 프론트엔드 취업 완성 과정 3기 - async/await 에 대한 고찰

JYROH·2022년 11월 18일
4
post-thumbnail

얼마전까지 영화 API를 활용하여 영화검색사이트를 만드는 노랑박스프로젝트를 진행했었습니다. 처음으로 해보는 JS프로젝트라 수도없이 많은 오류와 문제점들에 직면했었지만 어떻게든 우겨넣어서 해결은 한것 같습니다.

말나온김에 놀러오세요 [노랑박스] https://yellow-box-jyroh.netlify.app/

프로젝트를 진행함에 있어서, 가장 "아~" 싶었던 부분이 있었는데 그것을 설명해볼까 합니다. 제목에도 있듯이, async/await 관련입니다.

Promise 비동기 함수


최근 저희 조원분들을 포함하여 저까지 강의중 가장 힘들어했었던 주제가 있는데 바로 비동기부분... Promise, then, async/await 부분이었습니다. 도대체 Promise가 뭘까요?? .....
저도 아직 정확하게 이해를 못했습니다....ㅎㅎ 저는 이렇게 이해가 안가는 주제에 있어서는 우선 실사용을 할수있을정도로만 간단하게 이해를 하고 넘어가는 편인데요, 간단히 설명을 해보겠습니다.

비동기작업

비동기작업이 JS의 특징인것은 모두가 잘 알고있을것입니다. 동기작업과 비동기작업은 이런느낌입니다

코드가 a,b,c순서대로 적혀있다고 할때...

동기:
a -> b -> c

비동기:
a -> c -> b

전혀 감이 안오시죠. 간단히 말하자면 코드가 적혀있는것과 다른순서로 작동할수있는것을 비동기라고 합니다. 비동기 방식의 최고장점은 "한번에 여러가지 일을 수행할수있다" 는 것이고(놀랍게도 JS는 싱글스레드 언어임에도!) 아쉬운점이라면 코드의 흐름자체를 예측하기가 어렵다는 것이죠. 그래도 장점이 너무나도 큽니다

Promise

Promise는 영어로 약속이라는 뜻입니다. 저희가 약속을하면 그것을 이행할수가 있고, 약속을 지키지 않을수도 있죠. 중요한점은 약속을 만들고나서는 결과가 나올때까지 다른일을 하고있으면 된다는 점입니다. 그렇기 때문에 Promise방식은 비동기 작업을 생성하는 방법으로 널리 쓰이고 있습니다. Promise방식은

  1. pending(대기)
  2. fulfilled(이행)
  3. rejected(거부)

의 세가지 상태를 가집니다. 이것을 .thencatch를 사용하여 이행과 거부를 수행할수 있습니다. 즉 비동기 작업의 생성을 Promise가, 동작 자체는 then과 catch가 한다는 것입니다.

async/await

위의 Promise과정보다도 더 간단하게 비동기 코드를 짤수있게 해주는것이 바로 async/await입니다. async는 비동기 함수를 생성할때 붙여줄수 있고 그러면 비동기함수가 되는것입니다. 이 async는 Promise와 동작과정이 아예 동일한데, 그렇다면 저희는 async함수의 리턴값은 무조건 Promise라는것을 알 수 있습니다.

그렇다면 await은 뭘까요? wait.... 기다려라 입니다. 언제까지? 비동기작업이 끝날때 까지 입니다.

저희가 동기작업을할때(async가 아닌)는 비동기작업을 기다릴 필요가없습니다. 그냥 동기작업이나 계속 하면되기 때문이죠. 반대로, async함수를 이용해서 비동기 작업을 하고있을때는, 그 비동기 작업을 기다려줘야합니다. 예를들어 API fetch의 동작을 할때, 당연히 비동기 fetch가 끝날때까지 기다려야 그 API로 다른일을 해줄수가 있겠죠. 따라서 async와 await은 쌍둥이로 같이다닌다고 보면 될것입니다.

그렇다면 왜 Promise보다 async/await을?

간편하기 때문입니다. Promise는 thencatch를 콜백형태로 넘겨주어 비동기작업 생성과 수행을 분리를 했었는데, async/await 방식은 이것을 하나로 합쳐주어 하나의 흐름속에서 코드를 짤수있게 해줍니다. 분명 비동기로 적은 코드이지만, await에 의해서 동기처럼 작동을 한다는 말입니다! 코드를 작성하는것도 편해질것이고, 전체적인 흐름파악도 용이해지겠죠.

네 결론은 어려우면 그냥 async/await을 쓰자입니다.

비동기작업의 함정


이번 과제 진행중에, 한번에 여러번의 fetch를해서 그 정보를 토대로 화면에 띄어줘야하는 과정이 있었습니다.
for (let i = searchDecades + 1; i <= searchDecades + 10; i++) {
      const res2 = await fetch(
        `https://omdbapi.com/?apikey=7035c60c&s=${title}&page=${page}&type=${type}&y=${i}`
      );
      const json2 = await res2.json();
      let { Search: movies2 } = json2;
      if (!movies) movies = [];
      if (!movies2) movies2 = [];
      console.log(movies2);
      movies = movies.concat(movies2);
    }

10년치의 정보가 필요했기에, for문을 통하여 10번의 fetch를 해준것입니다. 당연히 동작상에는 아무 문제가 없었지만.... 시간이 좀 오래 걸린다는 생각이 들었습니다. 이유를 아실까요??

보시면 API fetch 를 통하여 정보를 가져오고 있습니다. 예를들어 2010년 부터 2019년까지 10년치를 가져온다고 해보겠습니다. 제 코드의 문제는 2010년코드를 fetch하는걸 기다리고(await) 다 받아온다음 2011년을 fetch하는걸 기다리고.... 이런 방식이라는 겁니다.

비동기방식의 의의는 한번에 여러일을할수있다는 것입니다. 그러면, await를 써줄때 저희는 항상 조심해야합니다. 이런식이면 동기방식과 다를게 없어지기 때문이죠...
fetch가 끝나든 끝나지않든 await을 걸어주지말고, 이 데이터들을 모아둔다음에 한번에 await Promise.all 로 가져와야한다는것을 이해해야합니다.

따라서 코드를

// 여기 코드 수정하자. await을 for문안에 넣으면 망하는지름길인듯.
  if (searchDecades != "") {
    let promises = [];
    for (let i = searchDecades + 1; i <= searchDecades + 10; i++) {
      const res2 = fetch(
        `https://omdbapi.com/?apikey=7035c60c&s=${title}&page=${page}&type=${type}&y=${i}`
      );
      promises.push(res2);
    }
    // promise all로 한번에 받아온다. for문에서 하나하나 await하면 비동기 의미가없음.
    let promiseMovies = await Promise.all(promises);
    promises = [];
    promiseMovies.forEach((x) => {
      promises.push(x.json());
    });
    promiseMovies = await Promise.all(promises);
    for (let i = 0; i < 10; i++) {
      let { Search: movies2 } = promiseMovies[i];
      if (!movies) movies = [];
      if (!movies2) movies2 = [];
      movies = movies.concat(movies2);
    }
  }

이런식으로 다 받아온다음 await과정은 한번만 하는것이 더 효율적으로 동작할 것입니다. 스스로 많은것을 느끼게 해준 기능이었습니다.

프로젝트


현재 노랑박스 1차리뷰를 받은 상태입니다. 다른 분들 결과물들 보면 참으로 멋있는 기능이많은데(특히 localstorage 사용) 리팩토링때 한번 넣어볼까도 생각은 해보고있습니다 ㅎㅎ.... 일단 코드에서 지적받았던 내용들도 많기에, 그것들을 먼저 고쳐봐야겠습니다.
profile
안녕하세요 노준영입니다.

3개의 댓글

comment-user-thumbnail
2022년 11월 22일

10년치씩 가져오는 거 정말 신박한 기능이라고 생각했는데 저런 고찰이 있었군요!
잘 읽고 갑니다😊

1개의 답글