공유 사전으로 압축 효율성 극대화

데이터 압축은 오랜 기간에 걸쳐 검증된 성능 최적화 기법으로, 요건을 충족하는 페이지 리소스의 크기를 줄입니다. 한동안 웹 서버에서 주로 gzip을 사용하여 HTML, CSS, JavaScript 파일과 같은 일반적인 텍스트 기반 페이지 리소스를 압축하고 클라이언트로 전송하여 압축을 푸는 것이 일반적이었습니다. 그 결과 페이지의 의도된 동작에 영향을 주지 않으면서 리소스의 로드 시간이 단축됩니다.

gzip은 그 자체로 매우 효과적이지만 최근 몇 년 동안 웹의 압축이 더욱 개선되었습니다. 2016년에는 Brotli 알고리즘이 Chrome에 제공되어 대상 리소스의 전반적인 압축률이 개선되었습니다. 2017년 말까지 모든 최신 브라우저에서 Brotli를 지원했으며, 서버 지원도 점점 더 확대되기 시작했습니다. 최근에는 Chrome에서 ZStandard 압축을 출시했습니다.

하지만 여기서 끝나는 것이 아닙니다. Chrome팀은 웹에서 공유 사전을 사용할 수 있도록 노력해 왔으며, 이제 Brotli 및 ZStandard 모두의 출처 체험판에서 사용할 수 있습니다. 공유 사전은 Brotli 및 ZStandard 압축을 보완하여 업데이트된 코드를 자주 제공하는 웹사이트의 압축률을 상당히 높일 수 있으며 경우에 따라 90% 이상의 압축률을 제공할 수 있습니다. 이 게시물에서는 공유 사전의 작동 방식과 웹사이트에서 Brotli 및 ZStandard에 이를 사용하기 위해 출처 체험판을 등록하는 방법을 자세히 설명합니다.

공유 사전 설명

압축은 입력에서 중복 시퀀스를 찾아 그 정보를 사용하여 훨씬 더 작은 출력을 만드는 프로세스이며, 나중에 이 출력을 역으로 변환할 수 있습니다. 압축은 리소스 로드 시간을 크게 줄여주므로 웹에서 잘 작동합니다. Brotli와 ZStandard 모두 이러한 알고리즘이 압축 중에 사용할 수 있는 추가 패턴 모음인 압축 사전을 사용하여 효과를 더욱 높일 수 있습니다. 실제로 Brotli의 높은 효율성은 내부 사전을 사용하여 어느 정도 달성됩니다.

그러나 사용자가 선별한 맞춤 사전은 특정 리소스에만 해당하는 패턴이 포함된 Brotli 및 ZStandard와 함께 사용할 수 있습니다. 실제로 맞춤 사전은 모든 입력에 적용할 수 있는 외부 파일입니다. 사전은 애플리케이션의 프로덕션 코드에 매우 구체적이거나 모든 콘텐츠에 적용될 수 있습니다. 특정 사전이 입력에 얼마나 적용 가능한지는 전체 압축 효율에 큰 영향을 미칠 수 있습니다. 입력의 콘텐츠와 매우 유사한 사전은 일반적이거나 유사하지 않은 콘텐츠가 포함된 사전보다 압축률이 높은 출력을 생성합니다.

맞춤 압축 사전이 얼마나 효과적인지 보여주는 예를 들면 다음과 같습니다. 웹사이트에서 Angular 프레임워크를 사용하고 현재 사용 중인 버전이 1.7.9라고 가정해 보겠습니다. 이 버전의 Angular 프레임워크는 압축되지 않은 상태에서 약 172KiB입니다. Brotli의 기본 설정으로 압축하면 크기가 약 53KiB가 됩니다. 이렇게 하면 압축률이 거의 70% 가 됩니다. 하지만 나중에 Angular 1.8.3으로 업그레이드하기로 결정했다고 가정해 보겠습니다. 이 버전의 Angular는 버전 1.7.9와 크기가 거의 동일하므로 이전 버전과 거의 동일한 압축률을 예상할 수 있습니다.

이때 델타 압축이라는 프로세스를 사용하여 커스텀 사전을 유용하게 사용할 수 있습니다. 델타 압축은 이전 버전의 리소스 사전을 사용하여 이후 버전을 압축하는 것입니다. 이전 예를 사용하여 버전 1.7.9를 사전으로 사용하여 Angular 버전 1.8.3을 압축하면 출력은 4KiB를 약간 넘게 됩니다. 이는 약 98%의 압축률을 나타냅니다. 압축 사전은 로드 성능에 큰 영향을 줄 수 있으며, 그 효과는 이미 실제 애플리케이션에서 실현되었습니다.

