React Core 라이브러리 분리와 Akamai CDN 캐시 적용
React 프로젝트의 성능 최적화를 위해 React Core 라이브러리를 react-vendor라는 별도 번들로 분리했습니다.

React Core 라이브러리를 왜 분리했을까?
React Core 계열 라이브러리를 별도의 react-vendor 청크로 분리했습니다.
// vite.config.ts
build: {
rollupOptions: {
output: {
// react 코어 계열만 별도 vendor 청크로 분리
manualChunks(id) {
if (/node_modules\/(react|react-dom|scheduler|react-router|react-router-dom)\//.test(id))
{
return 'react-vendor';
}
},
},
},
},React Core 라이브러리를 별도로 분리한 이유는 애플리케이션 코드에 비해 변경 빈도가 낮기 때문입니다.
기존에는 애플리케이션 코드와 React Core 라이브러리가 하나의 JS 파일로 함께 번들링 됐습니다.
예를 들어 다음과 같은 구조라고 가정해 보겠습니다.
index-ABC123.js이후 애플리케이션 코드가 일부 변경되면 전체 번들이 다시 빌드되면서 파일 hash도 변경됩니다.
index-GHI789.js이때 실제로 React Core 라이브러리에는 변경 사항이 없더라도 하나의 파일로 묶여 있기 때문에 React 라이브러리까지 새로운 파일로 다시 받아야 합니다.
반면 React Core 라이브러리를 별도의 파일로 분리하면 다음과 같은 구조가 됩니다.
index-ABC123.js
react-vendor-XYZ789.js이후 애플리케이션 코드가 변경되어 새로운 빌드가 생성되더라도 React Core 라이브러리가 변경되지 않았다면 react-vendor의 hash는 그대로 유지됩니다.
index-GHI789.js ← 변경
react-vendor-XYZ789.js ← 동일즉, 애플리케이션 코드가 변경되더라도 변경되지 않은 React Core 라이브러리는 동일한 URL을 계속 사용할 수 있습니다.
이처럼 변경 빈도가 낮은 라이브러리를 별도의 파일로 분리하면 CDN 캐시와 함께 활용했을 때 효과를 기대할 수 있습니다.
예를 들어 이전 배포에서 react-vendor-XYZ789.js가 CDN에 캐시 되어 있다면, 다음 배포에서 애플리케이션 코드만 변경된 경우 해당 파일을 다시 Origin에서 받아올 필요 없이 기존 CDN 캐시를 재사용할 수 있습니다.
번들 분리 결과
실제 빌드 결과 gzip 기준 react-vendor는 약 72.94KB, index는 약 129.13KB였습니다.
따라서 두 파일의 초기 JS 전송 용량은 gzip 기준 약 202.07KB이며, 이 중 react-vendor가 약 36%를 차지하고 있었습니다.
따라서 이번 작업에서는 React Core 라이브러리를 별도 번들로 분리하고, 해당 정적 리소스를 Akamai CDN에서 캐시 할 수 있도록 구성하는 것을 목표로 했습니다.
그런데 Akamai 캐시가 적용되지 않고 있었다
react-vendor를 분리한 이후 운영 환경에서 정적 리소스의 Akamai 캐시 상태를 확인했습니다.
확인 결과 React 정적 리소스가 Akamai Edge Cache에 정상적으로 캐시되지 않고 있었습니다.
Akamai 디버그 헤더를 통해 확인한 결과 다음과 같이 캐시할 수 없는 상태였습니다.
X-Check-Cacheable: NO
정적 JS 파일임에도 캐시되지 않는 원인을 확인하기 위해 Origin에서 내려오는 Response Header와 정적 리소스 요청 처리 과정을 확인했습니다.
그 결과 캐시 적용을 방해하고 있던 두 가지 요소를 확인할 수 있었습니다.
적용되지 않았던 이유
1. Vary 헤더
먼저 React 정적 리소스 응답에 다음과 같은 Vary 헤더가 포함되어 있었습니다.
Vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers원인을 확인하는 과정에서 Spring MVC의 정적 리소스 처리 과정에서 CORS 관련 로직이 동작하고 있다는 것을 확인했습니다.
정적 리소스 요청도 CORS 처리 대상에 포함되면서 실제 CORS 요청 여부와 관계없이 Vary 헤더가 응답에 추가되고 있었습니다.
하지만 해당 정적 리소스는 요청 Origin에 따라 응답 내용이 달라지는 구조가 아니었습니다.
즉, 실제 캐싱에 필요하지 않은 Vary 헤더가 추가되고 있었고, 이로 인해 Akamai에서 캐시가 적용되지 않는 문제가 발생하고 있었습니다.
Akamai 공식 문서에서도 Vary 헤더가 캐싱에 영향을 줄 수 있으며, 필요한 경우 해당 헤더를 제거하는 방법을 안내하고 있습니다.

2. Set-Cookie
또 다른 문제는 정적 리소스 응답에 Set-Cookie가 포함되고 있다는 점이었습니다.
React 정적 리소스를 요청하는 과정에서 공통적인 쿠키 처리 로직이 함께 동작하면서 다음과 같은 Set-Cookie가 응답에 포함되고 있었습니다.
Set-Cookie: INFO=...확인 결과 해당 쿠키는 정적 JS/CSS 리소스를 제공하는 데에는 필요하지 않은 값이었습니다.
따라서 정적 리소스 요청에서는 해당 쿠키가 생성되지 않도록 처리했습니다.
이처럼 Vary와 Set-Cookie는 React 정적 리소스 자체의 문제가 아니라, Origin에서 정적 리소스 응답에 불필요한 응답 헤더가 포함되면서 CDN 캐시 적용을 방해하고 있던 문제였습니다.
해결 방법
캐시가 적용되지 않는 원인을 확인한 후 각각의 문제를 해결하고, 이후 Akamai에 정적 리소스 캐싱 정책을 적용했습니다.
1. Vary 헤더 제거
Vary 헤더는 기존 애플리케이션의 공통 로직을 직접 수정할 수도 있었지만, 공통 로직을 변경할 경우 다른 요청에 영향을 줄 가능성이 있었습니다.
따라서 기존 애플리케이션의 동작에 미치는 영향을 최소화하기 위해 Akamai에서 React 정적 리소스에 한정하여Vary헤더를 제거하는 방식으로 처리했습니다.
2. Set-Cookie 처리
Set-Cookie는 Origin에서 정적 리소스 요청을 별도로 처리하여 불필요한 쿠키가 생성되지 않도록 수정했습니다.
이를 통해 정적 JS/CSS 응답에서 불필요한 Set-Cookie가 포함되지 않도록 했습니다.
3. Akamai CDN 캐싱 적용
Vary와 Set-Cookie 문제를 해결한 이후 실제 CDN 캐시 적용을 위해 Akamai에 React 정적 리소스 경로의 캐싱 정책 적용을 요청했습니다.
각 서비스에서 제공하는 React 정적 리소스가 Akamai Edge Cache를 사용할 수 있도록 캐싱 정책을 적용했습니다.
적용 결과

캐싱 정책 적용 이후 동일한 정적 리소스를 반복 요청하여 Akamai Edge Cache 동작 여부를 확인했습니다.
확인 결과 다음과 같이 정상적으로 캐시가 적용되는 것을 확인할 수 있었습니다.
cache-control: max-age=2592000
server-timing: Hit from child또한 반복 요청 시 Akamai Edge에서 TCP_HIT이 발생하는 것을 확인하여, 정적 리소스가 Origin이 아닌 CDN Edge Cache에서 제공되고 있음을 확인했습니다.
이를 통해 애플리케이션 코드가 변경되어 index 번들의 hash가 변경되더라도 변경되지 않은 react-vendor는 기존 CDN 캐시를 재사용할 수 있는 구조를 구성했습니다.
느낀 점
처음에는 react-vendor를 분리한 목적 중 하나인 CDN 캐싱 효과를 확인하려 했지만, 실제 운영 환경에서는 캐시가 정상적으로 적용되지 않고 있었습니다.
원인을 찾아가는 과정에서 Vary와 Set-Cookie가 CDN 캐싱에 영향을 줄 수 있다는 점을 새롭게 알게 되었고, 직접 원인을 분석하고 해결하면서 CDN과 캐싱에 대한 이해도 한층 높일 수 있었습니다.
결과적으로 처음 목표했던 CDN 캐싱까지 정상적으로 적용할 수 있었고, 이번 작업을 통해 성능 최적화뿐만 아니라 새로운 기술을 배우고 한 단계 더 성장할 수 있는 계기가 된 것 같습니다.
- #Akamai
- #autowini
- #Frontend
- #JavaScript
- #manualChunks
- #react
- #react-vendor
- #ViTE
- #성능 최적화
- #캐싱