ReactNextCentral

권한 부여

Published on
사용자가 애플리케이션의 어떤 부분에 접근할 수 있는지 결정합니다.
Table of Contents

권한 부여

사용자가 인증되면 특정 경로에 방문할 수 있는지, 서버 액션을 통한 데이터 변형과 라우트 핸들러 호출과 같은 작업을 수행할 수 있는지 확인해야 합니다.

미들웨어를 이용한 경로 보호

Next.js의 미들웨어는 웹사이트의 다른 부분에 대한 접근을 제어하는 데 도움을 줍니다. 사용자 대시보드와 같은 영역을 보호하는 동시에 마케팅 페이지와 같은 다른 페이지를 공개적으로 유지하는 것이 중요합니다. 모든 경로에 미들웨어를 적용하고 공개 접근을 위한 예외를 지정하는 것이 좋습니다.

Next.js에서 인증을 위한 미들웨어를 구현하는 방법은 다음과 같습니다.

미들웨어 설정:

  • 프로젝트의 루트 디렉토리에 middleware.ts 또는 .js 파일을 생성합니다.
  • 인증 토큰 확인과 같은 사용자 접근 권한을 부여하는 논리를 포함시킵니다.

보호된 경로 정의:

  • 모든 경로가 권한 부여를 요구하는 것은 아닙니다. 미들웨어의 matcher 옵션을 사용하여 권한 부여 검사가 필요하지 않은 경로를 지정합니다.

미들웨어 로직:

  • 사용자가 인증되었는지 확인하는 로직을 작성합니다. 경로 권한 부여를 위해 사용자 역할이나 권한을 확인합니다.

무단 접근 처리:

  • 무단 사용자를 로그인 또는 오류 페이지로 적절하게 리디렉션합니다.

미들웨어 파일 예시는 다음과 같습니다.

import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  const currentUser = request.cookies.get('currentUser')?.value

  if (currentUser && !request.nextUrl.pathname.startsWith('/dashboard')) {
    return Response.redirect(new URL('/dashboard', request.url))
  }

  if (!currentUser && !request.nextUrl.pathname.startsWith('/login')) {
    return Response.redirect(new URL('/login', request.url))
  }
}

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|.*\\.png$).*)'],
}

이 예시는 요청 파이프라인의 초기 단계에서 리디렉션을 처리하기 위해 Response.redirect를 사용합니다. 이 방식은 효율적이며 접근 제어를 중앙화합니다.

특정 리디렉션 요구 사항에 대해, 서버 컴포넌트, 라우트 핸들러 및 서버 액션에서 redirect 함수를 사용하여 더 많은 제어를 제공할 수 있습니다. 이는 역할 기반 네비게이션 또는 컨텍스트에 민감한 시나리오에 유용합니다.

import { redirect } from 'next/navigation'

export default function Page() {
  // 리디렉션이 필요한지 결정하는 로직
  const accessDenied = true
  if (accessDenied) {
    redirect('/login')
  }

  // 다른 경로와 로직 정의
}
JavaScript
import { redirect } from 'next/navigation'

export default function Page() {
  // 리디렉션이 필요한지 결정하는 로직
  const accessDenied = true
  if (accessDenied) {
    redirect('/login')
  }

  // 다른 경로와 로직 정의
}

성공적인 인증 후, 사용자의 역할에 따라 사용자 네비게이션을 관리하는 것이 중요합니다. 예를 들어, 관리자 사용자는 관리자 대시보드로 리디렉션되고 일반 사용자는 다른 페이지로 이동할 수 있습니다. 이는 역할별 경험과 조건부 네비게이션에 중요합니다. 예를 들어, 필요한 경우 사용자에게 프로필을 완성하라는 메시지를 표시할 수 있습니다.

권한 설정을 할 때, 앱이 데이터에 접근하거나 변경하는 곳에서 주요 보안 검사가 이루어지도록 하는 것이 중요합니다. 미들웨어는 초기 검증에 유용할 수 있지만, 데이터 보호의 유일한 방어선이 되어서는 안 됩니다. 대부분의 보안 검사는 데이터 접근 계층(DAL)에서 수행되어야 합니다.

이 접근 방식은 이 보안 블로그에서 강조하며, 모든 데이터 접근을 전담 DAL 내에 통합하도록 권장합니다. 이 전략은 일관된 데이터 접근을 보장하고, 권한 부여 버그를 최소화하며, 유지 관리를 단순화합니다. 포괄적인 보안을 보장하기 위해 다음 주요 영역을 고려하세요.

  • 서버 액션: 서버 측 프로세스, 특히 민감한 작업에서 보안 검사를 구현합니다.
  • 라우트 핸들러: 보안 조치를 관리하여 접근을 승인된 사용자로 제한합니다.
  • 데이터 접근 계층(DAL): 데이터베이스와 직접 상호 작용하며 데이터 트랜잭션의 검증 및 승인에 중요합니다. 데이터의 가장 중요한 상호 작용 지점인 접근 또는 수정 시에 DAL 내에서 중요한 검사를 수행하는 것이 데이터 보안에 필수적입니다.

DAL을 보호하는 방법에 대한 자세한 가이드, 예제 코드 조각 및 고급 보안 관행을 포함하여 보안 가이드의 데이터 접근 계층 섹션을 참조하세요.

서버 액션 보호

서버 액션을 대중에게 공개된 API 엔드포인트와 동일한 보안 고려 사항으로 처리하는 것이 중요합니다. 각 액션에 대한 사용자 권한을 검증하는 것이 필수적입니다. 서버 액션 내에서 사용자 권한을 결정하기 위한 검사를 구현하세요. 예를 들어 특정 액션을 관리자 사용자에게 제한하는 것과 같습니다.

