TIL 50일차 - 에러 핸들링 미들웨어 적용

박찬웅·2023년 3월 27일
0

항해99

목록 보기
55/105

23년 3월 27일

배운 것

금일에는 이전에 모두 marge를 한 dev에서 에러핸들링 미들웨어를 한번 적용해보고 이해하는 시간을 가졌다.

시도 한 것

지금까지 우리는 에러핸들링을 했다면 단순하게 try..catch 문을 이용해서 대부분 구현을 했을 것이다. 하지만 이 방식은 예상치 못한 오류로 catch문을 쓰게 되면 계속 리턴하고 에러 핸들링을 해야 한다는 단점이 있었다. 그리고 너무 많은 retrun은 그렇게 가독성이 좋지 않은 코드이기도 하다. 그래서 우리 백앤드 팀은 에러 핸들링을 별도로 미들웨어 파일을 만들고 그리고 기존에 req, res만 있었던 것에서 next도 추가를 해서 나를 포함해 팀원 3명이 동시에 같이 구현해보는 시간을 가지게 되었다.

module.exports = class CustomError extends Error{
    constructor(message,status){
        super()
        this.message = message
        this.status = status
    }

먼저 기존에 로그인 인증하기 위한 미들웨어 폴더에서 errorhandler 파일을 만들고 다음과 같은 코드를 작성하였다. 이 코드는 CustomError 클래스를 만들어서 this.message는 에러 메시지, this.status = status는 상태코드를 나타낸다.

//에러핸들링 미들웨어
app.use((err, req, res, next) => {
    res.status(err.status || 500);
    err.message = err.message || '예상치 못한 에러가 발생하였습니다.'
  
    console.error(err.stack)
    res.json({errormessage : err.message});
})

그런 다음에 app.js 파일에 다음 코드를 추가를 해준다. 여기도 보면 알겠지만 기존 req 앞에 err도 보이게 되는데 해당 부분은 err를 처리하는 코드를 말한다. 만약 catch문으로 빠져서 예상치 못한 오류가 발생되면 이 때애는 '예상치 못한 에러가 발생하였습니다.'뜨고 500번 HTTP 상태 코드를 반환해준다.
catch문으로 빠지지 않고 기존 try문에서 에러 핸들링이 걸렸으면 정상적으로 에러핸들링이 걸린게 있으면 해당 상태코드와 에러메시지가 나타나게 해주게 된다. 여기까지만 들으면 여전히 이해가 되지 않을 것이다. 실제로 이전에 내가 작성했던 댓글 삭제 API로 예시를 들어보겠다.

const CustomError = require("../middlewares/errorhandler.js")
...
router.delete("/:postId/comments/:commentId",authMiddleware, async(req, res,next) => {
    try {
        const { postId, commentId } = req.params;
        const { userId } = res.locals.user;
        // 게시글 존재 확인
        const post = await Posts.findOne({
            where: { postId: postId }
        });
        // 게시글이 존재하지 않는 경우
        if (!post) {
            throw new CustomError("게시글이 존재하지 않습니다",404)
        }
        // 댓글을 조회합니다.
        const existComment = await Comments.findOne({ where: { commentId: commentId } });
        if (!existComment) {
            throw new CustomError("댓글이 존재하지 않습니다",404)
        }
        // 로그인한 회원의 유저 아이디와 댓글 작성한 회원 아이디가 다른 경우
        if (existComment.userId !== userId)  {
            throw new CustomError("댓글 삭제의 권한이 존재하지 않습니다",403)
        }
        // 댓글의 권한을 확인하고, 댓글을 삭제합니다.
        await Comments.destroy(
            { where: { commentId: commentId } }
        );
        res.status(200).json({ message: "댓글을 삭제하였습니다." });
    } catch(err) {
        next(err)
    }
});

댓글 삭제 API 예시인데 보면 맨 위에 CustomError 라는 변수를 해서 에러핸들링 미들웨어를 가져온다. 그리고 아래 코드를 보면 이전과 좀 다른 것이 보일 수 있는데 next가 추가된 것을 볼 수 있다. 기존에는 에러핸들링을 했다면

retrun res.status(400).json({errormessage: "댓글이 존재하지 않습니다"})

이렇게 작성을 햇을 것이다. 이것을 맨 최상단을 보면 throw 함수를 던져서 new CustomError를 가져와서 에러메시지와 상태코드만 적으면 된다.

throw new CustomError("게시글이 존재하지 않습니다",404)

또한 catch문에서도 원래라면 여기에도 똑같이 아까 그 return 코드처럼 작성했을 것이다.
하지만 에러핸들러를 err로 만들어서 catch문으로 빠진 예외처리는 app.js에 있는 err를 받아서 아까 말한 500번의 상태코드와 그 메시지를 나타나게 한다. 이렇게 하면 코드 가독성도 좀 더 좋와질 뿐더러 return문으로 빠지지 않고 적용하게 되니 코드 실행속도도 개선되었다.

오늘 새롭게 배운건 이게 끝이였고, 나머지는 프론트앤드분들과 회의를 통해서 무한스크롤과 사진 하나 업로드 하는 방식을 하기로 결정하였다. 해당부분은 지금 프론트앤드분들이 구현을 하고 있는 중이다. 그래서 저녁먹고 남은 시간동안 우리는 3계층 아키텍쳐로 분리를 하는 작업을 시작하였고, 하다가 시간이 늦어 전부 구현하지는 못한 상태로 오늘 하루는 마무리 하였다.

해결

에러핸들러 미들웨어을 이용해서 상태코드와 메시지를 가독성을 높혔고, 이해하는데 큰 도움이 되었다.

알게 된 점

이전까지는 에러핸들러를 전혀 쓰지 않았다가 팀원 한분이 에러핸들러 적용해보자고 해서 해당 부분
공부를 하게 되고 적용하게 되었다. 그전까지는 전부다 catch문에서도 모두 에러메시지를 거기서 다 만들었기 때문에 코드도 더 길었다는 단점도 있었다. 하지만 에러핸들러 미들웨어를 다같이 동시에 같이 코드를 쓰고 이해해보니까 어떻게 사용하는지 잘 알게되었다. 이제는 귀찮게 일일히 retrun res문을 쓰지 않고 그냥 상태메시지랑 상태코드만 적으면 되고, catch문에서도 next(err)만 적으면 끝이니 코드 길이도 줄어드는 역할도 한 것 같았다. 이번에 팀원 한분을 통해서 새로운 요소를 배우게 되어서 좋은 경험이였다.

앞으로 할 일

내일은 3계층 아키텍쳐 분리하는 작업을 가지고 시간이 남으면 사진 저장을 어떻게 데이터베이스에 저장할지 고민을 해볼 생각이다. 그리고 프론트앤드분들이 무한스크롤 구현에 성공하면 우리 백앤드팀도 그 부분에서 코드를 추가하거나 수정해야 할 작업이 있을 계획이다.

profile
향해 13기 node.js 백앤드

0개의 댓글