핵심 요약
Express 4에서 async 핸들러가 던진 에러는 자동으로 에러 미들웨어로 안 가서 요청이 멈추거나 unhandledRejection이 된다. 모든 async 핸들러를 래퍼로 감싸 next(err)로 넘기고, 중앙 에러 미들웨어에서 한 번에 처리하는 게 정석이다.
1. async 래퍼
const wrap = fn => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next)
app.get('/users', wrap(async (req, res) => {
const users = await db.users() // throw 하면 next(err)로 감
res.json(users)
}))
2. 중앙 에러 미들웨어
app.use((err, req, res, next) => {
console.error(err)
res.status(err.status || 500).json({ error: err.message })
}) // 인자 4개여야 에러 미들웨어로 인식
3. 함정
- 에러 미들웨어는 인자 4개(err, req, res, next)여야 한다 — 하나 빠지면 일반 미들웨어로 취급
- Express 5는 async 에러를 자동 전달하지만, 여전히 명시적 처리가 안전하다
- 응답을 두 번 보내지 않도록
res.headersSent확인
자주 묻는 질문
try/catch를 라우트마다 쓰면 안 되나요?
되지만 반복이 많고 빠뜨리기 쉽습니다. wrap 헬퍼로 한 번 감싸면 모든 라우트가 일관되게 중앙 에러 미들웨어로 흘러갑니다.
Express 5에선 필요 없나요?
5는 async 에러를 자동으로 next에 넘겨줍니다. 다만 중앙 에러 미들웨어 설계는 여전히 필요합니다.

댓글 0