TIL 41일차 - 댓글 API 구현

박찬웅·2023년 3월 18일
0

항해99

목록 보기
46/105

23년 3월 18일

배운 것

본격적으로 구현에 맞써서 나는 댓글 API를 구현하기 시작하였다.

시도 한 것

오늘부터 본격적으로 구현을 시작하였다. 우리 조의 백앤드는 총 3명인데 로그인, 회원가입 API을 구현하시는 한 분과 게시글 API 한 분, 그리고 댓글 API 한분 이렇게 분류해서 작업을 시작하였다. 나는 같이 작업하는 백앤드 팀원 중에서 내가 가장 실력이 약한 모습이 있어서 상대적으로 구현할게 적은 댓글 API를 구현을 맡게 되었다.
오늘 내가 구현한 API는 총 3가지로였다. 각각 해당 게시글의 댓글 작성 API, 해당 게시글의 댓글 전체 조회 API, 해당 게시글의 댓글 삭제 API 이였다. 원래는 해당 게시글의 댓글 수정 API 넣으려고 했었으나 프론트앤드분들이 작업량이 많을 것 같아서 이 부분은 우선 제외했다. 나중에 여유가 된다고 하면 추가 할 예정이다.
참고로 로그인 구현을 어제 팀원 한 분이 기본적인 부분만 빠르게 마무리 하여서 로그인 회원을 가지고 시작하였다. 아직 게시글 API는 완성되지 않았기 때문에 임의의 Posts 테이블에 임의로 컬럼을 추가해서 게시글 1~2개 만들고 진행하였다. API 테스트는 이전까지는 VSCode에서 제공하는 썬더 클라이언트로 진행했었는데, 불편한 점이 많아서 별도로 Postman 프로그램을 설치해서 API 테스트를 시작하였다.

먼저 첫번째 해당 게시글의 댓글 작성 API를 구현하였다.

router.post('/:postId/comments', authMiddleware, async(req, res) => {
    try {
        const { postId } = req.params;
        const { comment } = req.body;
        const { userId } = res.locals.user;

        // 게시글 미존재
        if (!postId) {
            res.status(404).json({
                errorMessage: "게시글이 존재하지 않습니다"
            });
            return;
        }
        // 댓글 미입력
        if (!comment) {
            res.status(412).json({
                errorMessage: "댓글 내용의 형식이 일치하지 않습니다."
            });
            return;
        }

        // 댓글 생성
        await Comments.create({
            postId: postId,
            userId: userId,
            comment : comment,
        });

        res.status(201).json({ message: "댓글 작성에 성공하였습니다." });

    } catch(err) {
        console.log(err);
        res.status(400).json({
            errorMessage: "댓글 작성에 실패했습니다."
        });
        return;
    }
});

댓글 작성 API는 POST 요청을 하였고, 로그인 하신 분들 한해서만 댓글 작성을 할 수 있게 구현하였다. 입력은 comment 하나만 입력하면 되었다. 댓글 작성 성공하면 '게시글 작성 완료' 메시지를 남겼다. 여러 애러 핸들링을 만들어서 해당 게시글이 없다던가, 댓글을 아무것도 적지 않았다던가 하면 에러메시지 처리를 하였다.

다음은 두번째로 댓글 조회 API를 구현하였다.

router.get('/:postId/comments', async(req, res) => {
    try {
        const { postId } = req.params;

        const comments = await Comments.findAll({
            attributes: ["commentId", "userId", "comment", "createdAt", "User.nickname"],
            include: [
                {
                    model: Users,
                    attributes: [],
                },
            ],
            where : [{ postId: postId }], 
            order: [['createdAt', 'DESC']],
            raw: true,
        })

        res.status(200).json({ comments : comments });

    } catch(err) {
        console.log(err);
        res.status(400).json({
            errorMessage: "댓글 조회에 실패했습니다."
        });
        return;
    }
});

