Promise를 통한 비동기 코딩(복습)
Promise로 비동기 동작을 하는 것을 복습해 봅시다. 어떤 원격 REST API를 호출을 하여 게시물 작성자의 이름을 리턴하는 함수를 작성하고 그 함수를 호출해보았습니다.
function fetchAuthorName(postId) {
return fetch(`https://jsonplaceholder.typicode.com/posts/${postId}`)
.then((response) => response.json())
.then((post) => post.userId)
.then((userId) => {
return fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
.then((response) => response.json())
.then((user) => user.name);
});
}
fetchAuthorName(1).then((name) => console.log("name:", name));
- 출력
name: Leanne Graham
브라우저 내장 함수인 fetch()를 호출하여 Promise 객체를 리턴받은 후에, Method Chaning 기법을 통해 then() 메서드를 연쇄적으로 호출하고 있습니다.
fetchAuthorName() 함수 자체도 결국 게시물 작성자의 이름을 얻을 수 있는 Promsie 객체를 리턴하기 때문에, 호출할 때도 then() 메서드를 사용하여 게시물 작성자의 이름을 출력해야 합니다.
문제점
Promise를 사용하면 자연스럽게 위와 같은 코딩 스타일로 비동기 처리를 하게되는데 여기에는 여러가지 문제점이 있습니다.
- 디버깅
위 코드의 8번째 줄을 아래처럼 에러가 발생하도록 의도적으로 수정 후에 코드를 실행을 해보면 다음과 같은 에러 메시지를 보게 됩니다.
.then(user => user1.name);
- 결과
ReferenceError: user1 is not defined
at fetch.then.then.then.then.then (<anonymous>:7:29)
동일한 이름의 메서드인 then()을 연쇄적으로 호출하고 있어서 도대체 몇 번째 then()에서 문제가 발생한 건지 Stack Trace을 보더라도 혼란스러울 수 있습니다.
이것 또한 callback 함수처럼 코드가 길어지다 보면 then 지옥이 펼쳐질 것이 분명합니다. 가독성이 너무 떨어지고 뭐가 무엇을 불러오는 코드인지도 점점.... 모르게 될 것입니다.
async/await 키워드를 통한 비동기 코딩
Promise의 이러한 불편한 점들을 해결하기 위해 ES7(ES2017)에서 async/await 키워드가 추가되었습니다. async/await 키워드를 사용하면 비동기 코드를 마치 동기 코드처럼 보이게 작성할 수 있습니다.
위의 샘플 코드를 async/await 키워드를 이용하여 재작성 해보겠습니다.
async function fetchAuthorName(postId) {
const postResponse = await fetch(
`https://jsonplaceholder.typicode.com/posts/${postId}`
);
const post = await postResponse.json();
const userId = post.userId;
const userResponse = await fetch(
`https://jsonplaceholder.typicode.com/users/${userId}`
);
const user = await userResponse.json();
return user.name;
}
fetchAuthorName(1).then((name) => console.log("name:", name));
- 출력
name: Leanne Graham
달라진 점을 찾아 보면 먼저 함수 선언부를 보면 async 키워드가 function 앞에 붙었다는 것을 알 수 있습니다. 그리고 Promise를 리턴하는 모든 비동기 함수 호출부 앞에는 await 키워드가 추가되었습니다.
await 키워드는 async 키워드가 붙어있는 함수 내부에서만 사용할 수 있으며 비동기 함수가 리턴하는 Promise로 부터 결과값을 추출해줍니다. 즉, await 키워드를 사용하면 일반 비동기 처리처럼 바로 실행이 다음 라인으로 넘어가는 것이 아니라 결과값을 얻을 수 있을 때까지 기다려줍니다. 따라서 일반적인 동기 코드 처리와 동일한 흐름으로 (함수 호출 후 결과값을 변수에 할당하는 식으로) 코드를 작성할 수 있으며, 따라서 코드를 읽기도 한결 수월해집니다. 상수를 선언하는 이름만 잘 지정한다면 무엇을 구하는 것인지 더욱 잘 알 수 있겠죠?
직접 활용한 예시
const task = cron.schedule(' 0 0 * * * *', async () => {
const KR_TIME_DIFF = 9 * 60 * 60 * 1000;
let today = new Date(new Date().getTime() + KR_TIME_DIFF);
const waitingScheduler = await schedulerDao.getSchedulerDetailInfo(
`AND cj.state ='waiting'`
);
for (j of waitingScheduler) {
let startday = new Date(j.start_day);
let endday = new Date(j.end_day);
if (startday < today && endday > today) {
await schedulerDao.patchSchedulerWorking(j.idx);
}
}
.
.
.
.
}
제가 스케줄러를 만들 때 for 문 안에 비동기 함수인 await 를 사용한 적이 있습니다. 비동기 함수와 아닌 함수의 차이점을 알아보려고 await 을 사용하지 않고 문장을 실행해본 적이 있습니다.
await을 사용하지 않은 for 문은 당연히 비동기 함수보다 속도가 훨씬 빠르겠죠? 동시 다발적으로 실행을 시키기 때문입니다. 하지만, 너무나도 많은 동작이 이루어지고 여러가지 동작을 실행시키다보니까 다른 곳에서 사용한 변수의 값이 DB에 저장되는 것을 알게 되었습니다.
예를 들어, 첫 번째 동작시킨 단어가 '샌드위치'고 두 번째 동작시킨 단어가 '핫도그' 이면 DB에는 샌드위치가 들어가야하는데 이상하게 핫도그가 들어가는 현상이 발생하고는 했습니다. 뭐 제가 코드를 잘못 작성했을 수도 있지만 제가 짠 코드에서는 이상하게 동작을 해서... 비동기로 실행하는 for문을 작성했습니다.
이렇게 비동기로 실행해야하는 코드들이 생각보다 많기 때문에 지속적으로 사용하는 것을 추천합니다. 처음에는 이거 왜 쓰는거야 하는데, 저처럼 한번씩 '아...이래서 사용하는구나'를 알게 될 것입니다.
출처
'코딩 개발 > Javascript' 카테고리의 다른 글
Javascript & DOM (0) | 2023.06.04 |
---|---|
Execution Context & Call Stack (0) | 2023.05.14 |
JavaScript - Promise (0) | 2023.02.07 |
JavaScript - Callback (0) | 2023.02.06 |
JavaScript - 최대공약수, 최소공배수 (0) | 2023.01.15 |