하지만 이 흐름을 웹에서 작동하도록 하는 데는 어려움이 있습니다. 단점은 사전을 사용하여 리소스를 압축하는 경우 압축을 풀려면 동일한 사전이 필요하다는 점입니다. 이 흐름은 이전에 웹(예: SDCH)에서 시도되었지만 안전하게 구현하기가 어려웠습니다. 공유 사전 압축에 관한 이 최신 제안은 정적 및 동적 리소스 모두에 상당한 이점을 제공하면서 이러한 문제를 해결합니다.

Chrome에서 공유 사전 지원을 광고하는 방법

모든 브라우저는 Accept-Encoding 요청 헤더를 통해 지원하는 압축 알고리즘을 광고합니다. 헤더의 콘텐츠는 지원되는 인코딩의 쉼표로 구분된 목록입니다.

Accept-Encoding: gzip, br, zstd

이 특정 Accept-Encoding 헤더는 리소스를 요청하는 브라우저가 gzip, Brotli, ZStandard 압축 알고리즘을 지원한다고 나타냅니다. 그러면 요청에 응답하는 웹 서버가 요청에 응답할 때 사용할 알고리즘을 결정할 수 있습니다.

공유 사전 지원이 사용 설정되어 있고 리소스에 관련 사전을 사용할 수 있는 경우 Accept-Encoding 헤더에 추가 토큰이 추가됩니다. 이러한 토큰은 Brotli의 경우 br-d이고 Zstandard의 경우 zstd-d입니다. Chrome에는 사용 가능한 사전의 해시도 포함되며 이는 다음에 설명합니다.

Accept-Encoding: gzip, br, zstd, br-d, zstd-d
Available-Dictionary: :pZGm1Av0IEBKARczz7exkNYsZb8LzaMrV7J32a2fFG4=:

웹 서버가 이 토큰을 인식하도록 구성되어 있고 사전을 인식하는 경우 해당 인코딩의 사전을 사용하여 압축된 리소스로 해당 요청에 응답할 수 있습니다. 실제로 이를 실행하는 방법은 요청이 정적 리소스인지 동적 리소스인지에 따라 다릅니다.

정적 리소스의 공유 사전 압축

정적 페이지 리소스는 요청된 URL에 대해 항상 동일한 응답을 생성하는 리소스입니다. 압축 가능한 정적 페이지 리소스의 일반적인 예로는 JavaScript 및 CSS 파일이 있습니다. 이러한 리소스는 일반적으로 캐싱 목적으로 어떤 방식으로든 버전이 지정됩니다. 파일 이름에 파일 콘텐츠의 해시 (예: styles.abcd1234.css)가 포함되거나 리소스의 지문을 생성하는 다른 방법이 사용되는 경우도 있습니다. 이러한 리소스 유형은 공유 사전에서 제공하는 델타 압축에 적합합니다. 정적 리소스는 장기간 캐시되는 경우가 많고 빈번하게 업데이트되는 경향이 있기 때문입니다.

정적 리소스의 Use-As-Dictionary 응답 헤더를 설정하여 사전을 지정할 수 있습니다. 헤더는 몇 가지 키-값 쌍 중 하나를 사용하지만, 사전이 사용되어야 하는 리소스 경로를 지정하는 URLPattern 구문을 허용하는 match만 필요합니다.

Use-As-Dictionary: match="/dist/styles.*.css"

Use-As-Dictionary 헤더는 리소스의 향후 버전 중 내에 지정된 패턴과 일치하는 버전에 적용되는 메커니즘이라고 생각하면 됩니다. 예를 들어 웹사이트에서 모든 스타일을 단일 CSS 파일로 제공한다고 가정해 보겠습니다. 편의상 이 리소스의 첫 번째 버전이 /dist/styles.v1.css에 있고 /dist/styles.*.cssmatch 값이 포함된 Use-As-Dictionary 응답 헤더와 함께 전송된다고 가정해 보겠습니다.

시간이 지나 웹사이트의 CSS를 업데이트하고 /dist/styles.v2.css에 있는 새 버전을 출시합니다. 이전 버전의 Use-As-Dictionary 응답 헤더에 사용된 match 값이 이 요청에 적용되므로 브라우저는 구조화된 필드 바이트 시퀀스로 인코딩된 사전 해시가 포함된 Available-Dictionary 헤더를 전송합니다.

Accept-Encoding: gzip, br, zstd, br-d, zstd-d
Available-Dictionary: :pZGm1Av0IEBKARczz7exkNYsZb8LzaMrV7J32a2fFG4=:

이 시점에서 일치하는 사전이 사용되도록 서버에서 압축을 구성해야 합니다. 그러면 해당 사전으로 압축된 리소스가 전송되고 사용자의 브라우저 캐시에서 사용 가능한 사전이 압축을 푸는 데 사용됩니다.

웹사이트에 새 코드를 자주 출시하는 경우 델타 압축이 큰 도움이 될 수 있습니다. 하지만 이 절차는 유연합니다. 브라우저가 사용자의 브라우저 캐시에서 사전을 사용할 수 있다고 판단하지 않으면 Accept-Encoding 헤더에 추가 br-d 또는 zstd-d 토큰을 지정하지 않습니다. 이 경우 표준 압축 흐름이 적용됩니다.

