-
Notifications
You must be signed in to change notification settings - Fork 35
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
[이형준]sprint7 #209
The head ref may contain hidden characters: "React-\uC774\uD615\uC900-sprint7"
[이형준]sprint7 #209
Conversation
…cription row length)
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 async function getProductComments({ productId, limit }) { | ||
const query = `limit=${limit}`; | ||
const url = `${BASE_URL}/products/${productId}/comments?${query}`; | ||
|
||
try { | ||
const response = await fetch(url); | ||
if (!response.ok) { | ||
throw new Error( | ||
`상품댓글정보를 불러오는데 실패했습니다. Status: ${response.status}` | ||
); | ||
} | ||
const body = await response.json(); | ||
return body; | ||
} catch (error) { | ||
throw new Error( | ||
`상품댓글정보 요청에 실패했습니다. Error: ${error.message}` | ||
); |
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.
깔끔하게 잘하셨어요! 👍 파라미터도 지금처럼 객체로 받으면 getProductComments
함수를 사용할 때 가독성이 올라가고, 확장성에 더 좋아요.
className={styles["file-input"]} | ||
id="file" | ||
type="file" | ||
accept=".png, .jepg, .jpg" |
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.
아래처럼 jpeg
바꿔주세요~
accept=".png, .jpeg, .jpg"
<div | ||
className={styles["imgfile-preview"]} | ||
style={{ | ||
backgroundImage: `url(${previewImg})`, | ||
display: isFileSelected ? "block" : "none", | ||
}} | ||
> | ||
<div | ||
className={`${styles["imgfile-delete-btn"]} ${styles["inactive"]}`} | ||
onClick={handleFileDelete} | ||
/> | ||
</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.
이렇게 CSS로 처리할 수도 있고, 조건부 랜더링으로 변경할 수도 있어요.
{isFileSelected ? (
<div
className={styles["imgfile-preview"]}
style={{ backgroundImage: `url(${previewImg})` }}
>
<div
className={`${styles["imgfile-delete-btn"]}`}
onClick={handleFileDelete}
/>
</div>
): null}
const [postImg, setPostImg] = useState(null); | ||
const [previewImg, setPreviewImg] = useState(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.
해당 상태는 파일 업로드 된 상태를 만약 다른 컴포넌트에도 공유해야되거나, ImgFileInput
컴포넌트 재사용성을 올리기 위해선 상태값을 상위로 올려서 props로 전달받는 방식으로 변경하는것도 고려하면 좋겠어요.
function ImgFileInput({ postImg, setPostImg, previewImg, setPreviewImg }) {
useEffect(() => { | ||
comment ? setIsCommentValid(true) : setIsCommentValid(false); | ||
}, [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.
가독성이 아래 방법이 더 좋을 것 같아요.
const [comment, setComment] = useState('');
...
useEffect(() => {
setIsCommentValid(comment.trim().length > 0);
}, [comment]);
<CommentInputContainer> | ||
<CommentTitle>문의하기</CommentTitle> | ||
<CommentInput | ||
as="textarea" |
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.
as 문법 사용 굳! 👍
const GrayBgInput = styled.input` | ||
background-color: var(--cool-gray-100); | ||
color: var(--cool-gray-400); | ||
border: none; | ||
|
||
&:focus { | ||
border: solid 2px var(--cool-gray-400); | ||
outline: none; | ||
} | ||
|
||
&::placeholder { | ||
color: var(--cool-gray-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.
shared 부분 공통으로 뺀 부분 좋아요.
const CommentsContainer = styled.ol` | ||
width: 1200px; | ||
margin: 0 auto; | ||
|
||
@media screen and (max-width: 1199px) { | ||
width: auto; | ||
margin: 0 24px; | ||
} | ||
|
||
@media screen and (max-width: 767px) { | ||
margin: 0 16px; | ||
} | ||
`; |
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.
styled-component에서 반응형 처리할 때 아래 처럼 많이 사용해요.
- styles 폴더 내
device.js
파일 생성 - 아래처럼 변수로 mobile,tablet, desktop 등 디바이스별 값 선언
const size = {
mobile: "767px",
tablet: "1199px",
};
export const device = {
mobile: `(max-width: ${size.mobile})`,
tablet: `(max-width: ${size.tablet})`,
};
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 CommentsContainer = styled.ol`
width: 1200px;
margin: 0 auto;
@media screen and ${device.tablet} {
width: auto;
margin: 0 24px;
}
@media screen and ${device.mobile} {
margin: 0 16px;
}
`;
function ProductDetails({ details }) { | ||
const { name, description, price, tags, favoriteCount, isFavorite, images } = | ||
details; | ||
|
||
return ( | ||
<DetailsContainer> | ||
<ProductImg src={images}></ProductImg> | ||
<DetailsTextContainer> | ||
<KebabMenuBtn src={kebabMenuImg} alt="메뉴 더보기" /> | ||
<DetailsMainContainer> | ||
<ProductName>{name}</ProductName> | ||
<ProductPrice>{price.toLocaleString("ko-KR")}원</ProductPrice> | ||
</DetailsMainContainer> | ||
<DetailsSubContainer> | ||
<SubDetailTitle>상품 소개</SubDetailTitle> | ||
<ProductDescription>{description}</ProductDescription> | ||
<SubDetailTitle>상품 태그</SubDetailTitle> | ||
<ProductTagsContainer> | ||
{tags.map((tag) => { | ||
const hashedTag = "# " + tag; | ||
return <ProductTag key={tag}>{hashedTag}</ProductTag>; | ||
})} | ||
</ProductTagsContainer> | ||
<LikeCountContainer> | ||
<HeartImg src={isFavorite ? heartActive : heartInActive}></HeartImg> | ||
<LikeCount>{favoriteCount}</LikeCount> | ||
</LikeCountContainer> | ||
</DetailsSubContainer> | ||
</DetailsTextContainer> | ||
</DetailsContainer> | ||
); | ||
} |
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.
해당 파일처럼 스타일링 관련된 컴포넌트가 많아서, 중요한 컴포넌트에 관심을 가지기 어려운 상태가 있을 수 있어요.
그렇다면, 파일을 나누는 방법이 있어요.
ㄴ Market
ㄴ ProductDetails
ㄴ ProductDetails.js
ㄴ ProductDetails.styled.js
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.
import React from "react";
import {
CommentsContainer,
NoCommentImg,
CommentContainer,
CommentContent,
CommentInfoContainer,
CommentUserPfp,
CommentInfoTextContainer,
CommentUserNickname,
CommentElapseTime,
} from "./ProductDetails.styled";
function ProductDetailComments({ comments }) {
return (
...
)
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.
혹시 말씀하시는 것이 style만 하는 style components 만 다빼서 ProductDetails.styled.js에 넣고 현제 ProductDetails 컴포넌트의 return 값은 지금 이대로 쓰라는 뜻인가요?
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.
@leehj322 네 맞습니다!
useEffect(() => { | ||
const productId = params.itemId; | ||
handleProductDetailLoad({ productId }); | ||
handleProductComments({ productId }); | ||
}, []); |
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.
의존성 디펜던시에 params.itemId
값이 추가되야해요~
useEffect(() => {
const productId = params.itemId;
handleProductDetailLoad({ productId });
handleProductComments({ productId });
}, [params.itemId]);
styled component를 처음 사용하는데 이런식으로 사용하는게 맞을까요? -> 관련해서 리뷰 남겨드렸습니다! |
api 요청을 어디서 실행하는 것이 좋은지 잘 모르겠습니다. 찾아보니까 상위 컴포넌트에서 하는것을 추천하던데 이유가 있나요? -> 하위에서 할 경우, 값 공유가 어렵기 때문이에요. 리액트는 하위에서 상위로 값을 보낼 수 있는 방법이 없거든요. 하지만, 상태관리 라이브러리를 사용하거나 react-query를 사용하게되면 반드시 상위에서 요청을 하지 않아도 돼요. 설계에 따라 변경 가능한 부분이고 절대적인 정답은 없습니다. |
이번에 작성한 부분에 한해서라도 추상화 가능한 부분이나 가독성을 높일 수 있는 부분이 있다면 짚어주시면 감사하겠습니다.. 코드를 잘 작성해서 협업에 신경을 쓰고 싶습니다. -> 가독성 부분에서 코멘트 남겨드렸습니다! 전반적으로 작성잘하셨어요! 노력이 느껴집니다..👍 |
사이트 배포 링크
요구사항
기본 요구사항
체크리스트 (기본)
상품상세
=> favoriteCount : 하트 개수
=> images : 상품 이미지
=> tags : 상품 태그
=> name : 상품 이름
=> description : 상품 설명
상품 문의 댓글
=> image : 작성자 이미지
=> nickname : 작성자 닉네임
=> content : 작성자가 남긴 문구
=> description : 상품 설명
=> updatedAt : 문의글 마지막 업데이트 시간
스프린트 미션6 누락 사항
피드백 반영
추가 예정 사항
주요 변경사항
멘토에게