로컬 환경에서 velog.io/////kina94와 같이 중첩된 슬래시로 라우팅 시 자동으로 velog.io/kina94로 리디렉션됐으나 ec2 배포 이후에는 리디렉션이 되지 않고, React DevTools의 컴포넌트 트리도 로딩되지 않고, 하이드레이션도 안 돼서 페이지가 먹통이 됨.
//error message
Error: invariant: invalid relative URL, router received //proudct
개발/프로덕션 모두 동일한 NextNodeServer 및 requestHandler 기반 라우팅 로직을 사용하고 있으나 배포 시 Nginx를 타면서 문제가 발생함.
요약하자면 내 경우 잘못된 URL로 인해 클라이언트 라우터가 동작하지 않으면서 hydration 전제 조건이 무너진 케이스였음.
서버 렌더링 시 req.url을 기반으로 HTML을 생성하고, 클라이언트에서는 window.location을 기반으로 라우팅 상태를 초기화함.
사용자가 주소창에 입력:
브라우저 -> Nginx로 요청 전송:
Nginx에서 경로 정리 후 Next.js에 전달:
리디렉션
을 유도하지는 않음Next.js SSR:
브라우저는 여전히 URL: //products
Next.js 클라이언트 라우터:
// nextjs/.../router/utils
const globalBase = new URL(
typeof window === 'undefined' ? 'http://n' : getLocationOrigin()
)
const resolvedBase = base
? new URL(base, globalBase)
: url.startsWith('.')
? new URL(
typeof window === 'undefined' ? 'http://n' : window.location.href
)
: globalBase
const { pathname, searchParams, search, hash, href, origin } = new URL(
url,
resolvedBase
)
// 바로 이 부분!!!!!
if (origin !== globalBase.origin) {
throw new Error(`invariant: invalid relative URL, router received ${url}`)
}
로컬 환경: Next.js 서버가 중첩 슬래시를 직접 정규화하고 308 응답 후 리디렉션
배포 환경: Nginx가 요청을 먼저 받아 정규화를 해버렸지만 301이나 308 응답을 따로 보내지 않았기 때문에 리디렉션이 작동하지 않아서 이상한 URL을 해석하다 터짐
Nginx에서 301 또는 308 응답을 보내서 브라우저가 HTTP 응답을 받아 URL을 이동할 수 있도록 있도록 수정
// 예시 (페이지가 하나임)
// /etc/nginx/conf.d/...
if ($request_uri ~ "//+") {
return 308 $scheme://$host$uri;
}
// 기본 설정값
module.exports = {
trailingSlash: true,
}
2. 중복 슬래시 제거
// Next.js 내부 코드
// next.js/packages/next/src/shared/lib/utils
export function normalizeRepeatedSlashes(url: string) {
const urlParts = url.split('?')
const urlNoQuery = urlParts[0]
return (
urlNoQuery
// first we replace any non-encoded backslashes with forward
// then normalize repeated forward slashes
.replace(/\\/g, '/')
.replace(/\/\/+/g, '/') +
(urlParts[1] ? `?${urlParts.slice(1).join('?')}` : '')
)
}
hydration 시 React는 현재 브라우저 URL을 기준으로 라우팅/상태/컴포넌트 초기화를 함