Frontend/Next.js

[Next.js] App Router 경로 구성하기 (next13/Parallel Routes/Intercepting Routes)

JOKUN 2024. 6. 26. 15:15

https://nextjs.org/docs/app/building-your-application/routing

 

Building Your Application: Routing | Next.js

Learn the fundamentals of routing for front-end applications.

nextjs.org

이 전 포스팅중에서 next.js의 app router에 대해 짧게 정리했던 게 있는데 이번 포스팅에서는 조금 더 상세히 개념 정리를 해보려고 한다. 

 

 

App Router란?

next 13 버전부터 react server components 기반의 라우터가 도입되었다. (이전까지는 pages router를 사용함)

기존의 pages router의 문제들을 개선하기 위해 만들어졌다.

 

가장 크게 달라진 점

  • 각종 폴더 유형 추가로 디렉토리 라우팅이 편해짐
  • 레이아웃 기능
  • 페이지별 권한 체크
  • 서버 컴포넌트 분리로 인한 최적화
  • 데이터 캐시
  • 서버 액션

 

Layout & Page

Layout

  • 여러 페이지에서 공유되는 UI
    • 페이지 이동을 했을 때 유지되어야하는 컴포넌트들을 떠올려보자!
  • 레이아웃의 children에는 app 폴더 하위에 정의된 page.tsx 컴포넌트가 렌더링 됨
    • layout.tsx - page.tsx 어떻게 영향을 주는지 경로/위치를 통해 잘 생각해 보아야 함 
  • RooyLayout
    • 최상위 레이아웃
    • 필수 레이아웃이며 초기 next 세팅하면 자동으로 생성되는 파일
    • 루트 레이아웃에는 html / body 태그가 포함되어야함
export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

 

 

page

  • 경로에 대한 고유한 UI
  • 폴더 구성에 따라 보여질 page를 설정
  • src/page.tsx 는 애플리케이션의 baseURL 화면에 보여진다.
    • 주소로 예를 들면 'http://localhost:3000/' 상태의 아무 경로가 없을 때 보여지는 페이지

 

이 외에도 loading, not-found, error 등 상태에따라 보여지게할 화면을 설정할 수 있으니 공식 문서에 해당하는 것을 참고해보면 좋다.

 


 

경로 구성하기

기본적인 구조

기본적으로 폴더 구조에 따라 경로가 구성되는데

만약에 'http://localhost:3000/i/flow/login' 와 같은 주소가 있다고 하면 실제로 폴더 구성은 

src
└ app
  └ i
    └ flow
       └ signup

이렇게 tree 계층 구조로 폴더안에 - 하위 폴더  - 안에 하위 폴더 이런식으로 구성하면 된다. 

 

 

근데 모든 URL이 기본적인 형태로만 존재하는 것이 아니다.

 

Path Parameter & Query Parameter

동적 세그먼트 구성

URL 주소의 개념중 path / query parameter가 있다.

next에서는 path parameter - params | query parameter - searchParams 라고 생각하면 된다.

 

 

path parameter

path parameter를 받으려면 폴더를 구성할 때 폴더명에 대괄호를 치고, props로 params를 받으면 된다.

app - channel - [id] 라고 폴더명을 지정해주었다. 

그러면 주소는 'https://yt-music-clone-next-beige.vercel.app/channel/1' 와 같은 형태로 구성된다.

도메인/channel/[id] 인 것!

page.tsx에서는 아래와 같이 받을 수 있다.

나는 따로 props type을 지정해서 받았는데 

type ChannelPageProps = {
  params: {
    id: string;
  };
};

const page = async (props: ChannelPageProps) => {
	return <div>{내용}</div>
}

 

usePathname을 이용해서도 받을 수 있다.

"use client";
import { usePathname } from "next/navigation"

  ...

export default function Home() {
  const path = usePathname();
}

 

 

query parameter

