[8주차/조이] 워크북 제출합니다.#81
Conversation
| const toggle = () => setIsOpen((prev) => !prev); | ||
|
|
||
| // ESC 키로 닫기 | ||
| useEffect(() => { |
There was a problem hiding this comment.
현재 구조에서는 사이드바가 화면에 닫혀있는 상태(isOpen === false)일 때도 브라우저 전체(document)가 ESC 키보드 입력을 계속해서 감시하게 되는 구조입니다
나중에 이 useSidebar 훅을 프로젝트 내의 여러 페이지나 다른 모달 컴포넌트에서 재사용하게 되면, 화면에 아무것도 열리지 않았는데도 백그라운드에 ESC 감지용 keydown 리스너가 수십 개씩 중첩 등록되어 메모리 누수(Memory Leak)나 원치 않는 부작용을 유발할 위험이 있다고 합니다.
오직 사이드바가 열렸을 때만 이벤트를 구독하고 닫히면 즉시 리스너가 해제되어 메모리를 안전하게 관리할 수 있도록 수정 해보면 어떨까요?
useEffect(() => {
if (!isOpen) return;
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') close();
};
document.addEventListener('keydown', handleKeyDown);
return () => document.removeEventListener('keydown', handleKeyDown);
}, [isOpen]);
| hasNextPage, | ||
| refetch, | ||
| } = useInfiniteQuery({ | ||
| queryKey: ['lps', order, trimmedQuery], |
There was a problem hiding this comment.
특정 시간동안의 여러 요청을 한번에 묶어 전달하는 디바운스를 사용해서 검색어 입력 후 로직 요청을 제어하신 점과 빈 공백이나 타이핑 중인 상태를 제어하기 위해 enabled와 isBlocked등의 안전 장치를 구축한 것이 인상적인 이번 주차 학습의 이해도가 보이는 코드 였습니다.
하지만 몇 가지 아쉬운 점이 보이는 것 같습니다
리엑트 쿼리는 querykey가 변해야 호출이 실행되는 구조입니다. 그리고 현재 querykey에는 디바운스 된trimmedQuery라는 값이 들어가 있습니다.
queryKey: ['lps', order, trimmedQuery]
따라서 enabled에 원본 query를 넣어서 타이핑 중 로직을 비 활성화하는 것은 굳이 필요치 않으며 query가 변할 때 마다 False과 True를 전환하며 내부 캐싱 상태가 복잡해질 위험이 있습니다.
또한 만약 사용자가 검색어를 지우다가 실수로 공백을 남겼을 시 query는 " "로, !query는 False가 되고 trimmedQuery는 trim으로 인해 공백이 제거되어 아무 값도 없으니 False 즉 !!trimmedQuery는 False가 됩니다.
그러면 enabled이 False가 되기에 쿼리가 비활성화 됩니다
이때 사용자는 전체 목록이 보이길 원하겠지만 아무것도 보이지 않는 문제가 발생 할 수 있습니다.
아마 이것을 해결하기 위해 isBlocked를 구상하신것 같습니다
제 제안으로는
enabled: !query || !!trimmedQuery,를 과감하게 제거하고
이것과 묶여있던
const isBlocked = query !== '' && trimmedQuery.length === 0; 이 코드도 제거하는 것은 어떨까요?
공백만 입력했을 때 발생하는 문제를 제거하고 쿼리의 기능을 온전히 살리며 직관적인 코드가 될 것 같습니다
| return debouncedValue; | ||
| } | ||
|
|
||
| export default useDebounce; |
There was a problem hiding this comment.
한 가지 공유해 드리고 싶은 잠재적 버그 케이스가 있습니다. 리액트 useEffect 의존성 배열은 객체를 비교할 때 참조값(얕은 비교)을 기준으로 삼습니다. 따라서 만약 다른 팀원이 이 훅에 객체 리터럴(useDebounce({ search }, 300))을 그대로 넘기게 되면, 리렌더링마다 타이머가 계속 초기화되어 디바운스가 무력화될 위험이 있습니다.
아예 훅 내부에서 JSON.stringify와 useRef를 활용해 실제 내용물이 바뀌었는지 검증하는 방어 코드를 추가해 두면 훨씬 안전한 훅이 될 것 같습니다.
// 1. 훅 내부에 이전 값을 기억할 Ref를 하나 선언합니다.
const prevSerializedRef = useRef<string>("");
// 2. useEffect 최상단에 아래 로직을 추가하여 스킵 조건을 만듭니다.
const currentSerialized = JSON.stringify(value);
if (prevSerializedRef.current === currentSerialized) return;
prevSerializedRef.current = currentSerialized;
✅ 워크북 체크리스트
✅ 컨벤션 체크리스트
📌 주안점