[Next.js] Routing 2부

Redirection

Next.js에서는 다양한 형태의 Redirect 전략을 제공하고 있습니다. 어떤 방식으로 Redirect를 사용할 수 있는지 알아보도록 하겠습니다.

redirect function

redirect는 서버에서 사용하는 메서드로 다른 URL로 이동하는 것을 허용하고 있습니다.

'use server'
 
import { redirect } from 'next/navigation'
import { revalidatePath } from 'next/cache'
 
export async function createPost(id: string) {
  try {
    // Call database
  } catch (error) {
    // Handle errors
  }
 
  revalidatePath('/posts') // Update cached posts
  redirect(`/post/${id}`) // Navigate to the new post page
}

 

알아두면 좋은 내용

  • redirect 사용 시 에러를 던지므로 try/catch문 내에서 사용할 수 있도록 합니다.
  • redirect 를 client component내에서도 사용가능 하지만 event handler내에서는 사용이 불가능합니다. 대시 useRouter를 사용 가능합니다.
  • 페이지 렌더링 전에 redirect를 해야 한다고하면 next.config.js 나 Middleware를 사용하시기 바랍니다.

 

permanentRedirect Function

해당 함수는 서버에서 영구적으로 URL을 이동할 때, 사용이 됩니다. 유저의 정보를 업데이트를 하고 이동 할때, 사용되기도 합니다.

'use server'
 
import { permanentRedirect } from 'next/navigation'
import { revalidateTag } from 'next/cache'
 
export async function updateUsername(username: string, formData: FormData) {
  try {
    // Call database
  } catch (error) {
    // Handle errors
  }
 
  revalidateTag('username') // Update all references to the username
  permanentRedirect(`/profile/${username}`) // Navigate to the new user profile
}

 

use Router hook

Client Component에서 이벤트 핸들러 사용 시에 페이지를 이동하고자 하면 해당 함수를 사용할 수 있습니다.

'use client'
 
import { useRouter } from 'next/navigation'
 
export default function Page() {
  const router = useRouter()
 
  return (
    <button type="button" onClick={() => router.push('/dashboard')}>
      Dashboard
    </button>
  )
}

 

알아두면 좋은 내용

  • 라우터를 사용하지 않고자 하면 <Link/> 컴포넌트를 사용하는 것도 방법입니다.

 

redirects in next.config.js

목적 경로에 요청이 오게 되었을 때, 자동으로 redirect를 하는 기능을 기본 설정으로 지정 할 수 있습니다.

module.exports = {
  async redirects() {
    return [
      // Basic redirect
      {
        source: '/about',
        destination: '/',
        permanent: true,
      },
      // Wildcard path matching
      {
        source: '/blog/:slug',
        destination: '/news/:slug',
        permanent: true,
      },
    ]
  },
}

 

알아두면 좋은 내용

  • redirects 는 Middleware 이전에 작동이 됩니다

 

NextResponse.redirect in Middleware

미들웨어는 요청이 완료되기 전에 코드를 수행하는 기능입니다. 여러 인증, 인가 기능에서 유용하게 사용이 가능합니다.

 

다음은 인가되지 않은 유저의 접근으로 보이게 되면 /login으로 가도록 하는 코드 입니다.

import { NextResponse, NextRequest } from 'next/server'
import { authenticate } from 'auth-provider'
 
export function middleware(request: NextRequest) {
  const isAuthenticated = authenticate(request)
 
  // If the user is authenticated, continue as normal
  if (isAuthenticated) {
    return NextResponse.next()
  }
 
  // Redirect to login page if not authenticated
  return NextResponse.redirect(new URL('/login', request.url))
}
 
export const config = {
  matcher: '/dashboard/:path*',
}

 

Route Group

app디렉토리 내에서 라우트들을 그룹핑 할 수 있는 기능을 제공하고 있습니다. 해당 기능을 사용하게 되면 라우트 세그먼트를 좀 더 깔끔하게 정리 할 수 있습니다.

 

그룹핑된 라우터간에는 서로의 layout.js를 공유를 하지 않으므로 별도의 레이아웃을 선택적으로 제공 할 수 있습니다. 반대로 그룹핑 된 페이지 간에 같은 레이아웃을 제공하고자 하면 페이지를 그룹 내로 이동하여야 합니다.

