패스트캠퍼스 데브캠프 104일차 [Next.js, App Route]

Su Min·2024년 10월 29일
0
post-thumbnail

🔗 AppRoute

Next.js에서 AppRoute는 파일 시스템 기반의 라우터라고 설명하는데, app폴더 하위에 위치한 세그먼츠와 특수한 파일들을 통해 라우트를 정의하기 때문이다.

🔗 Page

app
├─dashboard
│  ├─ settings
│  │  │  page.tsx // ex.com/dashboard/settings
│               // ex.com/dashboard 404 Not Found
│  page.tsx  // ex.com

가장 상위에 있는 app폴더는 루트 path를 가리키고 하위 세그먼트는 그 뒤로 URL path에 매핑된다. page.tsx 폴더는 라우트폴더를 퍼블릭하게 접근할 수 있게 만들어주며 해당 URL로 이동을 했을 때 page.tsx의 UI가 보여진다는 의미이다.

🔗 Layout

Layout파일은 여러 라우트에 공유되는 UI로, 페이지 이동 시 state를 보존하고 인터랙티브한 요소들이 유지되며 리렌더링이 되지 않는다.

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode
}>) {
  return (
    <html lang="en">
      <body>루트 레이아웃{children}</body>
    </html>
  )
}

위의 코드에서 children 하위에 또 다른 레이아웃이 있다면 레이아웃이 들어오게 되고 없다면 해당 폴더의 페이지가 children으로 들어오게 된다. 그렇기때문에 Layout 파일은 루트 레이아웃을 제외하고 children을 받아 리턴하는 형태가 기본이며, 루트 레이아웃은 html, body 태그가 필수이다.

🔗 Dynamic Route

정확한 폴더의 이름을 미리 정할 수 없어 동적으로 경로를 정의해야하는 경우 다이나믹 라우트를 사용하게 된다.

app
├─dashboard
│  ├─ [id]
│  │  │  page.tsx 

// ex.com/dashboard/123
// id === 123

다이나믹 라우트는 [] 대괄호를 감싸서 폴더를 만들고 page.tsx를 추가해주면 URL에서 접근이 가능하다.


export default function Page({ params }: { params: { id: string } }) {
  return <p>{params.id}</p>
}

다이나믹 라우트 값은 props의 params를 사용하여 가져올 수 있으며, params안에는 폴더에 정의한 다이나믹 라우트 이름으로 들어오게 된다.


Route = /dashboard/[...id]/page.tsx
URL = /dashboard/a/b/1
params = { id: ['a', 'b', 1] }

다이나믹 라우트를 전개연산자를 사용한 [...id]로 정의하면 하위에 있는 모든 url이 params안에 배열로 담긴다.


Route = /dashboard/[[...id]]/page.tsx
URL = /dashboard
params = {}

또한 [[...id]] 한번 더 대괄호로 감싸면 하위 모든 url에 매칭되는 동시에 id가 없을 때 빈 객체가 담기게 할 수도 있다.

🔗 Route Group

app 하위에 있는 폴더는 URL path에 맵핑이 되지만 라우트 그룹은 URL path에 포함되지 않기 때문에 구조에 영향을 끼치지 않고 라우트 세그먼트와 파일들을 그룹으로 구성한다.

app
├─ (auth)
│  ├─ signin
│  │  │  page.tsx 
│  ├─ components

// ex.com/auth/signin
// Not Found

// ex.com/signin
// page.tsx UI

보통 공통되는 UI나 컴포넌트를 사용할 때 상위에 세그먼트를 만들지 않고 라우트 그룹을 활용한다.

🔗 Route Handler

라우트 핸들러는 지정한 경로에 대해 들어오는 HTTP 요청을 처리하는 커스텀 Request 핸들러 파일이다.

app
├─ api
│  ├─ test
│  │  │  route.ts

export async function GET(request: Request) {
  ...
  return NextResponse.json(data)
}

