
이 글은 Owen Garrett이 진행하고 Andrew Alexeev가 소개한 웨비나를 바탕으로 작성되었습니다.
목차
소개
0:00 소개1:22 웨비나 소개2:17 콘텐츠 캐싱의 기본 원칙2:35 기본 원칙3:59 HTTP 캐싱의 작동 방식7:46 NGINX는 무엇을 캐시하나요?
콘텐츠 캐싱과 NGINX
9:55 NGINX 작동 방식10:06 NGINX 구성11:14 캐싱 프로세스15:32 캐싱은 HTTP에만 국한되지 않습니다17:10 현재 상황을 파악하는 방법17:38 캐시 계측19:08 캐시 계측(계속)20:09 캐시 상태21:57 NGINX의 콘텐츠 캐싱 작동 방식22:40 작동 원리23:53 캐시된 콘텐츠는 어떻게 저장되나요?26:36 디스크에서 캐시 로드하기28:07 디스크 캐시 관리하기29:22 디스크에서 콘텐츠 제거하기
캐싱 제어
31:27 캐싱 제어32:30 지연 캐싱34:45 캐시 시간 제어36:18 캐시하기 / 캐시하지 않기37:25 다중 캐시39:07 간단한 복습 – 왜 캐시해야 할까요?39:44 페이지 속도가 중요한 이유는 무엇일까요?40:46 Google이 규칙을 변경했습니다41:28 성능 저하로 인한 비용43:00 NGINX 캐싱으로 가능한 작업45:21 마무리
질의응답
47:20 바이트 범위 요청49:13 프록시 캐시 재검증50:07 동일한 디스크에 캐시 분산50:50 Vary 헤더51:25 캐싱 기본 요소51:52 업스트림 헤더 및 데이터52:13 *‑Encoding 헤더52:56 SPDY53:15 Vary 헤더, 2차53:45 PageSpeed54:00 기타 캐시
0:00 소개

Andrew Alexeev: 최신 웨비나에 오신 것을 환영합니다. 저는 Andrew입니다. NGINX는 전 세계 웹사이트가 더 빠르게 실행되고, 더 신속하게 응답하며, 손쉽게 확장될 수 있도록 지원한다는 취지로 Igor Sysoev가 개발했습니다. 현재 NGINX는 상위 웹사이트의 30% 이상과 인터넷상의 모든 웹사이트 중 20% 이상을 지원하고 있습니다. [편집자 주 – 이 통계는 웨비나가 진행된 2014년 5월 당시의 수치입니다. 현재 수치는 여기에서 확인하세요.] 이 웨비나의 내용이 현재 운영 중이거나 구축 예정인 NGINX 환경에 유용하게 활용되기를 바랍니다.
이제 Owen Garrett을 소개해 드리겠습니다. Owen은 NGINX에서 제품 개발을 담당하고 있습니다. 오늘 Owen은 NGINX의 강력한 캐싱 메커니즘을 활용하여 애플리케이션이 반복적인 콘텐츠를 계속해서 생성해야 하는 부담에서 벗어나게 하는 방법을 설명할 예정입니다.
1:22 이 웨비나 소개

Owen Garrett: 감사합니다, Andrew. 그리고 여러분, 앞으로 45~50분 동안 함께해 주셔서 진심으로 감사합니다. NGINX가 콘텐츠 캐싱과 관련하여 어떻게 작동하는지 설명하고, 성능을 개선할 수 있는 몇 가지 방법을 살펴보겠습니다. 또한 콘텐츠 캐싱이 실제로 어떻게 작동하는지 조금 더 깊이 알아봄으로써 NGINX 내부에서 일어나는 일을 디버깅하고 진단할 수 있도록 도와드리겠습니다. 마지막으로, 캐시 가능한 콘텐츠를 NGINX가 처리하는 방식을 매우 세밀하게 제어할 수 있는 유용한 요령과 팁을 소개하며 마무리하겠습니다.
이 모든 것은 Andrew가 설명한 것과 같은 핵심 목표를 지향합니다. 즉, 업스트림 서버가 반복적인 콘텐츠를 생성해야 하는 부담을 덜어 주어 비즈니스에 꼭 필요한 애플리케이션을 실행하는 데 집중할 수 있도록 하는 것입니다. 또한 이러한 서버의 한계를 넘어 성능을 확장함으로써 최종 사용자에게 더 나은 수준의 서비스를 제공하고, 인터넷 트래픽이 급증하거나 업스트림 서버에 장애가 발생할 가능성이 있는 상황에서도 서비스 전반의 안정성을 높일 수 있습니다.
2:17 콘텐츠 캐싱의 기본 원칙

NGINX 구현 구성을 살펴보기에 앞서, 모두가 동일한 핵심 정보를 바탕으로 시작할 수 있도록 콘텐츠 캐싱이 기본적으로 어떻게 작동하는지 간단히 짚어보겠습니다.
2:35 기본 원리

콘텐츠 캐싱의 기본 원리는 반복적인 작업을 업스트림 서버에서 덜어내는 것입니다. 첫 번째 사용자가 웹사이트의 콘텐츠 항목을 요청하면(파란색 아이콘과 파란색 선으로 표시), 해당 사용자의 HTTP 요청이 NGINX로 전달된 후 오른쪽에 회색으로 표시된 업스트림 서버로 전달됩니다.
응답은 원격 사용자에게 다시 전달되지만, 캐시할 수 있는 응답이라면(그 의미는 잠시 후에 설명하겠습니다) NGINX가 해당 응답의 사본을 저장합니다. 이후 주황색으로 표시된 다른 사용자가 동일한 콘텐츠를 요청하면, NGINX는 업스트림 서버에 요청을 전달하는 대신 로컬 캐시에서 해당 콘텐츠를 직접 제공할 수 있습니다.
캐시할 수 있는 변경되지 않는 콘텐츠를 저장하는 이 기본 원리는 웹 브라우저와 CDN, CDN을 활용하는 접속 대상 사이트, 그리고 다른 장치의 NGINX에서 사용됩니다. NGINX는 일반적으로 웹 콘텐츠와 웹 애플리케이션을 호스팅하는 원본 서버와 가까운 데이터 센터나 클라우드에 배포되는 리버스 프록시 캐시로 작동합니다.
3:59 HTTP 캐싱의 작동 원리

