DAY 08작업 8일째
페이지 전환이 느리던 이유를 찾았다
한국에서 들어와도 먼 해외 거점에서 응답하는 데다 CSS · JS · 그림을 페이지마다 다시 확인하고 있어서, 내용 해시 주소와 긴 캐시, 미리 받기로 왕복을 줄였다.
정적 페이지인데 글 목록에서 소개로 넘어갈 때마다 한 박자씩 멈췄다. 같은 주소를 여러 번 재 보니 첫 응답까지 0.45초 안팎이 걸렸고, 가끔은 1초에서 5초까지 튀었다. 파일 크기는 30KB 남짓이라 무게 문제는 아니었다.
응답이 바다 건너에서 오고 있었다
응답을 준 거점을 확인하니 접속 위치는 한국인데 거점은 로스앤젤레스였다. 무료 요금제로 한국에서 접속하면 통신사와 CDN 사이의 연결 사정 때문에 서울 대신 해외 거점으로 가는 일이 흔하다. 거점을 고르는 설정은 없어서 이건 바꿀 수 없는 조건으로 두었다. 연결 한 번에 왕복 0.14초가 든다는 걸 전제로, 왕복 횟수를 줄이는 쪽으로 방향을 잡았다.
페이지마다 파일을 다시 묻고 있었다
응답 헤더를 보니 CSS 세 개, JS, 그림까지 모두 max-age=0, must-revalidate였다. 브라우저가 파일을 갖고 있어도 페이지를 옮길 때마다 바뀌었는지 거점에 다시 물어본다. 화면을 그리려면 CSS 확인이 끝나야 해서, 그 왕복만큼 전환이 늦어졌다.
파일을 오래 캐시하면 고친 디자인이 바로 안 보일 수 있다. 그래서 주소에 내용 해시를 붙였다. 빌드할 때 파일 내용으로 해시를 만들고, 템플릿은 그 해시를 붙인 주소로 부른다. 내용이 바뀌면 주소가 바뀌니 브라우저는 새 파일을 받는다.
export const asset = (ctx, p) => (ctx.assetVersion?.[p] ? `${p}?v=${ctx.assetVersion[p]}` : p);
응답할 때는 해시가 붙은 CSS · JS에 1년 캐시를 걸고, 올린 그림에는 한 달 캐시를 걸었다. 그림은 올릴 때 겹치지 않는 이름으로 저장돼서 같은 이름이 다른 그림으로 바뀌는 일이 거의 없다. 페이지 HTML은 그대로 매번 확인해서 새 글을 발행하면 바로 보인다.
if (url.searchParams.has('v') && /\.(css|js)$/.test(url.pathname)) return 'public, max-age=31536000, immutable';
if (/^\/(images|og)\//.test(url.pathname)) return 'public, max-age=2592000';
남은 왕복 하나는 미리 당겨 온다
HTML 한 번의 왕복은 남는다. 이건 링크에 마우스를 올리는 순간 다음 페이지를 미리 받아 두는 것으로 가렸다. 브라우저의 미리 받기 규칙(Speculation Rules)을 페이지마다 넣었고, 구독 주소(RSS)는 뺐다. 미리 렌더링까지 하면 방문 통계가 실제로 열지 않은 페이지까지 셀 수 있어서, HTML만 받아 두는 prefetch로 정했다.
바꾼 뒤 글 목록에서 소개로 옮겨 보니 첫 응답이 0.15초, 로딩 완료가 0.21초였고, CSS와 JS 네 개는 모두 브라우저 캐시에서 바로 나왔다.
감수한 것
처음 들어오는 방문자는 여전히 해외 거점까지 왕복해서 파일을 받는다. 미리 받기는 Chrome · Edge 계열에서만 동작하고, 다른 브라우저에서는 HTML 왕복이 그대로 남는다. 그림을 같은 이름으로 바꿔 올리면 한 달 동안 예전 그림이 보일 수 있어서, 그림은 늘 새 이름으로 올린다.
AI(Claude Code)가 작업 기록을 바탕으로 쓰고, 검토를 거쳐 올린 글이에요. 어떻게 쓰이나요 →