Last-Modified — 본질, 메커니즘 및 수정 날짜 헤더 구성

저자: IT Sectr 게시일: 2026-03-09 읽는 시간: 9 분

Last-Modified는 서버에서 리소스가 마지막으로 수정된 날짜와 시간을 나타내는 HTTP 응답 헤더로, 클라이언트가 If-Modified-Since를 통해 조건부 요청을 수행할 수 있게 합니다. 지정된 날짜 이후로 리소스가 변경되지 않은 경우, 서버는 응답 본문을 보내지 않고 304 Not Modified를 반환하여 대역폭을 크게 절약합니다. RFC 7232(IETF, 2014)에 따르면, Last-Modified를 사용한 조건부 요청은 재방문 시 페이지 로드 시간을 30-60% 단축합니다. 이 헤더는 대부분의 HTTP 서버와 프록시에서 자동으로 지원됩니다.

주요 포인트

  • Last-Modified — If-Modified-Since 조건부 요청을 위한 리소스 최종 수정 날짜가 포함된 HTTP 헤더
  • 304 Not Modified — 리소스가 변경되지 않은 경우 서버 응답; 클라이언트는 캐시된 복사본 사용
  • 초 단위 정밀도 — 헤더 제한: 1초 이내의 변경은 감지되지 않을 수 있음
  • ETag와의 협력 — 서버가 두 헤더를 모두 반환, 클라이언트가 두 조건부 요청을 모두 전송
  • 자동 생성 — Nginx와 Apache가 파일 시스템에서 정적 파일의 Last-Modified를 설정

Last-Modified란?

Last-Modified는 조건부 요청 헤더 그룹에 속하는 HTTP 헤더입니다. 서버는 GET 또는 HEAD에 대한 응답에 이를 추가하여 HTTP-date 형식으로 요청된 리소스의 마지막 수정 날짜와 시간을 나타냅니다: Last-Modified: Wed, 02 Jul 2025 14:30:00 GMT. 클라이언트(브라우저, 모바일 앱, 프록시)는 이 날짜를 캐시된 리소스와 함께 저장합니다. 반복 요청 시 클라이언트는 동일한 날짜로 If-Modified-Since 헤더를 보내고, 서버는 이를 리소스의 현재 수정 시간과 비교합니다.

Last-Modified를 사용한 조건부 요청 프로토콜은 RFC 7232에 정의되어 있으며 모든 최신 HTTP 서버에서 지원됩니다. 날짜 형식은 엄격하게 규제됩니다 — 시간대 표시 없이 GMT(그리니치 표준시)만 사용합니다. 서버는 세 가지 가능한 형식(RFC 1123(표준), RFC 850(구식) 또는 ANSI C asctime)으로 날짜를 반환해야 합니다. 실제로는 거의 모든 서버가 29자의 고정 길이를 가진 RFC 1123 형식을 사용합니다.

Last-Modified는 캐시 검증 메커니즘 범주에 속합니다. 응답을 캐시할 수 있는지 여부를 클라이언트에 알리는 것이 아니라, 이미 캐시된 리소스의 유효성을 확인하는 도구를 제공합니다. 캐시 정책은 Cache-Control 헤더를 통해 별도로 설정됩니다. Akamai(2025)의 연구에 따르면, Last-Modified를 Cache-Control과 함께 올바르게 구성하면 정적 콘텐츠의 오리진 서버 부하를 최대 70%까지 줄일 수 있습니다.

Last-Modified는 언제 등장했나?

Last-Modified 헤더는 HTTP/1.0(RFC 1945, 1996)에서 정의되었으며 웹에서 최초의 캐시 관리 메커니즘 중 하나가 되었습니다. HTTP/1.1에서 ETag가 등장하기 전까지는 조건부 요청을 수행하는 유일한 방법이었습니다. 오래되었음에도 불구하고, 이 헤더는 단순성 덕분에 여전히 관련성을 유지하고 있습니다 — 서버는 콘텐츠 해시를 계산할 필요 없이 파일 시스템에서 파일 타임스탬프나 데이터베이스의 updated_at 필드만 읽으면 됩니다.