아래 예시에서는 액션을 진행하기 전에 사용자의 역할을 확인합니다.

'use server'

// ...

export async function serverAction() {
  const session = await getSession()
  const userRole = session?.user?.role

  // 사용자가 액션을 수행할 권한이 있는지 확인
  if (userRole !== 'admin') {
    throw new Error('무단 접근: 사용자에게 관리자 권한이 없습니다.')
  }

  // 권한이 있는 사용자에게 액션 진행
  // ... 액션의 구현
}
JavaScript
'use server'

// ...

export async function serverAction() {
  const session = await getSession()
  const userRole = session?.user?.role

  // 사용자가 액션을 수행할 권한이 있는지 확인
  if (userRole !== 'admin') {
    throw new Error('무단 접근: 사용자에게 관리자 권한이 없습니다.')
  }

  // 권한이 있는 사용자에게 액션 진행
  // ... 액션의 구현
}

라우트 핸들러 보호

Next.js의 라우트 핸들러는 들어오는 요청을 관리하는 데 중요한 역할을 합니다. 서버 액션처럼, 라우트 핸들러 또한 특정 기능에 대한 접근을 승인된 사용자만 할 수 있도록 보안을 강화해야 합니다. 이는 종종 사용자의 인증 상태와 그들의 권한을 검증하는 것을 포함합니다.

라우트 핸들러를 보호하는 예시는 다음과 같습니다.

export async function GET() {
  // 사용자 인증 및 역할 검증
  const session = await getSession()

  // 사용자가 인증되었는지 확인
  if (!session) {
    return new Response(null, { status: 401 }) // 사용자가 인증되지 않음
  }

  // 사용자가 'admin' 역할을 가지고 있는지 확인
  if (session.user.role !== 'admin') {
    return new Response(null, { status: 403 }) // 사용자가 인증되었지만 적절한 권한이 없음
  }

  // 승인된 사용자를 위한 데이터 가져오기
}
JavaScript
export async function GET() {
  // 사용자 인증 및 역할 검증
  const session = await getSession()

  // 사용자가 인증되었는지 확인
  if (!session) {
    return new Response(null, { status: 401 }) // 사용자가 인증되지 않음
  }

  // 사용자가 'admin' 역할을 가지고 있는지 확인
  if (session.user.role !== 'admin') {
    return new Response(null, { status: 403 }) // 사용자가 인증되었지만 적절한 권한이 없음
  }

  // 승인된 사용자를 위한 데이터 가져오기
}

이 예시는 인증과 권한 부여를 위한 이중 보안 검사를 갖춘 라우트 핸들러를 보여줍니다. 먼저 활성 세션을 확인한 다음, 로그인한 사용자가 'admin'인지를 검증합니다. 이 방식은 인증되고 승인된 사용자에게만 보안 접근을 보장하며, 요청 처리에 대한 강력한 보안을 유지합니다.

서버 컴포넌트를 이용한 권한 부여

Next.js의 서버 컴포넌트는 서버 측 실행을 위해 설계되었으며, 권한 부여와 같은 복잡한 로직을 통합하기 위한 안전한 환경을 제공합니다. 이들은 백엔드 리소스에 직접 액세스를 가능하게 하여 데이터 중심 작업의 성능을 최적화하고 민감한 작업의 보안을 강화합니다.

서버 컴포넌트에서는 사용자의 역할에 따라 조건부로 UI 요소를 렌더링하는 것이 일반적인 관행입니다. 이 접근 방식은 사용자가 보기에 승인된 콘텐츠에만 접근할 수 있도록 함으로써 사용자 경험과 보안을 모두 향상시킵니다.

예시는 다음과 같습니다.

export default async function Dashboard() {
  const session = await getSession()
  const userRole = session?.user?.role // 'role'이 세션 객체의 일부라고 가정

  if (userRole === 'admin') {
    return <AdminDashboard /> // 관리자 사용자를 위한 컴포넌트
  } else if (userRole === 'user') {
    return <UserDashboard /> // 일반 사용자를 위한 컴포넌트
  } else {
    return <AccessDenied /> // 무단 접근 시 표시되는 컴포넌트
  }
}
JavaScript
export default function Dashboard() {
  const session = await getSession()
  const userRole = session?.user?.role // 'role'이 세션 객체의 일부라고 가정

  if (userRole === 'admin') {
    return <AdminDashboard /> // 관리자 사용자를 위한 컴포넌트
  } else if (userRole === 'user') {
    return <UserDashboard /> // 일반 사용자를 위한 컴포넌트
  } else {
    return <AccessDenied /> // 무단 접근 시 표시되는 컴포넌트
  }
}

이 예시에서는 대시보드 컴포넌트가 'admin', 'user', 그리고 무단 역할에 따라 다른 UI를 렌더링합니다. 이 패턴은 각 사용자가 자신의 역할에 적합한 컴포넌트와만 상호 작용하도록 하여 보안과 사용자 경험을 모두 향상시킵니다.

모범 사례

  • 안전한 세션 관리: 무단 접근과 데이터 유출을 방지하기 위해 세션 데이터의 보안을 우선시하세요. 암호화와 안전한 저장 관행을 사용하세요.
  • 동적 역할 관리: 사용자 역할에 대한 유연한 시스템을 사용하여 권한과 역할의 변경에 쉽게 대응하세요. 하드코딩된 역할은 피하세요.
  • 보안 우선 접근: 권한 부여 로직의 모든 측면에서 보안을 우선시하여 사용자 데이터를 보호하고 애플리케이션의 무결성을 유지하세요. 이는 철저한 테스팅과 잠재적 보안 취약점 고려를 포함합니다.