그룹핑을 app 디렉토리 바로 아래에서 사용하게 되면 여러 Root Layout을 제공할 수 있습니다.

 

알아두면 좋은 내용

  • 그룹핑을 사용하여도 같은 라우트 세그먼트명을 사용하게 되었을 때, 에러를 발생시키게 됩니다.
  • 루트 레이아웃 간의 이동이 발생하면 완전한 페이지 로드를 수행하게 됩니다.

 

Project Organization

Next.js에서는 다양한 기능을 제공하는 만큼 구성 또한 지정이 되어진 부분이 존재합니다. 이번에는 어떻게 프레임워크를 구성하는지에 대한 자세한 부분을 알아보도록 하겠습니다.

 

Safe colocation by default

라우트 세그먼트를 구성하게 되면 자동으로 해당 서버의 URL로 맵핑이 되게 됩니다. 여기서 유저가 원하는 페이지를 제공하거나 라우터를 제공하고자 할 때, page.js 또는 route.js를 작성하지 않으면 접근을 할 수 없는 상태로 유지 됩니다.

다시 말해, page.jsroute.js를 작성하면 유저에게 제공이 가능한 상태가 됩니다.

이러한 방식을 통해 하나의 app 디렉토리 내에 여러 폴더를 사용하는 것이 가능합니다.

 

알아두면 좋은 내용

  • 페이지 라우터의 경우 내부 경로가 모두 라우팅에 등록되는 것으로 app 라우터와 차이점을 보여주고 있습니다.
  • 원하는 경우 app 디렉토리 밖에 지정하여 파일을 작성 할 수 있습니다.

 

Project organization feature

Next.js에서는 프로젝트를 구성하기 위하여 어떤 기능을 추가로 제공하는지 알아 보도록 하겠습니다.

 

Private Folders

비공개 폴더를 만들고자 하면 _ 를 사용하여 폴더 명을 작성하면 됩니다. 이러한 기능을 사용한 경우 아래 page.js를 작성하여도 라우터에 잡히지 않게 됩니다.

 

알아두면 좋은 내용

  • 프레임워크 컨벤션으로 지정된 내용은 아니지만 private 파일의 경우, 폴더 명에 private 명을 사용하는 패턴을 적용할 수 있습니다.
  • 라우터에 _ 들어간 URL을 등록하고자 하면 %5F를 폴더 명 내에 사용하면 됩니다.

 

src Directory

프로젝트 환경설정 코드와 프로젝트 코드를 구분하기 위해서 사용이 가능한 기능입니다.

 

Project organization strategies

프로젝트를 구성하는 여러 방식이 존재합니다. 어떠한 방식이 존재하는지 간단하게 알아보도록 하겠습니다.

Dynamic Routes

다이나믹 라우트는 정확한 라우트 세그먼트의 명칭을 모를 때 사용하기 좋은 기능입니다. 다이나믹 라우트를 사용하고자 하면 폴더명을 네모 브라켓 내에 입력하여 사용하면 됩니다. [folderName]

 

URL Path Param을 받아서 페이지 내에 사용하는 예시입니다.

export default function Page({ params }: { params: { slug: string } }) {
  return <div>My Post: {params.slug}</div>
}

 

 

Generation Static Params

해당 함수를 사용하면 동적으로 생성되는 페이지를 빌드 타임에 미리 생성 해둘 수 있게 됩니다.

export async function generateStaticParams() {
  const posts = await fetch('https://.../posts').then((res) => res.json())
 
  return posts.map((post) => ({
    slug: post.slug,
  }))
}

 

generateStaticParams 내에서 fetch 함수를 사용하여 데이터를 가져오면, 이러한 요청은 자동으로 메모이제이션됩니다. 즉, 같은 인자로 여러 번 수행되는 fetch 요청은 실제로는 단 한 번만 실행되며, 그 결과는 필요할 때마다 재사용됩니다. 이는 여러 페이지나 레이아웃에서 동일한 데이터 요청이 발생할 경우, 빌드 시간을 줄이는 데 도움을 줍니다.

 

Catch-all Segment

경로에 존재하는 모든 Path 파람을 가져오고자 하면 [...folderName]과 같이 폴더명을 작성하면 됩니다.

Optional Catch-all Segment

경로에 오는 Path 파람을 옵션널로 들고오고자 할 때는 [[...folderName]]을 사용하면 됩니다