Last-Modified 작동 방식

전체 사이클은 세 단계로 구성됩니다. 첫 번째 요청에서 서버는 Last-Modified 헤더와 HTTP 상태 200 OK와 함께 리소스를 반환합니다. 클라이언트는 날짜와 함께 응답을 캐시합니다. 반복 요청 시 클라이언트는 저장된 날짜로 If-Modified-Since 헤더를 보냅니다. 서버는 이 날짜를 리소스의 현재 수정 시간과 비교합니다. 리소스가 변경되지 않은 경우 — 빈 본문과 함께 304 Not Modified를 반환합니다. 변경된 경우 — 새 데이터와 새 Last-Modified로 200 OK를 반환합니다.

http
// 첫 번째 요청 — 서버가 날짜와 함께 리소스 반환
HTTP/1.1 200 OK
Content-Type: application/json
Last-Modified: Wed, 02 Jul 2025 14:30:00 GMT

[{"id": 1, "name": "Alice"}]

// 반복 요청 — 클라이언트가 저장된 날짜 전송
GET /api/users HTTP/1.1
Host: example.com
If-Modified-Since: Wed, 02 Jul 2025 14:30:00 GMT

// 응답 — 데이터가 변경되지 않음
HTTP/1.1 304 Not Modified

모바일 애플리케이션의 경우 데이터 동기화에 Last-Modified가 특히 유용합니다. 애플리케이션은 마지막 성공적인 업데이트 날짜를 저장하고 If-Modified-Since로 서버에 보냅니다. 더 많은 데이터가 있거나 변경된 경우 — 서버가 전체 세트를 반환합니다. 그렇지 않은 경우 — 304, 애플리케이션은 로컬 복사본을 사용합니다. OkHttp와 URLSession은 내장 캐싱 시스템을 통해 이 메커니즘을 자동으로 지원합니다.

서버가 날짜를 결정하는 방법

정적 파일의 경우 Nginx와 Apache는 파일 시스템 속성 — mtime(수정 시간) — 에서 날짜를 가져옵니다. 동적 콘텐츠의 경우 서버 코드는 비즈니스 로직에 따라 명시적으로 Last-Modified를 설정해야 합니다: 데이터베이스의 updated_at 필드, Git의 마지막 커밋 날짜, 빌드 아티팩트 타임스탬프 등. Last-Modified가 명시적으로 설정되지 않은 경우, 서버는 헤더를 전혀 보내지 않을 수 있으며, 클라이언트는 날짜에 따른 조건부 요청을 수행할 수 없게 됩니다.

Last-Modified vs ETag

Last-Modified와 ETag는 비슷한 작업 — 클라이언트가 캐시 유효성을 확인할 수 있도록 함 — 을 수행하지만 근본적인 차이점이 있습니다. Last-Modified는 타임스탬프를 사용하고, ETag는 고유한 버전 식별자를 사용합니다. 각 접근 방식에는 더 효과적인 시나리오가 있으며, HTTP 명세는 두 헤더를 함께 사용할 것을 권장합니다.

기준Last-ModifiedETag
본질최종 수정 날짜고유 버전 식별자
정밀도초 단위비트 단위(해시)
구현 복잡성낮음 — 파일 시스템에서 자동중간 — 해시 계산 필요
클러스터 서버문제: 노드 간 mtime이 다를 수 있음노드 간 동일 데이터에서 안정적
범위 지원Range 요청에 영향 없음범위에 강력한 ETag 필요
권장정적 파일 및 단순 API용정확한 검증이 중요한 API용

Last-Modified의 주요 장점은 단순성입니다. 서버는 콘텐츠 해시를 계산할 필요가 없어 각 요청마다 CPU 리소스를 절약합니다. 정적 파일이나 명확한 타임스탬프가 있는 데이터를 제공하는 트래픽이 많은 프로젝트의 경우 Last-Modified가 최적의 선택입니다. 반면 ETag는 절대적인 정밀도를 제공합니다 — JSON 응답에서 한 글자를 변경하면 ETag가 변경되지만, 날짜는 변경되지 않을 수 있습니다(파일이 동일한 버전으로 덮어쓰여진 경우).

