minguri brain is busy

무한스크롤 페이지에서 스크롤 위치 복원 본문

FE/개발

무한스크롤 페이지에서 스크롤 위치 복원

minguri.k 2022. 12. 23. 17:51
반응형

무한 스크롤 리스트에는 뒤로가기를 갔다가 보던 위치를 기억해주는 기능은 UX적으로 필수적이다.

다른 페이지에 갔다가 뒤로가기로 페이지에 돌아올 경우, 더보기로 새로 로드된 데이터는 초기화되고 없으므로 실제로 보던 위치로 돌아가지 못하는 문제가 있다. sessionStorage를 사용해서 해결한 방법을 적고자한다.

 

우선 리스트에서 상품을 클릭할때 sessionStorage에 현재 스크롤 위치와 로드한 데이터를 저장한다.

// ProductItem.tsx
const handleProductClick = async () => {
  await setScrollDataInStorage();
  handleChangeLocation(`${publicRuntimeConfig.config.productHost}/product/${product.productId}`);
};

const setScrollDataInStorage = async () => {
  if (props.scrollPosition) {
    sessionStorage.setItem('scrollPosition', props.scrollPosition.toString());

    if (props.productList) {
      sessionStorage.setItem('selectedProductList', JSON.stringify(props.productList));
    }
    if (props.hasMoreProduct) {
      sessionStorage.setItem('hasMoreProduct', props.hasMoreProduct.toString());
    }
  }
};

 

컴포넌트 상위에 ref를 주어서 스크롤이 될때마다 현재 스크롤 값을 state에 저장한다.

현재 페이지에 돌아올때 sessionStorage에 저장된 상품리스트 값이 있으면 해당 데이터를 보여주고 없다면 새로 로드한다. 저장된 상품리스트가 있을 때에는 쿼리를 따로 치지 않고 갖고 있는 데이터를 보여주므로 비용적인 면에서도 용이하다. 상품 리스트 값이 바뀌면 스크롤 위치값을 sessionStorage에 저장된 스크롤 위치값으로 셋팅해준다.

const scrollRef = useRef(null);
const [scrollPosition, setScrollPosition] = useState(0);
const [productList, setProductList] = useState < IProduct[] > ([]);
const [isScrollRestored, setIsScrollRestored] = useState<boolean>(false);

useEffect(() => {
  const savedScrollPosition = JSON.parse(sessionStorage.getItem('selectedProductList'));
  if (!savedScrollPosition) {
    getProduct(); // 상품을 fetch하는 lazyQuery
  } else {
    setProductList(savedScrollPosition);
  }
}, []);

useEffect(() => {
  setScrollPosition(Number(sessionStorage.getItem('scrollPosition')));
  scrollRef.current?.scrollTo(0, scrollPosition);
  if (sessionStorage.getItem('scrollPosition')) {
    setIsScrollRestored(true);
  }
}, [productList]);

// 복원 후에는 저장된 storage 값을 제거해서 다시 그 위치로 복원되지 않도록 하는 로직을 추가했다.
useEffect(() => {
  if (isScrollRestored) {
    sessionStorage.removeItem('selectedProductList');
    sessionStorage.removeItem('scrollPosition');
  }
}, [isScrollRestored]);


return (
  <div
    style={{ overflow: 'auto' }}
    ref={scrollRef}
    onScroll={({ target }: any) => { setScrollPosition(target.scrollTop); }}
  >
  ...
  </div>
)

여기까지 하면 뒤로가기 했다 돌아왔을 때 스크롤 위치가 복원되었다.

내 경우 처음엔 scrollTo가 동작하지 않았는데, body: height: 100%랑 같이 사용안된다는 이슈가 있었고, view component을 감싸는 div에 overflow: 'auto'를 주어서 해결했다.

 

스크롤은 생각보다 간단하게 기억이 되었지만 실제 구현 서비스에는 뒤로와서 상품리스트가 복원된 후 상품을 fetch하는 variables의 1뎁스 카테고리, 2뎁스 카테고리, 소팅값 state가 변경되었을 때의 조건을 주는 부분에서 고민하는 시간이 더 많았다. primitive 타입의 state 값을 reference 타입으로 수정했는데 그 부분은 다른 포스팅으로 좀 더 다뤄보도록 해야겠다.

반응형
Comments