-
Notifications
You must be signed in to change notification settings - Fork 22
[김수빈]Sprint10 #38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[김수빈]Sprint10 #38
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
고생많으셨습니다!
전체적으로 코드가 깔끔한 것 같습니다. 다만 레포지터리 레이어에서 비즈니스 로직이 섞인 듯한 부분이 많이 보여서 전체적으로 서비스 레이어에서 처리해도 될 만한 것들이 보이는 것 같습니다. 최대한 데이터 접근만 담당하도록 하고 데이터 가공, 여러 테이블간의 결합들은 서비스 레이어로 이동하면 좋을 듯 합니다!
| export const ExceptionMessage = { | ||
| ARTICLE_NOT_FOUND: 'Article not found', | ||
| PRODUCT_NOT_FOUND: 'Product not found', | ||
| COMMENT_NOT_FOUND: 'Comment not found', | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good!
| export const createArticle = async (req, res) => { | ||
| const { title, content } = req.body; | ||
| const userId = req.userId; // 로그인된 유저 ID | ||
| console.log('컨트롤러에서 유저아이디:', req.user); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P0]
디버깅용 console들은 remote에 푸쉬하기전에 꼭 삭제하는 습관을 들이시면 좋습니다!
| if (!refreshToken) { | ||
| throw new HttpError(400, '리프레시 토큰이 필요합니다'); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P2]
추후 이런 부분은 validationLayer와 같은 미들웨어에서 처리하면 깔끔해질 것 같습니다!
|
|
||
| function validateProductData(data) { | ||
| if (!data.name || typeof data.name !== 'string' || data.name.trim() === '') { | ||
| throw new HttpError(400, '상품 이름은 필수입니다'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P2]
ExecptionMessage 하신 것처럼 코드 상수로 관리하면 추후 국제화라던지 유지보수면에서 좋을 것 같습니다.
|
|
||
| validateProductData(req.body); | ||
|
|
||
| const updatedProduct = await productService.updateProduct(productId, userId, req.body); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P1]
Body를 직접 넘겨주는 것보다 필요한 값들만 뽑아서 넣어주는 것이 좋습니다. 혹은 바디가 모두 필요하다면
const productInfo = req.body와 같이 전달해주는 편이 좋을 것 같아요!
그럼 아래 코드들도 간결해질 것 같습니다.
validateProductDate(productInfo)
...
| function assertUserId(req) { | ||
| if (!req.userId) { | ||
| throw new HttpError(401, '로그인이 필요합니다'); | ||
| } | ||
| return req.userId; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P1]
미들웨어로 빼면 좋을 것 같습니다!
| export const Update = async (id, data) => { | ||
| return prismaClient.$transaction(async (tx) => { | ||
| const exists = await tx.article.findUnique({ where: { id } }); | ||
| if (!exists) throw new Error('Article not found'); | ||
| return tx.article.update({ | ||
| where: { id }, | ||
| data, | ||
| include: { | ||
| author: { | ||
| select: { | ||
| id: true, | ||
| nickname: true, | ||
| }, | ||
| }, | ||
| }, | ||
| }); | ||
| }); | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P1]
네이밍이 findOneAndUpdate가 맞을 것 같습니다. 추후 찾지않고 바로 업데이트하는 함수가 필요해질 경우 겹칠 것 같아요!
|
|
||
| // 회원 가입용 (비밀번호 bcrypt로 해싱 후 저장) | ||
| export async function save(user) { | ||
| const hashedPassword = await bcrypt.hash(user.password, 10); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P1]
비밀번호 해싱관련해서는 서비스 로직에서 담당하는게 맞을 것 같습니다.
비즈니스 로직이다보니 서비스에서 하면 좋을 것 같아요!
7b282af
into
codeit-sprint-fullstack:express-김수빈
백엔드 구현 요구사항
상품 등록
[ o ] "상품 등록하기" 버튼 클릭 시, 상품 정보 등록 API 엔드포인트에 요청을 보내 상품을 등록합니다.
[ o ] 상품 등록 시 필요한 필드(이름, 설명, 가격 등)의 유효성을 검증하는 미들웨어를 구현합니다.
[ o ] multer 미들웨어를 사용하여 이미지 업로드 API를 구현해 주세요.
[ o ] 업로드된 이미지는 서버에 저장하고, 해당 이미지의 경로를 response 객체에 포함해 반환합니다.
상품 상세
[ 애매 ] 상품을 조회할 때, 해당 상품에 대한 댓글 리스트, 사용자가 '좋아요'를 눌렀는지 여부를 확인할 수 있도록 응답 객체에 포함시켜 반환해 주세요.
좋아요 기능
[ o ] '좋아요' API를 만들어 주세요.
[ o ] 사용자는 상품 또는 게시글에 '좋아요'를 할 수 있습니다.
[ o ] $transaction을 사용해 주세요.
[ o ] '좋아요' 취소 API를 만들어 주세요.
[ o ] 사용자는 상품 또는 게시글에 '좋아요'를 취소할 수 있습니다.
[ o ] $transaction을 사용해 주세요.
[에매 ] 상품 또는 게시글을 조회할 때, 사용자가 '좋아요'를 누른 항목인지 확인할 수 있도록 isLiked 필드를 응답 객체에 포함시켜 반환해 주세요.
에러 처리
[ o ] 모든 예외 상황을 처리할 수 있는 에러 핸들러 미들웨어를 구현합니다.
[ o ] 서버 오류(500), 사용자 입력 오류(400 시리즈), 리소스 찾을 수 없음(404) 등 상황에 맞는 상태값을 반환합니다.
라우트 중복 제거
[ o ] 중복되는 라우트 경로(예: /users에 대한 get 및 post 요청)를 app.route()로 통합해 중복을 제거합니다.
[ o ] express.Router()를 활용하여 중고마켓/자유게시판 관련 라우트를 별도의 모듈로 구분합니다.
인증
[ o ] User 스키마를 작성해 주세요.
[ o ] id, email, nickname, image, encryptedPassword, createdAt, updatedAt 필드를 가집니다.
[ o ] 회원가입 API를 만들어 주세요.
[ o ] email, nickname, password 를 입력하여 회원가입을 진행합니다.
[ o ] password는 해싱해 저장합니다.
[ o ] 로그인 API를 만들어 주세요.
[ o ] 사용자의 신원을 확인하고, 성공적인 인증 후에는 액세스 토큰을 발급해 response 객체에 포함해 반환합니다.
상품 기능 인가
[ ] 로그인한 사용자만 상품을 등록할 수 있습니다.
[ o ] 상품을 등록한 사용자만 해당 상품의 정보를 수정 및 삭제를 할 수 있습니다.
[ o ] 로그인한 사용자만 상품에 '좋아요'를 추가하거나 삭제할 수 있습니다.
게시글 기능 인가
[ o ] 로그인한 사용자만 게시글을 등록할 수 있습니다.
[ o ] 게시글을 등록한 사용자만 해당 게시글 정보를 수정 및 삭제할 수 있습니다.
[ o ] 로그인한 사용자만 게시글에 '좋아요'를 추가하거나 삭제할 수 있습니다.
댓글 기능 인가
[ o ] 로그인한 사용자만 상품에 댓글을 등록할 수 있습니다.
[ o ] 로그인한 사용자만 게시글에 댓글을 등록할 수 있습니다.
[ o ] 댓글을 등록한 사용자만 댓글을 수정하거나 삭제할 수 있습니다.
심화 요구사항
상태코드 (웹 API 관련)
[ o ] 프론트엔드에서는 서버 응답의 상태코드에 따라 적절한 사용자 피드백을 제공합니다.
멘토님께 전하는 말
MVC패턴으로 나름 API를 작성하였는데 수요일부터 팀프로젝트 작업으로 인해 통일성이 좀 부족합니다.
전역 에러핸들링 미들웨어를 잘 작성한것인지 모르겠습니다.
좋아요 기능 구현은 햇는데 그 Optimistic Update 하다가 말아서 isLiked 값 보내주는거랑 Optimistic 구현이 제대로 안된거 같습니다.
나머지 MVC패턴은 나름 구현을 햇는데 처음이라 잘 한것인지 헷갈립니다.