Next.js(ver.13)/Routing

Parallel Routes

ecoEarth 2023. 8. 4. 04:15
실험한 깃 레포
https://github.com/JeonInguk/Parallel-Routes-Next-app-router
 

GitHub - JeonInguk/Parallel-Routes-Next-app-router

Contribute to JeonInguk/Parallel-Routes-Next-app-router development by creating an account on GitHub.

github.com

 

Parallel Routes

Parallel Routes의 존재이유는 각 경로가 독립적으로 스트리밍될 때 각 경로에 대해 독립적인 오류 및 로딩 상태를 정의할 수 있다는 점이다.

export default function Layout(props: {
  children: React.ReactNode
  analytics: React.ReactNode
  team: React.ReactNode
}) {
  return (
    <>
      {props.children}
      {props.team}
      {props.analytics}
    </>
  )
}

 

Parallel Routes을 사용하면 인증 상태와 같은 특정 조건에 따라 조건부로 슬롯을 렌더링할 수도 있다. 이를 통해 동일한 URL에서 완전히 분리된 코드를 사용할 수 있다.

 

 

Convention

  • 슬롯은 경로 segment 아니며 URL 구조에 영향을 미치지 않는다. 파일 경로 /@team/members는 /members에서 액세스할 수 있음
  • app/page.js의 URI은 app/@children/page.js의 구조로 접속하는 것과 같다. 

 

 

 

Unmatched Routes

  • 기본적으로 슬롯 내에서 렌더링되는 콘텐츠는 현재 URL과 일치한다.
    -> (app/page.js의 URI은 app/@children/page.js의 구조로 접속하는 것과 같다는 뜻)
  • 일치하지 않는 슬롯의 경우 라우팅 기술 및 폴더 구조에 따라 Next.js가 렌더링하는 콘텐츠가 달라진다.
    -> 몽말인지 모르겠음;;(ㅇ?ㅇ)

 

default.js

Next.js가 현재 URL을 기반으로 슬롯의 활성 상태를 복구할 수 없을 때 대체 파일로 렌더링할 default.js 파일을 정의할 수 있다.
다음 폴더 구조를 고려해보자. 팀 슬롯에는 설정 디렉터리가 있지만 @analytics에는 설정 디렉터리가 없다.

-> reset()으로 오류 복구하려할때 안되면 defalut.js가 나타난다는 말인듯..?

 

 

useSelectedLayoutSegment(s)

사용선택된 레이아웃 세그먼트와 사용선택된 레이아웃 세그먼트는 모두 해당 슬롯 내에서 활성 경로 세그먼트를 읽을 수 있는 parallelRoutesKey를 허용한다.
'use client'
 
import { useSelectedLayoutSegment } from 'next/navigation'
 
export default async function Layout(props: {
  //...
  auth: React.ReactNode
}) {
  const loginSegments = useSelectedLayoutSegment('auth')
  // ...
}

사용자가 URL 표시줄에서 @auth/login 또는 /login으로 이동하면 로그인 세그먼트는 "login" 문자열과 동일하다.


실험

글로만 봐서 이해갈듯말듯 하지만 적용하려고 보면 적용안된다. 직접 실험해보자.

 

폴더구조

 

error.tsx

"use client"; // 에러 컴포넌트는 항상 Client Component여야 합니다.

import { useEffect } from "react";

export default function Error({ error, reset }: { error: Error; reset: () => void }) {
  useEffect(() => {
    console.error(error);
  }, [error]);

  return (
    <>
      <h2>Left 컴포넌트에서 에러 발생했음!</h2>
      <button onClick={() => reset()}>다시 시도하기</button>
    </>
  );
}

 

loading.tsx

import React from "react";

export default function Loading() {
  return <>왼쪽 로딩중...</>;
}

 

page.tsx

export default async function Left() {
  await new Promise((resolve) => setTimeout(resolve, 2 * 1000));
  // throw new Error("주석을 풀어서 일부로 에러를 발생시켜보세요🤙");
  const value = Math.random();

  return (
    <>
      <div className="w-40 mr-20 bg-orange-400 h-80">왼쪽 {value}</div>
    </>
  );
}

 

global-error.tsx

"use client"; // 에러 컴포넌트는 항상 Client Component여야 합니다.

import { useEffect } from "react";

export default function Error({ error, reset }: { error: Error; reset: () => void }) {
  useEffect(() => {
    console.error(error);
  }, [error]);

  return (
  // global-error는 html태그와 body 꼭 추가시켜주셔야합니다잉
    <html>
      <body>
        <div>
          <h2>전역 어디선가 에러가 발생했음!</h2>
          <button onClick={() => reset()}>다시 시도하기</button>
        </div>
      </body>
    </html>
  );
}

 

layout.tsx

import "./globals.css";
import { ErrorBoundary } from "react-error-boundary";
import Error from "./global-error";

export default function RootLayout(props: { children: React.ReactNode; Left: React.ReactNode; Right: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <ErrorBoundary fallback={<Error />}>
          <div className="flex flex-row items-center justify-center w-screen h-20 text-3xl bg-amber-400">네비게이션바인척 하는놈</div>
          <div className="flex flex-row justify-center mt-20 flex-nowrap">
            {props.children}
            {props.Left}
            {props.Right}
          </div>
        </ErrorBoundary>
      </body>
    </html>
  );
}

 

(루트)page.tsx

import Another from "@/Components/Another";

export default function Home() {
  return (
    <>
      <Another />
    </>
  );
}

실험결과 의도한대로 잘 작동된다.

 

각 페이지별 로딩상태 다름

 

각 페이지별 에러상태 다름(여기서는 Left만 에러발생시켜서 Left만 에러가 발생했습니다잉)