-
Notifications
You must be signed in to change notification settings - Fork 24
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
[강대원] sprint9 #129
The head ref may contain hidden characters: "next-\uAC15\uB300\uC6D0-sprint9"
[강대원] sprint9 #129
Conversation
import image from "@/public/images/img_default.png"; | ||
export const DEFAULT_IMAGE = image; |
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한 이미지를 그대로 export 시켜버리기보다는 default 이미지의 경로를 상수로 정의해서 사용하는 케이스가 조금 더 메이지한 접근 방법인 것 같네요.
<ul> | ||
{articles?.list?.map((article) => ( | ||
<AllArticleCard key={article.id} article={article} /> | ||
))} | ||
</ul> |
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.
이 부분 전체를 wrapping하는 컴포넌트가 하나 있어도 좋을 것 같네요.
staleTime: 1000 * 60 * 5, | ||
gcTime: 1000 * 60 * 30, | ||
refetchOnWindowFocus: false, | ||
refetchInterval: 1000 * 60 * 10, |
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.
하나 정도는 괜찮은데 이렇게 단위가 흩뿌려져 있으니 보기가 어려워지는군요. ㅋㅋ
이런 케이스는 미리 unit에 따라 생수를 정의해두고 필요에 따라 로드해서 사용하면 어떨까 싶네요.
e.g.
const SECONDS = 1000;
const MINUTES = 60 * SECONDS;
type ProductFormProps = { | ||
defaultValues?: ProductCreateRequest; | ||
productId?: number; | ||
isEdit?: boolean; | ||
}; |
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.
컴포넌트의 모든 props가 nullable이라는 건 props의 실효성에 대해 의심해봐야 할 신호이기도 합니다.
그리고 혹시 productId와 isEdit을 하나로 합치는 건 불가능할까요? productId의 존재 여부에 따라 isEdit 값이 종속적으로 결정되는 것이 아닌지?
const mutation = useMutation({ | ||
mutationFn: (content: string) => createComment(Number(productId), content), | ||
onSuccess: () => { | ||
// 댓글 등록 성공 시, 해당 제품의 댓글 리스트를 다시 불러옴 | ||
queryClient.invalidateQueries({ | ||
queryKey: ["comments", String(productId)], | ||
}); | ||
setCommentContent(""); // 입력 필드 초기화 | ||
}, | ||
onError: (error) => { | ||
console.error("댓글 등록 실패:", 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.
코드를 보다 반복적으로 발견한 부분인데 id에 대해서 자꾸 불필요한 형 변환이 진행되고 있는 것 같습니다. 어떤 타입이든 하나의 타입으로 변환해서 고정하는 게 복잡성을 줄일 수 있을 것 같네요.
e.g. 서버에서 string 타입으로 반환 => 하위 컴포넌트에서도 전부 string으로만 사용 => 필요에 따라 한 번씩 캐스팅 (이런 경우는 거의 없을 것 같긴 합니다.)
}); | ||
|
||
const [dropdownStates, setDropdownStates] = useState<{ | ||
[key: string]: boolean; |
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.
모든 타입이 그렇긴 하지만, 특히 이렇게 넓은 타입은 의미를 명확하게 전달하기 위해 alias를 해서 사용하면 좋습니다.
export const getComments = async ( | ||
productId: number, | ||
limit: number, | ||
cursor: number, | ||
) => { | ||
const response = await api.get<CommentListResponse>( | ||
`/products/${productId}/comments?limit=${limit}&cursor=${cursor}`, | ||
); | ||
return response.data; | ||
}; |
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.
- axios로 요청을 보내는 부분은 params로 처리하면 좋을 것 같네요.
- 직접 정의한 getComments에서 limit과 cursor parameter를 받을 때도 positional하게 필수로 받는 것보다는 유연함을 위해 객체로 묶어서 처리하는 방식이 좋지 않을까 싶네요.
<div className="mb-[90px] max-w-[400px]"> | ||
<h1 | ||
className="mb-[4rem] max-w-[35rem] whitespace-normal break-keep text-center text-[4rem] font-bold leading-[5.6rem] sm:max-w-[24rem] sm:text-center sm:text-[3.2rem] sm:leading-[4rem] md:max-w-full" | ||
dangerouslySetInnerHTML={{ __html: title }} |
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.
보통 이 속성은 사용할 일이 거의 없기도 하지만, 꼭 필요한 사용하지 않는 것이 좋습니다. 🤔
{paginationArr.map((value) => ( | ||
<div | ||
key={value} | ||
className={`flex h-10 w-10 cursor-pointer items-center justify-center rounded-full border ${value === pageNo ? "border-[#E5E7EB] bg-[#2F80ED] text-[#F9FAFB]" : "border-[#E5E7EB] text-[#6B7280]"}`} |
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.
컬러 코드를 직접 사용하는 패턴은 특히나 변경에 취약하기 때문에 앞으로 계속 사용될 것 같은 색상이라면 이름을 정의해두고 사용하는 걸 권장드립니다.
type PostAndCommentActionsDropdownProps = { | ||
id?: string | number; // 수정 시 필요한 id 값 | ||
basePath?: "/items" | "/articles"; // 경로 변경을 위한 추가 prop | ||
onDelete: () => void; | ||
onEdit?: () => void; // 댓글 수정 시 필요한 함수 | ||
type: "post" | "comment"; | ||
}; | ||
|
||
const PostAndCommentActionsDropdown = ({ | ||
id, | ||
basePath, | ||
onDelete, | ||
onEdit, | ||
type, | ||
}: PostAndCommentActionsDropdownProps) => { | ||
const router = useRouter(); | ||
|
||
const onClick = (action: string): void => { | ||
switch (action) { | ||
case "edit": | ||
console.log("수정하기"); | ||
if (type === "post" && basePath && id) { | ||
router.push(`${basePath}/${id}/edit`); // 수정 페이지로 이동 | ||
} else if (type === "comment" && onEdit) { | ||
onEdit(); | ||
} | ||
|
||
break; | ||
case "delete": | ||
console.log("삭제하기"); | ||
onDelete(); | ||
break; | ||
default: | ||
break; |
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.
이름에서부터 And가 등장한다는 건 좋은 징조가 아닙니다. 액션을 보면 타입을 onClick 액션의 플래그로 사용하고 있는데, 이 경우라면 type을 받는 대신 action 자체를 외부에서 주입받고 Dropdown 컴포넌트를 type으로부터 독립시키는 게 더 좋을 것 같네요.
기본 요구사항
공통
로그인/회원가입 페이지
로그인 페이지
회원가입 페이지
로그인, 회원가입 페이지 공통
GNB (상단 내비게이션 바)
상품 상세 페이지
심화 요구사항
로그인 및 회원가입 페이지 공통
유저 기능
React-Query로 마이그레이션
로딩 및 에러 핸들링
상품 데이터 캐싱 및 업데이트