TIL

내일배움캠프 11주차 화요일 TIL

news0516 2025. 1. 7. 21:01

금일은 베이직반 수업을 통해 3) 3계층 아키텍처 (3-Layered Architecture)의 구조, 구현 순서에 대해 학습하였다.
이를 프로젝트에 적용하기로 회의하였고 이후 남은 SA작성과 피드백을 통해 API 명세서, ERD를 수정하였다.

이번 프로젝트에서 일단 첫번째로 리뷰 CRUD 기능 개발을 맏고, 프로젝트에 적용하기로 한 계층형 아키텍처 패턴 (Layered Architecture Pattern)을 구성하기 전에 이전에 하던 방식대로 CRUD API를 먼저 구성하였다.

import express from 'express';
import { prisma } from '../uts/prisma/index.js';
import authMiddleware from '../middlewares/auth.middleware.js';

const router = express.Router();

// 리뷰 등록 
router.post(
  '/users/me/restaurants/:restaurantId/reviews',
  authMiddleware,
  async (req, res, next) => {
    try {
      const { paymentId, content, star } = req.body;
      const { restaurantId } = req.params;
      const { userId } = req.user;

      const newRiview = await prisma.review.create({
        data: {
          restaurantId: +restaurantId,
          paymentId: paymentId,
          userId: userId,
          content: content,
          star: +star,
        },
      });

      return res
        .status(201)
        .json({ message: '리뷰가 생성되었습니다.', data: newRiview });
    } catch (error) {
      next(error); // 에러를 다음 핸들러로 전달
    }
  },
);

// 특정 가계의 리뷰 전체 조회
router.get(
  '/users/me/restaurants/:restaurantId/reviews',
  async (req, res, next) => {
    const { restaurantId } = req.params;

    const getReviews = await prisma.review.findUnique({
      where: { restaurantId: parseInt(restaurantId, 10) }, // ID로 게시글 검색
      select: {
        reviews: true,
        restaurantId: true,
        paymentId: true,
        content: true,
        star: true,
        createdAt: true,
        updatedAt: true,
      },
    });

    return res.status(200).json({ data: getReviews });
  },
);


// 내 리뷰 수정
router.patch(
  '/users/me/restaurants/:restaurantId/reviews/:reviewId',
  authMiddleware,
  async (req, res, next) => {
    try {
      const { restaurantId, reviewId } = req.params;
      const { paymentId, content, star } = req.body; // 수정할 데이터

      // 게시글 업데이트
      const updatedReview = await prisma.review.update({
        where: {
          reviewId: parseInt(reviewId, 10),
          restaurantId: parseInt(restaurantId, 10),
        },
        data: {
          ...(content && { content }),
          ...(star && { star }),
        },
      });

      return res
        .status(200)
        .json({ message: '게시글이 수정되었습니다.', data: updatedReview });
    } catch (error) {
      next(error); // 에러를 다음 핸들러로 전달
    }
  },
);

// 리뷰 삭제
router.delete(
  '/users/me/restaurants/:restaurantId/reviews/:reviewId',
  authMiddleware,
  async (req, res, next) => {
    try {
      const { restaurantId, reviewId } = req.params;

      // 리뷰 존재 확인
      const isReview = await prisma.review.findUnique({
        where: {
          reviewId: parseInt(reviewId, 10),
          restaurantId: parseInt(restaurantId, 10),
        },
      });

      if (!isReview) {
        return res.status(404).json({ message: '리뷰가 존재하지 않습니다.' });
      }

      // 게시글 삭제
      await prisma.review.delete({
        reviewId: parseInt(reviewId, 10),
        restaurantId: parseInt(restaurantId, 10),
      });

      return res.status(200).json({ message: '리뷰가 삭제되었습니다.' });
    } catch (error) {
      next(error); // 에러를 다음 핸들러로 전달
    }
  },
);

export default router;


아직 수정하거나 추가할 여지가 있으나 임시로 먼저 작성하였다.
이를 토대로 베이직반 수업처럼 리포지토리 계층을 먼저 구성해보았다.


// src/repositories/reviews.repository.js

import { prisma } from '../utils/prisma/index.js';

export class PostsRepository {
  findAllReviews = async () => {
    // ORM인 Prisma에서 review 모델의 findMany 메서드를 사용해 데이터를 요청합니다.
    const reviews = await prisma.review.findMany();

    return reviews;
  };

  findReviewById = async (reviewId) => {
    // ORM인 Prisma에서 review 모델의 findUnique 메서드를 사용해 데이터를 요청합니다.
    const review = await prisma.review.findUnique({
      where: { reviewId: +reviewId }, // 수정 필요
    });

    return review;
  };

  createReview = async (restaurantId, paymentId, userId, content, star) => {
    // ORM인 Prisma에서 review 모델의 create 메서드를 사용해 데이터를 요청합니다.
    const createdReview = await prisma.review.create({
      data: {
        restaurantId: +restaurantId,
        paymentId: paymentId,
        userId: userId,
        content: content,
        star: +star,
      },
    });

    return createdReview;
  };

  updateReview = async (reviewId, restaurantId, content, star) => {
    // ORM인 Prisma에서 review 모델의 update 메서드를 사용해 데이터를 수정합니다.
    const updatedReview = await prisma.review.update({
      where: {
        reviewId: parseInt(reviewId, 10),
        restaurantId: parseInt(restaurantId, 10),
      },
      data: {
        ...(content && { content }),
        ...(star && { star }),
      },
    });

    return updatedReview;
  };

  deleteReview = async (reviewId, restaurantId) => {
    // ORM인 Prisma에서 review 모델의 delete 메서드를 사용해 데이터를 삭제합니다.
    const deletedReview = await prisma.review.delete({
      where: {
        reviewId: parseInt(reviewId, 10),
        restaurantId: parseInt(restaurantId, 10),
      },
    });

    return deletedReview;
  };
}

테이블 구성에 따라 일단 두가지 ID를 참조하여 기존 리뷰를 수정하거나 삭제하는데, review 테이블의 고유키인 reviewId만으로 증명이 가능해보이긴 한다.
또한 아직 유효성 검사를 실행하고 있지 않아 상황에 맞는 검증 과정이 필요하다.
일단 기존 방식대로 작성한 API를 바탕으로 3계층 아키텍쳐 구조에 맞게 진행할 예정이며 베이식반 과제와 더불어 효율적인 구조에 대한 이해, 연습을 계속 수행할 것 같다.