[노현지] Sprint7#69
Hidden character warning
Conversation
…sed on the current path
|
스프리트 미션 하시느라 수고 많으셨어요. |
| } | ||
|
|
||
| export async function getItemComments(productId = "", { cursor = 0 }) { | ||
| const query = `limit=${COMMENTS_LIMIT}&cursor=${cursor}`; |
There was a problem hiding this comment.
쿼리는 URLSearchParams로 손쉽게 사용할 수 있어요 !
| const query = `limit=${COMMENTS_LIMIT}&cursor=${cursor}`; | |
| const query = new URLSearchParams({cursor, limit: COMMENTS_LIMIT, }); |
URLSearchParams와 함께 객체로 손쉽게 핸들링할 수 있습니다 !
객체로 구성할 수 있어 가독성이 좋고, URL 인코딩을 자동으로 처리하여 특수 문자나 공백이 포함된 값에서도 안전하게 동작합니다 !
URLSearchParams:
URLSearchParams인터페이스는 URL의 쿼리 문자열을 대상으로 작업할 수 있는 유틸리티 메서드를 정의합니다.
| function PrimaryButton({ | ||
| children, | ||
| className = "", | ||
| onClick = () => {}, | ||
| disabled = false, | ||
| }) { | ||
| const buttonClass = `${styles.button} ${className}`; | ||
| return ( | ||
| <button className={buttonClass} onClick={onClick} disabled={disabled}> | ||
| {children} | ||
| </button> | ||
| ); | ||
| } |
There was a problem hiding this comment.
(심화/제안/고민해보기) 혹시 버튼 컴포넌트들을 공통 버튼 컴포넌트로 리팩토링 할 수는 없을까요?
MenuButton, DeleteButton, PrimaryButton.. 더 나아가서 SecondaryButton, UpdateButton 등등 목적에 따라 버튼이 생겨날 수 있을 것 같아요.
만약 디자인 시스템이 바뀌어서 일괄적으로 버튼의 radius를 바꿔야하는 상화이 온다면 유지보수하기가 어려워질 수 있겠지요?
예를 들어서 다음과 같은 컴포넌트를 만들어볼 수 있을 것 같아요:
import styles from "./Button.module.css";
function Button({
children,
variant = "primary",
icon,
// onClick = () => {},
className = "",
// disabled = false,
...rest
}) {
return (
<button
className={`${styles.button} ${styles[variant]} ${className}`}
// onClick={onClick}
// disabled={disabled} 해당 내용들은 `rest`에 자동 등재
{...rest}
>
{icon && <img src={icon} alt="" className={styles.icon} />}
{children}
</button>
);
}
export default Button;There was a problem hiding this comment.
방금 제안드린 내용에서 추가
사실, 여기서 icon 또한 button의 children으로 포함시킬 수도 있긴 하겠네요 😊
예를 들어서 다음과 같이요 !:
<Button variant="error" className="flex justify-between w-24 items-center">삭제<DeleteIcon /></Button>| import debounce from "lodash.debounce"; | ||
|
|
||
| function useWindowSize() { | ||
| function useWindowSize(delay = 300) { |
| <h2 className={styles.featureTag}>Search</h2> | ||
| <h3 className={styles.bold}>구매를 원하는 상품을 검색하세요</h3> | ||
| <p className={styles.featureDescription}> | ||
| 구매하고 싶은 물품은 검색해서 | ||
| <br /> | ||
| 쉽게 찾아보세요 | ||
| </p> |
| <div className={`${styles.features} ${styles.wrapper}`}> | ||
| <div className={styles.feature}> | ||
| <img src={HomeImg1} width="68.5%" alt="인기 상품" /> | ||
| <div className={styles.featureContent}> | ||
| <h2 className={styles.featureTag}>Hot item</h2> | ||
| <h3 className={styles.bold}>인기 상품을 확인해 보세요</h3> | ||
| <p className={styles.featureDescription}> | ||
| 가장 HOT한 중고거래 물품을 | ||
| <br /> | ||
| 판다 마켓에서 확인해 보세요 | ||
| </p> | ||
| </div> | ||
| </div> |
There was a problem hiding this comment.
(제안/선택) section을 사용하셔도 될 것 같아요.
| <div className={`${styles.features} ${styles.wrapper}`}> | |
| <div className={styles.feature}> | |
| <img src={HomeImg1} width="68.5%" alt="인기 상품" /> | |
| <div className={styles.featureContent}> | |
| <h2 className={styles.featureTag}>Hot item</h2> | |
| <h3 className={styles.bold}>인기 상품을 확인해 보세요</h3> | |
| <p className={styles.featureDescription}> | |
| 가장 HOT한 중고거래 물품을 | |
| <br /> | |
| 판다 마켓에서 확인해 보세요 | |
| </p> | |
| </div> | |
| </div> | |
| <section className={`${styles.features} ${styles.wrapper}`}> | |
| <div className={styles.feature}> | |
| <img src={HomeImg1} width="68.5%" alt="인기 상품" /> | |
| <div className={styles.featureContent}> | |
| <h2 className={styles.featureTag}>Hot item</h2> | |
| <h3 className={styles.bold}>인기 상품을 확인해 보세요</h3> | |
| <p className={styles.featureDescription}> | |
| 가장 HOT한 중고거래 물품을 | |
| <br /> | |
| 판다 마켓에서 확인해 보세요 | |
| </p> | |
| </div> | |
| </section> |
영역이 구분되며 h로 각 섹션의 정체성이 명확하므로 section태그도 괜찮은 의미일 것 같아서 제안드려요:
다음은 MDN의 <section>에 대한 설명 중 첫 문장입니다.
The
<section>HTML element represents a generic standalone section of a document, which doesn't have a more specific semantic element to represent it. Sections should always have a heading, with very few exceptions.
<section>HTML 요소는 문서의 일반적인 독립형 섹션을 나타내며 이를 나타내는 더 구체적인 의미 요소가 없습니다. 섹션에는 거의 예외를 제외하고 항상 제목이 있어야 합니다.
| function ItemCommentCard({ | ||
| comment: { | ||
| content, | ||
| updatedAt, | ||
| writer: { nickname, image }, | ||
| }, | ||
| }) { |
There was a problem hiding this comment.
크으 이렇게 props를 받으니까 comment가 어떤 구성으로 되어있는지 파악하기 좋네요 👍👍
| {isEditMode ? ( | ||
| <div className={styles.buttonContainer}> | ||
| <button | ||
| type="button" | ||
| className={styles.cancelButton} | ||
| onClick={handleEditCancel} | ||
| > | ||
| 취소 | ||
| </button> | ||
| <PrimaryButton | ||
| type="submit" | ||
| className={styles.editCompleteButton} | ||
| disabled={!editAvailable} | ||
| onClick={handleEditComplete} | ||
| > | ||
| 수정 완료 | ||
| </PrimaryButton> | ||
| </div> | ||
| ) : ( | ||
| "" | ||
| )} |
There was a problem hiding this comment.
다음과 같이 작성해볼 수 있습니다 !:
| {isEditMode ? ( | |
| <div className={styles.buttonContainer}> | |
| <button | |
| type="button" | |
| className={styles.cancelButton} | |
| onClick={handleEditCancel} | |
| > | |
| 취소 | |
| </button> | |
| <PrimaryButton | |
| type="submit" | |
| className={styles.editCompleteButton} | |
| disabled={!editAvailable} | |
| onClick={handleEditComplete} | |
| > | |
| 수정 완료 | |
| </PrimaryButton> | |
| </div> | |
| ) : ( | |
| "" | |
| )} | |
| {isEditMode && ( | |
| <div className={styles.buttonContainer}> | |
| <button | |
| type="button" | |
| className={styles.cancelButton} | |
| onClick={handleEditCancel} | |
| > | |
| 취소 | |
| </button> | |
| <PrimaryButton | |
| type="submit" | |
| className={styles.editCompleteButton} | |
| disabled={!editAvailable} | |
| onClick={handleEditComplete} | |
| > | |
| 수정 완료 | |
| </PrimaryButton> | |
| </div> | |
| )} |
| const handleLoad = async () => { | ||
| try { | ||
| const { list, nextCursor } = await getItemComments(productId, { | ||
| cursor, | ||
| }); | ||
| setComments((prev) => [...prev, ...list]); | ||
| setCursor(nextCursor); | ||
| } catch (error) { | ||
| alert(error.message); | ||
| console.error("ERROR: ", error); | ||
| } | ||
| }; |
There was a problem hiding this comment.
해당 함수는 useCallback을 사용해볼 수 있습니다 !:
const handleLoad = useCallback(async () => {
try {
const { list, nextCursor } = await getItemComments(productId, { cursor });
setComments((prev) => [...prev, ...list]);
setCursor(nextCursor);
} catch (error) {
alert(error.message);
console.error("ERROR: ", error);
}
}, [productId, cursor]); // ✅ 의존성 추가|
수고하셨습니다 현지님 ! 미션 수행하시느라 수고 많으셨습니다 현지님 ! 😊👍 |
요구사항
기본
상품 상세
상품 문의 댓글
심화
주요 변경사항
handleLoad함수를useEffect안에 선언alt=""로 수정handleLoad함수를useEffect안에 선언alt=""로 수정alt=""로 수정멘토에게