TIL 51일차 - 3계층 아키텍쳐 분리

박찬웅·2023년 3월 28일
0

항해99

목록 보기
56/105

23년 3월 28일

배운 것

오늘은 저번주때 맛보기로만 구현했던 3계층 아키텍쳐를 실제 프로젝트에다도 한번 적용하는 작업을 가졌다.

시도 한 것

이제 지금까지 만든 API 것들을 기존 라우터에만 있는 것이 아니라 콜랙터, 서비스, 레파지토리로 3계층을 분리하는 작업을 가졌다. 마찬가지로 댓글과 좋아요 API를 직접 분리해보는 시간을 가졌다. 사실 해당 작업은 어제 저녁부터 시작했던 작업이였고 오늘 오후까지 구현은 마무리 하였다. 이후에는 내가 적은 코드를 리뷰를 하는 방식으로 하루를 마쳤다.

먼저 댓글 API 3계층 분리는 어렵진 않았다. 그나마 지난 주특기 프로젝트때 3계층 연습을 혼자서 해보기도 하였고, 그걸로 회원가입, 로그인, 게시글까지만 하본 적이 있었기 때문에 게시글을 참고하면서 하면 어렵진 않았다. 다만 일부는 어려운 부분은 있었지만 큰 문제는 없이 마무리 하였다. 그래서 따로 코드 리뷰는 생략을 하였다.

하지만 가장 큰 고비는 당연히 좋아요 API였다. 게시글 좋아요 API는 우선 지금까지와는 다르게 조건에 따라서 성공 메시지를 2개를 나타내는 것이라 기존처럼 성공 하나만 있을때와는 전혀 다른 방식으로 구현을 했어야 했다. 이전 댓글 API도 시행착오로 계속해서 어제 저녁부터, 오늘 이른 아침에 마무리 하였다. 따라서 좋아요 API는 직접 쓴 코드를 기록하기로 하였다.

const express = require('express')
const authMiddleware = require("../middlewares/auth-middleware.js");
const LikeController = require("../controllers/like.controller.js")
const likecontroller = new LikeController();
const router = express.Router()

// 게시글 좋아요 업데이트 API
// localhost:3000/posts/:postId/like
router.put('/:postId/like', authMiddleware, likecontroller.updateLike);

module.exports = router;

먼저 route 파일이다. 해당 부분은 크게 어려운 부분은 없었다. 좋아요을 수정하는 요청을 하였고 likecontroller클래스의 updateLike 메소드를 호출하는 로직이다.

const LikeService = require("../services/like.service.js");

class LikeController {
    constructor() {
        this.LikeService = new LikeService();
    }

    updateLike = async (req, res, next) => {
            const { postId } = req.params;
            const { userId } = res.locals.user; // 토큰을 검사하여 해당 회원 확인
        try {
            const isExistPost = await this.LikeService.existPost({postId})

            // 좋아요 유무 따져서 좋아요가 없으면 좋아요 등록, 반대인 경우는 좋아요 취소
            const message = await this.LikeService.updateLike({postId, userId, isExistPost})
            res.status(200).json({ message: message });

        } catch (err) {
          next(err)
        }
    }
}

module.exports = LikeController;

다음은 controller 부분인데 파람스랑 토큰을 정의하는건 문제 없었고, 대신 두가지 메소드를 만들어서 호출하는 방식을 만들었다. 처음에는 난 모든걸 하나의 메소드로 보내려는 로직을 짜려고 했으나 도무지 답이 보이지 않았는데, 꼭 무조건 메소드 하나만 만들 필요는 없다는 것을 알게 되었고, 팀원 한분과 도움을 받아서 구현을 하였다.
먼저 route에서 전달 받아서 콜백함수를 만들어 주었다. 그런 다음 각 메소드는 LikeService클래스로 가는데 existPost는 postId값을 참조해서 게시글이 존재하는지 확인하는 메소드이고, updateLike는 좋아요를 유무를 따져서 postId, userId, isExistPost 세가지 값을 가져와서 message 호출하게 구현을 하였다. message는 어떤 내용을 담을지는 이후 계층에서 설명 할 수 있으므로 우선은 이렇게 구현을 하였다.

const LikeRepository = require("../repositories/like.repository")
const CustomError = require("../middlewares/errorhandler.js")

class LikeService {
    constructor() {
        this.LikeRepository = new LikeRepository();
    }
    existPost = async ({postId}) => {
        const isExistPost = await this.LikeRepository.existPost({postId}); // 게시글 있는지 확인
        // 게시글 없을 때
        if (!isExistPost) {
            throw new CustomError("게시글이 존재하지 않습니다", 400)
        }
        return isExistPost;
    }

    updateLike = async ({postId, userId, isExistPost}) => {  
        // postId와 userId 검색
        const isLiked = await this.LikeRepository.findOneLike({postId, userId})
        // 좋아요 유무 확인 후 좋아요가 없으면, 좋아요 생성, 그 반대는 좋아요 삭제    
        if (!isLiked) {
            const likes = isExistPost.likecount + 1;
            await this.LikeRepository.createLike({postId, userId})
            await this.LikeRepository.updatePostLike({likes,postId})
            let message = "좋아요 등록에 성공하였습니다."
            return message;
        } else {
            const likes = isExistPost.likecount - 1;
            await this.LikeRepository.destroyLike({postId, userId})
            await this.LikeRepository.updatePostLike({likes,postId})
            let message = "좋아요 취소에 성공하였습니다."
            return message;
        }
    }


}

