Time to First Byte의 캐시 지속 시간(Cache Duration) 하위 요소 줄이기
캐시 지속 시간은 서비스 워커 및 브라우저 캐시 조회 시간을 측정합니다. TTFB를 줄이기 위한 캐싱 전략, Cache-Control 헤더, bfcache 및 서비스 워커 최적화 방법에 대해 알아보세요.

Time to First Byte의 캐시 지속 시간 줄이기
이 문서는 Time to First Byte (TTFB) 가이드의 일부입니다. 캐시 지속 시간(Cache duration)은 TTFB의 두 번째 하위 요소이며 브라우저가 일치하는 응답을 찾기 위해 로컬 캐시와 활성화된 서비스 워커를 확인하는 데 소요하는 시간을 나타냅니다. 캐시 지속 시간이 주요 병목 현상이 되는 경우는 드물지만, 서비스 워커를 사용하거나 브라우저 캐싱 전략에 크게 의존하는 사이트에서는 이를 이해하는 것이 중요합니다.
Time to First Byte(TTFB)는 다음 하위 요소로 나눌 수 있습니다.
- 대기 + 리디렉션 (또는 대기 시간)
- 워커 + 캐시 (또는 캐시 지속 시간)
- DNS (또는 DNS 지속 시간)
- 연결 (또는 연결 지속 시간)
- 요청 (또는 요청 지속 시간)
Time to First Byte를 최적화하고 싶으신가요? 이 문서에서는 Time to First Byte의 캐시 지속 시간 부분에 대해 다룹니다. Time to First Byte를 이해하거나 문제를 해결하고 싶지만 캐시 지속 시간이 무엇을 의미하는지 모른다면, 이 문서를 시작하기 전에 Time to First Byte란 무엇인가와 Time to First Byte 문제 해결 및 식별을 먼저 읽어보세요.
참고: 일반적으로 Time to First Byte에서 캐시 지속 시간 부분은 병목 현상의 원인이 아닙니다. 따라서 a) 서비스 워커를 사용 중이거나 b) 저와 같은 페이지 속도(pagespeed) 열성 팬인 경우에만 계속 읽으시길 권장합니다.
일반적으로 Time to First Byte의 캐시 지속 시간 하위 요소는 병목이 아니며 10~20ms 이내에 완료됩니다. 서비스 워커를 사용할 때의 허용 가능한 시간은 60ms 미만입니다.
Table of Contents!
서비스 워커가 Time to First Byte에 미치는 영향은 무엇일까요?
서비스 워커는 Time to First Byte (TTFB)에 긍정적인 영향과 부정적인 영향을 모두 미칠 수 있지만, 이는 서비스 워커를 사용하는 웹사이트에만 해당됩니다.
서비스 워커가 TTFB에 미치는 영향은 다음과 같습니다.
서비스 워커 시작 시간으로 인한 TTFB 지연: workerStart 값은 요청된 리소스에 서비스 워커가 있는 경우 서비스 워커가 시작되는 데 걸리는 시간을 나타냅니다. 이 시작 시간은 TTFB 계산에 포함됩니다. 종료된 상태에서 서비스 워커를 시작하거나 재개해야 하는 경우, 초기 응답 시간에 지연이 추가되어 TTFB가 늘어날 수 있습니다.
캐싱을 통한 TTFB 향상: stale-while-revalidate와 같은 캐싱 전략을 사용하면, 서비스 워커는 캐시가 존재하는 경우 캐시에서 직접 콘텐츠를 제공할 수 있습니다. 브라우저가 서버 응답을 기다릴 필요가 없으므로 자주 액세스하는 리소스에 대해 거의 즉각적인 TTFB를 제공합니다. 이 전략은 최신 정보가 필요한 동적으로 생성된 콘텐츠보다 자주 업데이트되지 않는 콘텐츠에 가장 적합합니다.
app-shell을 통한 TTFB 향상: 클라이언트에서 렌더링되는 애플리케이션의 경우 앱 셸(app shell) 모델(서비스 워커가 캐시에서 기본 페이지 구조를 제공하고 나중에 동적으로 콘텐츠를 로드하는 방식)을 사용하면 해당 기본 구조의 TTFB를 거의 0으로 줄일 수 있습니다.
최적화되지 않은 코드로 인한 TTFB 지연: 복잡하고 비효율적인 서비스 워커는 캐시 조회 프로세스를 느리게 만들고 이로 인해 TTFB도 함께 느려질 수 있습니다.
서비스 워커는 페이지 속도에 악영향을 미치나요? 아니요. (일반적으로) 전혀 나쁘지 않습니다. 시작 시간으로 인해 초기에는 TTFB가 증가할 수 있지만, 네트워크 요청을 가로채고 캐싱을 관리하며 오프라인 지원을 제공하는 기능은 장기적으로(특히 재방문자의 경우) 엄청난 성능 향상을 가져올 수 있습니다.
서비스 워커 캐싱 전략
서비스 워커가 사용하는 캐싱 전략에 따라 속도와 최신성(freshness) 간의 균형이 결정됩니다. 각 전략은 TTFB의 캐시 지속 시간 하위 요소에 서로 다른 영향을 미칩니다.
Cache-First (네트워크로 폴백하는 캐시)
서비스 워커가 캐시를 먼저 확인합니다. 캐시된 응답이 있으면 즉시 반환합니다. 그렇지 않으면 요청을 네트워크로 보냅니다. 이 전략은 캐시된 리소스에 대해 가장 빠른 TTFB를 제공하지만 오래된 콘텐츠(stale content)를 제공할 위험이 있습니다.
최적의 사용처: 정적 에셋, 이미지, 글꼴 및 자주 변경되지 않는 콘텐츠.
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((cachedResponse) => {
if (cachedResponse) {
return cachedResponse;
}
return fetch(event.request).then((networkResponse) => {
const cache = await caches.open('v1');
cache.put(event.request, networkResponse.clone());
return networkResponse;
});
})
);
});
Network-First (캐시로 폴백하는 네트워크)
서비스 워커가 항상 네트워크를 먼저 시도합니다. 네트워크 요청이 실패하면(예: 오프라인 상태) 캐시된 버전이 제공됩니다. 이 전략은 네트워크를 사용할 수 있을 때 최신 콘텐츠를 보장하지만 온라인 사용자의 TTFB를 줄여주지는 않습니다.
최적의 사용처: API 응답, 자주 업데이트되는 콘텐츠 및 최신성이 중요한 페이지.
Stale-While-Revalidate
서비스 워커는 캐시된 버전을 즉시 반환(빠른 TTFB)하는 동시에 백그라운드에서 네트워크로부터 업데이트된 버전을 가져옵니다. 업데이트된 버전은 다음 방문 시 캐시된 복사본을 대체합니다. 이것은 종종 속도와 최신성 사이의 최상의 균형을 이룹니다.
최적의 사용처: 뉴스 기사, 블로그 게시물, 제품 목록 등 정기적으로 변경되지만 실시간 정확성이 요구되지 않는 콘텐츠.
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.open('v1').then((cache) => {
return cache.match(event.request).then((cachedResponse) => {
const fetchPromise = fetch(event.request).then((networkResponse) => {
cache.put(event.request, networkResponse.clone());
return networkResponse;
});
return cachedResponse || fetchPromise;
});
})
);
});
Cache-Control 헤더 구성
TTFB의 캐시 지속 시간 하위 요소는 특별히 서비스 워커 및 브라우저 캐시 조회에 소요된 시간을 측정하지만, 적절한 Cache-Control 헤더는 브라우저가 서버에 전혀 접속할 필요가 있는지 여부를 결정합니다. 올바른 캐시 헤더를 적용하면 재방문자에 대해 전체 TTFB를 효과적으로 우회할 수 있습니다.
다양한 리소스 유형에 대해 권장되는 Cache-Control 구성은 다음과 같습니다.
# HTML pages (always revalidate) Cache-Control: no-cache # Static assets with content hashing (cache forever) Cache-Control: public, max-age=31536000, immutable # Images without content hashing (cache for 1 week) Cache-Control: public, max-age=604800 # API responses (no caching) Cache-Control: no-store
주요 지시어(directives) 설명:
- no-cache: 브라우저는 캐시된 복사본을 사용하기 전에 서버에서 재검증(revalidate)을 거쳐야 합니다. 이는 "캐시하지 않음"을 의미하는 것이 아니라 "항상 먼저 확인"을 의미합니다.
- no-store: 브라우저가 응답을 전혀 캐시하지 않아야 합니다. 민감하거나 매우 동적인 콘텐츠에 사용합니다.
- max-age: 재검증 없이 캐시에서 응답을 제공할 수 있는 시간(초)입니다.
- immutable: 브라우저에 해당 리소스가 절대 변경되지 않음을 알립니다. 정적 에셋의 경우 콘텐츠 해시가 적용된 파일명(예:
style.a1b2c3.css)과 결합하여 사용하세요. - public: 공유 캐시(CDN, 프록시)에서 응답을 캐시할 수 있도록 허용합니다. 사용자별 콘텐츠에는 private를 사용하세요.
Cloudflare와 같은 CDN을 사용하는 경우 엣지 캐싱(edge caching) 규칙도 구성할 수 있습니다. 자세한 지침은 성능을 위한 Cloudflare 구성 가이드를 참조하세요.
Back/Forward Cache (bfcache)
Back/forward cache (bfcache)는 사용자가 페이지를 벗어날 때 페이지의 완전한 스냅샷을 메모리에 저장하는 브라우저 최적화 기능입니다. 사용자가 뒤로 가기 또는 앞으로 가기 버튼을 누르면 페이지가 메모리에서 즉시 복원되어 TTFB(및 기타 모든 로딩 지표)가 완전히 제거됩니다.
bfcache에서 제공되는 페이지는 네트워크 요청이 전혀 발생하지 않으므로 RUM 데이터에서 0밀리초의 TTFB를 보여줍니다. 브라우저는 단순히 메모리 내 스냅샷에서 페이지를 복원하기만 합니다.
페이지가 bfcache 대상이 되도록 하려면 다음 사항을 준수하세요.
unload이벤트 리스너를 사용하지 마세요(대신pagehide를 사용하세요).- HTML 문서에
Cache-Control: no-store를 사용하지 마세요. - 페이지가 숨겨질 때 열려 있는 IndexedDB 연결을 모두 닫으세요.
- 활성 WebSocket 또는 WebRTC 연결을 유지하지 마세요(
pagehide이벤트에서 닫으세요).
Chrome DevTools의 Application 탭 아래 "Back/Forward Cache" 섹션에서 bfcache 적용 자격을 테스트할 수 있습니다. Chrome은 페이지가 bfcache에 적합하지 않은 이유를 나열해 줍니다.
뒤로/앞으로 탐색 패턴이 상당한 사이트(예: 전자상거래 카테고리 및 제품 페이지, 검색 결과 페이지)의 경우, bfcache는 많은 탐색 부분에서 체감 TTFB를 극적으로 향상시킬 수 있습니다.
Time to First Byte의 캐시 지속 시간 하위 요소 측정 방법
다음 스니펫을 사용하여 Time to First Byte의 캐시 지속 시간 하위 요소를 측정할 수 있습니다.
new PerformanceObserver((entryList) => {
const [navigationEntry] = entryList.getEntriesByType('navigation');
// get the relevant timestamps
const activationStart = navigationEntry.activationStart || 0;
const waitEnd = Math.max(
(navigationEntry.workerStart || navigationEntry.fetchStart) -
activationStart,
0,
);
const dnsStart = Math.max(
navigationEntry.domainLookupStart - activationStart,
0,
);
// calculate the cache duration
const cacheDuration = dnsStart - waitEnd;
// log the results
console.log('%cTTFB cacheDuration', 'color: blue; font-weight: bold;');
console.log(cacheDuration);
}).observe({
type: 'navigation',
buffered: true
});
높은 캐시 지속 시간으로 인한 TTFB 문제 찾는 방법
캐시 지속 시간이 실제 사용자 경험에 미치는 영향을 파악하려면 CoreDash와 같은 RUM 도구를 사용해야 합니다. Real User Monitoring을 사용하면 Core Web Vitals를 매우 자세히 추적할 수 있습니다.
CoreDash에서 "Time to First Byte"로 이동하여 세부 정보를 확인하기만 하면 됩니다. 이렇게 하면 TTFB가 모든 하위 요소로 분석되어 나타나며 75번째 백분위수에서 cacheDuration이 정확히 얼마나 걸리는지 알 수 있습니다.

