TIL 37일차 - node 심화 개인과제(2)

박찬웅·2023년 3월 14일
0

항해99

목록 보기
42/105

23년 3월 14일

배운 것

어제에 이어서 계속해서 LV4 개인과제를 진행하였다. 오늘은 가장 구현하기 어려웠던 좋아요(Likes) DB 추가랑 그것에 관한 테이블 관계 설정한 다음에 게시글 좋아요 추가 기능이랑 게시글 좋아요 조회 기능 API를 추가하였다.

시도 한 것

오전 내내 Likes의 데이터베이스 마이그레이션을 하였고 관계를 다음과 같이 설정을 하였다.

	  // 1. Likes 모델에서
      this.belongsTo(models.Users, { // 2. Users 모델에게 N:1 관계 설정을 합니다.
        targetKey: 'userId', // 3. Users 모델의 userId 컬럼을
        foreignKey: 'userId', // 4. Likes 모델의 userId 컬럼과 연결합니다.
      });
      // 1. Likes 모델에서
      this.belongsTo(models.Posts, { // 2. Posts 모델에게 N:1 관계 설정을 합니다.
        targetKey: 'postId', // 3. Posts 모델의 postId 컬럼을
        foreignKey: 'postId', // 4. Likes 모델의 postId 컬럼과 연결합니다.
      });