api 세그먼트 외의 폴더에 위치한 파일들에서는 /api/test로 API 요청이 가능하다.
Next.js 14버전까지는 캐시가 되는 것이 default이고 15버전에서는 캐시를 하지 않는 것이 default로 바뀌어졌다.

app
├─ api
│  ├─ test
│  │  ├─ [id]
│  │  │  │  route.ts

export default function POST(request: Request, { params }: { params: { id: string } }) {
  ...
  return NextResponse.json(data)
}

라우트 핸들러에서 다이나믹 라우트 값을 받는 것도 가능하다. 같은 방식으로 [] 대괄호로 감싸주고 첫번째 인자로는 request, 두번째 인자 params로 다이나믹 라우트 값이 들어오게 된다. 이렇게하면 /api/test/${id}로 API 요청이 가능하다.

🔗 Loading UI

loading.js파일과 React Suspense를 통해 로딩 UI를 구성할 수 있는데, 해당 컨벤션을 사용하면 라우트 세그먼트의 컨텐츠가 로드되는 동안 즉시 로딩 상태를 보여주고 렌더링이 완료되면 컨텐츠가 자동으로 교체된다.

<Suspense fallback={ <Loading /> }>
  <Page />
</Suspense>

로딩 파일을 추가하면 서스펜스가 페이지를 감싸는 형태와 동일하게 된다.

Streaming

SSR은 서버에서 fetch를 하고 HTML 생성한 이후, 생성된 HTML과 CSS,JS를 클라이언트로 전송한다. 그리고 클라이언트는 non-interactive한 상태의 HTML과 CSS를 먼저 보여준 이후에 React가 hydrates를 해서 페이지를 인터랙티브하게 만든다. 하지만 이 단계들은 순차적이라 어느 한 단계에서 지연이 발생하게 되면 그만큼 전체 페이지를 보여주게 되는 시간도 늦어지게 된다.
이러한 한계로 Next.js에는 스트리밍이 있는 것인데, 스트리밍은 페이지의 HTML을 더 작은 청크로 나눠 클라이언트에 점진적으로 전송을 하기때문에 모든 부분이 완성될때까지 기다리지 않고 완성된 부분 먼저 사용자한테 보여주어 SSR의 한계를 개선한다.

import Card from "@/components/Card"
import { Suspense } from "react"

export default async function Page() {
  return (
    <p>
      <Suspense fallback={<div>card1 loading중...</div>}>
        <Card />
      </Suspense>
      <Suspense fallback={<div>card2 loading중...</div>}>
        <Card />
      </Suspense>
      <Suspense fallback={<div>card3 loading중...</div>}>
        <Card />
      </Suspense>
    </p>
  )
}

Card 컴포넌트에 API 응답에 대해 delay를 랜덤하게 걸어주고 페이지에서 Suspense로 감싸주면 응답이 오지 않은 컴포넌트는 로딩 UI가 보여지고 응답이 오면 Card 컴포넌트로 교체된다.

🔗 Error Handling

Next.js에서 Error는 예상되는 에러와 예상되지 않는 에러로 나뉜다. 먼저 예상되는 에러는 server action 안에서 try-catch로 잡는 에러로 useActionState를 사용해서 처리한다. 반면 예상되지 않는 에러는 error 파일과 global-error 파일들로 에러 바운더리를 구현해 처리한다. error파일의 형태는 로딩 파일과 동일하게 <ErrorBoundary fallback={ <Error /> }>로 감싼 형태이고 global-error파일은 루트 레이아웃처럼 html, body 태그를 포함시키면 된다.

🔗 Not Found

notFound 함수가 라우트 세그먼트에서 throw 되었을 때나 URL에 일치하는 라우트가 존재하지 않을 때 표시된다.
Not Found페이지도 app폴더 하위에 not-found.tsx 파일을 추가하여 커스텀이 가능하다.

profile
성장하는 과정에서 성취감을 통해 희열을 느낍니다⚡️

0개의 댓글

관련 채용 정보