Conversation
hyesngy
left a comment
There was a problem hiding this comment.
이번 주 워크북을 통해 React의 성능 최적화 기법인 debounce와 throttle을 적용해봤습니다! 쉽지 않은 개념이지만 모두 잘 해내셔서 대단합니다! 👏🏻👏🏻👏🏻
이런 최적화 기법들은 실제 서비스에서 UX를 크게 향상시키는 요소입니다. 앞으로 있을 데모데이 프로젝트에서도 꼭 활용해서 구현해보면 좋겠습니다!👍🏻👍🏻👍🏻
| <div className="flex justify-end mb-6"> | ||
| <div className="bg-gray-800 rounded-lg p-1 inline-flex"> | ||
| <button | ||
| className={`px-4 py-2 rounded-md transition-all ${ | ||
| order === 'desc' | ||
| ? 'bg-pink-600 text-white' | ||
| : 'bg-transparent text-gray-300 hover:text-white' | ||
| }`} | ||
| onClick={() => setOrder('desc')} | ||
| > | ||
| 최신순 | ||
| </button> | ||
| <button | ||
| className={`px-4 py-2 rounded-md transition-all ${ | ||
| order === 'asc' | ||
| ? 'bg-pink-600 text-white' | ||
| : 'bg-transparent text-gray-300 hover:text-white' | ||
| }`} | ||
| onClick={() => setOrder('asc')} | ||
| > | ||
| 오래된순 | ||
| </button> | ||
| </div> | ||
| </div> |
There was a problem hiding this comment.
현재 search-page와 home-page에서 정렬 버튼 UI가 중복되고 있는데, 이 부분을 별도의 SortButton 컴포넌트로 분리하면 코드 중복을 줄이고 일관된 UI를 유지할 수 있을 것 같습니다.
| <LpCard key={lp.id} lp={lp} /> | ||
| ))} | ||
|
|
||
| {!isLoading && <LpCardSkeletonList count={20} />} |
There was a problem hiding this comment.
현재 home-page에서 스켈레톤을 로딩 중이 아닐 때도 항상 표시하도록 구현되어있는데, 의도된 동작인지 이해가 가지 않습니다. 추가 데이터를 불러올 때에만, 스켈레톤을 보여주는 것이 사용자에게도 명확한 피드백을 전달할 수 있을 것 같습니다.
일반적으로 isFetchingNextPage 상태에 따라 조건부로 렌더링하여 예시로는 {isFetchingNextPage && <LpCardSkeletonList count={20} />} 이런 식으로 작성할 수 있을 것 같습니다.
| const [search, setSearch] = useState(''); | ||
| const [order, setOrder] = useState<'desc' | 'asc'>('desc'); | ||
| const debouncedSearch = useDebounce(search, 500); | ||
| const { ref, inView } = useInView({ threshold: 0 }); | ||
| const throttledInView = useThrottle(inView, 1000); | ||
|
|
||
| const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading } = | ||
| useGetInfiniteLpList(10, order, debouncedSearch); | ||
|
|
||
| useEffect(() => { | ||
| if (throttledInView && hasNextPage && !isFetchingNextPage) { | ||
| fetchNextPage(); | ||
| console.log('다음 페이지 호출'); | ||
| } | ||
| }, [throttledInView, hasNextPage, isFetchingNextPage, fetchNextPage]); |
There was a problem hiding this comment.
2025-05-22.220636.mp4
현재 무한 스크롤에서 여러 번의 요청이 연속으로 발생하는 문제가 있습니다.
home-page와 search-page 모두에서 useEffect(() => { if (throttledInView && hasNextPage && !isFetchingNextPage) { fetchNextPage(); } }, [throttledInView, hasNextPage, isFetchingNextPage, fetchNextPage])로 구현되어 있는데, fetchNextPage() 실행 후 hasNextPage가 여전히 true이고 throttledInView도 계속 true로 유지되면서 연속적으로 요청이 발생하고 있는 것 같습니다.
또한, useInView({ threshold: 0 })로 설정되어 있어서 타겟 요소가 화면에 조금이라도 보이면 즉시 트리거됩니다. 또한 <div ref={ref} className="h-10 mt-4"></div> 요소가 계속 화면에 노출되어 있어서 지속적으로 inView가 true 상태를 유지하게 됩니다.
fetchNextPage() 호출 직후 throttledInView를 일시적으로 false로 만들거나, 요청 완료 후에만 다시 감지할 수 있도록 상태 관리를 개선해야 합니다. useCallback으로 fetchNextPage 함수를 감싸서 불필요한 의존성 변경을 방지하는 것도 고려해볼 수 있을 것 같습니다.
Intersection Observer에서도 threshold를 0.1 정도로 높이거나, rootMargin을 음수값으로 설정하여 요소가 완전히 화면 중앙에 들어왔을 때만 트리거되도록 조정하여 정확한 시점에만 감지되도록 개선해주세요!
|
|
||
| const SearchPage: React.FC = () => { | ||
| const [search, setSearch] = useState(''); | ||
| const [order, setOrder] = useState<'desc' | 'asc'>('desc'); |
There was a problem hiding this comment.
현재 HomePage에서 하드코딩된 "asc" | "desc" 타입을 사용하고 있습니다. PAGINATION_ORDER enum을 별도 파일에 정의해놨음에도 불구하고 문자열 리터럴 타입을 직접 사용하면 타입 정의가 중복되고 유지보수성도 떨어집니다.
정의해둔 enum을 사용해서 useState<PAGINATION_ORDER>(PAGINATION_ORDER.DESC) 같은 형태로 변경하면, 타입 안정성을 높이고 코드 유지보수성도 개선할 수 있을 것 같습니다!
📝 미션 번호
8주차 Misson 1, 2
📋 구현 사항
📎 스크린샷
✅ 체크리스트