-
Notifications
You must be signed in to change notification settings - Fork 40
[윤예지] Sprint10 #316
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 #316
The head ref may contain hidden characters: "Next-\uC724\uC608\uC9C0-sprint10"
Conversation
clsx나 classnames 라이브러리를 사용하는 방법이 있을 것 같아요! clsx 는 현업에서도 정말 많이 사용하는 라이브러리입니다. |
arthurkimdev
left a comment
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 getComment = async ( | ||
| productId: number, | ||
| params: {}, | ||
| ): Promise<AxiosResponse<GetCommentResultResponse>> => { |
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.
이런 함수 파라미터를 작성할 땐 일반적으로 객체로 받는 것이 좋습니다. 객체로 작성하게 되면 아래 장점이 있어요.
앞으로는 모든 함수에 파라미터는 객체로 작성해보세요~
- 파라미터 순서에 구애받지 않음
- 선택적 파라미터 처리가 용이
- 추후 파라미터 추가가 쉬움 (확장성)
- TypeScript에서 타입 관리가 더 명확
export const createComments = async ({
productId,
content,
}: CreateCommentParams) => {
| } catch (error) { | ||
| if (axios.isAxiosError(error)) { | ||
| console.error('Axios Error:', error.response?.data || error.message); | ||
| throw new Error('데이터를 불러오는도중 에러가 발생했습니다.'); | ||
| } else { | ||
| console.error('Unknown Error:', error); | ||
| throw new Error('기타 에러입니다.'); | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| export const createComments = async ( | ||
| productId: number, | ||
| content: { content: string }, | ||
| ) => { | ||
| try { | ||
| return await axiosInstance.post( | ||
| `/products/${productId}/comments`, | ||
| content, | ||
| { | ||
| headers: { | ||
| Authorization: `Bearer ${process.env.NEXT_PUBLIC_ACCESS_TOKEN}`, | ||
| }, | ||
| }, | ||
| ); | ||
| } catch (error) { | ||
| if (axios.isAxiosError(error)) { | ||
| console.error('Axios Error:', error.response?.data || error.message); | ||
| throw new Error('데이터를 불러오는도중 에러가 발생했습니다.'); | ||
| } else { | ||
| console.error('Unknown Error:', error); | ||
| throw new Error('기타 에러입니다.'); | ||
| } | ||
| } | ||
| }; |
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.
지금 보면 아래 에러 처리 코드가 동일하게 반복되는데요. 이런 경우 별도 함수로 분리해볼 수 있는 방법이 있어요.
그렇게 되면 에러처리에 추가 로직이 필요할 때 추가가 용이하고, 공통으로 일관되게 처리할 수 있을거예요.
예를들면 네트워크 에러 (offline) 같은 경우도 공통적인데 이런 부분들은 handleAxiosError 에 추가하면 이제 일괄 처리가 되겠죠?
catch (error) {
if (axios.isAxiosError(error)) {
console.error('Axios Error:', error.response?.data || error.message);
throw new Error('데이터를 불러오는도중 에러가 발생했습니다.');
} else {
console.error('Unknown Error:', error);
throw new Error('기타 에러입니다.');
}
}
const handleAxiosError = (error: unknown) => {
if (axios.isAxiosError(error)) {
console.error('Axios Error:', error.response?.data || error.message);
throw new Error('데이터를 불러오는도중 에러가 발생했습니다.');
}
console.error('Unknown Error:', error);
throw new Error('기타 에러입니다.');
};
...
catch (error) {
handleAxiosError(error);
}
| @@ -0,0 +1 @@ | |||
| NEXT_PUBLIC_ACCESS_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NDkzLCJzY29wZSI6ImFjY2VzcyIsImlhdCI6MTczMzU2NzgwMywiZXhwIjoxNzMzNTY5NjAzLCJpc3MiOiJzcC1wYW5kYS1tYXJrZXQifQ.15l2pH_P-kh0tMemBn521wnFwugH38QssNljRzNa3Qo" No newline at end of file | |||
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.
이런 .env 파일은 절~대 git에 올리시면 안됩니다. 😸
이 파일에 API KEY 등 보안적인 요소가 적힌 파일이기 때문이에요.
.gitignore에 .env 추가하시면 git에 올라가지 않을거예요~
| const Comment: React.FC<CommentProps> = ({ comment }) => { | ||
| return ( | ||
| <div className={styles.comment}> | ||
| <div className={styles['comment-top']}> | ||
| <div className={styles.content}>{comment.content}</div> | ||
| <Image src={Iconkebab} alt=""></Image> | ||
| </div> | ||
| <div className={styles['comment-bottom']}> | ||
| <div> | ||
| <Image | ||
| src={profile} | ||
| width={24} | ||
| height={24} | ||
| alt="프로파일이미지" | ||
| ></Image> | ||
| </div> | ||
| <div className={styles.profile}> | ||
| <div className={styles.nickName}>{comment.writer.nickname}</div> | ||
| <div className={styles.date}>{comment.createdAt}</div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
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.
UI 분리되게 잘 작성하셨어요! Image에 alt 값만 넣어주세요~
| const handleResize = debounce(() => { | ||
| const currentWidth = window.innerWidth; | ||
|
|
||
| if (currentWidth >= 1024) { | ||
| if (currentWidth >= BREAKPOINTS.desktop) { | ||
| setScreenType('desktop'); | ||
| } else if (currentWidth >= 768) { | ||
| } else if (currentWidth >= BREAKPOINTS.tablet) { | ||
| setScreenType('tablet'); | ||
| } else { | ||
| setScreenType('mobile'); | ||
| } | ||
| }; | ||
| }, 250); |
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.
리뷰 반영 굳 입니다. 👍
| const [image, setImage] = useState<File | null>(null); | ||
| const [preview, setPreview] = useState<string | null>(null); |
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.
상태 변수를 하나로 좀 더 단순하게 할 수 있겠어요.
const [imageState, setImageState] = useState<{
file: File | null;
preview: string | null;
}>({
file: null,
preview: null
});
| <div | ||
| className={styles.image} | ||
| onClick={() => fileInputRef.current?.click()} | ||
| > |
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.
일반적으로 키보드 접근성 때문에 div 태그에 onClick 은 브라우저가 지원하지 않는 특수한 경우 말고는 사용하지 않아요~ button 태그를 사용해주세요.
| export async function getServerSideProps(context: GetServerSidePropsContext) { | ||
| try { | ||
| const bestProductsResponse = await getProducts({ | ||
| orderBy: 'favorite', | ||
| page: 1, | ||
| pageSize: 1, | ||
| }); | ||
| const allProductsResponse = await getProducts({ | ||
| orderBy: 'recent', | ||
| page: 1, | ||
| pageSize: 10, | ||
| }); | ||
|
|
||
| return { | ||
| props: { | ||
| initialBestProducts: bestProductsResponse.data.list, | ||
| initialAllProducts: allProductsResponse.data.list, | ||
| }, | ||
| }; | ||
| } catch (error) { | ||
| console.error('데이터를 불러오는 데 실패했습니다:', error); | ||
| return { | ||
| props: { | ||
| initialBestProducts: [], | ||
| initialAllProducts: [], | ||
| }, | ||
| }; | ||
| } | ||
| } | ||
|
|
||
| const Board = ({ | ||
| initialBestProducts, | ||
| initialAllProducts, | ||
| }: { | ||
| initialBestProducts: Product[]; | ||
| initialAllProducts: Product[]; |
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.
일반적으로 getServerSideProps 부분은 아래에 위치하고 Board 컴포넌트가 상위로 올라오는 구조로 작성합니다!
const Board = ({
initialBestProducts,
initialAllProducts,
}: {
initialBestProducts: Product[];
initialAllProducts: Product[];
...
export async function getServerSideProps(context: GetServerSidePropsContext) {
try {
const bestProductsResponse = await getProducts({
orderBy: 'favorite',
page: 1,
pageSize: 1,
});
const allProductsResponse = await getProducts({
orderBy: 'recent',
page: 1,
pageSize: 10,
});
return {
props: {
initialBestProducts: bestProductsResponse.data.list,
initialAllProducts: allProductsResponse.data.list,
},
};
} catch (error) {
console.error('데이터를 불러오는 데 실패했습니다:', error);
return {
props: {
initialBestProducts: [],
initialAllProducts: [],
},
};
}
}
요구사항
기본
상품 등록 페이지
상품 상세 페이지
심화
주요 변경사항
스크린샷
멘토에게
현재는 추가 클래스 props를 받아서 CSS에서 .컴포넌트css .addClass css 이렇게 주고있습니다!
추가적인 컴포넌트 css없이도 한 deps로만 줄수있는 방법이있을까요??