오리진 서버는 널리 알려져 있고 그 의미가 명확한 여러 HTTP 응답 헤더 중 하나 이상을 사용하여 콘텐츠의 캐시 가능 여부를 선언합니다. 물론 캐싱 서버는 이 동작을 무시하거나 재정의하거나 변경할 수 있습니다. 하지만 구성된 콘텐츠 캐싱을 이해하려면 먼저 오리진 서버가 콘텐츠를 캐시할 수 있고, 콘텐츠가 변경되지 않으며, [캐시된] 사본에 일정한 유효 기간이 있음을 나타내는 방식을 제대로 이해해야 합니다.
콘텐츠 캐싱은 Expires라는 간단한 HTTP 응답 헤더에서 시작되었습니다. 오리진 서버는 콘텐츠를 제공하면서 해당 콘텐츠가 Expires 헤더에 지정된 날짜까지 유효하다고 선언했습니다. 이 방식은 곧 더 효과적이고 유연한 방식인 Cache-Control 헤더로 대체되었습니다.
Expires는 사용하기가 다소 번거롭고 비효율적입니다. 날짜는 올바른 형식으로 지정하고 파싱해야 하는 반면, Cache-Control은 훨씬 더 간결하며 콘텐츠 캐시의 요구 사항과 처리 속도에 적합합니다. Cache-Control은 콘텐츠를 공개 또는 비공개로 지정하며, 공개 콘텐츠인 경우 max-age를 지정합니다. 이는 캐싱 객체가 해당 콘텐츠를 다시 요청해야 하기 전까지 캐시할 수 있는 시간(초)입니다.
캐싱을 직접 제어하는 세 번째 헤더는 X-Accel-Expires입니다. 이 헤더는 NGINX 전용이므로 NGINX만 인식합니다. 위 헤더의 동작을 재정의하고 특정 콘텐츠 항목을 얼마나 오래 캐시할지 NGINX에 직접 정확히 지정하려는 경우에 사용합니다.
웹 브라우저에는 콘텐츠를 장기간 캐시하도록 하되, 프록시 캐시(원본 서버 앞에 위치한 NGINX)에는 더 짧은 기간 동안 캐시하도록 설정하고 싶은 경우가 있습니다. 이렇게 하면 변경 사항이 더 빠르게 반영되어 새 클라이언트에 전달되는 한편, 기존 클라이언트는 필요하지 않을 때 콘텐츠를 계속 다시 요청하지 않게 됩니다.
하지만 이 방법은 마지막 두 헤더를 사용해서도 구현할 수 있습니다. 오리진 서버는 콘텐츠 항목이 마지막으로 수정된 시점을 명시할 수 있으며, 해당 콘텐츠를 식별하는 ETag(엔터티 태그)라는 것도 명시할 수 있습니다. 이는 대개 해시 값으로 이루어진 불투명한 문자열입니다.
그러면 클라이언트는 조건부 GET 요청을 보낼 수 있습니다. 요청에 If-Modified-Since 또는 If-None-Match 헤더를 포함할 수 있습니다. 이렇게 하면 클라이언트는 특정 날짜에 마지막으로 수정되었거나 특정 ETag를 가진 콘텐츠의 캐시된 버전을 보유하고 있음을 알리게 됩니다. 서버가 보유한 최신 버전이 클라이언트가 가진 버전과 일치하면 서버는 간단히 304 Not Modified로 응답합니다. 이는 네트워크 대역폭을 절약하는 빠른 응답이며, 클라이언트가 편한 때에 캐시된 콘텐츠 사본이 여전히 유효한지 확인할 수 있게 해 줍니다.
이 다섯 가지 헤더는 원본 서버의 관점에서 콘텐츠를 캐시할 수 있는 방식, 즉 콘텐츠의 유효성과 최신성, 그리고 ETag의 경우 콘텐츠 자체의 세부 정보를 정의합니다.
7:46 NGINX는 무엇을 캐시하나요?

NGINX와 같은 프록시 캐시는 해당 헤더를 얼마나 엄격하게 준수할지 비교적 자유롭게 해석할 수 있습니다. 캐시할 수 없는 항목을 캐시해서는 안 되는 것이 분명하지만, 오리진 서버가 캐시할 수 있다고 명시하더라도 반드시 캐시해야 할 의무는 없습니다.
NGINX의 기본 동작은 응답에 GET 및 HEAD 헤더가 없는 경우, 오리진 서버에서 캐시 가능하다고 지정한 모든 Set-Cookie 요청을 캐시하는 것입니다. 이는 일반적으로 Set-Cookie 헤더에 각 요청별 고유 데이터가 포함되므로 기본적으로 해당 값을 캐시하는 것이 적절하지 않기 때문입니다.
NGINX는 각 리소스를 특정 키, 즉 캐시 키를 기준으로 캐시합니다. 동일한 캐시 키를 생성하는 모든 요청에는 동일한 리소스가 제공됩니다. 기본적으로 캐시는 원본 URL 또는 구성에서 이 슬라이드에 표시된 문자열 [$scheme$proxy_host$uri$is_args$args]에 매핑됩니다. NGINX가 콘텐츠를 캐시할 때 [유효 기간]은 우선순위에 따라 X-Accel-Expires 헤더가 있으면 해당 헤더로, 그렇지 않으면 Cache-Control 헤더 또는 기존 Expires 헤더로 정의됩니다.
NGINX를 조정하여 이러한 헤더 중 일부를 무시하거나, 원본 서버의 지시와 관계없이 고정된 캐시 시간을 대신 적용할 수 있습니다. 참고로 RFC 2616에서는 HTTP와 관련하여 프록시 캐시가 따라야 할 동작을 정의합니다.
이처럼 캐싱이 얼마나 보편적으로 활용되는지, 그리고 웹사이트 속도를 높이고 오리진 서버의 부하를 줄이기 위해 일반적으로 캐싱해도 안전한 콘텐츠를 캐싱하는 NGINX의 기본 동작이 무엇인지 간략히 살펴보았습니다.
9:55 NGINX 작동 방식

이제 NGINX가 실제로 작동하는 모습을 간단히 살펴보겠습니다. NGINX에서 콘텐츠 캐싱을 활성화하도록 구성하는 것은 정말 매우 간단합니다.
10:06 NGINX 구성

NGINX 구성 파일에 몇 줄만 추가하면 됩니다. 첫 번째 줄은 파일 배치 방식, [해당 캐시에 있는 객체의] 만료 시간, 캐시 크기를 지정하는 일련의 매개변수로 디스크에 캐시를 생성합니다. 두 번째인 proxy_cache 지시어는 NGINX 프록시와 연결되며 콘텐츠(결과, 응답)를 지정된 이름의 캐시에 저장하도록 지시합니다.
여기서는 one이라는 캐시를 하나 만들었습니다. 메타데이터용 메모리 크기는 10 MB이지만, 캐시된 콘텐츠를 저장하는 디스크 크기에는 제한이 없습니다. 콘텐츠는 캐시된 후 60분 동안 사용되지 않으면 제거됩니다. one이라는 이 캐시는 기본 서버에서 사용됩니다. 이 두 [지시어], proxy_cache_path와 proxy_cache만으로 NGINX의 프록시 서버에서 안정적이고 일관된 캐싱을 활성화할 수 있습니다.
11:14 캐싱 프로세스