우선 Users에서 보면 한명의 유저는 여러개의 좋아요를 남길 수 있기에 1대 n으로 설정을 하였고 Users에서 userId를 컬럼과 연결을 하였다.
그리고 Posts에서 보면 하나의 게시글은 여러개의 좋아요를 남길 수 있기에 1대 n으로 설정을 하였고, Posts에서 postId를 컬럼과 연결을 하였다.
그리고 Likes 모델은 다음과 같은 컬럼들을 추가하였다.

  Likes.init({
    likeId: {
      allowNull: false,
      autoIncrement: true,
      primaryKey: true,
      type: DataTypes.INTEGER,
    },
    postId: {
      type: DataTypes.INTEGER,
      allowNull: false,
    },
    userId: {
      type: DataTypes.INTEGER,
      allowNull: false,
    },
    createdAt: {
      allowNull: false,
      type: DataTypes.DATE,
      defaultValue: DataTypes.NOW,
    },
    updatedAt: {
      allowNull: false,
      type: DataTypes.DATE,
      defaultValue: DataTypes.NOW,
    },
  },

좋아요 번호, 게시글 번호, 유저 번호, 생성시간, 업데이트 시간 이렇게 설정을 하였다.
이 과정에서 아침이 다 지나고 점심을 먹고 나서 본격적으로 API를 구현하기 시작하였다.
근데 도무지 갈피를 잘 못잡아서 좋아요 API는 이전에 숙련주차 퀴즈에서 해답지가 있어서 그걸 참고해서 구현을 하였다. 먼저 게시글 좋아요 추가 API 부터 구현을 하였다. put요청 할때 해당 게시글에 좋아요가 없으면 좋아요 등록을 하고, 이미 있는 좋아요가 있을 때 put 요청을 하면 좋아요를 취소를 하는 것을 구현을 하였다. 좋아요 등록 기능도 로그인을 한 유저들만 이용을 할 수 있게 authMiddleware를 적용하였다.

// 게시글 좋아요 업데이트 API
router.put('/:postId/like', authMiddleware, async (req, res) => {
    try {
        const { postId } = req.params;
        const { userId } = res.locals.user; // 토큰을 검사하여 해당 회원 확인
  
        const isExistPost = await Posts.findByPk(postId); // 게시글 있는지 확인
        // 게시글 없을 때
        if (!isExistPost) {
        return res.status(404).json({
            errorMessage: '게시글이 존재하지 않습니다.',
            });
        }
        // postId와 userId 검색
        let isLike = await Likes.findOne({
            where: {
                postId: postId,
                userId: userId,
            },
        });
        // 좋아요 유무 따져서 좋아요가 없으면 좋아요 등록, 반대인 경우는 좋아요 취소
        if (!isLike) {
            await Likes.create({ postId: postId, userId: userId });
  
            return res.status(200).json({ message: '게시글의 좋아요를 등록하였습니다.' });
        } else {
            await Likes.destroy({
                where: { postId: postId, userId: userId },
            });
  
            return res.status(200).json({ message: '게시글의 좋아요를 취소하였습니다.' });
        }
    } catch (error) {
        console.error(`${req.method} ${req.originalUrl} : ${error.message}`);
        return res.status(400).json({
            errorMessage: '게시글 좋아요에 실패하였습니다.',
        });
    }
});

그런 다음에 좋아요 조회 API를 구현하였다. 사실 여기가 가장 어려웠던 부분이였다. 우선 해당 API를 소개하자면 로그인한 유저로 내가 해당 게시글에 좋아요를 등록한 게시글만 나타나게 하는 것이였다.

// 좋아요 조회 API
router.get('/like', authMiddleware, async (req, res) => {
    try {
        const { userId } = res.locals.user; // 토큰을 검사하여 해당 회원 확인
  
        const parseLikePostsModel = (likes) => {
            return likes.map((like) => {
                let obj = {};
  
            for (const [k, v] of Object.entries(like)) {
                if (k.split('.').length > 1) {
                    const key = k.split('.')[1];
                    obj[key] = v;
                } else obj[k] = v;
            }
                return obj;
            })
        }
        // 좋아요 조회에 넣을 컬럼들 추가
        const posts = await Posts.findAll({
            attributes: [
                'postId',
                'title',
                'createdAt',
                'updatedAt',
                [sequelize.fn('COUNT', sequelize.col('Likes.postId')), 'likes'], // 좋아요 갯수
            ],
            include: [
                {
                    model: Users,
                    attributes: ['userId', 'nickname'],
                }, // 유저 테이블의 userId와 nickname 포함
                {
                    model: Likes,
                    attributes: [],
                    required: true,
                    where: {
                        [Op.and]: [{ userId: userId }],
                    }, // 라이크 테이블의 userId 포함
                },
            ],
            group: ['Posts.postId'], // 해당 게시글 기준으로 Group by
            order: [['createdAt', 'DESC']], // 게시글 생성 날짜 기준으로 내림차순
            raw: true,
        }).then((likes) => parseLikePostsModel(likes));
  
        return res.status(200).json({ posts: posts }); // posts 조회
        } catch (error) {
            console.error(`${req.method} ${req.originalUrl} : ${error.message}`);
            return res.status(400).json({
            errorMessage: '좋아요 게시글 조회에 실패하였습니다.',
        });
    }
});

우선 해당 코드들을 간략하게 소개하자면 parseLikePostsModel 변수를 생성해서 윗쪽에 likes를 콘솔로그로 찍어보면 다음과 같이 나온다.

likes의 변수들을 가지고 obj 변수에다 like map 함수를 사용해서 다음과 같은 k,v를 넣어준다. 여기서 k와 v는 key와 value의 약자로 즉 k.split('.')[1]은 다시 말해서 각각 userId와 nickname을 출력하게 된다. 이게 어떤 코드지 이해하지 못해서 저녁에 기술매니저님한테 코드 해석을 해서 알게 되었다. 즉 다시 말해서 맨 아래에 있는 .then((likes) => parseLikePostsModel(likes)); 이거를 통해서 저런 것들을 가져 왔다고 이해하면 되었다.

그리고 나서 이 아래 내용은 어떤 내용을 넣을지 attributes 넣고, includes를 통해서 이미 관계 연결된 다른 컬럼들을 가져와서 Users 테이블에 'userId', 'nickname'도 추가 되고 Likes 테이블에도 likes를 추가해서 좋아요가 있으면 조회 되고, 없으면 조회 되지 않게 설정을 하였다.
그렇게 해서 lv4 개인과제는 모두 마무리 하였다.

번외 : 테스트 코드

오늘 세션 강의로 계층 아키텍쳐에서 테스트 코드에 대해서 설명하고 어떻게 적용하는지 간단하게 알려주었다. 세션 강의내용을 정리하면 다음과 같이 정리 할 수 있다.

Test Code
테스트 코드란, 개발자들이 구현한 코드가 생각한 대로 동작하는지 확인하기 위해 작성하는 코드입니다. 대표적으로 우리가 테스트 코드를 사용하는 이유는 프로젝트를 진행하면서 매번 API Client 또는 Front End 페이지만을 가지고 테스트하는 것은 너무 많은 시간이 소모되기 때문이고, 백엔드 구조에서 문제가 발생할 수 있는 부분을 서버 배포 전 미리 인지할 수 있도록 만들어 주기위해 사용됩니다.

Test Code의 종류

  • 단위 테스트 (Unit Test): 가장 작은 규모의 기능을 테스트합니다.
  • 통합 테스트 (Integration Test): 여러가지 기능을 합쳤을때 생기는 문제를 방지하기 위한 테스트입니다.
  • E2E 테스트 (End-to-end Test): 끝에서 끝(종단 간)을 의미하는 End to end 테스트입니다.
    쉽게 말하면 백엔드부터 시작해서 최종적으로 웹 페이지가 원하는대로 동작하며 원하는 데이터를 잘 보여주는지 확인합니다.

그리고 나서 테스트 코드를 사용을 하는 완성된 코드를 보여주면서 실행했는데 상당히 어려운 내용이라 나중에 현업에서 필요한 것들이라 우선 코드 이해하는것보다는 개념이라도 정리하기 위해서 이렇게 정리를 하였다.

해결

오늘 좋아요 관련 API 구현을 끝으로 LV4 개인과제를 마무리 하였다. 깃허브 링크는 다음과 같다.
시퀄라이즈로 구현한 게시판 프로젝트

알게 된 점

오늘 같은 경우에는 직접 내가 구현하는건 거의 없었고 사실상 이전에 숙련주차때 퀴즈때 풀었던 코드를 참고해서 구현을 했던거라 그 코드를 이해하는데 시간이 많이 걸렸었다. 좋아요 기능은 지금까지 LV1~3까지 한번도 구현을 해본 적이 없던 API라 그래서 많이 고민도 했고, 시행착오가 많았었다. 그래도 이전에 완성되었던 코드를 참고하면서 구현을 하는데에는 성공했고, 그 코드를 이해하고 분석하는데 시간을 많이 쓴 것 같다. 저녁에는 LV4 내가 쓴 코드들을 보고 어떻게 코드를 적었는지 리뷰 하는데에 투자를 한 것 같다. 이번 심화주차에서 목표인 LV4까지 구현 성공해서 나도 이제 어느정도 Sequelize 구현하는데는 적응이 좀 되었구나 생각을 하였지만, 한편으로 몇몇 코드를 몰라서 기술매니저님한테 물어봤는데 이걸 한번 생각해본적 있는지 물어보니까 아직 완벽하게 내가 이해하지 못한 것들이 있구나 생각을 가지게 되었다. 이렇게 LV4 구현을 마무리 하게 되었다.

앞으로 할 일

내일부터 아마 마지막 LV5 개인과제를 시작 할것 같다. 미리 봤는데 LV4까지 구현했던 라우터 폴더에다 api를 모두 다 적었는데 3계층 아키텍쳐로 3단 분리 하는 방식이다. 시간상 전부 다 구현 될지는 모르겠지만 그래도 한번 도전해보겠다.

profile
향해 13기 node.js 백앤드

0개의 댓글