[Node.js] 자바스크립트 비동기 개념에 익숙해지기 3편 – async await 구문

이 글은 [Node.js 백엔드 개발자 되기]에서 발췌했습니다.
골든래빗 출판사

[Node.js] 자바스크립트 비동기 개념에 익숙해지기‘는 앞서 2편에서 콜백 함수, 프로미스를 소개했습니다.

마지막인 3편에서는 async와 await 구문을 알아봅시다.

 

1. async await 구문

async와 await는 자바스크립트에 가장 최근 도입된 비동기 처리 방식입니다. 기존의 비동기 처리 방식인 콜백 함수와 프로미스의 단점을 보완했으며 가독성 높은 코드를 작성할 수 있습니다.

async는 함수 앞에 붙이는 키워드입니다. async는 asynchronous비동기라는 의미입니다. async function만 보고도 비동기 함수라는 것을 유추할 수 있습니다.

여기서 말하는 비동기는 콜백이 아니라 프로미스를 말하는 겁니다. 즉 async가 붙은 함수는 프로미스를 반환한다고 이해하면 되겠습니다.

 

01 간단한 async 코드를 작성하겠습니다.

▼ async 예제

async function myName() {
	return "Andy";
}
console.log(myName());
$ node async-await.js
Promise { 'Andy' }

 

‘Andy’라는 문자열이 아니라 Promise가 결괏값으로 넘어왔습니다. 명시적으로 Promise를 사용해야 하는 경우도 있지만, 그렇지 않더라도 async를 쓰면 Promise로 반환값을 감싸서 넘겨줍니다.

 

02 await 사용법도 알아봅시다.

await는 기다린다는 뜻의 영어입니다. 자바스크립트에서 사용하는 await는 무엇을 기다릴까요? 성공 또는 실패로 Promise 객체의 실행이 완료되기를 기다립니다. 그러므로 await의 뒤에는 Promise가 오게 됩니다. await는 async 키워드를 사용한 함수 안에서만 사용할 수 있습니다. 앞서 만든 코드를 수정해 await로 이름을 출력해봅시다.

▼ async await 예제

async function myName() {
	return "Andy";
}
async function showName() { // ❶ 이름을 출력하는 함수
	const name = await myName();
	console.log(name);
}

console.log(showName()); // ❷ 콘솔에 이름 출력
$ node async-await.js
Promise { <pending> }
Andy

 

await은 Promise 객체인 myName( ) 함수의 실행이 끝나길 기다립니다. 출력 결과에서 Promise { <pending> }은 ❷ console.log(showName( ))의 결괏값입니다. showName( )도 async가 붙 어 있으니 Promise입니다. ❶ await의 결과로 ‘Andy’가 출력되는 것을 볼 수 있습니다.

 

03 이번에는 async await, setTimeout( )을 사용해서 1부터 10까지 1초 에 하나씩 출력하는 코드를 작성합니다.

▼ async await, setTimeout()으로 1부터 10까지 세기

function waitOneSecond(msg) { // ❶ 1초 대기하고 메시지 출력
	return new Promise((resolve, _) => {
		setTimeout(() => resolve(`${msg}`), 1000);
	});
}
async function countOneToTen() { // ❷ 10초 동안 1초마다 메시지 출력
	for (let x of [...Array(10).keys()]) { // ❸ 0부터 9까지 루프를 순회
		// ❹ 1초 대기 후 result에 결괏값 저장
		let result = await waitOneSecond(`${x + 1}초 대기 중...`); console.log(result);
	}
	console.log("완료"); 
}

countOneToTen();
$ node async-await.js 
1초 대기 중...
2초 대기 중...
3초 대기 중...
... 생략 ...
10초 대기 중... 
완료

 

