OPEN HYPER STEP
← 목록으로 (stack-analysis)
STACK-ANALYSIS · 16 / 90
stack-analysis
CHAPTER 16 / 90
읽기 약 2
FUNCTION

미들웨어 실전: 인증 + 지역화 + A/B 테스트


핵심 개념

middleware.ts·matcher·NextResponse — 인증 게이트·로케일 리다이렉트·실험.

본문

기본 미들웨어

TYPESCRIPT📋 코드 (20줄)
// middleware.ts (루트)
import { NextResponse, NextRequest } from 'next/server';

export function middleware(request: NextRequest) {
  // 모든 요청 전에 실행
  const url = request.nextUrl.clone();

  // 1. 헤더 추가
  const response = NextResponse.next();
  response.headers.set('x-correlation-id', crypto.randomUUID());

  return response;
}

export const config = {
  matcher: [
    // _next, api, 정적 파일 제외
    '/((?!api|_next/static|_next/image|favicon.ico).*)',
  ],
};

인증 게이트

TYPESCRIPT📋 코드 (39줄)
import { NextResponse, NextRequest } from 'next/server';
import { jwtVerify } from 'jose';

const JWT_SECRET = new TextEncoder().encode(process.env.JWT_SECRET!);

export async function middleware(request: NextRequest) {
  const path = request.nextUrl.pathname;

  // 보호 경로
  if (path.startsWith('/dashboard') || path.startsWith('/admin')) {
    const token = request.cookies.get('auth-token')?.value;

    if (!token) {
      const url = request.nextUrl.clone();
      url.pathname = '/login';
      url.searchParams.set('callbackUrl', path);
      return NextResponse.redirect(url);
    }

    try {
      const { payload } = await jwtVerify(token, JWT_SECRET);

      // 관리자 경로 추가 검증
      if (path.startsWith('/admin') && payload.role !== 'admin') {
        return NextResponse.redirect(new URL('/403', request.url));
      }

      // 사용자 정보 헤더로 전달
      const response = NextResponse.next();
      response.headers.set('x-user-id', payload.sub as string);
      response.headers.set('x-user-role', payload.role as string);
      return response;
    } catch {
      return NextResponse.redirect(new URL('/login', request.url));
    }
  }

  return NextResponse.next();
}

지역화 (i18n) 리다이렉트

TYPESCRIPT📋 코드 (23줄)
const SUPPORTED_LOCALES = ['ko', 'en', 'ja', 'zh'];

export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;

  // 이미 로케일이 있으면 통과
  const hasLocale = SUPPORTED_LOCALES.some(
    l => pathname === `/${l}` || pathname.startsWith(`/${l}/`)
  );
  if (hasLocale) return NextResponse.next();

  // Accept-Language로 추론
  const acceptLang = request.headers.get('accept-language') ?? '';
  const detected = SUPPORTED_LOCALES.find(l => acceptLang.includes(l)) ?? 'ko';

  // 쿠키에 저장된 선호 언어 우선
  const preferred = request.cookies.get('locale')?.value;
  const locale = preferred && SUPPORTED_LOCALES.includes(preferred) ? preferred : detected;

  const url = request.nextUrl.clone();
  url.pathname = `/${locale}${pathname}`;
  return NextResponse.redirect(url);
}

A/B 테스트

TYPESCRIPT📋 코드 (19줄)
export function middleware(request: NextRequest) {
  // 이미 그룹 할당됨
  let bucket = request.cookies.get('ab-bucket')?.value;

  if (!bucket) {
    // 50/50 할당
    bucket = Math.random() < 0.5 ? 'A' : 'B';
  }

  // /home → /home-A 또는 /home-B 리라이트 (URL은 그대로)
  const url = request.nextUrl.clone();
  if (url.pathname === '/' || url.pathname === '/home') {
    url.pathname = `/home-${bucket}`;
  }

  const response = NextResponse.rewrite(url);
  response.cookies.set('ab-bucket', bucket, { maxAge: 60 * 60 * 24 * 30 });
  return response;
}

다음 챕터

CH.17 "이미지 최적화" — next/image + CDN.


AI 프롬프트
🤖 AI에게 잘 물어보는 법 — 모델·전략별 프롬프트
Claude

무료: Sonnet 4.6 / Pro $20/mo: Opus 4.6

내 코드의 Next.js 미들웨어 부분을 분석해서
실전 분석 + 개선 우선순위를 알려줘.
ChatGPT

무료: GPT-5.5 / Plus $20/mo: GPT-5.5 Pro

Next.js 미들웨어 관련 인기 라이브러리/패턴 5개를
비교 분석해서 패턴 추출를 알려줘.
Gemini

무료: 2.5 Flash / Pro $19.99/mo: 3.1 Pro

내 프로젝트 전체에서 Next.js 미들웨어
최적화 가능 위치를 보고해줘.
Grok

무료: Grok 4.1 / SuperGrok $30/mo

2026년 한국 프론트엔드 시장의
Next.js 미들웨어 트렌드를 솔직히 알려줘.

⭐ 이것만 기억하세요
미들웨어 실전: 인증 + 지역화 + A/B 테스트 이 3가지만 확실히 잡으세요
1.미들웨어 = 모든 요청 전에 실행 — 인증/i18n/A/B 표준
2.NextResponse.redirect / rewrite / next() 3가지 응답
3.matcher로 적용 경로 제어 — 정적 파일 제외


공유하기
진행도 16 / 90