query parameter는 주소상으로 'https://yt-music-clone-next-beige.vercel.app/playlist?list=1'  ?물음표 다음으로 오는 것을 보면 된다.

path parameter처럼 폴더명에 따로 지정해주어야하는 것은 없고,

기본 구성과 똑같이 하되 page.tsx에서 props로 searchParams를 받으면 된다.

나는 여기서도 props type을 따로 지정해줘서 썼는데 usePathname를 불러와서 쓴 것 처럼 useSearchParams를 이용해서도 가져올 수 있음.

type PlayListProps = {
  searchParams: {
    list: string;
  };
};

const page = async (props: PlayListProps) => {
	return <div>{내용}</div>
}

 

 

라우트 그룹화

일반적으로 app 폴더 하위에 생성된 폴더는 URL 경로로 매핑 되는데, 폴더명을 괄호로 묶어서 생성하면 URL 경로 구조에 영향을 주지 않고 그룹화하여 경로를 구성할 수 있다.

 

그룹화가 필요한 경우를 생각해보면!

예를 들어

app/(beforeLogin)/home

app/(afterLogin)/login

이런식으로 구성해서 로그인 전 후 로 다른 그룹의 경로로 보여지게 할 수 있다.

주소상으로는 () 괄호 처리된 것은 나타나지 않기에 'http://localhost:3000/home'과 같은 형태로 보인다.

 

  • 라우트 그룹의 경우 별도의 layout을 가질 수 있기에 그룹별 레이아웃을 다르게 설정할 수 있다는 장점이 있다.
  • 대부분 그룹화를 하는 기준은 레이아웃에 있었음.

 

패러렐 라우트(Parallel Route)

paralle은 병렬이라는 의미를 가지고 있다.

폴더에 @를 입력하여 동일한 레이아웃에서 하나 이상의 페이지를 동시에 또는 조건부로 렌더링할 수 있다.

 

  • 라우트 세그먼트가 아니며 URL 구조에 영향을 주지 않는다. 
    • 예를 들어 'http://localhost:3000/@analytics/views' 와 'http://localhost:3000/views'는 동일하다.
  • 다른 계층의 구조를 갖고 있는 페이지를 병렬로 렌더링 할 수 없다. (아래 코드랑 같이 참고)
    • 같은 상위 폴더(beforeLogin) 아래에 @modal 슬롯과 layout.tsx 가 같이 있다면 별다른 import 없이 modal 을 props로 가져와 패러렐 라우트를 구성할 수 있다.
    • 위 코드에서 page.tsx 는 layout.tsx 의 {children} 에 렌더링 되고, @modal 은 {modal} 에 렌더링 된다.
// app/(beforeLogin)/layout.tsx
import { ReactNode } from "react";

type Props = { children: ReactNode; modal: ReactNode };

export default function Layout({ children, modal }: Props) {
  return (
    <div>
      {children}
      {modal}
    </div>
  );
}

// 주소가 localhost:3001일 때는 children->page.tsx, modal->@modal/default.tsx
// 주소가 localhost:3001/i/flow/login 때는 chldren->i/flow/login/page.tsx, modal->@modal/i/flow/login/page.tsx

 

 

 

 

병렬 라우팅을 활용한 사례 - 로딩 및 오류 UI 

병렬로 구성할 경우 경로가 독립적으로 스트리밍 될 때, 이렇게 각각의 오류 및 로드 상태를 정의할 수 있음.

 

 

 

병렬 라우팅을 활용한 사례 - Modals(모달)

모달은 Intercepting Routes 와 같이 활용하여 생성할 수 있다.혹, 모달을 두 개 이상 띄우고 싶으면 같은 위치에서 @폴더를 하나 더 생성해서 배치해주면 된다.

 

