개발 리포트 작성 방법

개발 리포트는 프로젝트가 끝난 후 전체적인 과정을 되돌아보고, 성과와 문제 해결 과정을 정리하여 팀과 공유하는 문서입니다. 프로젝트의 전반적인 진행 상황, 맡은 역할, 해결한 문제 등을 구체적으로 기술하여 향후 프로젝트에 대한 참고자료로 활용할 수 있습니다.

개발 리포트 작성 항목

1. 프로젝트 개요

2. 담당한 작업

3. 기술적 성과

Frontend Backend Library Deploy Tool
tailwind CSS
JavaScript
Next.js
React Express
Node.js
nodemon
cors
Prisma(ORM)
PostgreSQL ESLint
Prettier
clsx
date-fns
crypto-js
Tanstack Query Vercel(Front)
Render(Back) Git/GitHub
Figma
Notion

4. 문제점 및 해결 과정

//포토카드 승인
const putExchangeCard = async ({ userId, exchangeId }) => {
  return await prisma.$transaction(async (tx) => {
    //유효성 검사 1: 유효한 교환 요청인지 확인
    const exchange = await articleRepository.getExchangeById(exchangeId, {
      tx,
    });
    if (!exchange) {
      const error = new Error("존재하지 않는 교환 요청입니다.");
      error.code = 404;
      throw error;
    }
    const { requesterCardId, recipientArticleId, requesterUserId } = exchange;

    //유효성 검사 2: 작성자인지 확인
    const article = await articleRepository.getByIdWithDetails(
      recipientArticleId,
      { tx },
    );
    if (userId !== article.userPhotoCard.userId) {
      const error = new Error("게시글의 작성자가 아닙니다.");
      error.code = 403;
      throw error;
    }

    //유효성 검사 3: article의 quantity가 1 이상인지 확인
    if (article.remainingQuantity < 1) {
      const error = new Error("포토카드 수량이 부족합니다.");
      error.code = 400;
      throw error;
    }

    //요청자가 요청한 카드 보유하고 있는지 확인
    const card = await cardRepository.findByUserAndCard(
      requesterUserId,
      article.userPhotoCard.photoCardId,
      { tx },
    );

    //보유하고 있다면, quantity += 1
    if (card) {
      await cardRepository.increaseCard(card.id, 1, { tx });
    }
    //보유하지 않고 있다면, 새로운 userPhotocard 생성
    else {
      await cardRepository.createUserPhotoCard(
        {
          photoCardId: article.userPhotoCard.photoCardId,
          userId: exchange.requesterUserId,
          status: "OWNED",
          quantity: 1,
          price: article.price,
        },
        { tx },
      );
    }

    //교환 요청 삭제
    await articleRepository.deleteExchange(exchangeId, { tx });

    //요청자의 exchange requested userPhotocard 삭제
    await cardRepository.remove(requesterCardId, { tx });

    //article의 quantity 1 감소
    await articleRepository.decreaseCardArticleQuantity(
      article.id,
      article.remainingQuantity - 1,
      { tx },
    );
    //userPhotocard quantity 1 감소
    await articleRepository.decreaseQuantity(article.userPhotoCardId);

    //userPhotocard 0인 경우 soldout으로 변경
    if (article.remainingQuantity === 1) {
      await cardRepository.updateStatus(article.userPhotoCardId, "SOLDOUT", {
        tx,
      });
      // 유효성 검사 4 : Exchange 존재 여부
      if (article.exchange.length > 1) {
        // 포토카드 구매 6. 교환 신청 들어온 Exchange 전부 삭제
        await articleRepository.deleteExchanges(article.id, { tx });

        await Promise.all(
          article.exchange.map(async (ex) => {
            // 포토카드 구매 7. requester의 UserPhotoCard에서 status가 EXCHANGE_REQUESTED인 UserPhotoCard 전부 삭제
            const userPhotoCardId = ex.requesterCard.id;
            const exchangeCard = await cardRepository.getById(userPhotoCardId, {
              tx,
            });
            if (exchangeCard) {
              await cardRepository.remove(userPhotoCardId, { tx });
              // 포토카드 구매 8. requester의 UserPhotoCard에서 status가 OWNED인 UserPhotoCard 전부 수량 1개 증가
              const userId = ex.requesterCard.user.id;
              const photoCardId = ex.requesterCard.photoCard.id;

              const userPhotoCard = await cardRepository.findByUserAndCard(
                userId,
                photoCardId,
                { tx },
              );

              await articleRepository.increaseQuantity(userPhotoCard.id, {
                tx,
              });
            }
          }),
        );
      }
    }

    return {
      message: "교환이 승인되었습니다.",
    };
  });
};

5. 협업 및 피드백