글 작성자: 망고좋아
반응형

🎯 Async Request Handler

  • exprss의 공식 기술은 아니다. 패턴 중 하나이다.
  • Async Request Handler를 사용하면 async를 쉽게 사용할 수 있고, Request Handler를 처리하는데 공통적으로 오류처리를 할 수 있거나 간단하게 구현할 수 있다.

 

📝 request handler의 오류처리

  • request handler에서 오류를 처리하기 위한 방법
    • promise().catch(next)
    • async function, try ~ catch, next

 

📝 async request handler

  • async의 비동기 처리는 매우 편리하지만, 매번 try - catch 구문을 작성하는 것은 귀찮고 실수하기 쉽다.
  • request handler를 async function으로 작성하면서 try ~ catch, next를 자동으로 할 수 있도록 구성한 아이디어다.
// 함수형 미들웨어
const asyncHandler = (requestHandler) => {
  return async (req, res, next) => {
    try {
      await requestHandler(req, res); // 여기서 발생되는 에러들은 catch에서 잡힘
    } catch (err) {
      next(err);
    }
  };
};

//---
router.get("/", asyncHandler(async (req, res) => {
    const posts = await Posts.find({});
    if (posts.length < 1) {
      throw new Error("Not Found");
    }
    res.render("posts/list", { posts });
  })
);
  • asyncHandler는 requestHandler를 매개변수로 갖는 함수형 미들웨어다.
  • 전달된 requestHandler는 try ~ catch로 감싸져 asyncHandler 내에서 실행되고, throw 되는 에러는 자동으로 오류처리 미들웨어로 전달되도록 구성된다.
  • 즉, 오류처리를 간단하게 할 수 있는 미들웨어

 

📝 실습 예제

module.exports = (requestHandler) => {
  return async (req, res, next) => {
    try {
        await requestHandler(req, res);
    } catch(err) {
        next(err);
    }
  }
}

 

🛠 기존 코드

const { Router } = require('express');
const { Post } = require('../models');
const asyncHandler = require('../utils/async-handler');

const router = Router();

router.get('/', async (req, res) => {
  if (req.query.write) {
    res.render('post/edit');
    return;
  }

  const posts = await Post.find({});

  res.render('post/list', { posts });
});

router.get('/:shortId', async (req, res) => {
  const { shortId } = req.params;
  const post = await Post.findOne({ shortId });

  if (req.query.edit) {
    res.render('post/edit', { post });
    return;
  }

  res.render('post/view', { post });
});

router.post('/', async (req, res, next) => {
  const { title, content } = req.body;

  try {
    if (!title || !content) {
      throw new Error('제목과 내용을 입력해 주세요');
    }

    const post = await Post.create({ title, content });
    res.redirect(`/posts/${post.shortId}`);
  } catch (err) {
    next(err);
  }
});

router.post('/:shortId', async (req, res, next) => {
  const { shortId } = req.params;
  const { title, content } = req.body;
  try {
    if (!title || !content) {
      throw new Error('제목과 내용을 입력해 주세요');
    }

    await Post.updateOne({ shortId }, { title, content });
    res.redirect(`/posts/${shortId}`);
  } catch (err) {
    next(err);
  }
});

router.delete('/:shortId', async (req, res, next) => {
  const { shortId } = req.params;
  await Post.deleteOne({ shortId });
  res.send('OK');
});

module.exports = router;

 

🛠 리팩토링

const { Router } = require('express');
const { Post } = require('../models');
const asyncHandler = require('../utils/async-handler');

const router = Router();

router.get('/', asyncHandler(async (req, res) => {
  if (req.query.write) {
    res.render('post/edit');
    return;
  }

  const posts = await Post.find({});

  res.render('post/list', { posts });
}));

router.get('/:shortId', asyncHandler(async (req, res) => {
  const { shortId } = req.params;
  const post = await Post.findOne({ shortId });

  if (req.query.edit) {
    res.render('post/edit', { post });
    return;
  }

  res.render('post/view', { post });
}));

router.post('/', asyncHandler(async (req, res) => {
  const { title, content } = req.body;
  if (!title || !content) {
      throw new Error('제목과 내용을 입력해 주세요');
  }

  const post = await Post.create({ title, content });
  res.redirect(`/posts/${post.shortId}`);
}));

router.post('/:shortId', asyncHandler(async (req, res) => {
  const { shortId } = req.params;
  const { title, content } = req.body;
  if (!title || !content) {
    throw new Error('제목과 내용을 입력해 주세요');
  }

  await Post.updateOne({ shortId }, { title, content });
  res.redirect(`/posts/${shortId}`);
}));

router.delete('/:shortId', asyncHandler(async (req, res) => {
  const { shortId } = req.params;
  await Post.deleteOne({ shortId });
  res.send('OK');
}));

module.exports = router;
반응형