동적 리소스의 공유 사전 압축

동적 리소스도 공유 사전 압축의 이점을 누릴 수 있습니다. 동적 리소스는 뉴스가 발생할 때마다 기본 페이지가 자주 업데이트되는 뉴스 웹사이트와 같이 컨텍스트에 따라 변경되는 리소스입니다. HTML 문서는 종종 동적 리소스입니다. 이 경우 사전에는 사이트의 일반적인 HTML 구조와 템플릿 코드가 대부분 포함되어 각 페이지의 고유한 부분만 전송되는 압축된 페이지가 생성될 수 있습니다.

동적으로 생성된 리소스의 특성상 나중에 사용할 사전은 클라이언트에 로드해야 합니다. 사전을 미리 로드하면 동적 리소스에 공유 사전 압축을 적용하는 것이 추측적입니다. 이 경우 웹사이트에서 충분한 트래픽을 수신하여 다수의 탐색을 통해 사전 비용을 상각할 수 있기를 바랍니다. 사용해 보려면 먼저 페이지 HTML에서 <link> 요소를 사용하여 사전의 위치를 지정해야 합니다.

<link rel="dictionary" href="/dictionary.dat">

Chrome에서 이 <link> 요소를 발견하면 페이지가 유휴 상태가 되면 사전을 가져오고 대역폭 경합을 피하기 위해 우선순위를 낮게 설정할 있습니다. 사전 자체의 응답은 Use-As-Dictionary 헤더를 지정하고 적용할 동적 리소스 경로를 지정해야 합니다.

Use-As-Dictionary: match="/product/*"

여기서부터는 흐름이 정적 리소스와 거의 동일합니다. 브라우저는 사전 자체가 일치하는 리소스에 적용된다고 인식하고, 앞서 설명한 정적 리소스 흐름과 마찬가지로 사전 콘텐츠의 해시가 포함된 Available-Dictionary 헤더를 요청에 연결합니다.

빌드 시 정적 리소스 압축

번들러에 익숙한 경우 빌드 시간에 리소스를 압축하고 나중에 압축된 리소스를 제공할 수 있는 다양한 번들러용 플러그인을 알고 있을 수 있습니다. 예를 들어 Apache를 사용하면 요청 시 지시어를 통해 사전 압축된 리소스를 제공할 수 있습니다.

압축을 지원하는 대부분의 Node.js 기반 번들러는 Node의 기본 제공 Zlib 라이브러리를 사용합니다. Zlib은 Brotli를 지원하며 이를 사용하는 번들러는 일반적으로 사전 지원 압축을 지원하는 Zlib에 옵션을 직접 전달하는 인터페이스를 제공합니다. 다음은 사전 사용을 지원하는 몇 가지 번들러입니다.

특정 버전의 리소스에 사용 가능한 사전은 이전 버전의 리소스 중 하나를 사용할 수 있습니다. 즉, 사용자 트래픽을 분석하고 그에 따라 계획을 세워야 합니다. 균형을 유지하고 최대한 많은 재방문자에게 도움이 되는 리소스를 생성하세요. CDN 제공업체는 현재 공유 사전 압축을 실험하고 있습니다. 아직 공개적으로 사용할 수 있는 구현은 없지만 앞으로는 달라질 것으로 기대됩니다.

직접 해보기

공유 사전 압축을 브라우저의 기존 압축 기능과 통합하면 업데이트된 프로덕션 코드를 자주 출시하고 재방문자로부터 상당한 트래픽을 받는 웹사이트의 로드 성능을 크게 개선할 수 있습니다. 공유 사전 압축을 사용해 보고 싶다면 다음 두 가지 옵션 중 하나를 선택하세요.

  1. 공유 사전 압축이 작동하는 방식을 알아보기 위해 직접 공유 사전 압축을 조정하려는 경우 chrome://flags 페이지에서 압축 사전 전송 실험용 기능을 사용 설정하면 됩니다.
  2. 프로덕션 웹사이트에서 이 기능을 사용해 보고 공유 사전 압축이 실제 사용자에게 어떤 이점을 제공할 수 있는지 확인하고 싶다면 Origin Trial에 등록하여 토큰을 받고 Origin Trial의 작동 방식을 알아보세요.

결론

Google은 웹 압축 기술의 이러한 주요 발전과 사람들이 매일 사용하는 기존 애플리케이션을 얼마나 더 빠르게 만들 수 있는지에 대해 매우 기쁘게 생각합니다. 이 기능을 사용해 보고 의견을 공유해 주세요. 버그를 발견하면 crbug.com에서 신고하세요. 추가 리소스와 도구는 use-as-dictionary.com을 확인하세요. 마지막으로 작동 방식을 자세히 알아보려면 설명을 참고하세요.