Skip to content

Conversation

@De-cal
Copy link
Collaborator

@De-cal De-cal commented May 19, 2025

🐼 판다마켓 : https://been-panda.vercel.app

요구사항

기본 요구사항

공통

  • Github에 위클리 미션 PR을 만들어 주세요.
  • React.js 혹은 Next.js를 사용해 진행합니다.
  • RESTful를 설계하고 백엔드 코드를 변경하세요.
    • (풀스택) 설계한 백엔드 코드에 맞게 프론트엔드 코드를 변경해 주세요.
    • (백엔드) 프론트엔드 코드에 맞게 백엔드 코드를 변경해 주세요.
  • 백엔드 코드에 swagger를 추가해 API 명세 문서를 생성해 주세요.

프론트엔드 구현 요구사항

중고마켓 페이지

  • 디폴트 이미지로 처리한 이미지를 실제 Product Get API에서 가져온 이미지로 변경해 주세요.
  • 좋아요 순 정렬 기능을 붙여주세요.
  • 베스트 상품 기능을 추가해 주세요. 베스트 상품은 가장 많이 좋아요를 받은 순으로 PC 기준 최대 4개까지 조회 가능합니다.

상품 등록하기 페이지

  • 상품 이미지 등록 기능을 구현합니다. 파일을 선택해 이미지를 업로드하고, preview를 볼 수 있도록 구현합니다. 이미지는 최대 3개까지만 등록 가능하도록 구현해 주세요.
  • 동일하게 상품 이미지 수정 기능도 추가합니다.
  • 상품 등록 성공 시 중고마켓 페이지로 이동해 주세요.

백엔드 구현 요구사항

상품 등록

  • "상품 등록하기" 버튼 클릭 시, 상품 정보 등록 API 엔드포인트에 요청을 보내 상품을 등록합니다.
  • 상품 등록 시 필요한 필드(이름, 설명, 가격 등)의 유효성을 검증하는 미들웨어를 구현합니다.
  • multer 미들웨어를 사용하여 이미지 업로드 API를 구현해 주세요.
    • 업로드된 이미지는 서버에 저장하고, 해당 이미지의 경로를 response 객체에 포함해 반환합니다.

상품 상세

  • 상품을 조회할 때, 해당 상품에 대한 댓글 리스트, 사용자가 '좋아요'를 눌렀는지 여부를 확인할 수 있도록 응답 객체에 포함시켜 반환해 주세요.

좋아요 기능

  • '좋아요' API를 만들어 주세요.
    • 사용자는 상품 또는 게시글에 '좋아요'를 할 수 있습니다.
    • $transaction을 사용해 주세요.
  • '좋아요' 취소 API를 만들어 주세요.
    • 사용자는 상품 또는 게시글에 '좋아요'를 취소할 수 있습니다.
    • $transaction을 사용해 주세요.
  • 상품 또는 게시글을 조회할 때, 사용자가 '좋아요'를 누른 항목인지 확인할 수 있도록 isLiked 필드를 응답 객체에 포함시켜 반환해 주세요.

에러 처리

  • 모든 예외 상황을 처리할 수 있는 에러 핸들러 미들웨어를 구현합니다.
  • 서버 오류(500), 사용자 입력 오류(400 시리즈), 리소스 찾을 수 없음(404) 등 상황에 맞는 상태값을 반환합니다.

라우트 중복 제거

  • 중복되는 라우트 경로(예: /users에 대한 get 및 post 요청)를 app.route()로 통합해 중복을 제거합니다.
  • express.Router()를 활용하여 중고마켓/자유게시판 관련 라우트를 별도의 모듈로 구분합니다.

인증

  • User 스키마를 작성해 주세요.
    • idemailnicknameimageencryptedPasswordcreatedAtupdatedAt 필드를 가집니다.
  • 회원가입 API를 만들어 주세요.
    • emailnicknamepassword 를 입력하여 회원가입을 진행합니다.
    • password는 해싱해 저장합니다.
  • 로그인 API를 만들어 주세요.
    • 사용자의 신원을 확인하고, 성공적인 인증 후에는 액세스 토큰을 발급해 response 객체에 포함해 반환합니다.

상품 기능 인가

  • 로그인한 사용자만 상품을 등록할 수 있습니다.
  • 상품을 등록한 사용자만 해당 상품의 정보를 수정 및 삭제를 할 수 있습니다.
  • 로그인한 사용자만 상품에 '좋아요'를 추가하거나 삭제할 수 있습니다.

게시글 기능 인가

  • 로그인한 사용자만 게시글을 등록할 수 있습니다.
  • 게시글을 등록한 사용자만 해당 게시글 정보를 수정 및 삭제할 수 있습니다.
  • 로그인한 사용자만 게시글에 '좋아요'를 추가하거나 삭제할 수 있습니다.

댓글 기능 인가

  • 로그인한 사용자만 상품에 댓글을 등록할 수 있습니다.
  • 로그인한 사용자만 게시글에 댓글을 등록할 수 있습니다.
  • 댓글을 등록한 사용자만 댓글을 수정하거나 삭제할 수 있습니다.

심화 요구사항

상태코드 (웹 API 관련)

  • 프론트엔드에서는 서버 응답의 상태코드에 따라 적절한 사용자 피드백을 제공합니다.

