Node.js 8 LTS에서 util.promisify로 콜백 지옥 탈출하기
문제 상황
회사 프로젝트에서 Node.js 6에서 8 LTS로 마이그레이션을 진행하면서, 콜백 지옥으로 작성된 파일 처리 로직을 개선할 기회가 생겼다. 기존에는 bluebird 같은 외부 라이브러리를 사용했지만, Node.js 8부터 util.promisify가 표준으로 제공된다.
기존 콜백 코드
const fs = require('fs');
fs.readFile('config.json', 'utf8', (err, data) => {
if (err) {
console.error(err);
return;
}
const config = JSON.parse(data);
fs.writeFile('output.json', JSON.stringify(config), (err) => {
if (err) console.error(err);
});
});
util.promisify 적용
const fs = require('fs');
const util = require('util');
const readFile = util.promisify(fs.readFile);
const writeFile = util.promisify(fs.writeFile);
async function processConfig() {
try {
const data = await readFile('config.json', 'utf8');
const config = JSON.parse(data);
await writeFile('output.json', JSON.stringify(config));
} catch (err) {
console.error(err);
}
}
주의사항
util.promisify는 Node.js 콜백 컨벤션 (err, value) => {}을 따르는 함수에서만 작동한다. 커스텀 콜백 패턴을 사용하는 경우 util.promisify.custom 심볼을 활용해야 한다.
function customFunc(callback) {
// 커스텀 로직
}
customFunc[util.promisify.custom] = () => {
return new Promise((resolve, reject) => {
// Promise 래핑
});
};
결과
외부 의존성 없이 비동기 코드를 깔끔하게 작성할 수 있게 되었다. async/await와 조합하니 가독성이 크게 개선되었고, 에러 핸들링도 try-catch로 통일할 수 있었다. Node.js 8 LTS 도입을 고려 중이라면 util.promisify는 충분히 활용할 가치가 있다.