다음은 해당 게시글의 댓글 조회 API를 구현하였다. 댓글 조회는 GET 요청을 하였고, 로그인 안 한 사람도 댓글을 볼 수 있게 구현을 했으며, 나중에 프론트앤드 분들이 댓글 출력할때 해당 데이터를 사용 할 수 있게 구현하였다.
이 부분이 조금 어려웠는데 그전까지 내가 개인과제를 할때 Comments 테이블에는 nickname 컬럼도 추가를 해서 include를 사용하지를 않았었다. 하지만 이번 Comments 테이블에는 nickname 컬럼이 없었기에 Users 테이블의 nickname를 가져와야 했기 때문에 include를 사용을 하게 되었고 이 부분이 처음 적용해봤던 개념이라 약간 팀원에 도움을 받아서 진행하였다. 특이한 점은 Users라고 해서 Users.nickname라고 하면 오류가 나는데 User.nickname로 하면 정상적으로 출력되었다. 이 부분은 mysql 자체가 s로 끝나는 것이 복수 취급을 받아서 그런지 s를 생략해야 정상적으로 출력된다는 것을 알게 되었다.

마지막으로 댓글 삭제 API를 구현하였다.

router.delete("/:postId/comments/:commentId", authMiddleware, async(req, res) => {
    try {
        const { postId, commentId } = req.params;
        const { userId } = res.locals.user; // 토큰을 검사하여, 유효한 토큰일 경우에만 댓글 삭제 가능
        // 댓글을 조회합니다.
        const existComment = await Comments.findOne({ where: { commentId } });
        // 게시글이 존재하지 않는 경우
        if (!postId) {
            res.status(404).json({
                errorMessage: "게시글이 존재하지 않습니다."
            });
            return;
        }
        // 댓글이 존재하지 않는 경우
        if (!existComment) {
            res.status(404).json({
                errorMessage: "댓글이 존재하지 않습니다."
            });
            return;
        }
        // 로그인한 회원의 유저 아이디와 댓글 작성한 회원 아이디가 다른 경우
        if (existComment.userId !== userId)  {
            res.status(403).json({
                errorMessage: "댓글 삭제의 권한이 존재하지 않습니다."
            });
            return;
        }
        // 댓글의 권한을 확인하고, 댓글을 삭제합니다.
        await Comments.destroy(
            { where: { commentId, userId: userId } }
        );
        // 게시글 삭제
        if (existComment) {
            res.status(200).json({ message: "댓글을 삭제하였습니다." });
            return;
        } else {
            res.status(401).json({ errorMessage: "댓글이 정상적으로 삭제되지 않았습니다." });
            return;
        }
    } catch(err) {
        console.log(err);
        res.status(400).json({
            errorMessage: "댓글 삭제에 실패했습니다."
        });
        return;
    }
});

댓글 삭제 API 같은 경우에는 DELETE 요청을 하면 된다. 일단 이 부분은 나중에 프론트앤드 분들이랑 의논해서 변경될 요소가 많은 부분이라 우선 기본적인 API만 작성하였다. 이 기능도 로그인 한 분들만 이용이 가능하며, 그리고 한가지 조건으로 자신이 쓴 댓글만 삭제 할 수 있게 구현을 하였다. 다른사람꺼 댓글을 수정하면 안되기에 userId와 비교해서 다르면 댓글 삭제 못하게 구현하였다. 그 외로 해당 게시글이 없거나 해당 댓글이 없으면 에러처리도 하였다.

해결

오늘 기본적인 댓글 API를 마무리 하였다.

알게 된 점

나는 그래도 기본적으로 3주동안 개인프로젝트를 참고하면서 API를 구현을 하였기 때문에 생각 외로 오래 걸리진 않았다. 다만 다른 팀원 분들은 로그인 API는 구현했는데 refreshToken 구현을 추가적으로 도전하고 싶다고 하셔서 구현 도전 중이시고, 다른 한분은 게시글에서 사진 업로드를 어떻게 Posts 테이블에 어떻게 받아올지 이 부분에서 구현 도전을 하고 계시고 있다. 이거에 비하면 나는 기본적인것에 외로 새로운 것을 아직 구현해본적은 없었다. 아직 구현력이 부족하지만 만약 팀원들 중에 해당 구현을 성공한다면, 나중에 나도 팀원들이 작성한 코드들을 보면서 이걸 응용 할 수 있기 때문에 어떻게 구현 할 지 한번 물어 볼 것 같다. 그래도 오늘 댓글 API 작성은 마무리 하였다.

앞으로 할 일

내일은 일요일이라 쉬는날이기에 간만에 오랫동안 밖에서 당일 소풍을 갈 예정이다. 그래서 그간 자주 집에서 나가지 않아서 신선한 공기를 맡으며 쉴 것 같다. WIL은 내일 하루종일 밖에 있을 것 같아서 오늘 늦은 시간에 미리 올릴 것 같다.

profile
향해 13기 node.js 백앤드

0개의 댓글