NGINX가 요청을 수신하고 캐시를 조회할 때 거치는 프로세스는 다음과 같습니다. 먼저 요청을 읽고(이 슬라이드의 왼쪽 상단 상자), 원시 URI와 해당 요청에 대응하는 리소스를 식별하는 데 사용할 기타 매개변수를 조합하여 캐시 키를 생성합니다. 그런 다음 메모리의 메타데이터에 액세스하여 디스크의 캐시를 확인하고, 해당 요청에 대한 유효한 최신 응답 사본이 있는지 확인합니다.
그렇다면 캐시 적중으로 간주됩니다. 그러면 NGINX는 캐시에서 직접 응답할 수 있습니다. NGINX가 정적 콘텐츠를 제공하는 것과 똑같은 방식으로 디스크의 콘텐츠를 제공하여 캐시에서 응답합니다. 따라서 NGINX가 제공하도록 설계된 수준의 성능, 안정성 및 확장성을 얻을 수 있습니다. 정적 콘텐츠를 제공할 때와 마찬가지로 NGINX의 콘텐츠 캐시에서 콘텐츠를 제공할 때도 정확히 동일한 수준의 [성능]을 얻을 수 있습니다.
반면 캐시를 확인했을 때 캐시 미스가 발생할 수도 있습니다. 이는 해당 콘텐츠가 캐시에 없거나, 캐시에 있는 콘텐츠가 오래되어 새로 고쳐야 한다는 의미입니다. 가장 단순한 경우에는 캐시 미스가 발생하면 오리진 서버에 해당 콘텐츠를 요청하고 응답을 받은 다음, 캐시할 수 있는지 확인합니다.
캐시할 수 있다면 프록시 모드에서 처리되는 대용량 응답에 대해 NGINX가 수행하는 것처럼 해당 응답을 디스크로 스트리밍합니다. 그런 다음 [응답이] 디스크에 모두 기록되면 캐시에 복사하고 캐시에서 직접 응답합니다. 이 방식의 문제점 중 하나는 NGINX가 동일한 콘텐츠에 대한 여러 요청을 동시에 수신했는데 모두 캐시 미스로 처리되는 경우입니다.
일반적으로 NGINX는 이러한 요청을 모두 오리진 서버로 전달하므로, 특히 응답을 생성하는 데 오랜 시간이 걸리는 요청의 경우 서버에 과부하가 발생할 수 있습니다. 따라서 이를 방지하기 위해 캐시 잠금을 사용할 수 있습니다. proxy_cache_lock 지시어를 사용하면 콘텐츠가 갱신되는 동안 업스트림 서버로 한 번에 하나의 요청만 전송됩니다.
앞서 설명한 시나리오에서는 첫 번째 요청이 업스트림 서버로 전송되지만, 동일한 콘텐츠에 대한 나머지 요청은 응답이 반환되어 캐시에 삽입되거나(이 시점부터 모든 요청을 처리할 수 있음), [proxy_cache_lock_timeout 지시어로 설정된] 제한 시간이 초과될 때까지 보류됩니다. 제한 시간이 초과되면 NGINX가 서버에서 콘텐츠가 반환되기를 충분히 오래 기다렸다고 판단하여, 그 시점에 보류 중이던 요청을 오리진 서버로 전달합니다.
따라서 proxy_cache_lock과 시간 제한을 사용하면, 동일한 콘텐츠에 대한 요청이 많은 트래픽이 높은 사이트에서 해당 콘텐츠의 캐시가 만료되더라도 여러 요청으로 인해 오리진 서버에 갑자기 과부하가 걸리지 않도록 효과적으로 제어할 수 있습니다.
NGINX의 캐싱 프로세스에는 이 흐름도의 거의 모든 단계를 포괄하기 때문에 흐름도에 명확하게 포함하기 어려운 요소가 하나 더 있습니다. 바로 proxy_cache_use_stale 지시문으로 구성하는 기능입니다. 어느 시점에서든 이러한 단계 중 하나가 어떤 이유로 실패하는 경우, 예를 들어 콘텐츠를 업데이트하는 중에 시간 초과가 발생하거나 업스트림 서버에서 잘못된 응답을 받거나 그 밖의 오류가 발생하면, 캐시된 콘텐츠가 오래되었더라도 캐시에서 직접 응답하도록 선택할 수 있습니다.
업스트림 서버가 트래픽 폭주로 과부하 상태에 빠지거나 유지 관리 또는 치명적인 오류로 인해 작동하지 않을 때 매우 유용한 기능입니다. 이 기능을 사용하면 NGINX가 클라이언트에 오류 메시지를 반환하는 대신 캐시에 있는 오래된 콘텐츠를 사용하여 콘텐츠를 계속 제공할 수 있습니다.
15:32 캐싱은 HTTP만을 위한 것이 아닙니다

NGINX의 캐싱은 HTTP에만 국한되지 않습니다. NGINX는 요청을 업스트림 웹 서버로 전달하는 단순한 HTTP 프록시 그 이상입니다. 일반적으로 이러한 업스트림 웹 서버는 FastCGI나 SCGI 같은 API와 연동하는 역할을 합니다. NGINX는 HTTP 프록시와 매우 유사한 프록시 방식으로 이러한 작업을 직접 수행할 수 있습니다.
NGINX는 캐싱 기술을 HTTP 프록시, FastCGI 프록시, uWSGI 프록시, 그리고 SCGI 프록시에 적용할 수 있습니다. 모두 HTTP 프록시와 거의 동일한 방식으로 작동하며, 캐시를 디스크에 저장하고 직접 응답함으로써 이러한 업스트림 서비스로 프록시할 필요가 없습니다.
NGINX는 memcached 서버와 연동할 수도 있는데, 이는 약간 다른 캐싱 방식입니다. 이 경우 NGINX는 콘텐츠를 직접 저장하지 않고 memcached를 프록시로 사용하며, 필요한 콘텐츠를 memcached에 채우기 위해 외부 에이전트에 의존합니다. 이 역시 유용한 도구가 될 수 있으며, 필요한 경우 외부 사이트에서 memcached에 데이터를 채우는 방법도 다양합니다. 따라서 비즈니스 요구 사항에 따라 이를 NGINX용 캐시를 미리 채우는 방법으로 볼 수 있습니다.
17:10 현재 상황을 파악하는 방법

여러 계층으로 구성된 대규모 인프라에서 일부 계층은 캐싱을 수행하고 일부는 수행하지 않으며, 일부는 콘텐츠를 생성하고 일부는 생성하지 않는다면 캐싱은 매우 복잡해질 수 있습니다. 이 경우 어떤 일이 일어나고 있는지, 즉 콘텐츠가 어디에서 오는지 추적하고 발생한 문제를 진단하고 디버깅하기가 상당히 어려울 수 있습니다. NGINX는 이러한 작업을 최대한 쉽게 수행할 수 있도록 지원합니다.
17:38 캐시 계측

정교한 계측 기능을 활용하면 계측을 동적으로 제어하여 콘텐츠의 출처, 캐시 내 저장 위치, 캐시 상태를 추적할 수 있습니다.
첫 번째 단계는 $upstream_cache_status 변수입니다. 이 변수는 캐시에서 제공되었는지 여부와 관계없이 NGINX가 응답한 각 요청에 대해 계산됩니다. 그리고 add_header 지시어를 사용하여 이 변수의 값을 응답에 항상 추가합니다. 일반적으로 이 값은 응답의 X-Cache-Status 헤더에 넣습니다. 이 헤더는 해당 콘텐츠가 어떤 방식으로 제공되었는지를 나타내는 일곱 가지 값 중 하나를 가질 수 있습니다. 예를 들어 캐시를 우회했는지, 재검증을 통해 제공되었는지, 캐시 적중이었는지를 나타냅니다.
응답이 어디에서 오는지 파악하기 위한 첫 단계입니다. 로컬 NGINX 캐시에서 오는 것인지, 아니면 업스트림 서버에서 오는 것인지 확인할 수 있습니다. 또한 curl 같은 도구를 사용해 명령줄에서 확인하거나, 흔히 웹 브라우저의 대화형 디버거를 사용하는 등 다양한 방법으로 해당 응답 헤더를 검사할 수 있습니다.
물론 모든 최종 사용자에게 해당 값을 공개하고 싶지는 않을 수 있습니다. 따라서 응답 헤더에 해당 값을 삽입할 시점을 선별해야 합니다.
19:08 캐시 계측(계속)

NGINX의 구성 언어를 사용하면 필요한 항목만 선택적으로 처리할 수 있습니다. 이 예시는 이를 구현하는 여러 방법 중 하나를 보여 줍니다. 원격 주소를 확인하여 localhost인 경우에는 $upstream_cache_status를 임시 변수($cache_status)에 저장합니다. 마지막으로 응답을 반환할 때 이 임시 변수를 응답에 포함합니다.
이렇게 하면 특정 IP 주소에서 보낸 요청에만 $upstream_cache_status 변수의 값이 표시됩니다. 이 밖에도 다양한 작업을 수행할 수 있습니다. 잠시 후에는 콘텐츠가 디스크에 저장되는 방식을 살펴보겠습니다. 디스크상의 위치를 계산하는 데 사용되는 키를 응답에 포함할 수 있습니다. 또한 캐시가 실행되는 동안 캐시 상태를 진단하는 데 도움이 되도록 응답에 온갖 매개변수를 포함할 수 있습니다.
20:09 캐시 상태

