개발 리포트는 프로젝트가 끝난 후 전체적인 과정을 되돌아보고, 성과와 문제 해결 과정을 정리하여 팀과 공유하는 문서입니다. 프로젝트의 전반적인 진행 상황, 맡은 역할, 해결한 문제 등을 구체적으로 기술하여 향후 프로젝트에 대한 참고자료로 활용할 수 있습니다.
| 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 |
//포토카드 승인
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: "교환이 승인되었습니다.",
};
});
};
useModal()
⇒ 판매 모달만 useState를 이용하여 모달 상태를 따로 관리하니 문제가 해결되었습니다.
카드 컴포넌트
⇒ 카드 컴포넌트 중 디자인이 많이 다른 exchange 부분은 따로 구현하여 코드를 알아보기 쉽게 하였습니다.