[2024.06.03 TIL] 내일배움캠프 34일차 (팀프로젝트 구현, 게시글 CRUD 구현, 각종 유효성 검사 구현)

My_Code·2024년 6월 3일
0

TIL

목록 보기
45/112
post-thumbnail

본 내용은 내일배움캠프에서 활동한 내용을 기록한 글입니다.


💻 TIL(Today I Learned)

📌 Today I Done

✏️ 상품 게시글 작성 API

  • 상품 게시글 작성 API에서 사진URL를 picture 테이블에 넣어야 함

  • 그래서 trade 테이블 스키마에서 img 컬럼이 필요 없을 것 같음

  • trade 스키마 수정

  • 입력 받은 이미지 URL 배열을 순서대로 picture 테이블에 삽입

  • 찾아보니 트랜젝션을 이용해서 상품 등록과 상품 사진 URL 등록을 동시에 진행해야 함

// 상품 게시글 작성 API
tradeRouter.post('/create', accessTokenValidator, createTradeValidator, async (req, res, next) => {
  try {
    // 유효성 검사 거치고 req.body 가져옴
    const { title, content, price, region, img } = req.body;

    console.log(img);

    // 상품 생성 + 이미지 등록 트랜젝션으로 처리
    const trade = await prisma.$transaction(async (tx) => {
      // 상품 생성
      const newTrade = await tx.trade.create({
        data: { title, content, price, region, userId: req.user.id },
      });

      // 상품 사진 등록
      // 이미지가 여러 장인 경우 Promise.All로 비동기적으로 실행
      const tradeImg = await Promise.all(
        img.map(async (url) => {
          return await tx.tradePicture.create({ data: { tradeId: newTrade.id, imgUrl: url } });
        })
      );
      return [newTrade, tradeImg];
    });

    return res.status(HTTP_STATUS.CREATED).json({
      status: HTTP_STATUS.CREATED,
      message: MESSAGES.TRADE.CREATE.SUCCEED,
      data: { trade },
    });
  } catch (err) {
    next(err);
  }
});

✏️ 상품 게시글 목록 조회 API

  • 일종의 뉴스피드 구현

  • 데이터베이스에 등록된 모든 상품을 조회

  • 기본적으로 최신 순으로 정렬, 정렬 옵션으로 좋아요 순으로도 조회

  • 시간 순 정렬 또는 좋아요 순으로 정렬해서 조회

  • 상품 게시글 목록 조회에서 상품 사진도 같이 반환하도록 수정 필요

// 상품 게시글 목록 조회 API (뉴스피드)
tradeRouter.get('/', async (req, res) => {
  // 정렬 조건 쿼리 가져오기
  let sortDate = req.query.sort.toLowerCase();
  let sortLike = req.query.like?.toLowerCase();

  // 시간 순 정렬 기본 값 설정
  if (sortDate !== sort.desc && sortDate !== sort.asc) {
    sortDate = sort.desc;
  }
  // 좋아요 순 정렬 기본 값 설정 (상세한 내용은 회의가 필요)
  if (sortLike !== sort.desc && sortLike !== sort.asc) {
    sortLike = sort.desc;
  }

  // trade 테이블의 데이터 모두를 조회
  const trades = await prisma.trade.findMany({
    orderBy: [/*{ like: sortLike },*/ { createdAt: sortDate }],
    omit: { content: true },
  });

  return res
    .status(HTTP_STATUS.OK)
    .json({ status: HTTP_STATUS.OK, message: MESSAGES.TRADE.READ.SUCCEED, data: { trades } });
});

✏️ 상품 게시글 상세 조회 API

  • 상품 상세 조회는 아무나 사용 가능

  • 상품 목록과는 다르게 content 내용이 출력됨

  • 상품 게시글 상세 조회에서 상품 사진도 같이 반환하도록 수정 필요

// 상품 게시글 상세 조회 API
tradeRouter.get('/:tradeId', async (req, res) => {
  // 상품 ID 가져오기
  const id = req.params.tradeId;

  // 상품 조회하기
  const trade = await prisma.trade.findFirst({ where: { id: +id } });

  // 데이터베이스 상 해당 상품 ID에 대한 정보가 없는 경우
  if (!trade) {
    return res
      .status(HTTP_STATUS.NOT_FOUND)
      .json({ status: HTTP_STATUS.NOT_FOUND, message: MESSAGES.TRADE.READ.NOT_FOUND });
  }

  return res
    .status(HTTP_STATUS.OK)
    .json({ status: HTTP_STATUS.OK, message: MESSAGES.TRADE.READ.SUCCEED, data: { trade } });
});

