
useEffect로 관리자 페이지 막지 마세요 (Next.js Middleware 완벽 가이드)
`useEffect`로 관리자 페이지 접근을 막으려다 뚫릴 뻔했던 경험을 공유합니다. 클라이언트 사이드 보호의 위험성과 Next.js 미들웨어(Middleware)를 사용해 서버 레벨에서 안전하게 경로를 보호하는 방법, 그리고 Edge Runtime의 제약사항까지 깊이 있게 다룹니다.

`useEffect`로 관리자 페이지 접근을 막으려다 뚫릴 뻔했던 경험을 공유합니다. 클라이언트 사이드 보호의 위험성과 Next.js 미들웨어(Middleware)를 사용해 서버 레벨에서 안전하게 경로를 보호하는 방법, 그리고 Edge Runtime의 제약사항까지 깊이 있게 다룹니다.
프론트엔드 개발자가 알아야 할 4가지 저장소의 차이점과 보안 이슈(XSS, CSRF), 그리고 언제 무엇을 써야 하는지에 대한 명확한 기준.

Debug에선 잘 되는데 Release에서만 죽나요? 범인은 '난독화'입니다. R8의 원리, Mapping 파일 분석, 그리고 Reflection을 사용하는 라이브러리를 지켜내는 방법(@Keep)을 정리해봤습니다.

비트코인은 블록체인의 일부입니다. 이중 지불 문제(Double Spending), 작업 증명(PoW)과 지분 증명(PoS)의 차이, 스마트 컨트랙트, 그리고 Web 3.0이 가져올 미래까지. 개발자 관점에서 본 블록체인의 모든 것.

내 서버가 해킹당하지 않는 이유. 포트와 IP를 검사하는 '패킷 필터링'부터 AWS Security Group까지, 방화벽의 진화 과정.