NGINX Plus, NGINX의 상용 버전으로서 캐싱과 같은 사용 사례에 유용한 여러 추가 기능을 제공합니다. NGINX Plus는 모스크바의 엔지니어링 팀이 구축하고, 올바른 작동을 보장하기 위해 광범위한 회귀 테스트를 거친 상용 지원 NGINX 빌드입니다.
NGINX Plus에는 NGINX를 리버스 프록시 및 로드 밸런서로 사용하려는 기업을 위한 다양한 기능도 포함되어 있습니다. 여기에는 로드 밸런싱, 상태 확인, 고급 비디오 스트리밍 관련 기능뿐만 아니라 확장된 상태 정보, 향상된 진단 기능, NGINX에서 발생하는 상황을 시각화하는 기능도 포함됩니다.
[편집자 주 – 위 슬라이드와 다음 단락은 여기에서 원래 설명한 별도의 상태 모듈을 대체하며 더 이상 사용되지 않도록 하는 NGINX Plus API를 지칭하도록 업데이트되었습니다.]
데모를 보려면 demo.nginx.com/dashboard.html에 접속하세요. 그러면 NGINX Plus API를 사용하여 NGINX Plus에서 게시한 상태 데이터를 표시하는 웹 페이지를 볼 수 있습니다. 또한 안내된 curl 명령을 실행하면 NGINX Plus 바이너리에서 직접 가져온 원시 JSON 데이터를 볼 수 있습니다(여기서는 각 요소를 한 줄씩 표시하고 계층 구조에 따라 들여쓰기하기 위해 jq 유틸리티로 파이프합니다).
이 JSON 데이터에서는 NGINX Plus 배포 환경에 있는 각 캐시의 상태에 관한 실시간 데이터를 확인할 수 있습니다. 이 데이터는 $upstream_cache_status 변수 및 캐시를 계측하는 다른 방법들과 함께 NGINX가 콘텐츠를 캐시하는 방식을 전반적으로 파악할 수 있게 해 줍니다. 또한 개별 요청을 상세히 분석하여 해당 요청이 캐시에서 제공되었는지 여부와 현재 캐시 내 상태를 확인할 수 있습니다.
21:57 NGINX의 콘텐츠 캐싱 작동 방식

이제 외부에서 콘텐츠 캐싱을 살펴보는 방법을 알아보았으니, 내부에서는 어떻게 작동하는지 살펴보겠습니다. NGINX 내부에서는 어떤 방식으로 작동할까요? 앞서 말씀드렸듯이 NGINX의 콘텐츠 캐시는 디스크의 파일을 처리하는 방식과 거의 동일하게 작동합니다. 따라서 콘텐츠 캐시에서 콘텐츠를 제공할 때도 정적 콘텐츠를 제공할 때와 동일한 성능과 안정성, 운영 체제 최적화 효과를 얻을 수 있습니다. 바로 NGINX가 명성을 얻은 그 성능입니다.
22:40 작동 방식

콘텐츠 캐시는 디스크의 영구 캐시에 저장됩니다. 운영 체제와 연동하여 이 디스크 캐시를 메모리로 스왑하고, 어떤 콘텐츠를 메모리에 저장해야 하는지 운영 체제의 페이지 캐시에 힌트를 제공합니다. 따라서 캐시에서 콘텐츠를 제공해야 할 때 매우 빠르게 처리할 수 있습니다.
캐시에 저장된 항목과 만료 시간 등의 캐시 관련 메타데이터는 모든 NGINX 프로세스가 공유하는 메모리 영역에 별도로 저장되며 항상 메모리에 상주합니다. 따라서 NGINX는 캐시를 매우 빠르게 조회하고 검색할 수 있으며, 응답을 가져와 최종 사용자에게 제공해야 할 때만 페이지 캐시에 접근하면 됩니다.
콘텐츠가 캐시에 저장되는 방식, 시작 시 영구 캐시가 비어 있는 NGINX 워커 프로세스에 로드되는 방식, NGINX가 캐시에서 자동으로 수행하는 몇 가지 유지 관리 작업을 살펴본 다음, 특정 상황에서 캐시의 콘텐츠를 수동으로 제거하는 방법을 알아보며 마무리하겠습니다.
23:53 캐시된 콘텐츠는 어떻게 저장되나요?

콘텐츠 캐시는 proxy_cache_path라는 지시어를 사용하여 선언한다는 점을 기억하실 것입니다. 이 지시어는 캐시가 파일 시스템에 저장되는 위치, 캐시 이름, 메타데이터를 위해 메모리에 할당되는 캐시 크기, 디스크의 캐시 크기 등 여러 매개변수를 지정합니다. 이 경우 디스크의 캐시 크기는 40 MB입니다.
콘텐츠가 저장되는 위치를 이해하려면 캐시 키, 즉 NGINX가 캐시 가능한 각 리소스에 할당하는 고유 식별자를 이해해야 합니다. 기본적으로 이 식별자는 요청의 기본 매개변수인 스킴, Host 헤더, URI 및 모든 문자열 인수를 조합하여 생성됩니다.
하지만 원한다면 쿠키 값이나 인증 헤더, 심지어 런타임에 계산한 값 등을 사용하여 캐시 키를 확장할 수 있습니다. 예를 들어 영국 사용자와 미국 사용자를 위해 서로 다른 버전을 저장할 수 있습니다. 이 모든 것은 proxy_cache_key 지시어를 구성하여 구현할 수 있습니다.
NGINX가 요청을 처리할 때 proxy_cache_key를 계산한 다음, 그 값으로 MD5 해시를 계산합니다. 슬라이드 아래쪽에 표시한 명령줄 예제를 사용하면 직접 동일한 작업을 재현할 수 있습니다. 캐시 키인 httplocalhost:8002/time.php를 md5sum에 전달합니다. 셸에서 이 작업을 수행할 때 줄 바꿈 문자까지 함께 전달하지 않도록 주의하세요.
그러면 캐시 가능한 콘텐츠에 해당하는 MD5 해시값이 계산됩니다. NGINX는 이 해시값을 사용하여 해당 콘텐츠가 디스크에 저장될 위치를 계산합니다. proxy_cache_path에서 한 문자로 된 디렉터리와 그 아래 두 문자로 된 디렉터리로 구성된 2단계 캐시를 지정한 것을 볼 수 있습니다. 문자열 끝에서 해당 문자들을 가져와 4라는 디렉터리와 9b라는 하위 디렉터리를 만든 다음, 캐시 콘텐츠(헤더와 소량의 메타데이터 포함)를 디스크의 파일에 저장합니다.
콘텐츠 캐싱을 테스트할 수 있습니다. 캐시 키를 응답 헤더 중 하나로 출력한 다음, md5sum에 전달하여 해당 값에 대응하는 해시를 계산할 수 있습니다. 그런 다음 디스크에 저장된 값을 살펴 실제로 존재하는지 확인하고 NGINX가 캐시한 헤더도 검토하여 이 모든 요소가 어떻게 연계되는지 이해할 수 있습니다.
26:36 디스크에서 캐시 불러오기

