File name
Commit message
Commit date
08-14
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { SESSION_COOKIE_NAME } from '@/lib/auth/session-cookie';
/**
* 낙관적 체크 전용 — 세션 쿠키의 "존재 여부"만 확인한다. 최종 판단(서명·만료 검증)은
* lib/auth/dal.ts의 DAL이 담당한다 (Next.js 공식 가이드 지침: Proxy는 느린 조회·최종
* 인가 판단에 쓰지 않는다).
*
* allowlist 방식 — `/login` 외 모든 경로는 세션 쿠키가 없으면 `/login`으로 리다이렉트한다.
* 신규 라우트를 추가해도 이 목록에 넣지 않는 한 자동으로 보호된다(fail-closed).
*
* 위조/만료 쿠키를 가진 채 `/login`에 접근하는 경우의 `/` 리다이렉트는 여기서 하지 않는다 —
* proxy(`/login`→`/`)와 DAL(`/`→`/login`)이 무한 루프를 이룰 수 있으므로, 그 방향은
* `/login` 페이지의 `getSessionAdmin()` 검증(서명까지 확인)을 통과했을 때만 수행한다.
*/
const PUBLIC_PATHS = ['/login'];
/**
* 개발 환경에서만 인증을 면제하는 경로. 디자인 시스템 테스트 페이지(`/dev-test/design`)는
* 개발 전용 라우트이며 프로덕션에서는 페이지 자체가 `notFound()`로 404를 낸다 — 즉 여기서
* 열어도 프로덕션 노출 경로가 생기지 않는다. 백엔드나 세션 없이도 토큰·컴포넌트를 확인할
* 수 있게 한다.
*
* fail-closed 원칙은 유지된다: 이 목록은 개발 빌드에서만 평가되고, 프로덕션 빌드에서는
* `PUBLIC_PATHS`만 남는다.
*/
const DEV_ONLY_PUBLIC_PATHS = ['/dev-test/design'];
function isPublicPath(pathname: string): boolean {
if (PUBLIC_PATHS.includes(pathname)) {
return true;
}
return (
process.env.NODE_ENV !== 'production' &&
DEV_ONLY_PUBLIC_PATHS.includes(pathname)
);
}
export function proxy(request: NextRequest) {
const { pathname } = request.nextUrl;
const hasSessionCookie = request.cookies.has(SESSION_COOKIE_NAME);
if (!isPublicPath(pathname) && !hasSessionCookie) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: [
'/((?!_next/static|_next/image|favicon\\.ico|robots\\.txt|.*\\.(?:svg|png|jpg|jpeg|gif|webp|ico)$).*)',
],
};