middleware.ts는 왜 proxy.ts가 되었는가
이름이 바뀐 이유는 이름이 잘못됐었기 때문이다
middleware라는 이름은 처음부터 오해를 부르기 좋았습니다. Express나 Koa를 써본 사람에게 "미들웨어"는 요청-응답 체인 중간에 끼워 넣는 함수를 뜻합니다. 그런데 Next.js의 middleware는 라우트 핸들러 "사이"에 있는 게 아니라, 애플리케이션에 도달하기 전 네트워크 경계에 있었습니다. 이름과 실제 위치가 어긋나 있었던 거죠.
Next.js 16은 이 어긋남을 이름으로 고칩니다. middleware.ts → proxy.ts, export function middleware() → export function proxy(). 리버스 프록시가 하는 일 — URL 재작성, 헤더 주입, 지역 기반 라우팅 — 이 이 파일의 진짜 역할이라는 걸 이름에 그대로 반영한 겁니다.
// Before: middleware.ts
export function middleware(request: NextRequest) {
// ...
}
// After: proxy.ts
export function proxy(request: NextRequest) {
// ...
}파일명과 export 이름만 보면 기계적 치환처럼 보입니다. 실제로 next-codemod가 이 부분은 자동으로 처리해줍니다. 문제는 그 다음입니다.
런타임이 바뀌었다 — 그리고 이건 자동으로 안 고쳐진다
기존 middleware는 기본적으로 Edge 런타임에서 돌았습니다. Node.js API(파일 시스템, 일부 네이티브 모듈)를 못 쓰는 대신 지리적으로 가까운 곳에서 빠르게 실행된다는 게 장점이었죠. proxy는 다릅니다. 기본 런타임이 Node.js이고, 이건 설정으로 바꿀 수 없습니다. Edge에서만 되던 초저지연을 기대하고 설계한 라우팅 로직이 있다면 이 지점에서 가정이 깨집니다.
반대로 좋아지는 점도 있습니다. Edge 런타임의 제약(일부 npm 패키지 미지원, Buffer 없음 등) 때문에 우회 코드를 넣었던 프로젝트라면 그 우회가 이제 필요 없어질 수 있습니다. 다만 "필요 없어질 수 있다"이지 "필요 없어진다"가 아닙니다 — Edge 전용으로 배포되는 인프라(Vercel Edge Network의 일부 기능)에 의존하고 있었다면 별개로 확인해야 합니다.
인증 로직을 proxy에 두면 안 되는 이유
공식 문서는 인증 검증을 proxy 밖으로, 레이아웃이나 서버 액션으로 옮기라고 권합니다. 저는 이 권고에 동의합니다만 이유를 좀 더 명확히 하고 싶습니다.
proxy(그리고 예전의 middleware)는 모든 요청 앞에 섭니다. 정적 자산 요청도, 로그인 안 한 사용자의 방문도, <Link>의 프리페치도 전부 여길 지나갑니다. 여기서 데이터베이스 조회나 외부 인증 서버 왕복을 하면, 그 비용이 사이트의 모든 네비게이션에 고정비로 붙습니다. 인증이 필요 없는 페이지를 볼 때도, 프리페치가 백그라운드에서 도는 동안에도 그 비용을 냅니다.
직접 확인해본 결과, Next.js 16의 프리페치 요청은 next-router-prefetch나 rsc 같은 내부 헤더가 proxy에 도달하기 전에 제거됩니다. 즉 "이 요청이 프리페치인지"를 proxy 코드에서 헤더로 구분하는 방법은 없습니다. "프리페치일 때만 무거운 검증을 건너뛴다"는 식의 코드를 짜고 싶어질 수 있는데, 그 분기는 절대 실행되지 않는 죽은 코드가 됩니다.
sequenceDiagram participant Browser participant Proxy as proxy.ts participant App as 서버 컴포넌트/서버 액션 Browser->>Proxy: 모든 요청 (정적 자산, 프리페치, API 포함) Note over Proxy: 여기서 무거운 검증을 하면<br/>모든 네비게이션이 그 비용을 낸다 Proxy->>App: 통과 App->>App: 이 화면이 실제로 인증을 요구하는가?<br/>여기서만 검증하면 된다
그래서 실전 원칙은 이렇습니다. proxy는 쿠키의 존재 여부 같은 값싼 판단만 하고(세션 쿠키가 아예 없으면 통과, 만료가 임박했으면 갱신), 신원이 실제로 필요한 화면은 그 화면 자신이 검증하게 둡니다. "이 요청이 인증됐는가"를 proxy 하나가 전부 책임지려는 설계는, RLS나 서버 단 검증이 이미 있는 프로젝트에서는 중복 비용일 뿐입니다.