icon안동민 개발노트

보호된 라우트 생성


 Next.js에서 보호된 라우트를 구현하는 것은 인증 시스템의 핵심 요소입니다. 이 절에서는 NextAuth.js를 사용하여 인증된 사용자만 접근할 수 있는 보호된 라우트를 구현하는 방법, 미들웨어 사용, 그리고 클라이언트 및 서버 컴포넌트에서의 인증 상태 처리에 대해 알아보겠습니다.

NextAuth.js 세션 검사

 NextAuth.js는 세션 검사를 위한 다양한 방법을 제공합니다. 서버 컴포넌트에서는 getServerSession 함수를, 클라이언트 컴포넌트에서는 useSession 훅을 사용할 수 있습니다.

 서버 컴포넌트에서의 세션 검사

import { getServerSession } from "next-auth/next"
import { authOptions } from "@/app/api/auth/[...nextauth]/route"
import { redirect } from "next/navigation"
 
export default async function ProtectedServerComponent() {
  const session = await getServerSession(authOptions)
 
  if (!session) {
    redirect("/login")
  }
 
  return <div>Protected content for {session.user.email}</div>
}

 클라이언트 컴포넌트에서의 세션 검사

'use client'
 
import { useSession } from "next-auth/react"
import { useRouter } from "next/navigation"
import { useEffect } from "react"
 
export default function ProtectedClientComponent() {
  const { data: session, status } = useSession()
  const router = useRouter()
 
  useEffect(() => {
    if (status === "unauthenticated") {
      router.push("/login")
    }
  }, [status, router])
 
  if (status === "loading") {
    return <div>Loading...</div>
  }
 
  if (!session) {
    return null
  }
 
  return <div>Protected content for {session.user.email}</div>
}

미들웨어를 사용한 라우트 보호

 Next.js의 미들웨어를 사용하면 라우트 레벨에서 인증을 처리할 수 있습니다. middleware.js 파일을 프로젝트 루트에 생성하여 구현할 수 있습니다.

import { NextResponse } from 'next/server'
import { getToken } from 'next-auth/jwt'
 
export async function middleware(req) {
  const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET })
  const { pathname } = req.nextUrl
 
  if (pathname.startsWith('/protected') && !token) {
    return NextResponse.redirect(new URL('/login', req.url))
  }
 
  return NextResponse.next()
}
 
export const config = {
  matcher: '/protected/:path*',
}

 이 미들웨어는 /protected 경로로 시작하는 모든 요청을 인터셉트하고, 인증되지 않은 사용자를 로그인 페이지로 리다이렉트합니다.

인증되지 않은 사용자 리다이렉션

 인증되지 않은 사용자를 리다이렉트하는 방법은 여러 가지가 있습니다.

  1. 서버 컴포넌트에서 redirect 함수 사용
  2. 클라이언트 컴포넌트에서 useRouter 훅 사용
  3. 미들웨어에서 NextResponse.redirect 사용

 위의 예제들에서 이미 이러한 방법들을 볼 수 있습니다.

4장 라우팅 심화와의 연결

 보호된 라우트 생성은 4장의 라우팅 심화와 밀접하게 연관됩니다. 동적 라우트, 중첩 라우트, 그리고 라우트 그룹 개념을 이해하고 있다면, 더 복잡한 인증 및 권한 부여 시스템을 구현할 수 있습니다. 예를 들어, 사용자 역할에 따라 다른 대시보드 페이지로 라우팅하거나, 특정 제품 카테고리에 대한 접근을 제한하는 등의 기능을 구현할 수 있습니다.

실습 : 사용자 역할에 따른 대시보드 리다이렉션

 사용자 역할(일반 사용자, 관리자)에 따라 다른 대시보드 페이지로 리다이렉트하는 보호된 라우트를 구현해보겠습니다.

  1. 먼저, NextAuth.js 설정에 사용자 역할 정보를 추가합니다.
// app/api/auth/[...nextauth]/route.js
import NextAuth from 'next-auth'
import GoogleProvider from 'next-auth/providers/google'
 
export const authOptions = {
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_ID,
      clientSecret: process.env.GOOGLE_SECRET,
    }),
  ],
  callbacks: {
    async jwt({ token, user }) {
      if (user) {
        token.role = user.email.endsWith('@admin.com') ? 'admin' : 'user'
      }
      return token
    },
    async session({ session, token }) {
      session.user.role = token.role
      return session
    },
  },
}
 
const handler = NextAuth(authOptions)
 
export { handler as GET, handler as POST }
  1. 대시보드 페이지를 생성합니다.
// app/dashboard/page.js
import { getServerSession } from "next-auth/next"
import { authOptions } from "../api/auth/[...nextauth]/route"
import { redirect } from "next/navigation"
 
export default async function Dashboard() {
  const session = await getServerSession(authOptions)
 
  if (!session) {
    redirect("/login")
  }
 
  if (session.user.role === 'admin') {
    redirect("/dashboard/admin")
  }
 
  return <div>User Dashboard for {session.user.email}</div>
}
 
// app/dashboard/admin/page.js
import { getServerSession } from "next-auth/next"
import { authOptions } from "../../api/auth/[...nextauth]/route"
import { redirect } from "next/navigation"
 
export default async function AdminDashboard() {
  const session = await getServerSession(authOptions)
 
  if (!session) {
    redirect("/login")
  }
 
  if (session.user.role !== 'admin') {
    redirect("/dashboard")
  }
 
  return <div>Admin Dashboard for {session.user.email}</div>
}
  1. 미들웨어를 사용하여 전체 /dashboard 경로를 보호합니다.
// middleware.js
import { NextResponse } from 'next/server'
import { getToken } from 'next-auth/jwt'
 
export async function middleware(req) {
  const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET })
  const { pathname } = req.nextUrl
 
  if (pathname.startsWith('/dashboard') && !token) {
    return NextResponse.redirect(new URL('/login', req.url))
  }
 
  if (pathname === '/dashboard/admin' && token?.role !== 'admin') {
    return NextResponse.redirect(new URL('/dashboard', req.url))
  }
 
  return NextResponse.next()
}
 
export const config = {
  matcher: '/dashboard/:path*',
}

 이 실습을 통해 사용자 역할에 따라 다른 대시보드로 리다이렉트하는 보호된 라우트를 구현할 수 있습니다. 이는 실제 애플리케이션에서 자주 사용되는 패턴으로, 복잡한 권한 관리 시스템의 기초가 됩니다.

 보호된 라우트 구현은 인증과 권한 부여의 핵심 요소입니다. Next.js의 서버 컴포넌트, 클라이언트 컴포넌트, 그리고 미들웨어를 조합하여 강력하고 유연한 인증 시스템을 구축할 수 있습니다. 이를 통해 사용자 경험을 향상시키고 애플리케이션의 보안을 강화할 수 있습니다.