본문 바로가기
🔥React 뽀개기

path를 따라가는 로딩화면 구현하기

by 짱돌보리 2024. 8. 16.
728x90

✈️path를 따라가는 로딩화면 구현하기

import React from 'react';

type LoadingProps = {
  width?: number;
};

export default function Loading({ width = 1200 }: LoadingProps) {
  return (
    <div className="flex items-center justify-center">
      <svg
        xmlns="http://www.w3.org/2000/svg"
        width={width}
        viewBox="0 0 400 200"
      >
        <defs>
          <path
            id="flightPath"
            d="M 0 100 C 100 50, 300 150, 400 100"
            fill="none"
            stroke="lightgrey"
            strokeWidth="2"
          />
        </defs>

        {/* 비행 경로를 따라 선을 그리기 위한 경로 */}
        <path
          id="line"
          d="M 0 100 C 100 50, 300 150, 400 100"
          fill="none"
          stroke="lightblue"
          strokeWidth="2"
        >
          <animate
            attributeName="stroke-dasharray"
            from="0,400"
            to="440,0"
            dur="3s"
            repeatCount="indefinite"
          />
        </path>

        {/* 비행기 모양 */}
        <path
          id="plane"
          d="M 27,3 H 21 L 13,15 H 9 L 12,3 H 5 L 3,7 H -1 L 1,0 -1,-7 H 3 L 5,-3 H 12 L 9,-15 H 13 L 21,-3 H 27 C 33,-3 33,3 27,3 Z"
          fill="white"
          stroke="black"
          strokeWidth="1.5"
        >
          <animateMotion
            rotate="auto"
            begin="0s"
            dur="3s"
            repeatCount="indefinite"
          >
            <mpath xlinkHref="#flightPath" />
          </animateMotion>
        </path>
      </svg>
    </div>
  );
}

useLoading

  • routeChangeStart(url, { shallow }) - 라우트가 변경되기 시작할때 트리거됨.
  • routeChangeComplete(url, { shallow }) - 라우트가 완전히 변경되었을 때 트리거됨.
  • routeChangeError(err, url, { shallow }) - 라우트 변경 중에 에러가 발생했거나, 취소되었을 때 트리거됨.
// useLoading 
import { Router } from 'next/router';
import { useEffect, useState } from 'react';

export const useLoading = () => {
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    const start = () => {
      setLoading(true);
    };
    const end = () => {
      setLoading(false);
    };
    Router.events.on('routeChangeStart', start);
    Router.events.on('routeChangeComplete', end);
    Router.events.on('routeChangeError', end);
    return () => {
      Router.events.off('routeChangeStart', start);
      Router.events.off('routeChangeComplete', end);
      Router.events.off('routeChangeError', end);
    };
  }, []);

  return loading ? true : false;
};

 

 

레이아웃에 페이지 전환시 로딩 화면 띄우고 이동

export default function Layout({
  children,
  showHeader = true,
  showFooter = true,
}: LayoutProps) {
  const pathname = usePathname();
  const router = useRouter();

  const loading = useLoading();

  return (
    <>
      {showHeader && <Header />}
      <AnimatePresence mode="wait">
        <motion.div
          key={router.pathname}
          initial={isMyPage ? undefined : { opacity: 0, y: -50 }}
          animate={isMyPage ? undefined : { opacity: 1, y: 0 }}
          exit={isMyPage ? undefined : { opacity: 0, y: -50 }}
          transition={{ duration: 0.3 }}
        >
          {loading ? <Loading /> : children}
          {!loading && showFooter && <Footer />}
        </motion.div>
      </AnimatePresence>
    </>
  );
}

하지만 모든 페이지이동에 넣으니 빠른 화면 전환사이에 로딩화면이 있어서 짜침

페이지라우터는 초기 페이지 로딩만 느리고, 캐시된 이후에는 엄청 빠르다.

각 페이지를 처음 방문할때만 로딩화면을 넣고, 캐시된 이후에는 로딩화면 없애기!!

import { Router } from 'next/router';
import { useEffect, useState } from 'react';

export const useLoading = () => {
  const [showLoading, setShowLoading] = useState(true); // 처음에 로딩 화면을 보여줌
  const [hasLoadedPages, setHasLoadedPages] = useState(new Set()); // 로드된 페이지를 추적

  useEffect(() => {
    const start = (url: string) => {
      if (hasLoadedPages.has(url)) return; // 이미 로드된 페이지에서는 로딩 시작하지 않음
      setShowLoading(true); // 로딩 화면 표시
    };

    const end = (url: string) => {
      setShowLoading(false); // 로딩 종료 시 showLoading을 false로
      setHasLoadedPages((prev) => new Set(prev).add(url)); // 현재 페이지를 로드된 페이지에 추가
    };

    Router.events.on('routeChangeStart', start);
    Router.events.on('routeChangeComplete', end);
    Router.events.on('routeChangeError', end);

    return () => {
      Router.events.off('routeChangeStart', start);
      Router.events.off('routeChangeComplete', end);
      Router.events.off('routeChangeError', end);
    };
  }, [hasLoadedPages]);

  return showLoading; // showLoading을 반환
};

완성