Parallel Routes

병렬 라우팅의 경우, 같은 레이아웃 내에 있는 페이지를 조건부에 또는 동시에 렌더링을 할 수 있게 도와주는 기능입니다.

 

slots

Next.js의 슬롯 기능은 자식 컴포넌트를 구성할 수 있는 방식을 제공하여 레이아웃 컴포넌트에서 다양한 자식 컴포넌트들을 병렬로 렌더링할 수 있도록 해줍니다. 이 경우, @folder 컨벤션을 사용하여 각 슬롯을 정의하고, 해당 슬롯들은 app/layout.tsx 같은 상위 레이아웃 컴포넌트에 props로 전달됩니다. 슬롯을 사용하는 주요 목적은 구조적으로 각각의 부분을 독립적으로 관리하면서도 같은 페이지에서 렌더링할 수 있게 하는 것입니다.

 

Next.js에서 슬롯 기능은 기본적으로 rootLayout 기능과 관련이 있지만, 자동으로 rootLayout에 입력되는 것은 아닙니다. 각각의 슬롯은 개별적으로 rootLayout이나 다른 레이아웃 파일에서 명시적으로 렌더링을 관리해야 합니다. 슬롯이 URL 구조에 영향을 주지 않으므로, 예를 들어 /@analytics/views의 경우, 실제 URL은 /views가 됩니다. 이는 @analytics가 슬롯 이름으로 사용되고 있기 때문입니다.

 

Active state and navigation

Next.js에서 슬롯을 사용하는 병렬 라우팅 시스템은 능동적인 상태(Active state)를 유지하면서, 각 슬롯에 따라 다르게 내비게이션을 관리합니다. 이는 사용자 경험을 향상시키고, 특정 슬롯의 콘텐츠를 독립적으로 업데이트할 수 있도록 합니다.

Soft Navigation (부드러운 내비게이션):

  1. 클라이언트 사이드 내비게이션 동안, Next.js는 부분 렌더링을 수행하여 해당 슬롯의 하위 페이지만 변경합니다.
  2. 다른 슬롯들의 활성 하위 페이지는 현재 URL과 일치하지 않아도 유지됩니다.
  3. 이는 사용자가 다른 슬롯의 내용을 변경하지 않고 하나의 슬롯 내에서 내비게이션을 할 때 유용합니다.

Hard Navigation (강제 내비게이션):

  1. 전체 페이지 로드(브라우저 새로고침) 후에는, 현재 URL과 일치하지 않는 슬롯에 대해 Next.js는 활성 상태를 결정할 수 없습니다.
  2. 대신, 일치하지 않는 슬롯에 대해 default.js 파일을 렌더링하거나, default.js 파일이 없는 경우 404 오류 페이지를 렌더링합니다.

@team 슬롯에는 /settings 페이지가 있지만 @analytics 슬롯에는 없습니다. /settings로 내비게이션 할 때, @team 슬롯은 /settings 페이지를 렌더링하면서 @analytics 슬롯에 대해서는 현재 활성화된 페이지를 유지합니다. 새로고침을 하면, Next.js는 @analytics에 대해 default.js를 렌더링합니다. default.js가 없다면, 404 페이지가 대신 렌더링됩니다.

 

❓default.js
- 초기 로드나 전체 페이지 재로드 동안 일치하지 않는 슬롯을 위한 대체 콘텐츠로 default.js 파일을 정의할 수 있습니다.
- children이 암시적인 슬롯으로 취급되기 때문에, Next.js가 부모 페이지의 활성 상태를 복구할 수 없을 때 대체 콘텐츠로 렌더링하기 위해 children에 대해서도 default.js 파일을 생성해야 합니다.

 

useSelectedLayoutSegment(s)

Next.js에서 useSelectedLayoutSegment 훅은 특정 슬롯 내에서 활성화된 라우트 세그먼트를 읽어오는 데 사용됩니다. 이 훅은 parallelRoutesKey 파라미터를 받아, 지정된 슬롯의 현재 활성 라우트 세그먼트를 식별할 수 있게 합니다.

 

다음은 useSelectedLayoutSegment을 사용하는 방법을 설명하는 예시 코드입니다

// app/layout.tsx

'use client';

import { useSelectedLayoutSegment } from 'next/navigation';

interface LayoutProps {
  auth: React.ReactNode;
}

