본문 바로가기
일상이 개발

Next.js 접근 제어 & 권한 분기 완전 정복 – 로그인 보호부터 Role 기반 렌더링까지 실전 가이드

by 아빠고미 2025. 5. 17.
반응형

Next.js 접근 제어 & 권한 분기 완전 정복 – 로그인 보호부터 Role 기반 렌더링까지 실전 가이드

Next.js 앱에서 접근 제어와 권한 분기 처리 전략 – 페이지 보호부터 역할 기반 UI 렌더링까지 실전 설계 가이드

Next.js 앱에서 보안을 위해 꼭 필요한 요소가 있습니다. 바로 접근 제어권한 분기 처리입니다.

사용자가 로그인하지 않았거나, 권한이 없는 페이지에 접근했을 때 적절한 리디렉션과 UI 분기 처리가 이루어지지 않는다면 서비스 신뢰도는 물론 보안상 허점까지 생길 수 있습니다.

 

이번 글에서는 다음 내용을 중심으로 Next.js 접근 제어 전략을 실전 코드 중심으로 구성합니다:

  • ✅ 페이지 단위 보호 (SSR/CSR)
  • ✅ 미들웨어 인증 라우팅 처리
  • ✅ 권한(Role) 기반 조건 렌더링
  • ✅ 사용자 유형에 따른 네비게이션 분리
  • ✅ UX를 해치지 않는 분기 처리 기법

1. 🔐 인증되지 않은 사용자의 페이지 접근 차단

✅ 기본 흐름

  1. 1. 인증 토큰 존재 여부 확인 (SSR or CSR)
  2. 2. 인증되지 않은 경우 → 로그인 페이지로 redirect

📦 SSR 방식 예시 (getServerSideProps)

export async function getServerSideProps(context) {
  const token = context.req.cookies.token;

  if (!token) {
    return {
      redirect: {
        destination: '/login',
        permanent: false
      }
    };
  }

  return {
    props: {}
  };
}

📦 CSR 방식 예시 (useEffect + router)

import { useEffect } from 'react';
import { useRouter } from 'next/router';
import { useAuth } from '@/store/auth';

export default function ProtectedPage() {
  const router = useRouter();
  const { user } = useAuth();

  useEffect(() => {
    if (!user) {
      router.replace('/login');
    }
  }, [user]);

  return <div>이 페이지는 로그인된 사용자만 볼 수 있습니다.</div>;
}

➡️ SSR은 SEO와 초기 데이터 처리에 강하고, CSR은 UX 중심의 리액티브 제어에 강합니다.


2. ✨ 미들웨어를 통한 인증 라우팅 제어 (Next.js 13+)

✅ 왜 미들웨어를 쓸까?

  • 라우팅 단계에서 인증 여부 확인
  • SSR보다 빠르게 리디렉션 가능
  • 프론트 보호를 더 단단하게

📦 middleware.ts 예시

// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  const token = request.cookies.get('token')?.value;
  const pathname = request.nextUrl.pathname;

  const isProtected = pathname.startsWith('/dashboard') || pathname.startsWith('/admin');

  if (isProtected && !token) {
    const loginUrl = new URL('/login', request.url);
    return NextResponse.redirect(loginUrl);
  }

  return NextResponse.next();
}

📌 설정 필요

// next.config.js
module.exports = {
  matcher: ['/dashboard/:path*', '/admin/:path*']
}

➡️ 미들웨어를 활용하면 모든 페이지 접근 이전에 인증 상태를 먼저 검증할 수 있습니다.

3. 🧠 권한 기반 분기 처리 – Role별로 UI를 나누자

✅ 대표적인 권한 구조

  • admin: 관리자 페이지 전체 접근
  • user: 마이페이지, 주문 등 사용자 콘텐츠
  • guest: 로그인 이전의 방문자

📦 유저 정보 구조 예시

const user = {
  id: 'abc123',
  name: '홍길동',
  role: 'admin'
};

📌 조건 렌더링 예시

{user?.role === 'admin' && (
  <Link href="/admin">관리자 페이지</Link>
)}

✅ 권한별 페이지 보호

