나는 결국 모든 Express 컨트롤러에서 try-catch를 쓰는 것을 멈췄다 (당신도 그래야 합니다)

발행: (2025년 12월 25일 오전 10:15 GMT+9)
4 min read
원문: Dev.to

Source: Dev.to

Cover image for I finally stopped writing try-catch in every Express controller (and you should too)

솔직히 말해봅시다. 우리 모두 Express에서 오류 처리를 작성하는 것을 싫어합니다.

아름답고 깔끔한 컨트롤러 함수로 시작합니다. 이 함수는 한 가지 일을 해야 합니다: 사용자를 등록하는 것. 하지만 현실은 달라요. 요청 본문을 검증해야 하고, 이메일이 MongoDB에 존재하는지 확인해야 하며, 예상치 못한 충돌을 잡아야 합니다.

눈 깜짝할 사이에 5줄짜리 함수가 거대한 try/catch 블록에 둘러싸인 50줄짜리 스파게티 괴물로 변해버립니다.

예전에는 이 보일러플레이트를 프로젝트마다 복사‑붙여넣기 하곤 했지만, 더 깔끔한 방법을 찾게 되었습니다. 최근에 ds-express-errors를 사용하기 시작했는데, 덕분에 백엔드 로직 코딩이 다시 재미있어졌습니다.

옛날 나 방식 🍝

// controllers/authController.js
const User = require('../models/User');

exports.register = async (req, res, next) => {
  try {
    // 1. Manually checking for missing fields... boring.
    if (!req.body.email || !req.body.password) {
      return res.status(400).json({ status: 'error', message: 'Missing fields' });
    }

    const newUser = await User.create(req.body);

    res.status(201).json({ success: true, data: newUser });
  } catch (err) {
    // 2. The "Error parsing hell"
    // Is it a validation error? A duplicate key? Who knows?
    if (err.name === 'ValidationError') {
      const msg = Object.values(err.errors).map(val => val.message).join(', ');
      return res.status(400).json({ success: false, message: msg });
    }

    if (err.code === 11000) {
      return res.status(409).json({ success: false, message: 'Email already taken' });
    }

    // 3. Just giving up
    console.error(err);
    res.status(500).json({ success: false, message: 'Server Error' });
  }
};

컨트롤러가 20개라면, 이 catch 블록이 20번 복제됩니다. 오류 응답 형식을 바꾸면 모든 것을 리팩터링해야 합니다.

“새로운 나” 접근법 ✨

// controllers/authController.js
const User = require('../models/User');
const { asyncHandler, Errors } = require('ds-express-errors');

// No try‑catch. Just logic.
exports.register = asyncHandler(async (req, res) => {
  const newUser = await User.create(req.body);

  // Need to throw a custom error? Easy.
  if (!newUser) {
    throw Errors.BadRequest('Could not create user');
  }

  res.status(201).json({ success: true, data: newUser });
});

그게 전부입니다.

”잠깐, MongoDB가 오류를 발생시키면 어떻게 되나요?”

The library’s middleware automatically detects that it’s a Mongoose error.

  • Duplicate Key (E11000) → 400 Bad Request with “Duplicate field value entered”.
  • Validation Error → joins your validation messages and sends a 400.
  • JWT Error → 401 Unauthorized.

I didn’t have to write a single if statement for that.

설정은 정말 간단합니다

const express = require('express');
const { errorHandler } = require('ds-express-errors');

const app = express();

// ... define your routes here ...

// Just put this at the END of your middleware chain
app.use(errorHandler);

app.listen(3000, () => console.log('Server is flying 🚀'));

보너스: 밤에 더 잘 자기 (Graceful Shutdown)

const { initGlobalHandlers, gracefulHttpClose } = require('ds-express-errors');

const server = app.listen(PORT);

initGlobalHandlers({
  // Wraps the server close method safely
  closeServer: gracefulHttpClose(server),

  // Close your DB connections here
  onShutdown: async () => {
    console.log('Closing DB...');
    await mongoose.disconnect();
  }
});

이제 SIGTERM이나 처리되지 않은 거부가 발생하면, 앱이 종료하기 전에 정리 작업을 수행합니다.

Verdict

수많은 오류 처리기가 있지만, 저는 이걸 고수합니다. 왜냐하면 zero dependencies가 전혀 없고 TypeScript를 바로 지원하기 때문입니다. 코드에서 불필요한 잡음을 제거해 줍니다.

Link:

Back to Blog

관련 글

더 보기 »

Dev 커뮤니티 신규 회원

여러분 안녕하세요, 저는 dev 커뮤니티에 새로 온 사람이고 코딩 여정을 다시 시작하고 있습니다. 저는 2013년부터 2018년까지 코딩을 했었습니다. 그 이후에 새로운 기회를 탐색했고, st...