이제 콘텐츠가 디스크에 영구적으로 저장되므로 NGINX가 시작될 때 해당 콘텐츠를 메모리에 로드해야 합니다. 더 정확히 말하면, 디스크 캐시를 순회하면서 메타데이터를 추출한 다음 각 워커 프로세스가 사용하는 공유 메모리 세그먼트에 메타데이터를 로드해야 합니다. 이 작업은 캐시 로더라는 프로세스를 통해 수행됩니다.
캐시 로더는 시작 시 실행되어 한 번만 작동하며, 메타데이터를 작은 단위로 디스크에서 불러옵니다. 한 번에 100개 파일을 최대 200ms 동안 처리한 다음 50ms 동안 일시 중지하는 과정을 전체 캐시를 모두 훑어 공유 메모리 세그먼트를 채울 때까지 반복합니다.
그런 다음 캐시 로더는 종료되며, NGINX가 다시 시작되거나 재구성되어 공유 메모리 세그먼트를 다시 초기화해야 하는 경우가 아니면 다시 실행할 필요가 없습니다. 디스크가 매우 빠르고 부하가 적다면 캐시 로더의 동작을 조정하는 것이 적절할 수 있습니다. 캐시 로더를 더 빠르게 실행하도록 설정할 수도 있고, 파일 수가 매우 많은 캐시를 느린 디스크에 저장하면서 NGINX 시작 시 캐시 로더가 과도한 CPU를 사용하지 않도록 하려면 실행 속도를 조금 낮출 수도 있습니다.
28:07 디스크 캐시 관리

캐시가 메모리에 올라가고 파일이 디스크에 저장되면, 이후 한 번도 액세스되지 않는 캐시 파일이 영구적으로 남아 있을 수 있습니다. NGINX는 파일을 처음 확인할 때 저장하지만, 해당 파일에 대한 요청이 더 이상 없으면 무언가가 이를 정리할 때까지 [해당 파일]은 디스크에 그대로 남아 있게 됩니다.
이 역할을 하는 것이 바로 캐시 관리자입니다. 캐시 관리자는 주기적으로 실행되어 일정 기간 동안 액세스되지 않은 파일을 디스크에서 제거하며, 캐시가 너무 커져 지정된 크기를 초과한 경우에도 파일을 삭제합니다. 파일은 가장 오래 사용되지 않은 순서대로 삭제됩니다. 캐시 로더를 구성할 때와 마찬가지로 proxy_cache_path [지시어]의 매개변수를 사용하여 이 작업을 구성할 수 있습니다:
- 기본
inactive시간은 10분입니다. - The
max-size매개변수에는 기본 제한이 없습니다. 캐시에max-size제한을 설정하면 일시적으로 해당 제한을 초과할 수 있지만, 캐시 관리자가 실행되면 가장 오래 사용되지 않은 파일을 정리하여 다시 제한 이내로 줄입니다.
29:22 디스크에서 콘텐츠 제거

마지막으로, 디스크에서 콘텐츠를 완전히 삭제해야 할 때도 있습니다. 파일을 찾아 삭제하려는 경우, 앞서 설명한 기법, 즉 캐시 키를 md5sum에 전달하거나 파일 시스템 전체에서 재귀적으로 grep을 실행하여 삭제해야 할 파일을 식별하는 방법을 알고 있다면 비교적 쉽게 처리할 수 있습니다.
또는 NGINX Plus를 사용 중이라면 제품에 내장된 캐시 제거 기능을 사용할 수 있습니다. 캐시 제거 기능을 사용하면 요청에서 특정 매개변수를 가져올 수 있으며, 일반적으로 캐시 제거 요청임을 식별하기 위해 PURGE라는 메서드를 사용합니다.
퍼지는 URI를 검사하고 해당 URI와 일치하는 모든 파일을 캐시에서 삭제하는 특수 NGINX Plus 핸들러가 처리합니다. URI 끝에 별표를 붙여 접두 패턴으로 사용할 수 있습니다. 여기서는 퍼지 기능을 사용하여 localhost 호스트의 8001번 포트에서 제공되는 모든 파일을 삭제하지만, 물론 하위 디렉터리를 지정할 수도 있습니다.
어떤 방법을 사용하든 언제든지 디스크의 캐시에서 파일을 삭제하거나 전체 캐시 디렉터리에 rm -rf 명령을 실행해도 전혀 문제가 없습니다. NGINX는 중단 없이 계속해서 디스크에 파일이 있는지 확인합니다. 파일이 없으면 캐시 미스가 발생합니다. 그러면 NGINX는 원본 서버에서 콘텐츠를 다시 가져와 디스크의 캐시에 저장합니다. 따라서 캐시에서 개별 파일을 삭제해야 하는 경우에도 언제나 안전하고 안정적으로 처리할 수 있습니다.
31:27 캐싱 제어

지금까지 캐싱의 작동 방식과 NGINX 내부의 구현을 살펴보고, 정적 콘텐츠와 동일한 수준의 성능을 내기 위해 파일을 디스크에 저장하는 방식도 자세히 알아보았습니다. 이제 캐싱을 좀 더 지능적으로 활용하는 방법을 살펴보겠습니다.
단순한 사이트의 경우 캐싱을 활성화하기만 하면 대체로 원하는 수준의 성능과 캐시 동작을 유지하는 데 필요한 작업을 정확히 수행합니다. 하지만 언제나 최적화할 여지는 있으며, 기본 동작이 원하는 동작과 일치하지 않는 경우도 많습니다.
오리진 서버가 올바른 응답 헤더를 설정하지 않았거나, 오리진 서버에서 지정한 설정을 NGINX 자체에서 재정의하고 싶을 수 있습니다. 캐싱 작동 방식을 세밀하게 조정할 수 있도록 NGINX를 구성하는 방법은 매우 다양합니다.
32:30 지연 캐싱

캐싱을 지연할 수 있습니다. 방대한 콘텐츠 모음이 있고 그중 상당수가 한 시간이나 하루에 한두 번만 액세스되는 경우에 흔히 사용합니다. 예를 들어 읽는 사람이 거의 없는 회사 브로슈어가 있다면 해당 콘텐츠를 캐싱하려는 것은 시간 낭비인 경우가 많습니다. 지연 캐싱을 사용하면 임계값을 설정할 수 있습니다. 이 콘텐츠가 일정 횟수 이상 요청된 경우에만 캐시된 버전을 저장합니다. proxy_cache_min_uses 임계값에 도달하기 전까지는 캐시에 버전을 저장하지 않습니다.
이를 통해 어떤 콘텐츠를 캐시에 저장할지 더 선별적으로 결정할 수 있습니다. 캐시 자체는 한정된 리소스이며, 일반적으로 서버의 메모리 용량에 따라 제한됩니다. 캐시가 가능한 한 메모리에 상주하도록 해야 하기 때문입니다. 따라서 특정 유형의 콘텐츠는 제한하고 자주 요청되는 콘텐츠만 캐시에 저장하는 것이 좋습니다.
캐시 재검증은 최근 NGINX Open Source와 NGINX Plus에 추가된 기능입니다. 이 기능은 If-Modified-Since 기능을 변경하여 NGINX가 캐시된 값을 새로 고쳐야 할 때 해당 콘텐츠의 새 버전을 가져오기 위해 단순한 GET 요청을 보내는 대신 조건부 GET 요청을 보내면서 “이 특정 날짜와 시간에 수정된 캐시 버전을 가지고 있습니다”라고 알리도록 합니다.
오리진 서버는 304 Not Modified로 응답하여, 현재 보유한 버전이 여전히 최신 버전임을 알릴 수 있습니다. 이렇게 하면 업스트림 대역폭을 절약할 수 있습니다. 오리진 서버가 변경되지 않은 콘텐츠를 다시 전송할 필요가 없으며, 디스크 쓰기 작업도 줄일 수 있습니다. 또한 NGINX가 해당 콘텐츠를 디스크로 스트리밍한 다음 기존 버전을 덮어쓰도록 교체할 필요도 없습니다.
34:45 캐시 시간 제어

