Node.js 8 LTS에서 async/await를 도입하며 겪은 점들
배경
10월에 Node.js 8이 LTS로 전환되면서 프로덕션 환경에 안정적으로 적용할 수 있게 되었다. 가장 큰 변화는 async/await를 네이티브로 사용할 수 있다는 점이었다. 기존 Promise 체이닝 코드를 리팩토링하면서 몇 가지 배운 점을 정리한다.
Promise 체이닝에서 async/await로
기존에는 이런 식으로 작성했다.
function getUserData(userId) {
return db.findUser(userId)
.then(user => {
return db.findPosts(user.id);
})
.then(posts => {
return posts.map(formatPost);
})
.catch(err => {
logger.error(err);
throw err;
});
}
async/await로 바꾸니 훨씬 읽기 쉬워졌다.
async function getUserData(userId) {
try {
const user = await db.findUser(userId);
const posts = await db.findPosts(user.id);
return posts.map(formatPost);
} catch (err) {
logger.error(err);
throw err;
}
}
실수했던 부분
초반에 병렬로 실행할 수 있는 부분을 순차적으로 처리해서 성능 저하가 있었다.
// 잘못된 예 - 순차 실행
const user = await db.findUser(userId);
const settings = await db.findSettings(userId);
// 개선 - 병렬 실행
const [user, settings] = await Promise.all([
db.findUser(userId),
db.findSettings(userId)
]);
Express 미들웨어에서의 에러 처리
Express는 아직 async 함수의 에러를 자동으로 캐치하지 못한다. 래퍼 함수를 만들어 해결했다.
const asyncHandler = fn => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
app.get('/users/:id', asyncHandler(async (req, res) => {
const user = await db.findUser(req.params.id);
res.json(user);
}));
정리
async/await 도입으로 콜백 지옥과 Promise 체이닝에서 벗어났다. 하지만 병렬 처리가 필요한 부분은 여전히 Promise.all을 사용해야 하고, 에러 핸들링도 명시적으로 처리해야 한다는 점을 잊지 말아야겠다.