[NextJS] router를 이용한 Loading UI 구현

상현·2024년 1월 30일
post-thumbnail

개요

웹 어플리케이션에서 빠른 페이지 이동을 제공하는 것은 매우 중요하다. 하지만 어쩔수 없이 페이지 로딩이 길어지는 경우가 있고, 최적의 사용자 경험을 위해서는 페이지 이동 시 로딩 상태를 명확하게 전달하는 것 또한 중요하다.

NextJS에서는 useRouter hook을 이용하여 로딩 상태를 감지하고, 이를 토대로 로딩 UI를 제어할 수 있다.

참조: https://nextjs.org/docs/pages/api-reference/functions/use-router#routerevents

구현 코드

_app.tsx

const router = useRouter();
const [isPageLoading, setIsPageLoading] = useState<boolean>(false);

useEffect(() => {

  // 라우팅이 시작될 때
  const handleRouteStart = () => {
    setIsPageLoading(true);
  };
  
  // 라우팅이 성공했을 때
  const handleRouteComplete = () => {
    setIsPageLoading(false);
  };

  // 이벤트를 등록한다.
  router.events.on('routeChangeStart', handleRouteStart);
  router.events.on('routeChangeComplete', handleRouteComplete);

  return () => {
    router.events.off('routeChangeStart', handleRouteStart);
    router.events.off('routeChangeComplete', handleRouteComplete);
  };
}, [router]);


// ...

return (
  <QueryClientProvider client={queryClient}>
    /* isPageLoading의 값에 따라 Loading 컴포넌트를 보여준다. */
    {isPageLoading && <Loading />}
    <main className={clsx({ ['blur']: isPageLoading })}>
      {getLayout(<Component {...pageProps} />)}
    </main>
  </QueryClientProvider>
);
};

문제점 및 개선 1.

평상시에는 문제없이 잘 되었다. 그러나 라우팅에서 예상치 않은 오류가 났을 때, 로딩 컴포넌트가 계속해서 보이는 문제가 생겼다.
이런 때를 대비해서 오류가 생겨도 로딩창은 숨겨줘야 했기에 또 다른 핸들러를 추가했다.

개선 코드

useEffect(() => {

  // 라우팅이 시작될 때
  const handleRouteStart = () => {
    setIsPageLoading(true);
  };
  
  // 라우팅이 성공했을 때
  const handleRouteComplete = () => {
    setIsPageLoading(false);
  };
  
  // 라우팅에서 오류가 났을 때
  const handleRouteChangeError = () => {
    if (timer) clearTimeout(timer);
    setIsPageLoading(false);
  };

  // 이벤트를 등록한다.
  router.events.on('routeChangeStart', handleRouteStart);
  router.events.on('routeChangeComplete', handleRouteComplete);
  router.events.on('routeChangeError', handleRouteChangeError);

  return () => {
    router.events.off('routeChangeStart', handleRouteStart);
    router.events.off('routeChangeComplete', handleRouteComplete);
    router.events.off('routeChangeError', handleRouteChangeError);
  };
}, [router]);

문제점 및 개선 2.

이제 어느 상황에서도 로딩창이 잘 보이고, 사라졌다. 그러나 또 하나의 문제점이 생겼다. 페이지 이동에 걸리는 시간이 아주 짧은 시간에도 로딩 컴포넌트가 보였기 때문에, 오히려 페이지가 점멸되는듯한 효과를 주어 안좋은 사용자 경험을 주었다.

따라서 일정 시간이 지난 후에도 페이지 이동이 되지 않을 경우에만 로딩 컴포넌트를 보여주기로 했다.

개선 코드

useEffect(() => {
  // 로딩이 길어질 때만 로딩창이 뜨도록 변경
  let timer: ReturnType<typeof setTimeout>;
  const handleRouteStart = () => {
    
    // 150ms가 지나도 페이지 로딩이 완료되지 않으면 로딩 컴포넌트가 보여지도록 한다.
    if (!timer) {
      timer = setTimeout(() => {
        setIsPageLoading(true);
      }, 150);
    }
  };
  const handleRouteComplete = () => {
    if (timer) clearTimeout(timer);
    setIsPageLoading(false);
  };

  const handleRouteChangeError = () => {
    if (timer) clearTimeout(timer);
    setIsPageLoading(false);
  };

  // ... 생략
}, [router]);

결과

profile
블로그 이전 => https://shdev.blog/

0개의 댓글