export default function Layout({ auth }: LayoutProps) {
  // 'auth' 슬롯에 대한 현재 활성 라우트 세그먼트를 얻음
  const loginSegment = useSelectedLayoutSegment('auth');

  return (
    <div>
      {/* 조건부 렌더링을 통해 현재 활성 세그먼트에 따라 다른 내용을 표시할 수 있음 */}
      {loginSegment === 'login' ? (
        <div>Welcome to the login page!</div>
      ) : (
        <div>Other auth related content</div>
      )}
      {auth}
    </div>
  );
}

이 코드에서, 사용자가 URL /login 또는 /@auth/login으로 내비게이션할 때, loginSegment 변수는 문자열 "login"과 동일하게 설정됩니다. 이 값을 사용하여 특정 슬롯 내에서 어떤 하위 페이지가 현재 활성화되어 있는지 확인하고, 그에 따라 적절한 UI를 렌더링할 수 있습니다. 이 기능은 동적인 라우트 관리를 필요로 하는 복잡한 레이아웃에서 유용하게 사용될 수 있습니다.

 

Loading and Error UI

슬롯 내에서도 loading.jserror.js를 사용할 수 있습니다.

 

Intercepting Routes

인터셉트 라우트는 현재 레이아웃에서 다른 라우트를 부분적으로 불러 올 수 있게 해줍니다. 해당 기능은 유저가 라우트 변환없이 현재 페이지에서 다른 화면을 보여주고자 할 때, 좋은 기능입니다.

 

위에서 설명 한 것과 같이 슬롯 내에 모달전용 페이지를 작성하고 인터셉터가 가능한 위치에 페이지를 별도로 작성하면 모달 내에 해당 페이지를 보여줄 수 있습니다.

 

Route Handler

라우트 핸들러는 Web API를 통해 Next.js에서 API를 만드는 것을 제공합니다.

라우트 핸들러를 사용하고자 하면 app 디렉토리 내에서 폴더를 생성하고 하위 파일에 route.js 를 작성하면 됩니다. 주의 할 것은 page.jsroute.js는 같은 경로 내에 존재하면 안됩니다 .

 

캐싱

라우트 핸들러를 통해 GET 메소드를 사용하면 자동으로 캐시 처리를 수행합니다.

export async function GET() {
  const res = await fetch('https://data.mongodb-api.com/...', {
    headers: {
      'Content-Type': 'application/json',
      'API-Key': process.env.DATA_API_KEY,
    },
  })
  const data = await res.json()
 
  return Response.json({ data })
}

 

캐시가 되지 않게 하는 방법으로는 아래와 같습니다.

  • Request 오브젝트 사용
  • 다른 Http 메소드 사용
  • 쿠키와 헤더와 같은 동적 함수 사용
  • 옵션 지정
export async function GET(request: Request) {
  const { searchParams } = new URL(request.url)
  const id = searchParams.get('id')
  const res = await fetch(`https://data.mongodb-api.com/product/${id}`, {
    headers: {
      'Content-Type': 'application/json',
      'API-Key': process.env.DATA_API_KEY!,
    },
  })
  const product = await res.json()
  return Response.json({ product })
}
export async function POST() {
  const res = await fetch('https://data.mongodb-api.com/...', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'API-Key': process.env.DATA_API_KEY!,
    },
    body: JSON.stringify({ time: new Date().toISOString() }),
  })
 
  const data = await res.json()
 
  return Response.json(data)
}

 

캐시 데이터 재검증

next.revalidate 옵션을 제공하고 있어, 캐시 데이터를 몇 초마다 재검증하게 할 지 지정할 수 있습니다.

export async function GET() {
  const res = await fetch('https://data.mongodb-api.com/...', {
    next: { revalidate: 60 }, // Revalidate every 60 seconds
  })
  const data = await res.json()
  return Response.json(data)
}

설정 옵션을 지정하여 사용할 수 있습니다.

export const revalidate = 60

 

 

 

'Frontend > Next.js' 카테고리의 다른 글

[Next.js] Routing 1부  (0) 2024.06.22
[Next.js] Server and Client Composition Pattern  (0) 2024.06.22
[Next.js] Client Components  (0) 2024.06.22
[Next.js] Server Components  (0) 2024.06.22
[Next.js] 왜 두 번 렌더링 되는 것일까?  (1) 2024.02.28