✏️ 상품 게시글 수정 API

  • 수정할 항목들을 req.body로 입력 받음

  • 반드시 모두 입력할 필요 없음

  • 작성한 내용만 수정에 반영

  • 상품 게시글 작성과 마찬가지로 트랜젝션을 통해 게시글 등록, 사진 등록을 같은 트랜젝션에서 동작 시킴

// 상품 게시글 수정 API
tradeRouter.patch(
  '/:tradeId/edit',
  accessTokenValidator,
  updateTradeValidator,
  async (req, res, next) => {
    try {
      // 상품 ID 가져오기
      const id = req.params.tradeId;

      // 상품 조회하기
      const trade = await prisma.trade.findFirst({ where: { id: +id } });

      // 데이터베이스 상 해당 상품 ID에 대한 정보가 없는 경우
      if (!trade) {
        return res
          .status(HTTP_STATUS.NOT_FOUND)
          .json({ status: HTTP_STATUS.NOT_FOUND, message: MESSAGES.TRADE.READ.NOT_FOUND });
      }

      // 수정할 내용 입력 받음
      const { title, content, price, region, img } = req.body;

      // 게시글 수정 + 이미지 삭제 및 추가 트랜젝션으로 처리
      const changedTrade = await prisma.$transaction(async (tx) => {
        // 상품 수정
        const tradeTemp = await tx.trade.update({
          where: { id: trade.id },
          data: {
            ...(title && { title }),
            ...(content && { content }),
            ...(price && { price }),
            ...(region && { region }),
          },
        });

        // 상품 이미지 수정 (정확히는 삭제 후 등록)
        // 이미지 개수가 다를 수도 있고 어떤 이미지가 어떤 이미지로 수정되는지 알 방법이 없음
        await tx.tradePicture.deleteMany({ where: { tradeId: trade.id } });
        const tradeImg = await Promise.all(
          img.map(async (url) => {
            return await tx.tradePicture.create({ data: { tradeId: trade.id, imgUrl: url } });
          })
        );
        return [tradeTemp, tradeImg];
      });

      return res.status(HTTP_STATUS.CREATED).json({
        status: HTTP_STATUS.CREATED,
        message: MESSAGES.TRADE.UPDATE.SUCCESS,
        data: { changedTrade },
      });
    } catch (err) {
      next(err);
    }
  }
);


📌 Tomorrow's Goal

✏️ 팀프로젝트 코드 구현하기

  • 상품 게시글 목록 조회, 상세 조회 수정하기

  • 상품 게시글 삭제 API 구현하기

  • 삭품 게시글 좋아요 기능 구현하기



📌 Today's Goal I Done

✔️ 상품 게시글 CRUD 구현하기

  • 약간 뒤늦게 구현에 들어감

  • 그래서 오늘은 게시글 생성, 게시글 목록 조회, 게시글 상세 조회, 게시글 수정까지만 구현함

  • 사실 피곤한 상태에서 구현해서 내일 많은 테스트가 필요해 보임



⚠️ 구현 시 발생한 문제

✔️ 한 번에 두 가지 prisma 쿼리가 동작해야 함

  • 상품 게시글을 등록하면 상품의 정보는 trade라는 테이블에 저장되고 상품 사진 URL은 trade_picture라는 테이블에 저장되어야 함

  • 처음에는 따로따로 trade 테이블에 create 동작을 하고 trade_picture 테이블에 create 하는 동작을 하도록 함

  • 그러니 trade_picture에서 에러가 발생하면 게시글은 등록되고 사진은 등록지되 않는 현상이 발생함

  • 그래서 prisma의 트랜젝션 문법을 통해 동시에 처리함

// 상품 게시글 작성 API
tradeRouter.post('/create', accessTokenValidator, createTradeValidator, async (req, res, next) => {
  try {
    // 유효성 검사 거치고 req.body 가져옴
    const { title, content, price, region, img } = req.body;

    console.log(img);

    // 상품 생성 + 이미지 등록 트랜젝션으로 처리
    const trade = await prisma.$transaction(async (tx) => {
      // 상품 생성
      const newTrade = await tx.trade.create({
        data: { title, content, price, region, userId: req.user.id },
      });

      // 상품 사진 등록
      // 이미지가 여러 장인 경우 Promise.All로 비동기적으로 실행
      const tradeImg = await Promise.all(
        img.map(async (url) => {
          return await tx.tradePicture.create({ data: { tradeId: newTrade.id, imgUrl: url } });
        })
      );
      return [newTrade, tradeImg];
    });

    return res.status(HTTP_STATUS.CREATED).json({
      status: HTTP_STATUS.CREATED,
      message: MESSAGES.TRADE.CREATE.SUCCEED,
      data: { trade },
    });
  } catch (err) {
    next(err);
  }
});
profile
조금씩 정리하자!!!

0개의 댓글