❶ waitOneSecond( ) 함수는 1초 대기하고 메시지를 출력하는 함수입니다. 1초를 대기하 려면 setTimeout( )을 사용해야 하는데 setTimeout( )에는 반환값이 없기에 Promise 객체 를 직접 생성했습니다. 직접 Promise를 만들어서 반환하므로 async를 붙여주지 않아도 됩니다. reject를 사용하지 않기 때문에 _로 사용하지 않음 표시를 했습니다. ❷ async로 지정 한 countOneToTen( ) 함수는 총 10초 동안 1초마다 콘솔창에 출력합니다. ❸ […Array(10). keys( )]는 [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 리스트입니다. ❹ await 뒤에는 waitOneSecond( ) 함수 를 주었습니다.

 

04 영화 랭킹 예제를 async await로 변경하겠습니다.

Promise와 async await를 사용해 비동기 함수를 동기 방식의 코드처럼 직관적으로 작성했습 니다. 기본적인 사용법을 알아봤으니 2편에서 Promise로 만든 영화 랭킹 예제를 async await로 변경하겠습니다.

▼ Top20 영화 제목 가져오기 async await 버전

const axios = require("axios");

async function getTop20Movies() { // ❶ await를 사용하므로 async를 붙임
	const url = "https://raw.githubusercontent.com/wapj/jsbackend/main/movieinfo.json";
	try {
		// ❷ 네트워크에서 데이터를 받아오므로 await로 기다림
		const result = await axios.get(url);
		const { data } = result; // 결괏값(result)에는 data 프로퍼티가 있음 
		// data 또는 articleList 없을 때 예외 처리
		if (!data.articleList || data.articleList.size == 0) {
			throw new Error("데이터가 없습니다."); 
		}
		// data에서 필요한 영화 제목과 순위 정보를 뽑아냄
    const movieInfos = data.articleList.map((article, idx) => {
			return { title: article.title, rank: idx + 1 };
		});
		
		// 데이터 출력
		for (let movieInfo of movieInfos) { console.log(`[${movieInfo.rank}위] ${movieInfo.title}`);
		}
	} catch (err) {
	// ❸ 예외 처리는 기존 코드와 같게 try catch로 감쌈
	throw new Error(err);
	}
}

// await를 함수 안에서만 사용 가능하므로 함수를 하나 생성해 실행
getTop20Movies();

 

05 async await로 변경한 코드를 실행합니다.
$ node top20-movie-async-await.js
[1위] 처음부터 잘했으면 얼마나 좋니
[2위] <본즈 앤 올> 궁지로 내몰린 10대를 보는 시선
[3위] 경이로운 생生의 의지로 창조해낸 '페르시아어'
... 생략 ...
[18위] <오늘 밤, 세계에서 이 사랑이 사라진다 해도> 리뷰 
[19위] <더 메뉴> 180만 원짜리 먹으러 와서 사레 걸린기분 
[20위] 닭장을 나온 백호

기존 코드는 과도하게 then으로 연결되어 있습니다. async/await를 사용해 동기 프로그래밍 코드와 유사하게 변경했습니다. ❶ 함수 앞에 붙은 async와 ❷ Promise 앞에 붙은 await만 다를 뿐입니다. 예외 처리는 catch( ) 함수가 아니라 ❸ try catch 구문을 그대로 사용했습니다. 그 외 데이터를 정제하는 코드는 Promise를 사용했을 때와 같습니다.

개인적으로는 Promise가 필요한 경우 (setTimeout( )을 사용하거나, 여러 태스크를 동시에 실행해야 하는 경우) 이외에는 모두 async await를 사용합니다. 읽기 편한 코드는 디버깅에 유리하기 때문입니다. async, await는 내부적으로는 제너레이터를 활용해 동작합니다만, 이 책에서는 다루지 않습니다. 관심 있는 독자는 〈Async-Await ≈ Generators + Promises〉5를 읽어보시길 바랍니다.

* https://url.kr/ogculf

 

 

비동기 개념 마무리

자바스크립트로 프로젝트를 하면, 무조건 맞닥뜨리게 되는 것이 자바스크립트의 비동기성입니 다. 예전에는 콜백으로 힘들게 코드를 작성하던 시절이 있었습니다만, 이제는 Promise와 async await 덕분에 비동기 코드를 가독성 좋게, 디버깅하기 편하게 작성할 수 있게 되었습니다. 특히 async await는 다음 장부터 별다른 설명 없이 사용하게 되므로, 학습을 하다가 잘 이해가 안 되면 다시 학습하기 바랍니다.

▼ call back, promise, async await 비교

 

 

핵심 용어

  1. 동기는 모든 코드가 순차적으로 실행된다는 의미이며, 비동기는 순서대로 실행되지 않습니다. Node.js에서 비동기 코드는 이벤트 루프에 의해서 실행됩니다.
  2. 콜백은 비동기 프로그래밍을 구현하는 기법입니다. 요청이 완료되었을 때 실행해야 하는 콜백 함수를 매개변수로 같이 넘기는 방법입니다.
  3. Promise는 병행 프로그래밍 언어에서 프로그램 실행을 동기화하는 데 쓰는 객체입니다.
  4. async와 await는 프라미스를 반환하는 함수를 실행할 때 사용하는 키워드입니다.

 

 

추가로 알아보기

  1. async 작동 방식 : https://medium.com/hackernoon/async-await-generators-promises-51f1a6ceede2
  2. Promise API : https://ko.javascript.info/promise-api
  3. async 이터레이터와 제너레이터 : https://ko.javascript.info/async-iterators-generators

 

 

자바스크립트 비동기 개념에 익숙해지기

박승규


아직도 개발이 재미 있는 15년차 천상 개발자입니다. 웹 개발, 게임 백엔드 개발, 플랫폼 및 인프라 개발 등 다양한 영역을 경험했습니다. 현재는 카카오엔터테인먼트에서 백엔드 개발자로 일합니다.


현) 카카오엔터테인먼트 페이지 서비스 개발팀
전) 트리노드 (포코팡, 포코포코) 서버 개발자
전) NHN Japan 플랫폼 개발팀

Leave a Reply

©2020 GoldenRabbit. All rights reserved.
상호명 : 골든래빗 주식회사
(04051) 서울특별시 마포구 양화로 186, 5층 512호, 514호 (동교동, LC타워)
TEL : 0505-398-0505 / FAX : 0505-537-0505
대표이사 : 최현우
사업자등록번호 : 475-87-01581
통신판매업신고 : 2023-서울마포-2391호
master@goldenrabbit.co.kr
개인정보처리방침
배송/반품/환불/교환 안내