Javascript

HTTP 캐싱 완전 정복: 웹 성능 향상을 위한 필수 가이드

드리프트2 2025. 3. 29. 21:07

HTTP 캐싱 완전 정복: 웹 성능 향상을 위한 필수 가이드

지금부터 HTTP 캐싱의 기본 개념부터 강제 캐싱과 협상 캐싱의 차이, 그리고 모범 사례까지 차근차근 알아볼까요?

 

  1. HTTP 캐싱이란?
    HTTP 캐싱은 웹 성능을 높이기 위해 서버 부하를 줄이고 클라이언트의 응답 시간을 단축시키며 네트워크 대역폭 사용을 절감하는 기술입니다. 쉽게 말해, 웹 페이지를 로딩할 때 이미 로컬에 저장된 데이터를 활용하여 서버에 불필요한 요청을 보내지 않고 빠르게 화면을 구성할 수 있도록 합니다.
  2. 강제 캐싱
    강제 캐싱은 서버가 보내는 응답 헤더에 따라 일정 시간 동안 클라이언트가 로컬에 저장된 캐시 데이터를 바로 사용하는 방식입니다. 주요 헤더는 다음과 같습니다.
    • 캐시-컨트롤(Cache-Control)
      이 헤더는 최대 유효 기간(max-age), 공유 가능 여부(public 또는 private), 그리고 수정 불가(no-cache 또는 no-store) 등의 정보를 지정합니다.
      예를 들어,위와 같이 설정하면, 해당 리소스는 3600초 동안 유효하며 캐시될 수 있음을 의미합니다.
    • 캐시-컨트롤(Cache-Control): max-age=3600
    • 만료(Expires)
      이 필드는 절대적인 만료 시점을 지정하는 구식(header) 방식입니다.
      예를 들어,이는 해당 리소스가 2024년 8월 23일 오전 3시 36분 26초가 지나면 만료된다는 의미입니다.
      만약 캐시-컨트롤(Cache-Control)만료(Expires) 두 헤더가 동시에 존재한다면, 캐시 설정에는 캐시-컨트롤(Cache-Control)의 값이 우선 적용됩니다.
    • 만료(Expires): Wed, 23 Aug 2024 03:36:26 GMT
  3. 협상 캐싱
    협상 캐싱은 클라이언트가 매번 서버에 리소스의 최신 상태를 확인한 후, 변경이 없을 경우 서버로부터 304 상태 코드를 받아 로컬 캐시를 계속 사용하는 방식입니다. 반대로 리소스가 업데이트되었다면 200 상태 코드와 함께 새 리소스를 받아 로컬 캐시를 대체합니다. 이 방식에는 서버와 클라이언트가 다음과 같은 헤더를 사용합니다.
    • 최종 수정 시간(Last-Modified) / 변경 확인(If-Modified-Since)
      • 최종 수정 시간(Last-Modified)은 서버가 리소스의 마지막 수정 시점을 알려주며,
        예를 들어,
        최종 수정 시간(Last-Modified): Tue, 22 Aug 2024 02:36:26 GMT
        이는 리소스가 2024년 8월 22일 오전 2시 36분 26초에 마지막으로 수정되었음을 의미합니다.
      • 변경 확인(If-Modified-Since)은 클라이언트가 마지막으로 리소스를 획득한 시점을 서버에 전달합니다.
        예를 들어,
        변경 확인(If-Modified-Since): Tue, 22 Aug 2024 02:36:26 GMT
        두 타임스탬프가 같거나 서버의 최종 수정 시간이 이전이라면 리소스는 갱신되지 않은 것으로 판단합니다. 반면, 서버의 수정 시간이 더 늦은 경우에는 리소스가 업데이트된 것으로 간주합니다.
    • ETag / 없음 확인(If-None-Match)
      • ETag는 서버에서 리소스의 고유 식별자를 제공하는 헤더입니다.
        예를 들어,
        ETag: '5d3a9f6d-1f86'
        이는 리소스의 식별자가 '5d3a9f6d-1f86'임을 나타냅니다.
      • 없음 확인(If-None-Match)은 클라이언트가 기대하는 리소스 식별자를 서버에 전달합니다.
        예를 들어,
        없음 확인(If-None-Match): '5d3a9f6d-1f86'
        만약 두 식별자가 일치한다면 리소스는 변경되지 않은 것으로 판단하고, 값이 다르면 리소스가 업데이트된 것으로 판단합니다.
  4. HTTP 캐싱 모범 사례
    강제 캐싱과 협상 캐싱을 적절히 조합하면 불필요한 네트워크 요청을 줄이면서도 사용자가 항상 최신 콘텐츠를 볼 수 있도록 할 수 있습니다.
    • 정적 리소스(예: CSS, JS, 이미지)와 같이 자주 변경되지 않는 파일에는 긴 캐시 기간을 설정하는 강제 캐싱 방식을 사용합니다.
    • 자주 변경될 가능성이 있는 리소스에는 협상 캐싱 방식을 적용하여, 서버와의 확인 과정을 통해 최신 상태를 유지합니다.
    예를 들어, 백엔드 프레임워크로 익스프레스(.js)(Express.js)를 사용할 경우 다음과 같이 구현할 수 있습니다.
const express = require('express');
const app = express();

// 강제 캐싱: 정적 리소스에 대한 캐시 설정
app.use(
  '/static',
  express.static('public', {
    maxAge: '1y', // 캐시 유지 기간: 1년
  })
);

// 협상 캐싱: ETag와 최종 수정 시간(Last-Modified) 사용
app.get('/resource', (req, res) => {
  const content = '...'; // 리소스 콘텐츠 획득
  const etag = generateETag(content); // ETag 생성

  // If-None-Match 헤더 확인
  if (req.headers['if-none-match'] === etag) {
    res.status(304).end(); // 리소스 변경 없음, 304 상태 코드 반환
  } else {
    res.setHeader('ETag', etag);
    res.send(content); // 리소스 변경됨, 새로운 콘텐츠 반환
  }
});

function generateETag(content) {
  return require('crypto').createHash('md5').update(content).digest('hex');
}

app.listen(3000, () => {
  console.log('서버가 포트 3000에서 실행 중입니다.');
});
  1. 주요 고려 사항
    HTTP 캐싱을 효과적으로 운영하기 위해서는 몇 가지 사항을 고려해야 합니다.
    • 버전 관리
      강제 캐싱의 효과를 극대화하려면 리소스 URL에 버전 정보를 포함하는 것이 좋습니다. 예를 들어, /static/js/main.2024082301.js와 같이 작성하면, 리소스가 업데이트될 경우 버전 번호를 변경하여 사용자가 항상 최신 버전을 제공받을 수 있습니다.
    • 협상 캐싱의 비용
      협상 캐싱은 불필요한 데이터 전송을 줄여주지만, 매번 서버와의 네트워크 왕복이 필요합니다. 리소스가 드물게 변경된다면 강제 캐싱 방식을 활용하는 것이 더 효율적일 수 있습니다.

이처럼 HTTP 캐싱은 웹 사이트의 성능 향상과 서버 부하 감소에 큰 역할을 합니다.

 

각 방식의 특징을 이해하고 적절히 활용하면 사용자에게 빠르고 안정적인 서비스를 제공할 수 있습니다.

 

이제 HTTP 캐싱에 대해 충분히 이해했으니, 실제 웹 개발 환경에 적용해 보도록 합시다.