Skip to content

Conversation

@byeolee1221
Copy link
Collaborator

@byeolee1221 byeolee1221 commented Nov 16, 2024

스프린트 미션 12

요구사항

  • Javascript
  • React 19.0.0-rc
  • Next.js 15.0.2
  • Tailwind CSS 3.4.1
  • TanStack Query 5.19.0
  • axios 1.7.7
  • react-hook-form 7.53.2
  • zod 3.23.8
  • react-hot-toast 2.4.1
  • typescript 5
  • Jotai 2.10.1
  • js-cookie 2.2.1
  • framer-motion 11.11.11

배포 웹사이트: https://codeit-nextjs-mission.vercel.app/

기본

  • ‘상품 등록하기’ 버튼을 누르면 “/additem” 로 이동합니다.
  • 각 상품 클릭 시 상품 상세 페이지로 이동합니다.
  • 상품 상세 페이지 주소는 “/items/{productId}” 입니다.
  • 내가 등록한 상품일 경우 상품 수정, 삭제가 가능합니다.
  • 문의하기 input창에 값을 입력 후 ‘등록’ 버튼을 누르면 댓글이 등록됩니다.
  • 내가 등록한 댓글은 수정, 삭제가 가능합니다.
  • 이미지를 제외하고 input 에 모든 값을 입력하면 ‘등록' 버튼이 활성화 됩니다.
  • 활성화된 ‘등록' 버튼을 누르면 상품 등록이 완료됩니다.
  • 등록이 완료되면 해당 상품 상세 페이지로 이동합니다.

심화

  • api 요청에 TanStack React Query를 활용해 주세요.

변경사항

  • Bun으로 변경했습니다.
  • 스프린트 미션 11에서의 개선사항을 반영하였습니다.
  • js-cookie를 사용하여 로그인 상태를 유지하였습니다.
  • 로그인이 필요한 POST, PATCH, DELETE 요청 시 토큰이 만료되어 있을 때 axios의 인터셉터를 통해 토큰을 재발급 받아 요청을 보내도록 했습니다.
  • useQuery를 사용해서 클라이언트에서 GET 요청을 보내는 대신, server action을 사용하여 서버에서 데이터를 가져오도록 했습니다.
  • server action을 사용할 때 fetch 메서드를 적용하여 next.js의 캐싱 기능을 사용했습니다.
  • 사용자와의 상호작용이 필요한 GET 요청에서는 클라이언트단에서 tanstack query를 사용하였습니다.
  • 미들웨어를 사용해서 로그인이 필요한 페이지에 접근할 때 로그인 상태를 확인하도록 했습니다.

스크린샷

중고마켓 페이지 (데스크탑) 개별 상품 페이지 (데스크탑)
아이템 등록 페이지 (데스크탑)

멘토에게

  • 감사합니다.
  • 아직 추상화, server actions 전환 작업, 게시글 수정 및 삭제 작업이 덜 되어 있는 컴포넌트가 있습니다. 올려놓고 진행해보겠습니다.

@byeolee1221 byeolee1221 requested a review from dongqui November 16, 2024 07:42
@byeolee1221 byeolee1221 self-assigned this Nov 16, 2024
@byeolee1221 byeolee1221 added the 순한맛🐑 마음이 많이 여립니다.. label Nov 16, 2024
Copy link
Collaborator

@dongqui dongqui left a comment

Choose a reason for hiding this comment

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

창기님 고생 많으셨습니다~!! 👍

<label htmlFor="userEmail" className="text-sm font-bold md:text-lg">
이메일
</label>
<input
Copy link
Collaborator

Choose a reason for hiding this comment

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

컴포는트를 잘 활용해주세요~! :)

}

if (currentImg) {
imageUploadMutation(currentImg, {
Copy link
Collaborator

Choose a reason for hiding this comment

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

해당 부분도 서버에서 처리하면 코드가 많이 줄어들 거 같네요!

const imagePreview = new FileReader();
imagePreview.onloadend = () => {
if (imagePreview.result && typeof imagePreview.result === "string") {
setValue("postImg", imagePreview.result);
Copy link
Collaborator

Choose a reason for hiding this comment

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

postImg가 어디에 쓰이는지 잘 모르겠습니다 previewImage는 내부에서만 쓰여도 충분할 거 같아요.

Copy link
Collaborator

Choose a reason for hiding this comment

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

error도 마찬가지입니다. 내부 상태와 외부 상태가 동시에 있어서 따라가기가 힘들어요!

setValue: UseFormSetValue<z.infer<typeof addBoardSchema>>;
register: UseFormRegister<z.infer<typeof addBoardSchema>>;
watch: UseFormWatch<z.infer<typeof addBoardSchema>>;
errors: FieldErrors<z.infer<typeof addBoardSchema>>;
Copy link
Collaborator

Choose a reason for hiding this comment

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

바깥 로직과 너무 결합된 거 같아요. onChange, error 등의 프랍들만 넣어도 밖에서 충분히 해결할 수 있을 거 같습니다!

if (imagePreview.result && typeof imagePreview.result === "string") {
setValue("postImg", imagePreview.result);
setPreviewSrc(imagePreview.result);
setCurrentImg(file);
Copy link
Collaborator

Choose a reason for hiding this comment

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

register를 쓰면서 uncontrolled인듯 하나 사실상 controlled 같이 쓰이고 있는 거 같아요! 다시 한 번 고민해보시면 좋을 거 같습니다 🤔

@@ -0,0 +1,32 @@
import { useEffect, useRef, useState } from "react";

export const useObserver = (callback: () => void) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍

}

export const useFavoriteCount = ({ id, setNewFavoriteCount, location }: FavoriteCountProps) => {
const requestUrl = location === "board" ? `/articles/${id}/like` : `/products/${id}/favorite`;
Copy link
Collaborator

Choose a reason for hiding this comment

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

흠..!? 키 값은 하나인데 두 개의 데이터를 다루고 있군요..!?

const formData = new FormData();
formData.append("keyword", values.userSearch || "");

const response = await itemSearch(formData);
Copy link
Collaborator

Choose a reason for hiding this comment

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

현재 상태로는 서버액션을 사용하는 이점이 아무것도 없어 보여요! 불필요하게 중간 api를 하나 더 거치고 있을 뿐입니다..!

}, [itemId]);
};

useEffect(() => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

컴포넌트화 하고 해당 로직도 같이 들어가면 좀 더 깔금할 거 같네요!

@dongqui dongqui merged commit cc6ef20 into codeit-bootcamp-frontend:Next-문창기 Nov 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

순한맛🐑 마음이 많이 여립니다..

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants