forked from CodeitPart3/thejulge
-
Notifications
You must be signed in to change notification settings - Fork 0
useIntersection
이토 edited this page Jun 1, 2025
·
2 revisions
스크롤이 특정 요소에 도달했을 때 이벤트를 트리거하는 기능은 무한 스크롤, lazy loading 등 다양한 상황에서 반복적으로 요구되었습니다. 하지만 실제로는 각 페이지나 컴포넌트에서 IntersectionObserver를 직접 구현하고 연결해야 했고, 이는 다음과 같은 문제를 야기했습니다:
- 매번 동일한 observer 로직을 복붙해야 했고, 리팩토링 비용도 컸음
- 로직의 미묘한 차이로 인해 일관되지 않은 관찰 방식과 메모리 누수 가능성이 존재함
- 협업 시 초심자가 observer를 직접 다루는 진입장벽이 높음
이 문제를 해결하기 위해 다음과 같이 구조적인 개선을 진행했습니다:
- 공통된 IntersectionObserver 로직을 커스텀 훅으로 추출 - useIntersection이라는 훅을 만들어, callback과 deps만 넘기면 내부적으로 ref를 반환하고 observer를 자동 등록/해제하도록 구성했습니다.
- 의존성 배열로 동적 리렌더링 대응 - deps를 통해 외부 상태가 변경될 때도 observer가 갱신되도록 설계하여, 상태 기반 동작에도 유연하게 대응 가능하게 했습니다.
- 제네릭 타입으로 HTMLElement 범용 대응 -
<T extends HTMLElement>로 타입을 추론 가능하게 하여,div,ul,li등 어떤 요소든 타입 안전하게 ref 연결이 가능하게 했습니다.
// useIntersection.ts
interface UseIntersectionParams {
callback: IntersectionObserverCallback;
deps?: DependencyList;
}
const useIntersection = <T extends HTMLElement>({
callback,
deps,
}: UseIntersectionParams) => {
const dependencies = deps ?? [];
const targetRef = useRef<T>(null);
useEffect(() => {
const observer = new IntersectionObserver(callback);
const current = targetRef.current;
if (current) {
observer.observe(current);
}
return () => {
if (current) {
observer.disconnect();
}
};
}, [callback, ...dependencies]);
return targetRef;
};
export default useIntersection;// 실제 사용 예시
function Component() {
...
const targetRef = useIntersection<HTMLDivElement>({
callback: ([entry]) => {
if (entry.isIntersecting && !isLoading && hasNext) {
refetch();
}
},
});
return (
...
<div ref={targetRef} />
...
);
}- useIntersection 훅을 도입함으로써 모든 컴포넌트에서 observer 로직을 반복 없이 간결하게 적용 가능해졌습니다.
- 협업 시 동료가 복잡한 IntersectionObserver를 몰라도 빠르게 기능 구현할 수 있게 되었고,
- observer 연결/해제 관련 버그 가능성을 줄이며 코드 일관성과 유지보수성이 크게 향상되었습니다.