[useSearchParams] 쿼리 스트링을 활용해서 특정 섹션으로 스크롤 이동하기

Sheryl Yun·2024년 3월 5일
0

헤더의 Technology 메뉴에 마우스를 hover 했을 때 아래 '광원추론', '재질추론' 등의 서브 메뉴가 뜬 모습이다.

각 메뉴의 텍스트를 클릭하면 화면 하단의 해당되는 섹션으로 스크롤이 되는 기능을 구현하고자 했다.

하지만 곧바로 이슈와 마주치게 되는데.. 클릭 이벤트가 일어나는 컴포넌트와 스크롤되는 섹션 간의 거리가 너무 멀고 깊어 순간 전역 관리 외에 답이 떠오르지 않았다는 것이다.

위의 둘 사이에 props를 공유하려면 가장 가까운 부모는 src/components가 된다 😊

하지만 오직 이 하나의 기능을 위해서 전역 관리를 도입한다는 건 오버엔지니어링으로 느껴져서, 여러 가지 방법을 찾다가 우선은 URL의 쿼리 스트링이 변경되는 기능을 먼저 구현해보기로 했다.

그리고 위 문제의 답을 여기서 찾게 되었다.

useSearchParams 객체를 사용하는 쿼리 스트링 기능은 URL에 '부가적'으로 붙는 것이라서 따로 Router 설정 없이 Link에 바로 '?'를 넣어 작성해준다.

map에서 꺼낸 i를 활용해서 /technology?section={1,2,3,4 중 하나}로 URL을 변경하겠다는 의미이다.

여기서 i에 4를 더한 이유는 개발할 때 스크롤되는 컴포넌트를 Section 4부터 시작하도록 설정했는데 i는 0부터 시작하니 스크롤되는 섹션이 4부터 시작할 수 있도록 규칙적으로 숫자 4를 더해준 것이다.

LargerHeader에서 이렇게 설정하고 나자 스크롤되는 섹션 컴포넌트인 ObserverUIContainer에서 searchParams의 get 메서드로 쿼리 스트링 값을 손쉽게 가져올 수 있었다.

전역을 쓰지 않았지만 마치 전역처럼 상위 레벨(?)에서 값을 가져올 수 있었던 것이다.

하지만 이 경우는 필요한 값이 URL 쿼리 스트링에 쓰일 수 있다는 조건 하에만 가능하다.

// 스크롤되는 4, 5, 6, 7 섹션들의 UI를 모듈화한 컴포넌트

export const ObserverUIContainer = (...) => {
  ...
  const [searchParams] = useSearchParams();
  const scrollIndex = searchParams.get('section');

  const scrollRefs = [
    useRef<HTMLDivElement | null>(null),
    useRef<HTMLDivElement | null>(null),
    useRef<HTMLDivElement | null>(null),
    useRef<HTMLDivElement | null>(null),
  ];

  useEffect(() => {
    // scrollIndex에서 4를 빼준 이유: 쿼리 스트링으로 가져온 값(4부터 시작)을 scrollRefs 배열 인덱스(0부터 시작)와 맞춰주기 위해서
    if (scrollIndex)
      scrollRefs[Number(scrollIndex) - 4].current?.scrollIntoView({
        behavior: 'smooth',
      });
  }, [scrollIndex]);

  ...

  return (
    <div ref={scrollRefs[order - 4]}> // Container 하나에 ref를 두 개 걸 수 없어서 바깥쪽에 div 추가
      <Container ref={containerRef}>
        ...
      </Container>
    </div>
  );
};

결과

이렇게 해서 전역 관리 없이 스크롤 이동을 구현할 수 있었고 재배포 후 URL 쿼리 스트링 변경과 함께 스크롤 이동도 잘 되는 것을 확인할 수 있었다.

참고 자료

스크롤 이동 관련

useSearchParams 관련

profile
데이터 분석가 준비 중입니다 (티스토리에 기록: https://cherylog.tistory.com/)

0개의 댓글