함께 사용

명세는 두 헤더를 동시에 반환할 것을 권장합니다. 서버는 200 OK 응답에 Last-Modified와 ETag를 모두 포함합니다. 클라이언트는 두 조건부 헤더 — If-Modified-Since와 If-None-Match — 를 모두 보냅니다. 서버는 먼저 ETag(우선순위 있음)를 확인한 다음 Last-Modified를 확인합니다. 하나라도 변경을 알리는 경우 — 전체 응답이 반환됩니다. 이는 최대의 유연성을 제공합니다: ETag는 정밀도를 보장하고, Last-Modified는 ETag를 지원하지 않는 클라이언트를 위한 폴백 확인을 제공합니다.

서버에서 Last-Modified 구성

Last-Modified 구성은 서버 유형에 따라 다릅니다. Nginx와 Apache의 경우 정적 파일의 Last-Modified는 mtime을 기반으로 자동으로 설정됩니다. 동적 애플리케이션의 경우 서버 코드에서 헤더를 설정해야 합니다. 인기 있는 플랫폼에서의 구성을 살펴보겠습니다.

javascript
// Express.js — Last-Modified 설정
app.get("/api/users", async (req, res) => {
    const updatedAt = await getLastUpdate()
    const ifModifiedSince = req.get("If-Modified-Since")

    // If-Modified-Since 확인
    if (ifModifiedSince && new Date(ifModifiedSince)
        >= updatedAt) {
        return res.status(304).end()
    }

    const users = await getUsers()
    res.set("Last-Modified", updatedAt.toUTCString())
    res.json(users)
})

Express.js 예제에서 서버는 데이터베이스에서 데이터의 마지막 업데이트 날짜를 가져오고, 클라이언트의 If-Modified-Since를 확인하며, 캐시가 여전히 유효한 경우 304를 반환합니다. 데이터가 변경된 경우 — 새 Last-Modified를 설정하고 전체 응답을 반환합니다. toUTCString()은 날짜를 필요한 HTTP 형식으로 변환합니다. 프로덕션에서는 매 요청마다 데이터베이스 쿼리를 피하기 위해 Redis에 updatedAt을 캐시하는 것이 좋습니다.

Nginx: Last-Modified 구성

Nginx는 파일의 마지막 수정 시간을 기반으로 정적 파일의 Last-Modified를 자동으로 설정합니다. etag 지시문(ETag 비활성화) 또는 ngx_http_headers_module 모듈을 통해 이 동작을 비활성화하거나 변경할 수 있습니다. 백엔드로의 프록시 요청의 경우 Last-Modified는 업스트림 응답에서 변경 없이 전달됩니다. 중요: 백엔드가 Last-Modified를 반환하지 않는 경우, Nginx는 동적 응답에 대해 자동으로 추가하지 않습니다.

제한 사항 및 주의점

Last-Modified에는 몇 가지 알려진 제한 사항이 있습니다. 주요 제한은 초 단위 정밀도입니다. 리소스가 1초 이내에 두 번 변경되면 클라이언트가 새 버전을 놓칠 수 있습니다. 실제로는 드문 시나리오이지만, 고빈도 업데이트(티커 피드, 채팅)의 경우 ETag가 권장됩니다. 두 번째 제한은 클러스터링 문제입니다: 다른 서버에서는 복사나 배포로 인해 파일의 mtime이 다를 수 있어 Last-Modified가 일관되지 않게 됩니다.

세 번째 제한 — 초 단위 정밀도의 If-Modified-Since 처리는 서버를 자주 폴링할 때 불필요한 요청을 유발할 수 있습니다. 클라이언트가 500ms마다 If-Modified-Since를 보내는 경우, 서버는 날짜가 변경되지 않았기 때문에 매번 200 OK를 반환하지만, 리소스는 실제로 이미 업데이트되었습니다. 해결책은 ETag와의 조합을 사용하는 것입니다: ETag가 1초 내의 변경을 감지하고, Last-Modified는 폴백으로 유지됩니다.