관리자 페이지(/admin)를 만들고 있었습니다.
일반 유저가 들어오면 안 되니까, 당연히 리다이렉트를 시켜야겠죠.
저는 아주 자연스럽게 useEffect를 썼습니다.
/* AdminPage.tsx (BAD EXAMPLE) */
'use client';
export default function AdminPage() {
const { user, loading } = useAuth();
const router = useRouter();
useEffect(() => {
if (!loading && !user?.isAdmin) {
router.push('/'); // "나가!"
}
}, [user, loading, router]);
if (loading) return <p>Loading...</p>;
return <div>매출 데이터: 100억 원... (1급 기밀)</div>;
}
완벽해 보였습니다. 로그인 안 한 유저가 들어오면 메인으로 튕겨 나갔거든요. 그런데 인터넷 속도가 느린 카페에서 테스트하다가 등골이 서늘해졌습니다.
"매출 데이터: 100억 원..."이 0.5초 동안 보였다가 사라지는 겁니다! (FOUC)게다가 JavaScript를 끄고(NoScript) 접속해봤더니? 리다이렉트가 작동하지 않고, 기밀 데이터가 그대로 노출되었습니다.
이게 바로 클라이언트 사이드 보호(Client-side Protection)의 한계입니다.
useEffect를 실행합니다.Next.js의 Middleware는 서버와 페이지 사이의 문지기입니다. 유저가 페이지에 도달하기 전에 요청을 가로채서 검사합니다.
유저 요청 -> [미들웨어] -> (통과?) -> 페이지 렌더링
(거절!) -> 로그인 페이지로 납치
이제 middleware.ts를 작성해 봅시다. (파일 위치는 src/middleware.ts 또는 루트)
/* middleware.ts */
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// 1. 쿠키에서 세션 토큰 확인 (가장 확실함)
const token = request.cookies.get('auth_token')?.value;
// 2. 토큰이 없으면 로그인 페이지로 강제 이동
if (!token) {
const loginUrl = new URL('/login', request.url);
// 원래 가려던 곳으로 돌아오기 위해 callbackUrl 추가
loginUrl.searchParams.set('callbackUrl', request.nextUrl.pathname);
return NextResponse.redirect(loginUrl);
}
// 3. 통과!
return NextResponse.next();
}
// 중요: 어디에서 검문할지 정하기 (Matcher)
export const config = {
matcher: ['/admin/:path*', '/dashboard/:path*'],
};
이렇게 하면:
미들웨어를 작성하다 보면 가끔 이상한 에러를 만납니다.
Error: The edge runtime does not support Node.js 'crypto' module.
Next.js 미들웨어는 Node.js가 아니라 Edge Runtime에서 돌아갑니다. Edge Runtime은 V8 엔진의 경량화 버전으로, 가볍고 빠르지만 Node.js API를 전부 지원하지는 않습니다.
fs (파일 시스템 접근 불가)path, os 모듈 등fetch, Request, Responsecookies, headersWeb Crypto API (표준 암호화)그래서 JWT 검증을 할 때 jsonwebtoken 라이브러리 대신 jose 같은 Edge 호환 라이브러리를 써야 합니다.
또는, 미들웨어에서는 간단히 쿠키 유무만 체크하고, 상세한 권한 검증은 페이지(layout.tsx이나 page.tsx) 서버 컴포넌트에서 하는 "이중 보안" 전략을 추천합니다.
미들웨어 하나에서 모든 로직(인증, 로깅, A/B 테스트, I18n)을 다 처리하면 코드가 스파게티가 됩니다.
하지만 Next.js는 공식적으로 여러 미들웨어 파일을 지원하지 않습니다. (오직 하나의 middleware.ts만 가능)
이럴 땐 직접 체이닝(Chaining) 함수를 만들어야 합니다.
/* middlewares/chain.ts */
export function chain(middlewares, index = 0) {
const current = middlewares[index];
if (current) {
const next = chain(middlewares, index + 1);
return current(next);
}
return () => NextResponse.next();
}
이렇게 함수형 프로그래밍 스타일로 여러 미들웨어를 엮어서 사용하면 유지보수가 훨씬 쉬워집니다.
useEffect로 리다이렉트 시키는 건 UX(사용자 경험)를 위한 것이지, 보안이 아닙니다.
진짜 보안은 데이터가 유저에게 전달되기 전에, 서버(Middleware)에서 이루어져야 합니다.
여러분의 소중한 데이터를 "0.5초의 깜박임"으로 유출하지 마세요. 미들웨어, 선택이 아니라 필수입니다.
I was building a corporate Admin Dashboard (/admin).
Regular users shouldn't access it, so naturally, I added a redirect.
I innocently used useEffect, which is what every React tutorial teaches.
/* AdminPage.tsx (BAD EXAMPLE) */
'use client';
export default function AdminPage() {
const { user, loading } = useAuth();
const router = useRouter();
useEffect(() => {
if (!loading && !user?.isAdmin) {
router.push('/'); // Kick them out
}
}, [user, loading, router]);
if (loading) return <p>Loading...</p>;
return <div>Revenue Data: $10 Million... (TOP SECRET)</div>;
}
It looked perfect. Non-logged-in users were bounced to the main page. But while testing in a cafe with slow Wi-Fi, I saw something terrifying.
"Revenue Data: $10 Million..." flashed on the screen for 0.5 seconds before disappearing!I then tried disabling JavaScript via Chrome DevTools. The redirect didn't work at all, and the dashboard stayed open.
This is a classic FOUC (Flash of Unstyled Content), or in this case, a "Flash of Secure Content".
useEffect.router.push(). (Too late).Next.js Middleware is a Bouncer sitting between the Request and the Response. It intercepts the user BEFORE they reach the page.
User Request -> [Middleware] -> (Pass?) -> Render Page
(Reject!) -> Kidnap to Login Page
Let's write middleware.ts. (Place it in src/middleware.ts or root).
/* middleware.ts */
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
// 1. Check token in Cookie (Most reliable method)
const token = request.cookies.get('auth_token')?.value;
// 2. If no token, redirect immediately
if (!token) {
const loginUrl = new URL('/login', request.url);
loginUrl.searchParams.set('callbackUrl', request.nextUrl.pathname);
return NextResponse.redirect(loginUrl);
}
// 3. Pass!
return NextResponse.next();
}
// IMPORTANT: Define where to set up the checkpoint (Matcher)
export const config = {
matcher: ['/admin/:path*', '/dashboard/:path*'],
};
This ensures:
You might encounter this error while writing middleware:
Error: The edge runtime does not support Node.js 'crypto' module.
Next.js Middleware runs on the Edge Runtime, NOT standard Node.js. Edge Runtime is a lightweight version of V8. It's fast and lacks cold starts, but it doesn't support all Node.js APIs.
fs (No file system access)path, os modules.fetch, Request, Response.cookies, headers.Web Crypto API (Standard encryption).Do not use jsonwebtoken (it relies on Node streams). Use jose instead. It's specifically built for the Web/Edge environment and works perfectly in middleware.
If you try to jam Authentication, Logging, Geo-blocking, and I18n all into one function, your middleware.ts will become unreadable.
Since Next.js only allows one middleware.ts file, you need to implement Chaining manually.
Basic idea:
Create a "Stack" of middlewares where each function calls the next() function in the chain.
// Example structure
export default stackMiddlewares([
withAuth,
withLogging,
withI18n
]);
This keeps your code modular and testable.
Redirecting with useEffect is a UX feature, not a Security feature.
Real security implies that unauthorized users never even receive the HTML bytes.
Don't risk your sensitive data for a "0.5-second flash". Middleware is not optional for protected routes; it's mandatory.
console.log not showing up?Middleware runs on the Server (Edge). The logs appear in your Terminal, not the Browser Console.
No. Most database ORMs rely on Node.js specific APIs (TCP sockets, FS) which are not available in the Edge Runtime.
You must use an HTTP-based client (like Supabase supabase-js, PlanetScale, or Upstash Redis).
Check your matcher config. If you don't exclude _next/static, middleware runs for every CSS/JS file request, slowing down your site.
Always use the standard exclusion pattern:
'/((?!api|_next/static|_next/image|favicon.ico).*)'
No. Middleware typically modifies Headers, Cookies, or Redirects.
You cannot change the HTML content (Response Body) easily. If you need to inject data, do it in layout.tsx or page.tsx after the middleware passes the request.