본문 바로가기
일상이 개발

Next.js 접근 제어 완전정복 – 인증, 역할 분기, 리디렉션 UX까지 실전 전략 가이드

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

Next.js 앱에서 접근 제어와 권한 분기 처리 전략 – 인증, 역할, 페이지별 접근 설정 완전 가이드

대규모 웹 애플리케이션을 개발하다 보면 로그인하지 않은 사용자의 접근을 제한하거나, 로그인하더라도 권한이 없는 사용자는 특정 페이지에 접근하지 못하도록 제어해야 합니다.

특히 Next.js는 SSR(서버 사이드 렌더링)과 CSR(클라이언트 사이드 렌더링)이 혼재된 구조이기 때문에 클라이언트와 서버 양쪽에서 접근 제어를 정교하게 처리해야 합니다.

Next.js 접근 제어 완전정복 – 인증, 역할 분기, 리디렉션 UX까지 실전 전략 가이드

이번 글에서는 다음과 같은 구조로 실전 전략을 정리해드립니다:

  • ✅ 로그인 기반 페이지 접근 제어
  • ✅ 역할 기반 권한 분기 (RBAC)
  • ✅ SSR, CSR 접근 제어 전략 비교
  • ✅ 미인증 사용자 리디렉션 처리
  • ✅ 관리자 전용 UI 보호 및 분기 처리

1. 🔐 인증 기반 접근 제어 – 로그인 여부 확인

✅ 가장 기본적인 시나리오

다음과 같은 경우, 인증 상태 확인이 필요합니다:

  • 🔑 로그인 사용자만 접근 가능한 마이페이지
  • 🔐 비회원이 접근하면 로그인 페이지로 리디렉션
  • 🎫 로그인한 사용자는 회원가입/로그인 페이지 접근 제한

📦 예시: 클라이언트 기반 인증 체크

import { useEffect } from 'react';
import { useAuthStore } from '@/store/authStore';
import { useRouter } from 'next/navigation';

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

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

  return user ? <div>나의 페이지입니다</div> : null;
}

✅ App Router에서의 인증 리디렉션 처리

// app/mypage/page.tsx
import { redirect } from 'next/navigation';
import { getUserFromCookies } from '@/lib/auth';

export default async function Page() {
  const user = await getUserFromCookies();

  if (!user) {
    redirect('/login');
  }

  return <div>마이페이지 내용</div>;
}

➡️ App Router 환경에서는 서버 컴포넌트 내부에서도 인증 상태 확인 후 리디렉션이 가능합니다.


2. 👤 역할(Role) 기반 권한 분기 처리

✅ 역할 기반 시나리오

  • 👨‍💼 일반 사용자 vs 관리자 페이지 구분
  • 📢 특정 메뉴는 관리자만 노출
  • 📌 동적 컴포넌트 렌더링 분기

📦 사용자 정보 예시

interface User {
  id: string;
  name: string;
  role: 'user' | 'admin';
}

📌 클라이언트에서 분기 처리

const { user } = useAuthStore();

return (
  <>
    <h1>대시보드</h1>
    {user?.role === 'admin' && <AdminPanel />}
  </>
);

📌 서버 사이드에서 관리자 전용 보호

// app/admin/page.tsx
import { getUserFromCookies } from '@/lib/auth';
import { redirect } from 'next/navigation';

export default async function AdminPage() {
  const user = await getUserFromCookies();

  if (user?.role !== 'admin') {
    redirect('/unauthorized');
  }

  return <div>관리자 페이지입니다</div>;
}

3. 📦 서버 사이드 vs 클라이언트 사이드 제어 비교

구분 SSR 방식 CSR 방식
보안성 ⭕ 우수 (렌더링 전에 확인) △ 브라우저 노출 후 차단
속도 △ 느릴 수 있음 ⭕ 빠름
UX ⭕ 깔끔한 리디렉션 △ 깜빡이는 UX
적합 사례 관리자 페이지, 보안 민감 정보 마이페이지, 권한 메뉴 분기

4. 🧱 인증 보호용 레이아웃 구성

✅ App Router의 layout.tsx 활용

페이지 단위가 아닌 라우팅 그룹 전체를 보호할 때, layout.tsx에서 인증 체크를 적용할 수 있습니다.

📦 예시: app/(auth)/layout.tsx

import { getUserFromCookies } from '@/lib/auth';
import { redirect } from 'next/navigation';

export default async function AuthLayout({ children }) {
  const user = await getUserFromCookies();
  if (!user) {
    redirect('/login');
  }

  return <>{children}</>;
}

➡️ (auth) 그룹 내 모든 하위 경로에 인증 검사가 자동 적용됩니다.

📌 관리자 전용 레이아웃 예시

// app/(admin)/layout.tsx
if (user.role !== 'admin') redirect('/unauthorized');

5. 🛑 미인증 접근 UX 전략

✅ 무조건 리디렉션? NO!

  • 💬 경우에 따라 경고 메시지를 표시한 뒤 이동
  • ✅ 로그인 페이지로 리디렉션하되 원래 페이지 기억

📦 redirect query 적용 예시

router.replace(`/login?redirect=/mypage`);

📦 로그인 후 원래 위치로 복귀

const redirectTo = router.query.redirect || '/';

useEffect(() => {
  if (isLoggedIn) {
    router.replace(redirectTo);
  }
}, [isLoggedIn]);

📌 UX 개선 팁

  • 🧭 미인증 시 스켈레톤 + "로그인이 필요합니다" 메시지
  • 🪧 전용 Unauthorized 페이지 구성

6. ❌ 403, 401 에러 처리 페이지 구성

✅ Next.js 기본 에러 구성

/app/unauthorized/page.tsx

📦 사용자 권한 오류 시 렌더링

redirect('/unauthorized');

📌 Unauthorized UI 예시

<div className="unauthorized">
  <h2>접근이 제한되었습니다</h2>
  <p>해당 페이지에 대한 권한이 없습니다.</p>
  <Link href="/">홈으로 돌아가기</Link>
</div>

✅ 상태 코드 반환 (API 또는 Edge Function)

return new Response('Forbidden', { status: 403 });

7. 🧩 접근 제어 코드 리팩토링 전략

✅ 인증 확인 로직 모듈화

// lib/auth.ts
export const isAdmin = (user) => user?.role === 'admin';
export const isAuthenticated = (user) => !!user;

📦 권한 조건 분기

if (!isAuthenticated(user)) {
  redirect('/login');
}
if (!isAdmin(user)) {
  redirect('/unauthorized');
}

✅ 공통 Wrapper 컴포넌트 설계

<RequireAuth>
  <MyPageContent />
</RequireAuth>

➡️ 공통 권한 처리 코드를 추상화하면 재사용성과 유지보수성이 크게 향상됩니다.

8. 🧭 접근 제어 설계 패턴 요약

✅ 클라이언트 보호 패턴

  • useAuthStore() 또는 context로 로그인 여부 확인
  • useEffect에서 리디렉션 처리
  • 조건부 렌더링 ({user?.role === 'admin' && <AdminPanel />})

✅ 서버 보호 패턴 (App Router 기준)

  • layout.tsx 또는 page.tsx에서 getUserFromCookies() 실행
  • redirect()로 접근 차단
  • 정적 보호가 아닌 런타임 인증에 최적

✅ 관리 패널 권한 보호

  • 라우팅 그룹 (admin) 생성
  • layout.tsx 또는 middleware.ts에서 접근 검사

✅ 리디렉션 UX 최적화

  • 로그인 후 원래 페이지로 복귀 지원
  • 권한 부족 시 /unauthorized 전용 페이지 제공
  • 토스트 또는 안내 문구로 시각적 피드백

9. ✅ 실무 접근 제어 체크리스트

📌 기본 인증 체크

  • ✔️ 로그인 여부에 따른 페이지 접근 제한
  • ✔️ 인증 상태는 store/context에서 관리
  • ✔️ 로그인하지 않은 경우 리디렉션 처리

📌 권한(Role) 체크

  • ✔️ 사용자 role 기반 콘텐츠 렌더링 분기
  • ✔️ 관리자 전용 페이지 보호
  • ✔️ 서버/클라이언트 양방향에서 검증

📌 UX & 구조 체크

  • ✔️ unauthorized, not-found 페이지 준비
  • ✔️ 미인증 시 스켈레톤 or 안내 메시지
  • ✔️ auth helper 함수로 코드 일관성 유지

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

반응형