인증

  • 토큰 기반 방식을 사용할 경우, 만료된 액세스 토큰을 새로 발급하는 리프레시 토큰 발급 기능을 구현합니다.(jwt sliding session 적용)

OAuth를 활용한 인증

  • 구글 OAuth를 사용하여 회원가입 및 로그인 기능을 구현합니다.

프로젝트 구조 변경

  • 프로젝트의 구조와 복잡성을 관리하기 위해 MVC 패턴이나 Layered Architecture와 같은 설계 방식을 적용해 보세요.

(생략 가능) 자유게시판 게시물 등록

  • 프론트엔드를 Next.js로 Migration 했을 경우에만 진행해 주세요.
    • 게시물 등록 시 이미지 등록 기능을 구현합니다. 파일을 선택해 이미지를 업로드하고, preview를 볼 수 있도록 구현합니다. 이미지는 최대 3개까지만 등록 가능하도록 구현해 주세요.
  • 게시물 등록 시 필요한 필드(제목, 내용 등)의 유효성 검증하는 미들웨어를 구현합니다.
  • multer 미들웨어를 사용하여 이미지 업로드 API를 구현해 주세요.
    • 업로드된 이미지는 서버에 저장하고, 해당 이미지의 경로를 response 객체에 포함해 반환합니다.

주요 변경사항

멘토에게

  • app.route()는 요즘에는 많이 사용되지 않는다고 하여서, 상품 좋아요 / 좋아요 취소 한 가지만 예시로 적용해두었습니다.
  • BE 코드를 못만들어서 일요일에 안 올린 것이 아니라, BE 레포지토리에 올리는 걸 까먹고 일요일에 올리지 못했습니다. 참고 해주시면 감사하겠습니다.
  • 현재 프로젝트로 인해서 렌더에서 DB를 동시에 사용하지 못하는 이슈가 있습니다. 그래서 혹시 확인하실 때 한번만 말씀해주시면 그 때 DB를 켜도록 하겠습니다!

스크린샷

로그인

PC
20250501_023228_3
20250501_023228_1
20250501_023228_2
20250501_023228_4
20250501_023228_5
20250501_023228_6


회원가입

PC
20250501_023228_7
20250501_023228_8
20250501_023228_9
20250501_023228_10
20250501_023228_11


상품 상세 페이지 + 수정 페이지

PC
20250501_023228_12
20250501_023228_14
20250501_023228_13

Copy link

@reach0908 reach0908 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

고생하셨습니다.

트랜잭션이 데이터베이스에서 필요한 이유야 ACID 원칙, 잠금 수준과 같은 기본적인 부분들에 대하여 더 공부해보고 정말 트랜잭션이 필요한 행위인가를 사용자 플로우 관점에서 고민하는 방법을 공부하면 좋을 것 같습니다.

Comment on lines +10 to +14
const productsWithImages = products.map((product) => ({
...product,
productImages: undefined,
images: product.productImages.map((img) => `${baseUrl}/${img.imageUrl}`),
}));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1]
저는 이 로직도 서비스 로직에 들어가야한다고 생각합니다.
productService.getProdutsWithImages 와 같은 함수가 있어야하고 내부적으로 다시 getProducts를 사용하는 방향으로 개선해보는 것은 어떨까요?

Comment on lines +6 to +25
const [articles, totalCount] = await articleRepository.findAll(query);

if (!articles || articles.length === 0) {
const error = new Error("게시글이 없습니다.");
error.code = 404;

throw error;
}

const articletWithLikeCount = await Promise.all(
articles.map(async (article) => {
const likeCount = await articleRepository.findArticleLikeCountById(
article.id
);

return { ...article, likeCount };
})
);

return [articletWithLikeCount, totalCount];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P0]
대표적인 데이터베이스의 N+1 문제가 발생하는 것 같습니다.
SQL 쿼리를 통해 해결하는 방법과 좋아요에서 트랜잭션 처리와함께 항상 article 문서에도 likeCount 등을 업데이트해주는 방법이 있을 수 있습니다.
항상 함께 다닌다면 후자도 괜찮을 것 같아요

Comment on lines +28 to +57
// 상품 상세조회
const getProduct = async (userId, productId) => {
return await prisma.$transaction(async (tx) => {
const [product, images, likeCount, isLiked] =
await productRepository.findByIdWithTx(tx, userId, productId);

if (!product) {
const error = new Error("존재하지 않는 상품입니다.");
error.code = 404;

throw error;
}

const productTags = await productRepository.findProductTagByIdWithTx(
tx,
productId
);

const tags = productTags.map((tag) => tag.tag.name);
const imageUrls = images.map((image) => image.imageUrl);

return {
...product,
tags,
images: imageUrls,
likeCount,
isLiked: !!isLiked,
};
});
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P0]
조회 작업에 꼭 트랜잭션이 필요한 이유가 있을지 고민해보면 좋을 것 같습니다.

const { tags } = body;

return await prisma.$transaction(async (tx) => {
const newProduct = await productRepository.createWithTx(tx, userId, body);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const newProduct = await productRepository.createWithTx({userId, body}, tx);

위와 같이 관리하면 추후 create를 위한 Input DTO와 tx 변수들의 타입을 쉽게 관리할 수 있습니다. 변수 순서를 헷갈리거나 하는 부분도 해소할 수 잇을 것 같아요

@reach0908 reach0908 merged commit 1691c8c into codeit-sprint-fullstack:express-이태빈 May 21, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants