웹 개발
관리자 페이지와 공개 페이지를 처음부터 분리한 이유
같은 Next.js 앱 안에 관리자 라우트를 넣었다가 인증 미들웨어가 꼬이는 걸 겪고 나서, 아예 라우트 구조를 나눴다.
처음에는 /admin/* 경로를 공개 블로그와 같은 Next.js 앱 안에 넣었다. 작은 프로젝트니까 괜찮겠지 싶었는데, 인증 미들웨어를 손볼 때마다 공개 페이지에도 영향이 갔다. 결국 분리하기로 했다.
문제가 생긴 상황
Next.js App Router의 middleware.ts는 매처(matcher)로 경로를 필터링한다. /admin 경로에만 인증을 걸려고 했는데, 미들웨어 실수 한 번에 공개 블로그 전체가 로그인 페이지로 튕기는 일이 생겼다.
// 이렇게 쓰면 실수가 생기기 쉽다
export const config = {
matcher: ['/((?!_next|api/public|login).*)'],
};
정규식 한 자리 틀렸을 때의 여파가 너무 컸다.
분리 방식
Next.js App Router의 Route Groups를 쓰면 URL 구조를 바꾸지 않고 레이아웃과 미들웨어 적용 범위를 나눌 수 있다.
app/
(public)/
page.tsx → /
blog/[slug]/
page.tsx → /blog/...
layout.tsx (공개 레이아웃)
(admin)/
dashboard/
page.tsx → /dashboard
layout.tsx (관리자 레이아웃 + 인증 체크)
(admin) 그룹의 layout.tsx에서 세션을 확인하고 없으면 로그인 페이지로 보낸다. 이렇게 하면 미들웨어 경로 매처 없이도 라우트 그룹 단위로 인증을 격리할 수 있다.
// app/(admin)/layout.tsx
import { redirect } from 'next/navigation';
import { getSession } from '@/lib/auth';
export default async function AdminLayout({ children }: { children: React.ReactNode }) {
const session = await getSession();
if (!session) {
redirect('/login');
}
return <>{children}</>;
}
미들웨어는 최소한으로
미들웨어는 모든 요청에 끼어든다. 여기서 인증 DB 조회 같은 무거운 작업을 하면 퍼포먼스에 직접 영향이 간다. 나는 미들웨어에서는 JWT 토큰 유효성만 확인하고, 실제 권한 체크는 서버 컴포넌트나 API Route에서 한다.
얻은 것
- 공개 페이지와 관리자 페이지의 레이아웃을 독립적으로 관리할 수 있다.
- 인증 로직 변경이 공개 페이지 렌더링에 영향을 주지 않는다.
- 나중에 관리자 기능을 별도 앱으로 뽑아낼 때 경계가 명확하다.
작은 프로젝트라도 관리자 동선과 공개 동선은 처음부터 경계를 잡아두는 게 낫다. 나중에 분리하려면 생각보다 손이 많이 간다.