-
Notifications
You must be signed in to change notification settings - Fork 42
[윤정환] sprint5 #186
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
base: React-윤정환
Are you sure you want to change the base?
The head ref may contain hidden characters: "React-\uC724\uC815\uD658-sprint5"
[윤정환] sprint5 #186
Conversation
[Fix] delete merged branch github action
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 query = `orderBy=${orderBy}&page=${currentPage}&pageSize=${pageSize}${ | ||
keyword ? `&keyword=${keyword}` : "" | ||
}`; |
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.
URLSearchParams 객체 사용해서 여러개의 파라미터를 관리해보는 방법으로 바꾸면, 쿼리 스트링의 파싱, 조작, 인코딩 등의 작업을 간편하게 처리할 수 있으면서도 실수를 줄일 수 있겠죠? :)
아래 아티클 참고해보세요!
참고
const calcTotalPage = (totalCount, pageSize) => | ||
Math.ceil(totalCount / pageSize); |
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.
해당 함수가 컴포넌트의 바깥에 있으면 totalCount, pageSize props 값이 변경될때마다 함수가 새로운 값을 사용하여 계산되어야하는데, 이걸 반영하기 힘들겠죠?
컴포넌트 내부로 옮겨줍시다!
products, | ||
totalCount, | ||
pageSize, | ||
currentPage, |
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만 남기는게 중요합니다.
왜 명확한 props만 남기는게 중요한지는 아래 아티클 참고해보세요!
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들은 페이지네이션에 관련된 데이터이므로, AllProductList 컴포넌트뿐만 아니라 여러 페이지에서 사용될 일이 있을때마다 쓰일거예요. 이때 코드 중복을 줄이기위해 페이지네이션 관련 로직은 커스텀훅으로, UI는 컴포넌트로 분리하면 좋겠죠?
import styles from "./PageButton.module.css"; | ||
|
||
const PageButton = ({ pageNumber, selected = false, onClickPage }) => { | ||
const className = styles[`${selected ? "selected" : ""}`]; |
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.
조건부 스타일링이 복잡하거나, 관리 방식을 일관성있게 유지하고싶다면 이런 라이브러리를 사용해보는건 어떨까요?
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.
우선 사용 편의성과 재사용성, 역할 축소를 위해 해당 컴포넌트에서 로직을 커스텀훅을 만들어 분리해줍시다.
예시)
- usePagination.jsx
import { useCallback, useEffect, useState } from "react";
const usePagination = ({
totalPage,
signalSearch,
breakpoint,
onClickPage,
}) => {
const [pageList, setPageList] = useState([[1]]);
const [pageListIndex, setPageListIndex] = useState(0);
const resetPagination = useCallback(() => {
setPageListIndex((prev) => 0);
onClickPage((prev) => 1);
}, [onClickPage]);
const onMovePageList = (direction) => {
setPageListIndex((prev) => prev + direction);
onClickPage((prev) => pageList[pageListIndex + direction][0]);
};
const calcPageList = (totalPage) => {
if (!totalPage) {
setPageList((prev) => [[1]]);
return;
}
const wholePageList = [];
const dividedPageList = [];
let count = 0;
for (let i = 1; i <= totalPage; i++) {
if (count === 5) {
wholePageList.push([...dividedPageList]);
dividedPageList.splice(0);
count = 0;
}
dividedPageList.push(i);
count++;
if (totalPage === i) {
wholePageList.push([...dividedPageList]);
}
}
setPageList((prev) => [...wholePageList]);
};
useEffect(() => {
calcPageList(totalPage);
}, [totalPage]);
useEffect(() => {
resetPagination();
}, [signalSearch, breakpoint, resetPagination]);
return {
pageList,
pageListIndex,
onMovePageList,
};
};
export default usePagination;
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로 전달받아 표시해주는 역할만 담당하게 해봐요 :)
import ArrowButton from "./ArrowButton";
import PageButton from "./PageButton";
import styles from "./PaginationNav.module.css";
const PaginationNav = ({
pageList,
pageListIndex,
onMovePageList,
onClickPage,
currentPage,
}) => {
const PrevArrowButton = {
shape: "<",
direction: -1,
endIndex: 0,
};
const NextArrowButton = {
shape: ">",
direction: 1,
endIndex: pageList.length - 1,
};
return (
<div className={styles.container}>
<ArrowButton
onMovePageList={onMovePageList}
pageListIndex={pageListIndex}
{...PrevArrowButton}
/>
{pageList[pageListIndex].map((pageNumber) => {
return (
<PageButton
key={pageNumber}
pageNumber={pageNumber}
selected={pageNumber === currentPage}
onClickPage={onClickPage}
/>
);
})}
<ArrowButton
onMovePageList={onMovePageList}
pageListIndex={pageListIndex}
{...NextArrowButton}
/>
</div>
);
};
export default PaginationNav;
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와 비즈니스 로직을 명확히 구분 => 페이지네이션 로직만 필요할때는 커스텀훅으로 결과값만 조합할 수 있으니 페이지네이션 관련해서 다른 UI를 써줄때에도 쉽게 변경 및 재사용 가능
-
유지보수성 개선: 각 컴포넌트의 역할이 명확해지고, 코드 중복이 제거되고, 테스트하기 쉬운 구조로 변경됨
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를 다루는 로직과 얽혀있어 디버깅하거나 읽기 어려웠던 코드를 다시 수정해볼까요?
- 숫자 5와 같이 페이지별 아이템 갯수를 직접적으로 사용하지말고,
const ITEMS_PER_PAGE = 5;
와 같이 상수를 만들어 보기 쉽게 관리하도록하는건 어떨까요? - state update시 이전값이 불필요한데도 이전값을 참조 비교하는 방식을 사용하고있는데, 아래와 같이 단순화해서 복잡도를 줄여볼까요?
setPageListIndex(0);
onClickPage(1);
- useEffect 내부에서 calcPage 함수를 사용하고있는데, 좀 더 안정적인 흐름을 위해 메모이제이션된 calcPage 함수를 올바르게 deps list에 추가하고 활용할까요?
useEffect(() => {
calcPageList(totalPage);
}, [totalPage, calcPageList]);
<input | ||
className={styles.input} | ||
value={inputValue} | ||
placeholder="검색할 상품을 입력해주세요" | ||
onChange={handleChangeInputValue} | ||
onKeyDown={handleSearch} | ||
></input> |
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.
input 태그는 children을 포함하지않기때문에 self-closing 형태로 작성해주시는게 깔끔합니다.
const [breakpoint, setBreakpoint] = useState( | ||
getBreakpoint(window.innerWidth) | ||
); |
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.
NIT: 이 코드는 몇가지 기술적 배경을 고려했을때 개선하는게 좋습니다.
다른 분 PR에 달아드린 코멘트 참고해보세요 :)
질문에 대한 답변
네, 페이지네이션 컴포넌트 위주로 리팩토링 방법 안내해드렸습니다! 참고해보세요 :) |
요구사항
배포
https://sprint-mission5.netlify.app/
기본
중고마켓 반응형
심화
주요 변경사항
스크린샷
멘토에게