이 Codelab에서는 Express 서빙 프레임워크를 실행하는 Node.js 기반 웹 서버에서 반환하는 HTTP 캐싱 헤더를 변경하는 방법을 보여줍니다. 또한 Chrome DevTools의 Network 패널을 사용하여 예상하는 캐싱 동작이 실제로 적용되는지 확인하는 방법도 보여줍니다.
샘플 프로젝트 익히기
샘플 프로젝트에서 작업할 주요 파일은 다음과 같습니다.
server.js에는 웹 앱의 콘텐츠를 제공하는 Node.js 코드가 포함되어 있습니다. Express를 사용하여 HTTP 요청 및 응답을 처리합니다. 특히express.static()는 공개 디렉터리의 모든 로컬 파일을 제공하는 데 사용되므로serve-static문서가 유용합니다.public/index.html는 웹 앱의 HTML입니다. 대부분의 HTML 파일과 마찬가지로 URL의 일부로 버전 관리 정보가 포함되지 않습니다.public/app.15261a07.js및public/style.391484cf.css는 웹 앱의 JavaScript 및 CSS 애셋입니다. 이러한 파일에는 각각 콘텐츠에 해당하는 해시가 URL에 포함되어 있습니다.index.html는 로드할 특정 버전 URL을 추적합니다.
HTML의 캐싱 헤더 구성
버전 관리 정보가 포함되지 않은 URL 요청에 응답할 때는 응답 메시지에 Cache-Control: no-cache를 추가해야 합니다. 이와 함께 Last-Modified 또는 ETag 중 하나를 추가 응답 헤더로 설정하는 것이 좋습니다. index.html는 이 카테고리에 속합니다. 이 작업은 두 단계로 나눌 수 있습니다.
먼저 Last-Modified 및 ETag 헤더는 etag 및 lastModified 구성 옵션으로 제어됩니다. 두 옵션 모두 실제로 모든 HTTP 응답에 대해 기본적으로 true로 설정되므로 현재 설정에서는 이 동작을 사용하기 위해 선택할 필요가 없습니다. 하지만 구성에서 명시적으로 지정할 수 있습니다.
둘째, Cache-Control: no-cache 헤더를 추가할 수 있어야 하지만 HTML 문서(이 경우 index.html)에만 추가할 수 있어야 합니다. 이 헤더를 조건부로 설정하는 가장 쉬운 방법은 커스텀 setHeaders function를 작성하고 그 안에서 수신 요청이 HTML 문서에 대한 것인지 확인하는 것입니다.
server.js의 정적 서빙 구성은 다음과 같이 시작됩니다.
app.use(express.static('public'));
- 위에서 설명한 변경사항을 적용하면 다음과 같이 표시됩니다.
app.use(express.static('public', {
etag: true, // Just being explicit about the default.
lastModified: true, // Just being explicit about the default.
setHeaders: (res, path) => {
if (path.endsWith('.html')) {
// All of the project's HTML files end in .html
res.setHeader('Cache-Control', 'no-cache');
}
},
}));
버전이 지정된 URL의 캐싱 헤더 구성
'fingerprint' 또는 버전 관리 정보가 포함되어 있고 콘텐츠가 변경되지 않는 URL 요청에 응답할 때는 응답에 Cache-Control: max-age=31536000를 추가합니다. app.15261a07.js 및 style.391484cf.css가 이 카테고리에 속합니다.
마지막 단계에서 사용된 setHeaders function를 기반으로 추가 로직을 추가하여 지정된 요청이 버전이 지정된 URL에 대한 요청인지 확인하고, 그렇다면 Cache-Control:
max-age=31536000 헤더를 추가할 수 있습니다.
이를 수행하는 가장 강력한 방법은 정규 표현식을 사용하여 요청되는 애셋이 해시가 속하는 것으로 알려진 특정 패턴과 일치하는지 확인하는 것입니다. 이 샘플 프로젝트의 경우 항상 숫자 0~9와 소문자 a~f(즉, 16진수 문자) 집합의 8자입니다. 해시는 항상 양쪽에 . 문자로 구분됩니다.
이러한 일반 규칙과 일치하는 정규 표현식은 new RegExp('\\.[0-9a-f]{8}\\.')로 표현할 수 있습니다.
setHeaders함수를 다음과 같이 수정합니다.
app.use(express.static('public', {
etag: true, // Just being explicit about the default.
lastModified: true, // Just being explicit about the default.
setHeaders: (res, path) => {
const hashRegExp = new RegExp('\\.[0-9a-f]{8}\\.');
if (path.endsWith('.html')) {
// All of the project's HTML files end in .html
res.setHeader('Cache-Control', 'no-cache');
} else if (hashRegExp.test(path)) {
// If the RegExp matched, then we have a versioned URL.
res.setHeader('Cache-Control', 'max-age=31536000');
}
},
}));
DevTools를 사용하여 새로운 동작 확인
정적 파일 서버를 수정했으므로 DevTools Network 패널을 연 상태에서 실시간 앱을 미리 보면서 올바른 헤더가 설정되어 있는지 확인할 수 있습니다.
- 열 헤더를 마우스 오른쪽 버튼으로 클릭하여 가장 관련성 높은 정보를 포함하도록 Network 패널에 표시되는 열을 맞춤설정합니다.

여기서 주의해야 할 열은 Name, Status, Cache-Control, ETag, Last-Modified입니다.
- DevTools를 Network 패널로 열고 페이지를 새로고침합니다.
페이지가 로드되면 Network 패널에 다음과 같은 항목이 표시됩니다.

첫 번째 행은 탐색한 HTML 문서입니다. 이는 Cache-Control: no-cache로 올바르게 제공됩니다. 이 요청의 HTTP 응답 상태는 304입니다. 즉, 브라우저는 캐시된 HTML을 즉시 사용하지 않고 대신 Last-Modified 및 ETag 정보를 사용하여 웹 서버에 HTTP 요청을 전송하여 이미 캐시에 있는 HTML에 업데이트가 있는지 확인했습니다. HTTP 304 응답은 업데이트된 HTML이 없음을 나타냅니다.
다음 두 행은 버전이 지정된 JavaScript 및 CSS 애셋입니다. Cache-Control: max-age=31536000로 제공되며 각각의 HTTP 상태는 200입니다.
사용된 구성으로 인해 Node.js 서버에 대한 실제 요청이 없으며 항목을 클릭하면 응답이 '(디스크 캐시에서)' 전송되었다는 사실을 포함한 추가 세부정보가 표시됩니다.

ETag 및 Last-Modified 열의 실제 값은 크게 중요하지 않습니다. 중요한 것은 설정되고 있는지 확인하는 것입니다.
요약
이 Codelab의 단계를 완료했으므로 이제 HTTP 캐시를 최적으로 사용하기 위해 Express를 사용하여 Node.js 기반 웹 서버에서 HTTP 응답 헤더를 구성하는 방법을 알게 되었습니다. 또한 Chrome DevTools의 Network 패널을 통해 예상되는 캐싱 동작이 사용되고 있는지 확인하는 데 필요한 단계도 있습니다.