export async function getServerSideProps(context) {
  const token = context.req.cookies.token;
  const decoded = verifyToken(token);

  if (decoded.role !== 'admin') {
    return {
      redirect: {
        destination: '/',
        permanent: false
      }
    };
  }

  return { props: {} };
}

➡️ 백엔드에서 role 정보를 담아주면, 프론트에서 다양한 UI/라우팅 분기가 가능합니다.


4. 🧱 역할 기반 레이아웃/네비게이션 구성

📦 메뉴 분기 처리 예시

const userMenu = [
  { name: '마이페이지', href: '/mypage', role: 'user' },
  { name: '주문내역', href: '/orders', role: 'user' },
  { name: '회원관리', href: '/admin/users', role: 'admin' }
];
<nav>
  {userMenu
    .filter(item => item.role === user.role)
    .map(menu => (
      <Link key={menu.href} href={menu.href}>{menu.name}</Link>
    ))}
</nav>

✅ 팀 협업 시 팁

  • route-level guard는 미들웨어
  • UI-level guard는 user.role 조건
  • 페이지 안에서 데이터 접근 권한도 한 번 더 체크

5. 🎯 실전 설계 패턴 – 인증/권한 통합 관리 구조

📦 권장 구조 예시

/middleware.ts          → 라우팅 보호
/src/store/auth.ts     → user 상태 저장
/src/lib/token.ts      → decodeToken, getUserRole
/src/components/AuthGuard.tsx  → 고차 컴포넌트 방식 접근 제어

✅ 고차 컴포넌트 방식 예시

const withAuth = (WrappedComponent, allowedRoles = []) => {
  return function Guarded(props) {
    const { user } = useAuth();

    if (!user || !allowedRoles.includes(user.role)) {
      return <p>접근 권한이 없습니다.</p>;
    }

    return <WrappedComponent {...props} />;
  };
};

➡️ 컴포넌트 단위로도 권한 분기를 유연하게 설정할 수 있습니다.

6. ✅ UX 친화적인 접근 제어 – 사용자 입장에서 생각하자

✅ 문제 상황

  • 페이지 진입 시 갑작스러운 리디렉션 → “왜 날 쫓아냈지?”
  • 단순히 빈 화면 or 콘솔 에러

📌 UX 개선 포인트

  • 권한 부족 시 → 명확한 안내 메시지 제공
  • 리디렉션 전 → 잠깐 로딩 or 전환 메시지 보여주기
  • 403 에러 전용 페이지 구성

📦 예시: 403 페이지 구성

// pages/403.tsx
export default function Forbidden() {
  return (
    <div style={{ padding: '2rem', textAlign: 'center' }}>
      <h2>⛔ 접근 권한이 없습니다.</h2>
      <p>이 페이지는 관리자 전용입니다.</p>
      <a href="/">홈으로 돌아가기</a>
    </div>
  );
}

📦 middleware에서 redirect

if (token && userRole !== 'admin' && pathname.startsWith('/admin')) {
  return NextResponse.redirect(new URL('/403', req.url));
}

7. ❗ 에러 발생 시 대처 방식

✅ 클라이언트 단

  • API 에러: toast or inline 메시지
  • 권한 오류: 전용 메시지 + redirect or fallback UI

✅ 서버 단

  • SSR에서 인증 실패 시 → redirect or notFound
  • 백엔드에서 403 응답 → 프론트에서 감지 후 처리

➡️ 모든 에러는 사용자가 명확하게 인지할 수 있게 안내해야 합니다.


8. 📋 접근 제어 & 권한 분기 체크리스트

🛡️ 보안 + UX를 모두 고려한 설계 포인트

  • 로그인 여부 → SSR / 미들웨어로 제어
  • 권한(Role)에 따라 메뉴/페이지/UI 분기
  • 백엔드의 권한 검증도 반드시 함께 구현
  • 403, 401 페이지 구성
  • 권한 부족 시 사용자 친화적 안내 제공

📦 추천 구조

/middleware.ts
/src/pages/403.tsx
/src/store/auth.ts
/src/components/AuthGuard.tsx

긴 글 읽어주셔서 감사합니다! 공감, 댓글, 공유는 다음 글 제작에 큰 힘이 됩니다 🙌

반응형