이를 활용하면 해결할 수 있는 문제들

  • URL을 통해 모달 콘텐츠를 공유 가능하게 만듭니다 .
  • 모달을 닫는 대신 페이지를 새로 고칠 때 컨텍스트를 유지합니다 .
  • 이전 경로로 이동하지 않고 뒤로 탐색 시 모달을 닫습니다 .
  • 앞으로 탐색 시 모달을 다시 엽니다 .

 

기본 x.com 의 페이지의 모달 창으로 확인해볼 수 있다.

기본 x.com 경로의 페이지에서 로그인 버튼을 누르면 아래와 같이 모달 창이 생기는데 주소가 변한다! 

 

 

(모달을 그럼 어떻게 띄우는지에 대해서는 인터셉팅 라우트 설명에 이어서 . . )

 

 

인터셉팅 라우트(Intercepting Routes)

Intercepting은 가로채다라는 의미를 가지고 있다.

  • 인터셉팅 라우트를 이용하면 현재 레이아웃 내 애플리케이션의 다른 부분에서 경로를 로드할 수 있다.
  • 이 라우팅 패러다임은 사용자가 다른 컨텍스트로 전환하지 않고도 경로의 내용을 표시하려는 경우 유용하게 사용된다.

 

예를 들어, 피드에서 사진을 클릭하면 피드에 오버레이되어 사진을 모달로 표시할 수 있다. 이 경우 Next.js는 /photo/123 경로를 가로채서 URL을 마스크하고 이를 /feed에 오버레이한다. 

 

사용 방법

인터셉팅은 (..) 를 사용하여 정의할 수 있는데 이는 상대 경로(/.. ) 규칙과 유사함

  • (.) 동일한 수준의 세그먼트와 일치
  • (..) 한 수준 위의 세그먼트와 일치
  • (..)(..) 두 수준 위의 세그먼트와 일치
  • (...) 루트 app 디렉터리의 세그먼트와 일치
  • 예 ) photo 내에서 feed/(..)photo 세그먼트를 가로챌 수 있다.

 

 

 

다시 x.ocm 의 페이지로 예를 들면 

로그인 전에 로그인 모달창을 띄웠을 시 주소 'https://x.com/i/flow/login' 

 

폴더 구조상 이 부분에 해당한다. 

여기서 한가지 주의할 점은 인터셉팅 라우팅은 클라이언트에서 라우팅 할 때만 인터셉트 라우팅이 적용된다.

이게 무슨 소리인가 하면

 

처음 x.com 페이지에서 i/flow/login으로 경로이동을 할때만 적용이 된다는 것!

로그인 모달이 띄워진 상태에서 새로고침을 했을 경우에 아래와 같이 변한다

 

뒷 배경이 처음 모달을 띄웠을 때와는 다른 것을 확인할 수 있음

이게 주소상으로는 'https://x.com/i/flow/login'이 동일한데 실은 새로고침을 했을 때의 이 페이지는 폴더 구조상 이 부분에 해당한다.

새로고침한 것은 사용자가 직접 로그인 버튼을 눌러 라우팅을 발생시켜서 이동한 게 아니어서 인터셉트 라우팅이 적용되지 않는 것.

사용자가 새로고침하거나 아니면 직접 url을 입력해서 접속하는 것을 완벽하게 막을 수 있는 방법이 없기에 이런 점을 주의해야한다. 

 

 

여기서 또 한가지, 이렇게 경로 수정을 자주하다보면 페이지에서 제대로 안먹히는 경우가 있는데, 그럴 경우엔 그냥 npm run dev를 다시 실행해주면 된다. 
아무리봐도 틀린게 없는데 뭔가 안될때는 의심을 해보자.. 이것때문에 은근 시간을 많이 잡아먹었다. 
새로고침/강력 새로고침을해도 변경이 안되는 경우가있으니 아예 재시작을 하자@@

 

Private Folder

private folder는 폴더 정리용으로 주로 사용되고 아래와 같이 _ 를 사용하여 만들 수 있다.

이렇게 적용할시에 이 폴더도 주소창에 영향을 주지 않는다.