next.js14 router 새로고침 문제, shallow routing으로 해결

dobby·2024년 8월 6일
0

shallow routing

카테고리나 특정 필터 값을 선택하도록 하면, url에 해당 값이 출력되게 하는 경우가 있다.
데이터를 추가한 Url로 다른 페이지에서 해당 데이터를 뽑아 사용할 수도 있다.

하지만 한 가지 문제가 있었다.
useSearchParamsroute/navigation 라이브러리의 route.pushroute.replace 를 사용하여 url을 변경하게 되면 하드 새로고침이 되어 페이지 전체가 리렌더링이 된다.

이번 포스트는 위의 문제를 해결한 과정을 담아보려 한다.

router 새로고침 문제

route/navigationroute... 사용하여 url 변경 시 페이지 이동이 없더라도 새로고침되어 페이지 전체 리렌더링되는 문제

이는 같은 페이지에서 사용자가 선택한 데이터를 url에 선택한 데이터를 담게 했을 때 발생한다.

나는 카테고리를 선택하면, 선택한 데이터의 id를 url에 노출시켜주도록 했다.
그 결과, 위처럼 카테고리를 선택할 때마다 페이지 전체가 리렌더링되었다.

불필요한 부분까지 리렌더링이 진행되는만큼, 성능에 영향을 줄 수 있다.


해결방안

router의 shallow routing을 사용하면 url만 변경되고 상태가 바뀌지 않는다.
이를 활용해서 리렌더링을 제어할 수 있다.

stackoverflow question

위의 글을 참고했다.

// 기존 코드
router.push(`/home?${newSearchParams.toString()}`);
// 수정한 코드
window.history.pushState({...window.history.state, as: newUrl, url: newUrl}, '', newUrl);

이렇게 수정하고 화면을 보니,

분명 url은 변경이 되고, 전체 새로고침도 되지 않는다.
하지만 또 다른 문제가, url만 변경이 되고 그에 대한 상태가 바뀌지 않는다는 것이다.

나는 useSearchParams를 사용하여 url이 변경됨을 감지하고 그에 대응되는 데이터 값으로 상태를 변경시키도록 코드를 작성해두었다.


useSearchParams 업데이트 X 문제

이는 window.history.pushState를 사용하면 브라우저의 주소 표시줄은 변경되지만 Next.js 라우터 상태는 업데이트되지 않기 때문에 useSearchParams가 변화에 반응하지 않는 문제이다.


해결방안

카테고리에 대한 데이터는 Url에도 적용하지만, 전역 상태로도 저장해두고 다른 곳에서 사용 중이었기에 이 데이터를 활용하기로 했다.

전역 상태에 저장되어 있는 카테고리 데이터를 url이 변경될 때마다 상태가 바뀌어야 하는 컴포넌트에 추가해주었다.

  const { setCategory, category: selectedCategory } = useCreateAgora(
    useShallow((state) => ({
      setCategory: state.setCategory,
      category: state.category,
    })),
  );  

const changeCategoryParams = useCallback(
    (id: string) => {
      if (pathname !== '/home') return;

      const newSearchParams = new URLSearchParams(searchParams);

      newSearchParams.set('category', isValidCategoryKey(id) ? id : '1');
      newSearchParams.delete('q');
      search.reset();

      const newUrl = `/home?${newSearchParams.toString()}`;
      window.history.pushState({...window.history.state, as: newUrl, url: newUrl}, '', newUrl);
    },
    [router, searchParams, pathname, selectedCategory],
  );

기존엔 의존성 배열에 selectedCategory 를 넣어주지 않았다.
그래서 배열에 추가해주어 전역 상태가 바뀔 때마다 재실행하여 카테고리 버튼의 색상이 변경되도록 해주었다.

그리고 한 군데 더, 아고라 리스트를 출력하는 곳도 카테고리 값이 변경됨에 따라 같이 값에 따른 데이터를 호출해주어야 한다.

  const { category: selectedCategory } = useCreateAgora(
    useShallow((state) => ({
      category: state.category,
    })),
  );

... (React Query)
queryKey: ['agoras', 'search', 'category', { ...searchParams, category: selectedCategory }],
...

나는 react-query를 사용하고 있기에 category 전역 상태가 변경됨에 따라 queryKey를 다르게 주도록 하였다.

이렇게 수정한 후, 화면을 보면

원하던 대로 변경되는 부분만 리렌더링이 진행된다!!


음식/여행 카테고리를 선택할 때 데이터가 출력되지 않는 부분은 데이터를 호출하는 데 있어서 지연이 발생한 것 같다.

로더가 보이도록 수정해야할 것 같다!

profile
성장통을 겪고 있습니다.

0개의 댓글