콘텐츠를 캐시할 기간을 세밀하게 제어할 수 있습니다. 대개 원본 서버는 브라우저에 적합한 캐시 헤더와 함께 콘텐츠를 제공합니다. 즉, 비교적 자주 콘텐츠 새로 고침을 요청하면서 장기간 캐싱하도록 설정합니다. 하지만 변경 사항을 더 빠르게 반영하기 위해 원본 서버 바로 앞에 있는 NGINX 프록시가 파일을 조금 더 자주 새로 고치도록 설정할 수도 있습니다.
브라우저의 캐시 제한 시간을 60초에서 10초로 줄이면 부하가 크게 증가하지만, NGINX의 캐시 제한 시간을 60초에서 10초로 줄이면 부하는 아주 조금만 증가합니다. 요청 하나당 오리진 서버에 분당 5개의 요청이 추가되는 반면, 원격 클라이언트의 경우에는 사이트에서 유사한 작업을 수행 중인 클라이언트 수에 따라 달라집니다.
따라서 오리진 서버에서 지정한 로직과 의도를 재정의할 수 있습니다. 다음과 같은 특정 헤더를 숨기거나 NGINX가 무시하도록 설정할 수 있습니다: X-Accel-Expires, Cache-Control 또는 Expires. 또한 NGINX 구성에서 proxy_cache_valid 지시어를 사용하여 기본 캐시 시간을 지정할 수 있습니다.
36:18 캐시 / 캐시 안 함

경우에 따라 원본 서버에서 캐시할 수 있다고 지정한 콘텐츠를 캐시하지 않거나, NGINX에 저장된 콘텐츠 버전을 반드시 우회해야 할 수 있습니다. proxy_cache_bypass 및 proxy_no_cache 지시문을 사용하면 이러한 동작을 세밀하게 제어할 수 있습니다.
특정 요청 헤더(예: HTTP 인증 헤더)가 설정되어 있거나 요청 매개변수가 있는 경우 캐시를 우회하도록 지정하는 간편한 방법으로 이러한 지시어를 사용할 수 있습니다. 이때 NGINX의 캐시를 자동으로 업데이트하거나, 캐시를 완전히 건너뛰고 항상 원본 서버에서 콘텐츠를 가져오도록 할 수 있습니다.
일반적으로 이러한 설정은 상당히 복잡한 캐싱 결정을 내릴 때 사용됩니다. 쿠키와 인증 헤더의 값을 세밀하게 판단하여 무엇을 캐시할지, 무엇을 항상 원본 서버에서 가져올지, 무엇을 NGINX 캐시에 절대 저장하지 않을지를 제어합니다.
37:25 다중 캐시

마지막으로, 초대규모 배포 환경에서는 몇 가지 이유로 단일 NGINX 인스턴스 내에서 여러 캐시를 사용하는 방안을 고려할 수 있습니다. 사이트의 특성과 성능의 중요도에 따라 NGINX 프록시의 테넌트별로 서로 다른 캐시 정책을 적용할 수 있으며, 공유 호스팅 환경에서는 각 테넌트가 가입한 요금제에 따라서도 정책을 달리할 수 있습니다.
또는 NGINX 호스트에 여러 디스크가 있을 수 있으며, 이 경우 각 디스크에 개별 캐시를 배포하는 것이 가장 효율적입니다. 핵심 원칙은 디스크 간 복사 횟수를 최소화하는 것입니다. 이를 위해 각 캐시를 특정 디스크에 고정하고, 해당 캐시를 사용하는 각 프록시의 임시 파일도 올바른 디스크에 고정할 수 있습니다.
표준 동작에서는 NGINX가 업스트림 프록시로부터 콘텐츠를 수신하면, 콘텐츠가 메모리에 들어갈 만큼 충분히 작지 않은 한 해당 콘텐츠를 디스크로 스트리밍합니다. 콘텐츠가 디스크로 스트리밍된 후에는 캐시로 이동합니다. 임시 파일의 위치(임시 파일이 저장되는 디스크)와 캐시가 저장되는 디스크가 같으면 이 작업의 효율성이 크게 향상됩니다.
39:07 빠른 복습 – 캐시를 사용하는 이유는?

지금까지 캐싱과 NGINX에서 사용하는 방식, NGINX 내부의 구현, 그리고 이를 조정하는 방법에 대해 살펴보았습니다. 이제 마무리할 시간이 다가왔으니, 애초에 콘텐츠를 캐싱해야 하는 이유를 되짚어보도록 하겠습니다. NGINX는 전 세계 1억 1,400만 개의 웹사이트에 배포되어 있으며, 많은 사용자가 웹 가속 및 콘텐츠 캐싱 기능 때문에 NGINX를 사용합니다. [편집자 주 – 이 통계는 웨비나가 진행된 2014년 5월 당시의 수치입니다.]
39:44 페이지 속도가 중요한 이유는 무엇인가요?

이러한 기능은 웹사이트의 속도를 향상해 최종 사용자에게 더 나은 경험을 제공하며, 웹페이지 속도는 정말로 매우 중요합니다. 분석가들은 오랫동안 사용자 행동을 관찰하며 흔히 ‘N초 규칙’이라고 부르는 개념을 제시해 왔습니다. 이는 일반 사용자가 페이지가 로드되고 표시되기를 기다리다가 지루함과 답답함을 느껴 다른 웹사이트, 즉 경쟁사 사이트로 이동하기 전까지 감수할 수 있는 시간을 뜻합니다.
표준이 개선되고 사용자의 기대치가 점점 높아지면서, 사용자가 기다릴 수 있는 시간은 계속 줄어들고 있습니다. 다소 미심쩍은 계산을 적용해 이 추세를 연장해 보면, 2016년쯤에는 사용자의 인내심이 마이너스 수준에 이를 것이라는 결론을 내릴 수도 있습니다.
40:46 구글이 규칙을 바꾸다

하지만 사실 기술은 이미 우리를 앞질렀습니다. Google은 몇 년 전 Google Instant Search를 선보이며 이를 시각적으로 잘 보여주었습니다. 이제 Google에서는 검색창에 검색어를 입력하면, 입력을 마치기도 전에 예상 검색 결과가 표시됩니다. 이는 현대 인터넷에 대한 기대치가 얼마나 크게 달라졌는지를 보여줍니다. Google이 직접 말했듯이, “이제 사용자들은 책장을 넘길 때처럼 웹페이지도 빠르고 매끄럽고 자연스럽게 반응하기를 기대합니다.”
41:28 성능 저하로 인한 비용

