Skip to content
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-2 #266

Merged

Conversation

yongb2n
Copy link
Collaborator

@yongb2n yongb2n commented Jul 19, 2024

요구사항

기본

  • 게시글 등록 페이지 주소는 “/addboard” 입니다.
  • 게시판 이미지는 최대 한개 업로드가 가능합니다.
  • 각 input의 placeholder 값을 정확히 입력해주세요.
  • 이미지를 제외하고 input 에 모든 값을 입력하면 ‘등록' 버튼이 활성화 됩니다.
  • 게시글 상세 페이지 주소는 “/board/{id}” 입니다.
  • 댓글 input 값을 입력하면 ‘등록' 버튼이 활성화 됩니다.
  • 자유게시판 페이지에서 게시글을 누르면 게시물 상세 페이지로 이동합니다.

주요 변경사항

  • 미션 9, 10 코드 리뷰 피드백 반영

스크린샷

멘토에게

  • 멘토링 때 API 함수에 대해서 말씀해주신 내용을 토대로 리팩토링 해봤는데 이렇게 하는 게 맞을까요??!
  • 자바스크립트가 아닌 CSS로 반응형을 처리하니까 너무 편한 거 같습니다👍
  • svg 이미지를 컴포넌트로 만들어서 관리하면 좋을까요?
  • 많은 피드백 부탁드립니다! 항상 감사합니다!
  • 셀프 코드 리뷰를 통해 질문 이어가겠습니다.

@yongb2n yongb2n requested a review from jlstgt July 19, 2024 13:10
@yongb2n yongb2n added 매운맛🔥 뒤는 없습니다. 그냥 필터 없이 말해주세요. 책임은 제가 집니다. 미완성🫠 죄송합니다.. labels Jul 19, 2024
@yongb2n yongb2n changed the base branch from main to Next-고용빈 July 20, 2024 07:21
@yongb2n yongb2n self-assigned this Jul 20, 2024
@yongb2n yongb2n removed the 미완성🫠 죄송합니다.. label Jul 20, 2024
Comment on lines 2 to +12
const nextConfig = {
reactStrictMode: true,
images: {
domains: ['sprint-fe-project.s3.ap-northeast-2.amazonaws.com'],
domains: [
"sprint-fe-project.s3.ap-northeast-2.amazonaws.com",
"example.com",
],
},
}
};

module.exports = nextConfig
module.exports = nextConfig;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

원래 서버에서 받아오는 이미지들을 사용할 때, example.com이 없어도 잘 됐었는데 갑자기 에러가 나더니 추가해주니까 괜찮아졌습니다..!
스크린샷 2024-07-20 오후 9 36 45
example.com을 추가하고 동작은 잘 하지만 터미널에 위와 같은 404 에러가 계속 발생합니다.
next/image 공식 문서처럼 해결 해봤는데도 잘 안돼서 질문 드립니다!

Copy link
Collaborator

@jlstgt jlstgt Jul 21, 2024

Choose a reason for hiding this comment

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

next/image 컴포넌트를 사용하면 Next.js에서 제공하는 특별한 이미지 최적화 기법을 적용할 수 있습니다(사이즈별로 크기가 다른 이미지를 자동으로 생성해줌).

  1. 근데 domains 설정은, 저기 설정된 도메인들에 대해서만 next/image의 이미지 최적화를 실행하겠다는 뜻입니다. 따라서 저기에 아무것도 지정해주지 않아도 제대로 작동해야 합니다. ..인줄 알았는데 remotePatterns에 반드시 도메인을 지정하던가, next/image 컴포넌트 말고 그냥 <img> 요소를 이용해야 겠네요. 굳이 외부 이미지들에 대해서 최적화를 실행할 필요는 없어 보여서 저 이미지들은 그냥 <img> 요소를 사용하는 게 더 나아 보입니다.
  2. Next 14버전 부터는 domains 값은 deprecated 되었고, remotePatterns 값으로 대체 되었습니다.

Copy link
Collaborator

@jlstgt jlstgt left a comment

Choose a reason for hiding this comment

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

거의 완벽하게 피드백 드린 걸 반영해주셨네요! 😄
특히 커밋 메시지도 읽기 쉽게 작업별로 구분 되어 있어서 상당히 인상적이었습니다.
고생 많으셨습니다! 👍


SSG와 SSR에 대한 말씀을 짧게 드리면,
Next에서 모든 페이지는 별도의 SSR 관련 로직을 사용하지 않는다면 SSG로 처리됩니다. 따라서 지금 작성하신 모든 페이지는 HTML을 사전에 생성해두는 SSG를 사용하신 것이나 다름이 없습니다.
SSR은 특정 페이지에 대한 요청이 왔을 때, 그 페이지에서 호출해야 할 API를 사용자 브라우저에서 호출하는 게 아니라 프론트엔드 서버에서 직접 호출해서 완성된 HTML을 만들고 그걸 요청에 대한 응답으로 반환하는 것입니다. 사실 모든 페이지에서 SSR을 적용하고자 하면 다 할 수 있지만, 제품 상세 페이지와 같은 상세 페이지에 적용했을 때 그나마 제일 유용하다고 생각합니다.
(사실 개인적인 생각은, 학습하신 것처럼 axios나 fetch를 사용하는 건 모두 클라이언트 사이드(브라우저)에서 데이터를 요청하는 것이기도 하고, 대부분의 경우엔 이걸로도 충분해서 SSR를 적용하는 게 더 이득인 경우는 많지 않다고 생각합니다)

@@ -0,0 +1 @@
NEXT_PUBLIC_API_URL=https://panda-market-api.vercel.app
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍

@@ -1,21 +1,60 @@
import { useRef, useState } from "react";
import Image from "next/image";
import styles from "./styles.module.scss";
Copy link
Collaborator

Choose a reason for hiding this comment

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

이 파일도 같은 디렉토리 내에 있긴 하지만 절대 경로로 바뀌면 더 좋을 것 같네요.


function AddBoard() {
const fileInputRef = useRef<HTMLInputElement | null>(null);
const [titleInputValue, setTitleInputValue] = useState("");
const [contentTextAreaValue, setContentTextAreaValue] = useState("");
Copy link
Collaborator

Choose a reason for hiding this comment

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

이름에 TextArea라는 표현은 없어도 될 것 같습니다. 이 state에 담을 내용이 content라는 게 중요한 거지, 내용이 어떤 곳에 담길지는 이름에 없어도 될 것 같습니다. 이 필드가 textarea가 아니라 select나 일반 input으로 바뀐다면 이 이름도 변경이 필요하기 때문입니다. 위에 titleInputValue도 마찬가지입니다. 어떻게 보면 value라는 글자도 없어도 될 것 같긴 하네요

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

아직도 변수명을 짓는 게 어렵네요..! 무조건 구체적이게 짓는 게 좋다고 생각했는데, 한 번 더 생각하고 지어야겠습니다 ㅎㅎ 감사합니다!

Copy link
Collaborator

Choose a reason for hiding this comment

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

이게 각각의 코멘트를 나타내는 컴포넌트면, CommentItem 같은 이름으로 바꿔도 좋을 것 같습니다. 전체 코멘트 리스트를 나타내는 컴포넌트라고 혼동할 여지가 있습니다. 이렇게 바꾸면
Comment라는 타입을 CommentType이라는 이름으로 바꾸지 않아도 되어서 더 좋을 것 같아요.

Copy link
Collaborator Author

@yongb2n yongb2n Jul 21, 2024

Choose a reason for hiding this comment

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

감사합니다! 컴포넌트 이름을 바꾸니까 타입을 CommentType이라고 굳이 선언하지 않아도 되네요! 변수명과 마찬가지로 컴포넌트명 짓는 것도 많은 도움 주셔서 감사합니다 ㅎㅎ

const [comments, setComments] = useState<Article[]>([]);
const router = useRouter();
const [comments, setComments] = useState<CommentType[]>([]);
const router: any = useRouter();
Copy link
Collaborator

Choose a reason for hiding this comment

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

이젠 any를 제거해도 타입 오류가 안 뜰 것 같은데 아닌가요?

Copy link
Collaborator Author

@yongb2n yongb2n Jul 21, 2024

Choose a reason for hiding this comment

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

여전히 발생하고 있습니다 ㅜ 말씀해주신 방법들 적용해봤는데, any 말고는 해결이 안되네요...

@@ -32,15 +33,11 @@ function BoardsDetail() {
useEffect(() => {
const fetchArticle = async () => {
try {
const fetchedArticle = await getArticleById(id);
const fetchedArticle = await getArticleById(Number(id));
Copy link
Collaborator

Choose a reason for hiding this comment

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

id를 굳이 number로 안 바꾸고 string으로만 처리해도 될 것 같아요. 아마 이 파일 23행에서
const { id } = router.query;로 쿼리 스트링에서 id를 가져오면 id 타입이 string | string[] | undefined라고 나올텐데, undefined면 애초에 오류 메시지가 표시되어야 하고,
string[]일 땐 첫 번째 index만 가져와도 될 것 같네요. ex)

ex) 24행에 다음과 같이 추가
const articleId = Array.isArray(id) ? id[0] : id

사실 데이터 구조를 설계할 때 id가 항상 number인 것은 아닙니다. 알파벳과 숫자가 섞인 string이 id가 되는 경우도 흔합니다.

</div>
</div>

<div className={styles["mobile-dropdown"]}>
Copy link
Collaborator

Choose a reason for hiding this comment

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

js 로직이 없어지니까 훨씬 나아졌네요 👍

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

신세계입니다 ㅎㅎ

@@ -45,6 +45,7 @@ select {
}
html {
box-sizing: border-box;
font-family: Pretendard, sans-serif;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Pretendard 레포지토리에 나와 있는 font-family 설정법을 확인해보세요.
특히 어디서든 동일한 환경을 가지고자 한다면 아래와 같은 font-family 구성을 추천합니다. 라고 써있는 부분입니다.
https://github.com/orioncactus/pretendard?tab=readme-ov-file#font-family

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

왠지 적용이 안되더라구요! 안되는 거 같았을 때 설정 방법을 좀 더 찾아봤어야 하는데..! 말씀해 주신 방법대로 수정하니까 잘 되네요 ㅎㅎ 감사합니다!!!

.then((response) => response.data);
};

export const getArticleById = (articleId: number): Promise<Article> => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

여기에 리턴 타입으로 : Promise<Article>을 지정해주지 않아도 됩니다. get옆에 제너릭으로 넣어주면 됩니다.

return axiosInstance
    .get<Article>(`/articles/${articleId}`)
    .then((response) => response.data);

그리고 저번 멘토링 시간에 말씀드렸던 것처럼
.then((response) => response.data);
자체가 없는 게(그러니까 response에서 굳이 data를 빼서 반환하지 않는 게) 범용성이 더 높아질 것 같습니다.

): Promise<ApiResponse<Comment>> => {
return axiosInstance
.get(`/articles/${articleId}/comments`, {
params: { limit },
Copy link
Collaborator

Choose a reason for hiding this comment

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

params도 잘 사용하셨네요 👍

@jlstgt jlstgt merged commit 0c87b9b into codeit-bootcamp-frontend:Next-고용빈 Jul 21, 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