CDN 환경에서의 Next.js 라우팅과 CORS 에러
네이버파이낸셜 금융FE팀의 장기원입니다.
본 글은 Next.js 12.2.5 기준으로 작성되었습니다.
TL;DR
이슈
assetPrefix를 설정하여 CDN을 적용한 상태에서 next/router를 사용할 때, CSS 파일에서 CORS 에러가 발생하면서 페이지를 이동할 때마다 모든 데이터를 다시 받아오는 현상이 발생했습니다. 이는 성능저하를 야기합니다.
해결방안
CDN 서버에서 응답 헤더에 Access-Control-Allow-Origin
origin 등록합니다.
이슈 확인 전 배경지식
이슈를 확인하기 전에 이해를 돕기위해 먼저 Next.js 빌드 파일의 구성, CDN 설정 방식 마지막으로 CDN 적용 시 Next.js 의 라우팅 동작 방식에 대해 확인해보겠습니다.
1. 번들링(bundling)
Next.js 프로젝트의 빌드 파일은 번들로 나누어져 있습니다. 이는 빠른 페이지 로딩을 위해 사용됩니다. 아래는 예시입니다.
chunk 하위에는 js 파일, css 하위에는 CSS 파일, build id로 구성된 폴더 하위에는 manifest 파일이 있습니다. 여기서 라우팅을 위한 설정 파일인 manifest를 살펴보겠습니다.
manifest는 홈페이지를 구성하는 정보를 mapping 해줍니다. 하나의 페이지를 구성하는 파일에 대한 정보를 가지고 있으며, 이를 통해 클라이언트는 효율적인 데이터 요청을 할 수 있습니다.
참고로 Next.js로 구성된 홈페이지의 네트워크탭을 보면 최초 접속 시 manifest를 호출하는 것을 알 수 있습니다.
2. CDN 설정
Next.js에서 기본적으로 제공하는 기능을 이용합니다. next.config.js 에서 assetPrefix 설정을 하여 정적파일을 CDN에서 가져올 수 있습니다.
3. next/router with CDN
assetPrefix을 통해 CDN 설정이 완료된 상태에서 next/router에 페이지 이동 요청을 하면, 이동할 페이지에 필요한 정적 파일을 CDN 서버에 요청합니다. 위의 manifest 예시
를 보면 /example/fruit/apple
페이지로 이동하기 위해서 'static/chunks/1.js', 'static/css/1.css', 'static/chunks/2.js', 'static/css/2.css'
파일이 필요합니다.
그렇다면 위의 설정으로 구성되어있는 실제 서비스에서 확인해보겠습니다. 아래와 같은 유저 시나리오에 따라 진행될 때, 크롬 개발자도구의 performance를 통해 내부 동작을 확인해 보겠습니다.
- 페이지 이동 버튼 클릭
- 페이지 이동
위의 시나리오 실행 시 CDN 설정이 된 라우팅의 관점에서 중요한 동작을 보면 아래와 같습니다.
- 페이지 이동 버튼 클릭
a. 클릭 이벤트 발생
b. next/router push 메소드 호출
c. manifest를 기반으로 CDN 서버에 정적파일 요청 - 페이지 이동
아래의 performance 결과에서 직접 확인할 수 있습니다.
이슈: 페이지 이동 시 CORS 에러!?
드디어 이슈를 확인할 수 있겠네요! 페이지를 이동할 때, CSS파일에서 CORS 에러가 나오는 이슈가 발생했습니다. 여러 정적 파일 중 CSS파일에서만 에러가 발생하지만 이에 따라 페이지를 재호출하면서 성능저하가 발생합니다. 여기서 이상한 점은 처음 페이지를 접속할 때는 CSS파일에서 에러가 발생하지 않는 점이었습니다.
해당 상황을 크롬 개발자도구의 performance에서 분석해보면 다음과 같습니다.
- 페이지 이동 버튼 클릭
a. 클릭 이벤트 발생
b. next/router push 메소드 호출
c. manifest을 기반으로 CDN 서버에 정적파일 요청
-> CSS파일 CORS 에러 발생! - 페이지 이동(X) -> 페이지 재호출
여기서 재호출은 client side routering을 다시 시도하는것이 아니라 모든 데이터를 다시 불러오는 것을 의미합니다. (새로고침으로 생각해도 될 것 같습니다.)
이러한 원인은 Next.js 내부에서 페이지를 이동할 때 JS 파일은 script 태그를 사용하는 반면 css는 fetch를 이용합니다. 최초에 페이지에 접속할 때는 CSS파일은 link 태그, JS파일은 script 태그로 호출하여 CORS 에러가 발생하지 않았습니다.
참고로 아래 태그는 기본적으로 CORS를 포함한 요청을 할 수 없습니다. (crossOrigin 속성을 추가하면 가능합니다.)
<audio /> , <img /> , <link /> , <script />, <video />
해결방안
장황한 설명이 있었지만 해결방법은 간단합니다. CDN 서버에서 정적파일을 요청하는 origin을 CORS 허용 목록에 추가해주면됩니다. Next.js 설정에서 해결할 수 있는 방법은 지금 버전에서는 없는 것 같습니다. 비슷한 상황의 버그가 Next.js github의 이슈로 제보되어있는데 아직 해결은 안되었습니다.
참고문서
- Next.js 공식문서 — https://nextjs.org/docs/getting-started
- Next.js 이슈 — https://github.com/vercel/next.js/issues/34225
- HTML attribute: crossorigin — https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin
- Same-origin policy — https://web.dev/same-origin-policy/#what-is-permitted-and-what-is-blocked