클릭 이벤트로 특정 위치로 이동하기

박상우·3일 전
0
post-thumbnail

프로젝트 서비스 소개 페이지를 구현하면서 소개 리스트와 목차 컴포넌트를 구현하고, 목차 컴포넌트를 클릭했을때 특정 소개 영역으로 이동하도록 하는 요구사항이 있었다.

간단한 구현이라고 생각했는데 그 과정에서 알게된 부분들이 몇가지 있어 구현 과정을 정리해보았다.

ref 여러개 관리하기

위 이미지 처럼 소개 페이지 내부 영역이 여러 개이고 각 컴포넌트의 DOM 객체를 모두 관리해야하는데, 여러 DOM 객체를 배열로 관리해도 되는지에 대해서 헷갈리는 부분이 있었다. useRef내에서 배열로 관리할지, 아니면 useRef를 N 개 만큼 생성해서 관리할지 고민을 했었다.

우선 소개 영역이 거의 변하진 않겠지만 가능하다면 수정 가능성을 열어두고 개발한다면 임의로 N개의 useRef를 직접 만드는 것은 적절하지 않다고 판단했고, useRef 내부에서 배열로 관리하는 방향으로 방법을 찾아보았다.

그래서 ref 타입을

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

로 선언해서 활용했다. 이렇게 활용하면 index를 통해 각 DOM 객체에 직접 접근할 수 있다.

이제 ref 내 요소를 넣는 방식이 떠오르지 않았는데 기존에 ref에 DOM 객체를 할당하는 방식은

...
<div ref={ref}> ... </div>
...

위 코드처럼 ref 객체를 props로 넘겨주는 방식이었는데 기본적인 사용 방식을 기반으로

...
{
	someArray.map((_, index) => {
		return <div key={index} ref={refs[index]}> ... </div>;
	})
}
...

작성해보았는데 refs가 가지고 있는 값이 배열이지 refs 자체는 배열이 아니기 때문에 index를 활용할 수 없었다.

...
{
	someArray.map((_, index) => {
		return <div key={index} ref={refs.current[index]}> ... </div>;
	})
}
...

이런 식으로도 해볼까 했지만 props 타입이 RefObject가 넘어가야하는데, HTMLElement 객체 값이 직접 넘어가기 때문에 이 방식도 적절하지 않다.

그래서 ref를 활용할때 ref 객체 대신 콜백 함수를 넘겨줄 수 있다는 점을 활용해서

...
{
	someArray.map((_, index) => {
		return <div 
			key={index}
			ref={ref={(element: HTMLDivElement) => {
	      refs.current[index] = element;  // 각 요소를 배열의 해당 인덱스에 저장
	    }}
			> ... </div>;
	})
}
...

이렇게 작성하면 된다고 한다.

일반적으로 ref를 props로 넘겨주는 방식은 내부적으로 React가 랜더링이 완료되고, DOM 요소를 생성한 직후에 해당 DOM 요소를 ref.current에 저장하는데, 콜백함수를 활용하게 되면 이 동작 자체를 직접 정의할 수 있다. 콜백 함수의 인자로 DOM 요소를 받게 되고, 마찬가지로 React가 랜더링이 완료되고, DOM 요소를 생성한 시점에 콜백 함수를 실행한다.

위 코드에서는 refs.current의 특정 위치(Index)에 DOM 정보를 저장하도록 하고 있다.


특정 DOM 위치로 스크롤 이동 시키기

이 부분에 대한 요구사항을 봤을 때 DOM 객체에 현재 페이지에 위치에 대한 정보나 스크롤에 대한 정보가 있어서 그걸 활용해야하나 추측했었다.

이부분은 생각보다 수월했던 것이 이런 요구사항을 위해 .scrollIntoView() 라는 내장 API를 활용하면 되는 것을 알았다.

 const moveContent = (index: number) => {
    if (contentRefs.current[index]) {
      contentRefs.current[index]?.scrollIntoView({
        behavior: 'smooth',
        block: 'center',
      });
    }
  };

그래서 아까 저장한 refs 내 DOM 정보에 .scrollIntoView() 를 적용시키고, .scrollIntoView() 의 인자로 스크롤 이동시 부드럽게 이동시키기 위해 behavior 속성과, DOM이 뷰포트 내 위치를 지정하기 위해 block 속성을 각각 지정해주었다.


React 19에서 ref를 props로 받기

프로젝트내 React 버전이 19 버전으로 되어있어 최근 정식으로 업그레이드 된 19버전에 따라 Ref를 props로 받는 방법이 달라졌다.

기존 18버전 까지는 React.fowardRef 를 활용해서 ref를 다른 props들과 구별하여 전달받아 활용했는데, 19버전으로 올라가면서 forwardRef를 사용하지 않고 다른 props와 동일하게 ref를 넘겨주도록 수정되었다.

그래서 컴포넌트 내에 props 타입을 정의할 때로 Ref를 포함하여 정의했다.

interface IntroBlockProps {
  title: string;
  describe: ReactNode;
  content: ReactNode;
  ref: Ref<HTMLDivElement | null>;
}

끝내며…

ref를 element 속성에 직접 넘겨주고 ref.current를 활용하는 단순하고 반복적으로 활용하다보니 더 디테일하게 사용하는 방식이나 ref에 언제 DOM 정보가 저장되는지에 대한 내부적인 구현까지 파악하지 못하고 있었던 것 같다. ref를 더 잘 사용할 수 있게 된 것 같다.


Reference

https://ko.react.dev/learn/manipulating-the-dom-with-refs#how-to-manage-a-list-of-refs-using-a-ref-callback

https://developer.mozilla.org/ko/docs/Web/API/Element/scrollIntoView

profile
나도 잘하고 싶다..!

0개의 댓글

관련 채용 정보