module.exports = LikeService;

다음은 service 계층 부분이다. 각각 메소드는 모두 controller에서 가져온 파라미터를 받는다. existPost 메소드에선 게시글이 없을 때 에러메시지를 에러메시지를 나타내고 반환을 해주는 역할을 했다. 그리고 나서 LikeRepository 클래스의 existPost 메소드로 전달을 해 주었다.

그리고 이제 가장 많이 골을 때렸던 부분인데 updateLike 메소드 부분이였다. 이 부분이 가장 햇갈렸고 구현하는데 가장 어려웠던 부분이였다. 우선 서비스에서는 보통 전달 메시지를 주긴 한다. 데이터베이스 부분은 repository에서 처리를 하니까 여기서 무려 4가지의 메소드를 넘겨주는 작업이 필요하였다. findOneLike는 좋아요 데이터베이스가 유무를 판별, createLike는 좋아요를 생성을 하는 역할, updatePostLike는 좋아요를 눌렀을 때 그때의 데이터베이스가 수정되는 역할, destroyLike는 좋아요를 삭제하는 역할을 하였다.
마지막으로 각각 조건에 message 변수를 선언하여서 return으로 반환해주면 이전에 controller에 message을 받아서 좋아요 상태를 나타내 주었다.

const { Likes, Posts } = require('../models');

class LikeRepository {
    constructor() { }

    existPost = async ({postId}) => {
        console.log(postId)
        return await Posts.findOne({where : {postId}});
    }

    findOneLike = async ({postId, userId}) => {
        return Likes.findOne({ where: { postId: postId, userId: userId } }); 
    }
    createLike = async ({postId, userId}) => {
        await Likes.create(
            { postId: postId, userId: userId, isLike: true }
        );
    }
    updatePostLike = async ({likes,postId}) => {
        await Posts.update(
            { likecount: likes },
            { where: { postId: postId } }
        )
    }
    destroyLike = async ({postId, userId}) => {
        Likes.destroy(
            { where: { postId: postId, userId: userId } }
        );
    }

}

module.exports = LikeRepository;

마지막으로 repository 계층 부분이였다. 해당 부분은 데이터베이스의 변화를 보통 여기서 구현을 많이 하기에 다음과 같이 구현을 하였다. 이 부분도 service와 엄청 어려웠던 부분이였다.

순서대로 메소드를 설명하자면 첫번째 existPost는 Posts 테이블에서 파람스에서 받았던 postId 값 조건을 찾는 역할을 하였다.
다음 findOneLike도 마찬가지로 Like 테이블에서 postId와 userId 값을 받아와서 조건을 찾는 역할을 하였다.
createLike는 좋아요가 없을 때 좋아요 데이터를 추가하는 역할, updatePostLike는 좋아요 있을때나 없을때 Post의 likecount 컬럼을 변화 해주는 역할, 마지막으로 destroyLike는 이미 있는 좋아요를 다시 취소 할때 Post의 likecount 컬럼을 삭제 해주는 역할을 해 주었다.

이렇게 해서 가장 고난했던 게시글 좋아요 API를 3계층 아키텍쳐로 구현하는데 성공하였다. 이후 저녁에는 팀원들이랑 3계층 분리한 작업을 코드 리뷰를 진행하였고, 팀원 한 분이 게시글에 사진을 저장하는 방법을 폴더에다 담는 방식인 multer 패키지를 이용하여서 구현하는데 성공하였다.
그 부분까지 해결을 하고 2차 배포를 마무리 하였다.

해결

댓글 API랑 게시글 좋아요 API를 3계층 아키텍쳐 분리를 하는데 마무리 하였다.

알게 된 점

3계층 아키텍처를 주특기 프로젝트 후반에 백엔드에서 전부 다 구현한 뒤 남는시간에 스스로 3계층 아키텍쳐를 잠깐 연습으로 적용을 해 보왔다. 다만 결국 실제 있었던 프로젝트에서는 시간 부족으로 끝내 구현하지는 못하였다. 하지만 이번에는 프로젝트에서 이미 구현 되었던 것을 팀원 각자 맡은 부분을 3계층으로 분리를 했고 처음으로 성공을 하여서 기분이 좋았다. 물론 좋아요 API는 나중에 팀원들이랑 같이 구현을 하긴 했었지만 무사히 마무리 할 수 있었던 이유는 백앤드 팀원들이랑 같이 라이브 코딩하면서 고민하고 생각 할 수 있어서 성공 한게 아닌가 싶었다. 그리고 이제 다 완성후 오후 늦게 모두 머지 하고 최종 테스트에서도 큰 문제 없이 마무리 되어서 덕분에 2차 배포까지 성공적으로 마무리 할 수 있었다고 생각을 하였다. 3계층 아키텍처 로직을 구현생각은 계속 해봐야 겠지만, 그래도 예전보다는 자신감을 얻었다.

앞으로 할 일

현재 팀원들과 무한스크롤을 이용해서 데이터베이스를 특정 부분만 조회 하게 하는 것이랑 유저 프로필 이미지가 아직 안되는 오류가 있는데 그 부분을 같이 해결하는 시간을 가질 것 같다.

profile
향해 13기 node.js 백앤드

0개의 댓글