이러한 수준의 성능을 충족하지 못하면 웹사이트나 웹 서비스에 설정한 핵심 성과 지표(KPI)에 상당한 영향을 미칠 수 있습니다. 광고 클릭률을 예로 들면, Google은 검색 페이지의 로딩 시간이 0.5초 늘어났을 때 광고 클릭률이 20% 감소한다는 사실을 확인했습니다. 매출의 경우, Amazon은 느린 웹페이지가 미치는 영향을 조사하기 위해 의도적으로 페이지 로딩 시간을 100ms 단위로 늘렸으며, 그 결과 영향을 받은 고객에게서 발생하는 매출이 100ms 늘어날 때마다 일반적으로 1%씩 감소한다는 사실을 확인했습니다.
다른 많은 분석가와 웹사이트 및 조사 기관에서도 페이지 체류 시간, 이탈률 등 웹사이트 지표에 미치는 유사한 영향을 보고했습니다. 최근에는 Google이 검색 결과의 페이지 순위를 산정할 때 페이지 속도를 반영하기 시작했습니다. 이때 중요한 요소는 첫 바이트가 도착하기까지 걸리는 시간인 것으로 보입니다. 페이지를 로드할 때 첫 바이트를 받는 데 시간이 오래 걸릴수록 페이지 순위에 더 큰 불이익을 받습니다. 웹사이트가 Google 검색 결과의 3, 4, 5페이지에 표시되어 아예 방문조차 이루어지지 않는 상황이 발생할 수 있습니다.
43:00 NGINX 캐싱으로 가능한 작업

NGINX의 캐싱 기능을 활용하면 첫 바이트 수신 시간(TTFB)을 단축하고 웹 콘텐츠가 더 빠르고 즉각적으로 반응하도록 하여 최종 사용자 경험을 개선할 수 있습니다.
NGINX를 사용하면 웹 인프라를 통합하고 간소화할 수 있습니다. NGINX는 단순한 독립형 웹 캐시가 아닙니다. NGINX에는 웹 오리진 서버와 FastCGI 같은 API로 연결되는 직접 게이트웨이가 포함되어 있으며, NGINX Plus에는 정교한 엔터프라이즈급 로드 밸런서와 애플리케이션 전송 컨트롤러를 구축할 수 있는 기능도 포함되어 있습니다. 따라서 웹 인프라의 여러 네트워크 구성 요소를 NGINX 또는 NGINX Plus라는 하나의 구성 요소로 통합하여 더욱 안정적이고 디버깅이 쉬우며 빠른 솔루션을 구현할 수 있습니다.
NGINX를 사용하면 반복적인 작업을 업스트림 서버에서 분리하여 서버 용량을 늘릴 수 있습니다. 실제로 캐시할 수 없어 보이는 콘텐츠(예: 블로그 사이트의 첫 페이지)도 NGINX 프록시에서 단 1초 동안 캐시하면 효과를 볼 수 있습니다.
100명의 사용자가 같은 순간에 동일한 콘텐츠를 요청하면 NGINX는 오리진 서버로 보내는 요청을 하나로 줄이고, 콘텐츠가 최신 상태에서 1초 이상 뒤처지지 않도록 보장하면서 캐시에서 해당 사용자들에게 콘텐츠를 제공합니다. 이는 블로그나 이와 유사한 웹사이트에는 대개 충분하며, 업스트림 서버의 부하뿐 아니라 충분한 용량을 관리하고 배포하는 데 드는 비용 측면에서도 성능에 큰 차이를 가져옵니다.
마지막으로 NGINX의 전략적 활용 방법 중 하나를 잊지 마세요. 프록시 캐시의 ‘오래된 콘텐츠 사용(use stale)’ 기능을 통해 업스트림 서버 장애의 영향을 받지 않도록 보호하는 것입니다. 업스트림 서버의 응답이 느리거나 오류를 반환하거나 어떤 문제가 발생하면 NGINX는 로컬에 캐시된 콘텐츠 버전으로 대체하여 업스트림 서버가 복구될 때까지 계속 사용할 수 있습니다.
45:21 마무리 말씀