네 번째 문제 — Last-Modified는 동일한 날짜의 동일 리소스의 다른 버전을 구분하지 못합니다. 파일이 백업에서 복원되고 mtime이 원본과 일치하는 경우, 클라이언트는 콘텐츠가 변경되었음을 인지하지 못합니다. ETag는 이 문제를 해결합니다: 콘텐츠 해시는 타임스탬프와 관계없이 데이터 변경 시 확실히 변경됩니다. 중요한 데이터에는 항상 두 헤더를 모두 사용하세요.

  • 초 단위 정밀도 — 1초 이내 변경 감지 불가; 고빈도 업데이트에는 ETag 사용
  • 클러스터링 — 서버 간 mtime이 다를 수 있음; NTP로 동기화하거나 ETag 사용
  • 경합 조건 — If-Modified-Since 전송 후 서버 확인 전에 리소스가 변경된 경우
  • 프록시 오해석 — 일부 프록시가 캐싱 시 Last-Modified를 변경할 수 있음; HTTPS로 해결

자주 묻는 질문

Last-Modified에서 사용되는 날짜 형식은?

RFC 1123 형식의 GMT(그리니치 표준시)만 사용: 요일, 일, 월, 연도, 시:분:초. 예: Wed, 02 Jul 2025 14:30:00 GMT. 시간대는 항상 GMT이며, 다른 형식은 허용되지 않습니다.

Last-Modified가 미래의 날짜가 될 수 있나요?

기술적으로 가능하지만 RFC 7232를 위반합니다. 서버가 미래의 날짜를 반환하면 클라이언트는 해당 날짜가 도래할 때까지 리소스를 업데이트하지 않습니다. 이러한 구성은 오류로 간주됩니다 — 날짜는 과거 또는 현재여야 합니다.

Last-Modified는 POST 요청에서 작동하나요?

아니요, If-Modified-Since 조건부 요청은 GET과 HEAD에서만 작동합니다. POST 요청은 캐시되지 않으며 날짜 기반 검증을 사용하지 않습니다. POST에서 신선도 확인을 위해서는 ETag 또는 사용자 정의 메커니즘을 사용하세요.

Last-Modified는 Cache-Control과 어떻게 상호작용하나요?

Cache-Control은 캐시 정책(최대 저장 시간, 캐시할 수 있는 주체)을 정의하고, Last-Modified는 만료된 캐시의 검증 메커니즘입니다. max-age가 만료된 후, 클라이언트는 신선도를 확인하기 위해 If-Modified-Since를 보냅니다.

데이터 업데이트 시 Last-Modified가 변경되지 않으면 어떻게 하나요?

서버가 올바른 소스(데이터베이스, 파일 시스템 또는 API)에서 헤더를 설정하는지 확인하세요. 동적 응답의 경우 핸들러 코드에서 명시적으로 res.setHeader(“Last-Modified”, ...)를 호출하는지 확인하세요.

요약

  • Last-Modified — 304 조건부 요청을 위한 리소스 최종 수정 날짜가 포함된 HTTP 헤더
  • 간단한 구현 — 정적 파일(mtime)에서 자동 작동, API에는 최소 코드 필요
  • 초 단위 정밀도 — 주요 제한 사항; 고빈도 변경에는 ETag 사용
  • ETag가 더 정밀하고 Last-Modified가 더 간단 — 최적의 조합: 두 헤더 함께 사용
  • HTTP 날짜 형식 — GMT만, RFC 1123, 29자 고정 길이
  • 클러스터링 — 시간 동기화(NTP) 또는 ETag를 주요 메커니즘으로 사용 필요
  • 권장 — API에는 항상 Last-Modified 추가, Nginx/Apache를 통해 정적 파일에 활성화

턴키 방식의 모바일 애플리케이션을 개발해 드립니다

IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.

프로젝트 논의

더 읽어보기