서비스 워커 캐시 시간 영향을 최소화하는 방법
서비스 워커 사용 시 TTFB를 최적화하려면 다음과 같이 하세요.
- 서비스 워커 스크립트의 복잡성을 최소화하여 시작 시간을 줄입니다.
- 서비스 워커 내에 효율적인 캐싱 전략을 구현합니다(탐색 요청에는 stale-while-revalidate를 선호합니다).
- 서비스 워커 설치 중에 중요한 리소스를 사전 캐싱(pre-caching)하는 것을 고려하세요.
- 서비스 워커가 사이트의 TTFB에 미치는 영향을 정기적으로 모니터링하고 분석하세요.
navigation preload를 사용하여 네트워크 요청이 서비스 워커 시작과 병렬로 이루어지도록 하세요. 이렇게 하면 서비스 워커 부팅 시간이 TTFB에 추가되는 것을 방지할 수 있습니다.
다음과 같이 서비스 워커에서 navigation preload를 활성화할 수 있습니다.
self.addEventListener('activate', (event) => {
event.waitUntil(
(async () => {
if (self.registration.navigationPreload) {
await self.registration.navigationPreload.enable();
}
})()
);
});
구현을 제대로 하면 서비스 워커는 재방문자의 사이트 속도를 높여줍니다. 반면 잘못 구현하면 모든 페이지 로드마다 부팅 비용을 지불하게 됩니다.
추가 읽기: 최적화 가이드
관련 가이드:
- 103 Early Hints: 서버 응답이 준비되기 전에 주요 리소스 로드를 시작하여 캐싱 전략을 보완합니다.
- 성능을 위한 Cloudflare 구성: CDN 엣지 캐싱, 캐시 규칙 및 페이지 규칙을 설정하여 엣지 수준에서 캐싱 전략을 최적화합니다.
TTFB 하위 요소: 전체 가이드
캐시 지속 시간은 TTFB의 5가지 하위 요소 중 하나입니다. 전체적인 그림을 이해하려면 다른 하위 요소를 탐색해 보세요.
- TTFB 문제 식별 및 해결: 모든 TTFB 최적화를 위한 진단의 출발점입니다.
- 대기 지속 시간(Waiting Duration): 리디렉션, 브라우저 큐잉 및 HSTS 최적화.
- DNS 지속 시간(DNS Duration): DNS 공급자 선택, TTL 구성 및 dns-prefetch.
- 연결 지속 시간(Connection Duration): TCP 핸드셰이크, TLS 최적화, HTTP/3 및 preconnect.
- 요청 지속 시간(Request Duration): 서버 처리 시간, 데이터베이스 쿼리 및 백엔드 최적화.
진짜 뭐가 느린지부터.
실제 필드 데이터로 Critical Rendering Path를 뜯어봅니다. Lighthouse 리포트가 아니라 우선순위가 매겨진 수정 목록을 드립니다.
감사 받기