전 세계에서 트래픽이 가장 많은 웹사이트 중 38%가 주로 웹 가속 및 콘텐츠 캐싱 기능을 위해 NGINX를 사용합니다. [편집자 주 – 이 통계는 2014년 5월 웨비나 진행 당시의 수치입니다. 현재 수치는 여기에서 확인하세요.] 더 많은 솔루션과 자세한 내용은 nginx.com의 블로그와 기능 소개 자료에서 NGINX 및 NGINX Plus의 기능을 확인하세요. 또한 웨비나 목록에서 향후 웨비나뿐만 아니라 곧 추가될 이 시리즈의 지난 웨비나도 살펴보세요.
이러한 기능을 더 자세히 살펴보고 싶다면 물론 nginx.org와 nginx.com에서 관련 문서와 솔루션을 확인할 수 있지만, 직접 다운로드하여 사용해 보는 것만큼 좋은 방법은 없습니다. 오픈 소스 제품은 nginx.org에서, 추가적인 부하 분산, 애플리케이션 전송, 관리 및 사용 편의성 기능과 기술 지원을 제공하는 상용 제품은 nginx.com에서 확인할 수 있습니다.
여러분, 귀한 시간 내어 경청해 주셔서 대단히 감사합니다. 이번 프레젠테이션과 NGINX의 콘텐츠 캐싱에 대한 설명이 많은 분께 유익하고 이해를 넓히는 데 도움이 되었기를 바랍니다.
질의응답
이제 질문을 하나씩 풀어보면서 얼마나 잘하는지 확인해 봅시다.
47:20 바이트 범위 요청
바이트 범위 요청에 관한 질문이 있습니다. 바이트 범위 요청은 클라이언트가 콘텐츠를 요청하지만 그중 일부만 필요할 때 사용됩니다. 예를 들어 동영상 파일에서 클라이언트가 동영상의 일부만 필요할 수 있습니다. 또는 흔히 PDF 파일의 경우, 사용자가 PDF 파일의 색인이 포함된 헤더를 읽은 후 클라이언트가 특정 페이지만 다운로드하려고 할 수 있습니다. 이러한 요청은 NGINX의 콘텐츠 캐시에서 어떻게 처리되나요?
처리 과정은 다음과 같습니다. NGINX가 리소스에 대한 바이트 범위 요청을 수신했을 때 전체 리소스가 이미 캐시에 있으면, 클라이언트가 요청한 바이트 범위를 캐시에서 반환합니다. 해당 리소스가 캐시에 없으면 NGINX는 업스트림 서버에 전체 리소스를 요청하고 이를 캐시에 저장합니다. 현재는 이 시점에 NGINX가 바이트 범위 요청을 따르지 않고 전체 리소스를 클라이언트에 반환합니다. 대부분의 경우 이는 허용 가능한 동작입니다.
예를 들어 클라이언트가 PDF 문서를 다운로드하는 경우, 첫 번째 요청은 어차피 문서 전체를 대상으로 합니다. 해당 문서가 스트리밍될 때에만 클라이언트가 연결을 중단한 후 바이트 범위 요청을 보내기 시작합니다. 따라서 캐시된 콘텐츠의 경우 NGINX는 바이트 범위 요청을 처리합니다. 캐시되지 않은 콘텐츠의 경우에는 NGINX가 업스트림 서버에서 전체 콘텐츠를 가져와 해당 요청에 한해 클라이언트에 전체 콘텐츠를 반환합니다.
49:13 프록시 캐시 재검증
프록시 캐시 재검증 기능에 관한 질문입니다. 이 기능을 사용하면 NGINX가 콘텐츠의 변경 여부를 확인하기 위해 업스트림 서버에 조건부 GET 요청을 보낼 수 있습니다. 질문은 다음과 같습니다:
프록시 캐시 재검증 시 ETag도 고려하나요, 아니면 콘텐츠의 If-Modified-Since 날짜만 고려하나요?
결론부터 말하면 콘텐츠의 If-Modified-Since 날짜만 확인합니다. 따라서 실무에서는 응답에 항상 If-Modified-Since를 포함하고, ETag는 응답에서 처리하는 ‘마지막 수정’ 날짜만큼 일관되거나 폭넓게 지원되지 않으므로 선택 사항으로 취급하는 것이 좋습니다.
50:07 동일한 용량의 디스크에 캐시 분산하기
최상의 성능을 위해 NGINX가 단일 사이트의 캐시를 동일한 용량의 여러 디스크에 분산하도록 할 수 있나요?
네, 가능합니다. 다만 약간의 작업이 필요합니다. 일반적으로는 RAID를 구성하지 않은 여러 디스크를 배치한 다음, 각 디스크에 하나씩 고정된 개별 캐시를 구성합니다. 이를 위해서는 몇 가지 추가 설정과 트래픽 분할이 필요합니다. 구성에 도움이 필요하다면 커뮤니티에 문의해 주세요. 구체적인 요청을 확인해 드리겠습니다. NGINX Plus를 사용 중이라면 지원팀에 문의해 주세요. 기꺼이 도와드리겠습니다.
50:50 Vary 헤더
NGINX는 캐시 적중 및 미적중 여부를 판단할 때 Vary 헤더를 고려하나요?
아니요, NGINX는 Vary 헤더를 자동으로 처리하지 않습니다. 이것이 문제가 된다면 Vary 헤더를 프록시 캐시 키에 추가하면 됩니다. 그러면 응답을 저장하는 데 사용되는 고유 키에 Vary 헤더 값이 포함되어 서로 다른 여러 버전을 저장할 수 있습니다.
51:25 캐싱 기본 요소
모든 캐싱 기본 요소와 지시문이 제대로 준수되고 있나요?
대체로 그렇습니다. 다만 Vary 헤더가 적용되지 않는 것과 같은 몇 가지 예외적인 경우가 있습니다. 여러 캐시가 RFC의 요구 사항을 해석하는 방식에는 어느 정도 재량의 여지가 있는 경우가 많습니다. 가능한 경우 신뢰할 수 있고 일관되며 구성하기 쉬운 구현 방식을 채택했습니다.
51:52 업스트림 헤더 및 데이터
업스트림 헤더와 데이터가 모두 캐시되고 있나요?
예, 둘 다 캐시됩니다. 캐시에서 응답을 받는 경우 응답 본문뿐만 아니라 헤더도 캐시된 것입니다.
52:13 *‑Encoding 헤더
헤더 값도 응답 본문과 함께 캐시되므로… NGINX가 다양한 Transfer-Encoding 조합에서 제대로 작동하지 않는다면 상당히 놀라울 것입니다. Accept-Encoding은 흔히 Vary 헤더를 통해 구현되므로, 앞서 언급한 대로 Vary 헤더를 캐시 키에 포함해야 한다는 내용이 여기에도 적용됩니다(이를 지원하지 않는 클라이언트의 경우).
52:56 SPDY
SPDY에서도 캐싱이 작동하나요?
물론입니다. NGINX의 프런트엔드 프록시라고 생각할 수 있지만, 실제로는 NGINX 커널과 매우 긴밀하게 통합되어 있습니다. 네, SPDY에서도 캐싱이 작동합니다.
53:15 Vary 헤더, 2라운드
다음은 Vary 헤더에 관한 또 다른 질문입니다. 다시 말씀드리면, Vary 헤더가 포함된 응답과 gzip 압축을 사용하고 있다면 Trac 또는 저희 커뮤니티 사이트의 관련 논의를 살펴보고 해결 방법을 찾아보세요. 가장 일반적인 방법은 캐시 키에 Vary 헤더를 포함하는 것입니다.
53:45 PageSpeed
Q: PageSpeed는 NGINX 캐싱을 사용하나요, 아니면 자체 캐싱 메커니즘을 사용하나요?
그 질문은 PageSpeed 개발자에게 문의해야 합니다.
54:00 기타 캐시
Q: 다른 콘텐츠 캐시는 NGINX와 비교하면 어떤가요?
CDN은 매우 효과적인 콘텐츠 캐싱 솔루션입니다. CDN은 서비스 형태로 제공되므로 콘텐츠가 캐시되는 방식과 캐시 만료 방식을 제어하는 데에는 다소 제약이 있지만, 콘텐츠를 최종 사용자에게 더 가까이 제공하는 데 매우 효과적인 도구입니다. NGINX는 웹 애플리케이션을 가속하는 데 매우 효과적인 도구이며, 일반적으로 CDN과 함께 배포됩니다. Varnish와 같은 독립형 캐시 역시 여러 측면에서 NGINX와 유사하게 작동하는 매우 뛰어난 기술입니다. NGINX의 장점 중 하나는 오리진 서버 기능, 애플리케이션 게이트웨이, 캐싱 및 부하 분산을 하나의 솔루션으로 통합한다는 점입니다. 따라서 더 간단하고 통합된 인프라를 구축할 수 있으며, 배포와 관리가 더 쉽고 문제가 발생했을 때 디버깅과 진단도 더 수월합니다.
이 게시물의 바탕이 된 웨비나를 시청하려면 여기에서 확인하세요.
NGINX Plus를 사용해 보려면 지금 30일 무료 체험을 시작하거나 문의하여 사용 사례에 대해 상담해 보세요.
작성자 소개

관련 블로그 게시물
시청하기: KubeCon North America 2023에서의 NGINX Gateway Fabric
이 영상에서는 KubeCon의 NGINX 부스를 찾아가, 저희가 공개한 Kubernetes Gateway API 구현을 살펴봅니다.
F5 NGINX로 대규모 WAF 플릿을 확인하고 관리하는 2가지 방법
앱 보안을 강화하는 두 가지 방법을 소개합니다. NGINX Management Suite를 통해 WAF 운영에 대한 가시성을 높이고, NGINX App Protect WAF를 활용해 코드형 보안을 구현하는 것입니다.
Ingress 컨트롤러 선택 가이드, 4부: NGINX Ingress Controller 옵션
개발 주체, 개발 철학, 프로덕션 환경 준비도, 보안 및 지원을 기준으로 어떤 NGINX Ingress 컨트롤러가 가장 적합한지 알아보세요.
진화하는 공격 환경에 NGINX App Protect Denial of Service가 적응하는 방법
NGINX App Protect Denial of Service(DoS)가 계층 7 DoS 공격을 방어하기 위해 완화 방법을 실시간으로 조정하는 방식을 설명합니다. 정상적인 사용자 행동과 서버 상태에 대한 모델을 생성하여 공격을 나타내는 이상 징후를 감지하고 맞춤형 완화 시그니처를 적용할 수 있습니다.
NGINX Instance Manager를 활용한 대규모 구성 업데이트 및 인증서 관리
NGINX Instance Manager 버전 1.0에는 두 가지 새로운 기능이 도입되었습니다. NGINX 인스턴스에 태그와 사용자 역할을 지정하면 전체 그룹에 구성을 한 번에 적용할 수 있습니다. 인증서 검사는 만료 예정인 인증서를 감지하여 서비스 중단을 방지할 수 있도록 인증서를 교체할 수 있게 해줍니다.
Netflix가 자사 CDN의 핵심으로 NGINX를 선택한 이유
Netflix가 글로벌 콘텐츠 전송 네트워크인 Open Connect의 스트리밍 미디어 기술로 NGINX를 선택한 이유를 NGINX 개발자 Gleb Smirnoff에게서 알아보세요.