I will check the files in the workspace directory to find the content of "The Ultimate Core Web Vitals Checklist (2026)" that needs to be translated. I will read the contents of the file `tmp_source_260613043449.json` to see if it contains the source text for the translation. I will read the rest of `tmp_source_260613043449.json` using the ContentOffset parameter to get the full JSON content. I will read the contents of the python script `stateless_translate.py` to understand the translation process and structure. I will view the `com.arjen.stateless_translate.plist` file to understand how the translation service is set up. I will check the end of the `launchagent_out.log` file to verify how the translation tasks are running and see the log output. I will search the `translation_changelog.log` for any occurrences of "ko" or "Korean" to see if there are prior translations we can reference for style consistency. I will search `translated_text.html` for Korean characters to see if it is a previous Korean translation that we can use as a reference. I will search for any references to `ko` or Korean translation files/folders in the codebase to see if we have any other Korean files. I will read `translate_parts.py` to see if it holds any logic or references to other files or previous translations. 궁극의 Core Web Vitals 체크리스트 (2026)
I am going to list the contents of the workspace directory to find the text file containing the content to be translated. I am going to check the content of `tmp_source_260613043449.json` to see if it contains the source text to be translated. I am going to read the remaining bytes of the file `tmp_source_260613043449.json` using ContentOffset. I will view the `stateless_translate.py` script to understand how the translation is invoked and structured. I will view the end of the `launchagent_out.log` file to understand the context of the translation execution. I am going to check the end of `launchagent_out.log` to see what the script has been doing recently. I will search `stateless_translate.py` for references to `agy`. I will view lines 201 to 400 of `stateless_translate.py` to see how translation is performed. I am going to check the `parts` directory to see if there are any existing translated snippets or reference files. I am going to check if there have been any previous Korean translations logged in the changelog. I am going to search the translation changelog for "tagline" to see how taglines are formatted and translated. I am going to check if there are other taglines in the changelog to see how they are translated in other languages. I will search the `translated_text.html` file for Korean characters to see if it contains Korean translation examples. I will run a python script to search the changelog for recent Korean translations to see the exact wording and style previously used. I will check the git status in the workspace to see if there are untracked files or history we can inspect. I will inspect the first few lines of `translated_text.html` to see what language it was translated into. I will run a python script to find any local files that contain Korean characters, in order to check how technical terms were translated in the past. I will write a python script to search the log file for successfully completed Korean translation jobs to see if we can find any URLs or details about them. I will run a python script to search the `launchagent_out.log` file for log messages related to the Korean translation of `ultimate-checklist` (job ID `6620dbf138577b0abd235b32`). I will view the rest of the `stateless_translate.py` script to see how it handles individual fields of a translation job. I will check the `launchagent_out.log` file around the times the Korean checklist translation job ran earlier today to see if we can find any printed outputs or details of the translation. I will run a python script to fetch one of the existing Korean pages from the website to analyze the register, terminology, and translation style used for similar performance checklist headings and descriptions. I will fetch and decode the page title and the first few paragraphs of the Korean version of the `103-early-hints` page to see the exact style, tone, and vocabulary choice. I will fetch and decode the title, H1, and first few paragraphs of another Korean page, the Largest Contentful Paint guide, to ensure the style and register are consistent. I will fetch the `https://www.corewebvitals.io/ko/core-web-vitals/ultimate-checklist` page in Korean to see the exact tagline and content translations. I will fetch the English version of the ultimate checklist to see the structure of the tagline. I will write a python script to search the English page HTML for the tagline content to see where it is rendered. I will fetch the German version of the ultimate checklist and extract the tagline to see how it was translated in German. I will fetch and compare the tagline of the English and Korean versions of the `103-early-hints` page to see how taglines are translated. I will run a python script to search a few other URLs on the Korean corewebvitals.io site to see how tagline translations are ended (e.g. if they end with nouns directly like 최적화 or 최적화 방법). LCP, INP, CLS 성능 개선 시 확인해야 할 모든 최적화 항목

궁극의 Core Web Vitals 체크리스트
이 Core Web Vitals 체크리스트는 새 사이트를 배포하기 전, Largest Contentful Paint (LCP), Interaction to Next Paint (INP), Cumulative Layout Shift (CLS)를 개선할 때, 또는 사이트에 중요한 변경을 적용할 때 검증해야 하는 모든 최적화 작업을 다룹니다. 웹사이트가 Google의 Core Web Vitals 평가를 통과하고 빠르고 매끄러운 경험을 제공하도록 실무 레퍼런스로 활용하십시오.
이 체크리스트는 최신 인사이트에 따라 지속적으로 업데이트됩니다. 기여하고 싶다면 언제든지 연락하십시오.

Core Web Vitals 최적화 체크리스트
이것은 완전한 Core Web Vitals 체크리스트입니다. 성능 문제를 식별하고 모든 방문자에게 웹사이트가 빠르고 매끄럽게 제공되도록 이 리스트를 활용하십시오. 각 체크리스트 섹션은 관련 상세 가이드로 연결되어 모든 권장 사항의 구체적인 배경 이유를 학습할 수 있습니다.
Table of Contents!
이미지 최적화
보이는 viewport 내의 큰 이미지는 대부분 Largest Contentful Paint 요소가 됩니다. 이미지 최적화는 LCP 개선을 위해 취할 수 있는 가장 효과적인 조치 중 하나입니다. 이 Core Web Vitals 체크리스트 항목들을 활용해 이미지 속도를 개선하십시오. 전체 전략은 LCP 이미지 최적화 방법 가이드를 읽어보십시오.
- 화면상에 표시되는 최대 크기에 맞게 이미지 크기 조정: 화면의 최대 표시 크기보다 큰 이미지를 다운로드하여 바이트를 낭비하는 일을 방지합니다. 더 작은 화면 크기에는 이 방식을 반응형 이미지와 결합하십시오. 올바른 크기의 이미지를 제공하면 눈에 보이는 품질 저하 없이 이미지 파일 크기를 50% 이상 줄일 수 있습니다.
- 스크롤해야 보이는 이미지에 lazy loading 적용: lazy loading은 viewport 외부의 이미지가 스크롤되어 보일 때까지 로드를 지연시켜, First Contentful Paint (FCP)와 전반적인 페이지 로딩 속도를 향상시킵니다. LCP 이미지에는 절대 lazy load를 적용하지 마십시오. 로딩이 크게 지연됩니다.
- LCP 요소와 같이 시각적으로 중요한 이미지 사전 로드: 사전 로드는 브라우저가 나머지 콘텐츠보다 중요 이미지를 먼저 가져오도록 지시하여 LCP의 우선순위를 높입니다. 최상의 결과를 얻으려면
<link rel="preload" as="image">에fetchpriority="high"를 결합해 사용하십시오. 이 작업은 LCP 이미지가 CSS에서 참조되거나 JavaScript를 통해 로드될 때 특히 중요합니다. - width 및 height 설정: 이미지 크기를 미리 정의하면 브라우저가 이미지 로드를 대기하는 동안 발생하는 레이아웃 이동을 방지할 수 있습니다. 이는 CLS를 개선합니다. 최신 브라우저는 이미지가 로드되기 전에 width 및 height 속성을 사용하여 종횡비를 계산하고 필요한 공간을 미리 확보합니다.
- WebP 또는 AVIF 같은 최신 이미지 포맷 사용: 이러한 포맷은 JPEG나 PNG와 유사한 품질을 유지하면서도 더 작은 파일 크기를 제공하므로 로딩 시간이 단축됩니다. 일반적으로 WebP는 JPEG보다 파일 크기가 25~34% 더 작으며, AVIF는 최대 50%까지 파일 크기를 줄일 수 있습니다. 브라우저 호환성을 극대화하려면 포맷 fallback을 제공하는
<picture>요소를 사용하십시오. - 네이티브 lazy loading을 사용하고 JavaScript 기반 lazy loading 비활성화: lazy loading은 viewport 외부의 이미지가 스크롤되어 보일 때까지 이미지 로드를 지연시킵니다. 브라우저가
loading="lazy"속성을 통해 제공하는 네이티브 lazy loading은 추가적인 스크립트 파싱이나 실행이 필요하지 않으므로, 이 작업을 JavaScript에 의존하는 것보다 대개 더 효율적입니다. - srcset이 적용된 반응형 이미지 사용: 이 속성은 다양한 화면 크기에 맞춰 서로 다른 이미지 버전을 지정하여, 브라우저가 사용자 기기에 맞는 최적의 이미지를 전달하도록 보장함으로써 불필요한 대용량 다운로드를 줄여줍니다. 정밀하게 제어하려면
srcset을sizes속성과 결합해 사용하십시오. - decoding="async" 추가:
decoding="async"속성은 브라우저가 이미지를 디코딩하는 동안 다른 콘텐츠의 렌더링을 차단하지 않도록 방지합니다. 이를 통해 이미지 디코딩이 병렬로 처리되는 동안 렌더링 엔진이 다른 요소를 계속해서 그릴 수 있습니다. - 이미지 메타데이터 제거: 이미지에 포함된 EXIF 데이터 등의 메타데이터는 불필요한 바이트를 추가하게 됩니다. 이 정보를 제거하면 이미지 품질에 영향을 주지 않고 파일 크기를 줄일 수 있습니다. ImageOptim, Squoosh, Sharp 같은 도구를 사용하여 빌드 과정에서 메타데이터 제거를 자동화할 수 있습니다.
- LCP 요소에 CSS 배경 이미지 사용 피하기: CSS에서 참조되는 배경 이미지는 HTML의
<img>요소보다 브라우저에 늦게 발견됩니다. 배경 이미지를 LCP 요소로 반드시 사용해야 한다면, 이미지를 조기에 발견할 수 있도록<link rel="preload">태그를 사용하여 사전 로드하십시오. LCP 리소스 로드 지연에 대해 자세히 알아보십시오.
웹 폰트 최적화
웹 폰트는 First Contentful Paint를 지연시키고, 레이아웃 이동을 유발하며, 초기 대역폭 리소스를 두고 경쟁합니다. 매끄러운 웹 폰트 경험을 위해 이 체크리스트를 사용하십시오. 폰트 호스팅 모범 사례는 Google Fonts 자체 호스팅 가이드를 참조하십시오.
- 빠른 첫 페인트를 위해 font-display: swap 사용:
@font-face선언에서font-display속성을swap으로 설정하십시오. 이렇게 하면 브라우저가 백그라운드에서 웹 폰트를 로드하는 동안 fallback 폰트를 즉시 표시합니다. 웹 폰트가 준비되면 폰트가 매끄럽게 교체됩니다. 웹폰트 로드 중 텍스트가 계속 보이도록 유지하는 방법을 더 읽어보십시오. - 폰트로 인한 레이아웃 이동을 제거하려면 프리로딩과 font-display: optional 결합 사용:
font-display: optional과 프리로딩을 결합하면 속도와 잠재적 레이아웃 이동의 균형을 맞출 수 있습니다.optional값은 fallback 폰트를 사용하기 전에 텍스트를 잠깐(약 100ms) 숨깁니다. 프리로딩은 브라우저가 웹 폰트를 조기에 가져오도록 지시하여 fallback 폰트 표시 시간을 최소화하고 레이아웃 이동을 줄입니다. - fallback 폰트 크기를 웹 폰트 크기와 일치시키려면 font-face 디스크립터 사용: 이렇게 하면 웹 폰트가 교체될 때 CLS를 최소화할 수 있습니다. fallback 폰트에
size-adjust,ascent-override,descent-override,line-gap-override를 사용하여 유사한 메트릭을 지정하면, 폰트 로드 시 콘텐츠가 흔들리는 현상을 방지합니다. - 필요한 문자만 포함하도록 폰트 서브셋팅: 콘텐츠에 필요한 문자만 포함하도록 폰트를 서브셋팅하여 폰트 파일 크기를 줄이십시오. Font Squirrel,
pyftsubset, 또는glyphhanger같은 도구로 서브셋을 생성할 수 있습니다. 적절한 서브셋팅을 거치면 전체 라틴 문자 세트 폰트 크기를 100KB+에서 20KB 미만으로 줄일 수 있습니다. - 폰트 두께와 스타일 개수 제한: 과도한 폰트 변형 로드를 피하십시오. 중요 폰트 최대 2개(보통 프리로드됨)와 지연 로딩 폰트 2개(초기 렌더링 이후 로드됨)로 제한하십시오. 폰트 두께가 추가될 때마다 다운로드 크기가 15~50KB씩 증가합니다.
스크립트 최적화
스크립트는 Interaction to Next Paint 문제를 일으키고, Cumulative Layout Shifts를 유발하거나, Largest Contentful Paint를 지연시킵니다. 최적화되었고 비교적 무해한 초기 스크립트조차 리소스를 두고 경쟁하여 페인트 지표(LCP 및 FCP)를 지연시킵니다. 전체 가이드는 JavaScript를 지연시키는 14가지 방법을 참고하십시오.
- 불필요한 JavaScript 제거: 다운로드 및 실행해야 하는 코드의 양을 최소화하려면 사용하지 않는 JavaScript 코드를 찾아내어 제거하십시오. 사용하지 않는 코드를 찾으려면 Chrome DevTools의 Coverage 탭을 사용하십시오. 데드 코드를 제거하면 다운로드 시간과 main thread 처리가 모두 줄어듭니다.
- 기능과 중요도에 따른 스크립트 우선순위 지정: 표시되는 viewport에 큰 변화를 주는 스크립트는 render blocking이어야 합니다. 중요한 스크립트는 지연되거나 비동기로 로드되어야 합니다. 부가적인 스크립트는 브라우저가 유휴 상태일 때 로드해야 합니다. 세부 전략은 당사의 리소스 우선순위 지정 가이드를 참고하십시오.
- 코드 분할 및 lazy loading: 대규모 JavaScript 번들을 더 작은 청크로 나누고 필요한 경우에만 로드하십시오. 이렇게 하면 초기 로드 시간이 줄어듭니다. webpack, Rollup, esbuild와 같은 최신 번들러는 dynamic import를 기반으로 한 자동 코드 분할을 지원합니다.
- JavaScript 파일 압축 및 재컴파일: 항상 SWC, Terser 또는 esbuild와 같은 압축 도구를 사용하여 JavaScript 파일을 압축하고 재컴파일하십시오. 압축을 거치면 일반적으로 JavaScript 파일 크기가 30~50% 줄어듭니다.
- 서드파티 스크립트 제한: 서드파티 스크립트는 상당한 성능 오버헤드를 초래합니다. 필요성을 평가하고 가능한 경우 대안을 모색하십시오. 각 서드파티 스크립트는 DNS 조회, 연결 오버헤드, main thread 처리 시간을 추가합니다. Chrome DevTools의 Network 패널을 사용하여 서드파티 스크립트를 정기적으로 감사하십시오.
- 서드파티 스크립트 비동기 로드: 서드파티 스크립트는 동작을 예측하기 어렵기 때문에, 서드파티로 인해 렌더링이 차단되어서는 안 됩니다. 모든 서드파티 스크립트 태그에
async또는defer속성을 사용하십시오. - 서드파티 스크립트 성능 모니터링: 서드파티 스크립트가 INP 및 LCP에 미치는 실제 영향을 추적하려면 Long Animation Frames (LoAF) API 또는 CoreDash를 사용하십시오. 서드파티 JavaScript에 대한 성능 예산을 설정하고 정기적으로 검토하십시오.
스타일 최적화
스타일은 기본적으로 render blocking이다. 스타일을 최적화하면 페인트 메트릭이 최적화된다. 웹페이지의 스타일 성능을 개선하려면 다음 체크리스트를 따르라. render blocking CSS는 First Contentful Paint와 LCP 엘리먼트 렌더링 지연 모두에 직접적인 영향을 미친다. 사용하지 않는 스타일을 정리하는 팁은 사용하지 않는 CSS 제거 방법을 참고하라.
- CSS 파일 경량화: CSS 파일에서 공백, 주석, 포맷팅 등 불필요한 문자를 제거하라. 경량화된 파일은 크기가 더 작아 로딩 시간이 단축된다. cssnano, PostCSS 또는 CSS 전처리기에 내장된 압축 기능을 사용하면 이를 자동화할 수 있다.
- 사용하지 않는 CSS 제거: 웹 페이지에서 사용하지 않는 CSS 코드를 찾아 제거하라. 이는 브라우저가 다운로드하고 파싱해야 하는 데이터 양을 줄여 성능을 개선한다. PurgeCSS나 Chrome DevTools의 Coverage 탭 같은 도구를 사용하면 사용하지 않는 CSS를 쉽게 식별할 수 있다.
- 크리티컬 CSS 인라인화: 초기 페이지 콘텐츠를 렌더링하는 데 필수적인 스타일을 HTML 내에 직접 삽입하여 페인트 메트릭을 개선하라. 신규 방문자에게만 크리티컬 CSS를 인라인으로 제공하고, 재방문자에게는 캐시된 외부 스타일시트를 사용하도록 하는 방안을 고려하라. 이 기법은 외부 스타일시트를 가져오는 데 필요한 네트워크 왕복(round trip)을 제거하여 FCP를 줄여준다.
- CSS 파일 크기 균등 분할: 모든 CSS를 하나의 파일로 합치는 것이 효율적으로 보일 수 있지만, 파일 크기가 너무 커지면 다운로드 속도가 느려진다. 로딩을 최적화하고 브라우저가 스타일을 점진적으로 처리할 수 있도록 CSS를 크기가 균등한 작은 파일(각 10~15KB)로 분할하는 것을 고려하라.
- 화면 외 스타일 비동기 로드: 초기 viewport 외부의 엘리먼트에 적용되는 스타일은
media="print" onload="this.media='all'"패턴을 사용하여 비동기로 로드하라. 이를 통해 브라우저는 페이지의 초기 렌더링을 차단하지 않고 다른 리소스와 병렬로 해당 스타일을 가져올 수 있다.
리소스 힌트 최적화
리소스 힌트는 핵심 리소스의 다운로드 우선순위를 지정하는 데 도움이 된다. 사전 로드된 리소스는 보통 다운로드 대기열에 추가되므로, 사전 로드를 적용하지 않았을 때보다 훨씬 더 일찍 브라우저가 사용할 수 있다. 리소스 힌트를 효과적으로 사용하면 LCP 리소스 로드 지연을 크게 줄일 수 있다. 고급 구현의 경우 103 Early Hints에 대해 읽어보라.
- 비필수 리소스 힌트 제거: 초기 페이지 로드에 필수적이지 않은 리소스에 대한 preload 힌트를 제거하라. 이렇게 하면 대역폭이 제한된 초기 단계에서 리소스를 차지하기 위해 경쟁하는 불필요한 다운로드나 네트워크 연결을 방지할 수 있다. 불필요한 preload는 핵심 리소스에 사용해야 할 대역폭을 소모한다.
- 핵심 도메인에 Preconnect 설정: 중요한 도메인(콘텐츠 전송 네트워크나 폰트 제공업체 등)과의 연결을 조기에 수립하라. DNS, TCP, TLS 핸드셰이크를 미리 완료하면 해당 도메인의 핵심 리소스 다운로드 속도가 빨라진다. 핵심 서드 파티 오리진에는
<link rel="preconnect" href="https://example.com">을 사용하라. - preconnect 대안으로 DNS prefetch 고려: preconnect와 유사하게, DNS prefetch는 브라우저에 잠재적 연결에 대한 힌트를 준다. 하지만 preconnect는 완전한 연결 수립을 우선시하는 반면, DNS prefetch는 브라우저가 도메인 이름을 미리 해석하도록 지시할 뿐이다. preconnect의 완전한 연결 오버헤드가 불필요한 경우
<link rel="dns-prefetch">를 사용하라. - LCP 엘리먼트 Preload: LCP는 메인 콘텐츠가 로드되는 데 걸리는 시간을 측정한다. LCP 엘리먼트를 preload하면 브라우저가 이 핵심 리소스의 다운로드 우선순위를 높이도록 지시하여 사용자가 메인 콘텐츠를 보는 데 걸리는 시간을 단축한다. 이는 CSS에서 참조하거나 JavaScript를 통해 로드되는 이미지에 특히 중요하다.
- 핵심 폰트 Preload: 핵심 폰트를 preload하면 브라우저가 이를 조기에 가져오므로 텍스트 표시 지연을 방지하고 폰트 스와핑으로 인한 Cumulative Layout Shift를 개선한다. 가장 중요한 서체에는
<link rel="preload" as="font" type="font/woff2" crossorigin>을 사용하라. - 리소스 힌트에 103 Early Hints 우선 사용: 103 Early Hints HTTP 상태 코드를 사용하면 서버가 전체 응답이 준비되기 전에 리소스 힌트를 보낼 수 있다. 서버가 103을 지원하지 않는 경우 대신
Link응답 헤더를 사용하라. 헤더를 사용할 수 없는 경우, fallback으로 페이지의<head>에<link>요소를 추가하라. 힌트를 조기에 전달할수록 리소스를 더 빠르게 발견할 수 있다. - CSS 파일이 폰트를 발견하기 전에 preload: CSS에서 참조하는 폰트는 CSS 파일이 다운로드되고 파싱된 후에야 발견된다. HTML
<head>에 폰트를 직접 preload하면 CSS 파싱에 대한 의존성이 제거되어 폰트가 병렬로 로드되므로, FCP와 레이아웃 이동 위험을 모두 줄일 수 있다.
아이콘 최적화
아이콘을 최적화하지 않으면 페이지 용량이 크게 늘어난다. 대형 인라인 SVG 아이콘은 HTML을 비대하게 만들고, 아이콘 폰트는 대개 사용하지 않는 수천 개의 글리프를 포함한다. 아이콘 최적화는 LCP (HTML/CSS 용량 감소)와 CLS (올바른 영역 예약) 모두에 영향을 미친다.
- HTML 내 인라인 SVG 아이콘 피하기: 대형 SVG 아이콘을 인라인화하면 HTML 코드 크기가 늘어나고 페이지 로드가 느려진다. HTML 크기를 최소화하고 브라우저 캐싱을 적용할 수 있도록 별도 파일로 제공하거나 아이콘 폰트를 (주의하여) 사용하는 대안을 고려하라. 외부 SVG 스프라이트 시트가 성능과 유연성 사이에서 대개 가장 좋은 절충안이다.
- 대형 아이콘 폰트 피하기: Font Awesome 같은 대형 아이콘 세트 전체를 그대로 사용하지 마라. 서브셋(subsetting)을 사용해 최적화된 아이콘 폰트나 개별 SVG를 생성하여 웹 페이지의 전체 크기를 줄이고 로드 속도를 높여라. Font Awesome 전체 세트는 100KB를 초과할 수 있지만, 아이콘 20개짜리 서브셋은 5KB 미만이다.
- 아이콘의 width와 height 예약하기: 이미지와 마찬가지로, 아이콘에 width와 height를 지정하면 브라우저가 공간을 예약하여 로드 시 레이아웃 이동을 방지한다. SVG 요소에
width및height속성을 사용하거나 CSS에서 명시적인 크기를 설정하라. - 비필수 아이콘 세트의 우선순위 낮추기: 아이콘이 페이지의 초기 렌더링에 필수적이지 않다면 우선순위를 낮춰 로드하는 방안을 고려하라. 이렇게 하면 필수 콘텐츠가 먼저 로드되어 Core Web Vitals 지표에 미치는 영향이 최소화된다. lazy loading을 적용하거나 초기 페인트 이후에 아이콘 스타일시트를 비동기적으로 로드하라.
서버 응답 시간 최적화
Time to First Byte (TTFB)로 측정하는 서버 응답 시간은 모든 페인트 지표와 직접적인 관계가 있다. 서버 응답이 느리면 이후의 모든 과정이 지연된다. 자세한 최적화 전략은 TTFB 문제 진단 및 성능 향상을 위한 Cloudflare 설정 가이드를 참조하라.
- 빠르고 신뢰할 수 있는 호스팅 제공업체 사용: 탄탄한 인프라를 갖춘 빠른 호스팅 제공업체는 서버 응답 시간과 웹사이트 전반의 성능을 크게 개선한다. 인위적인 마케팅 주장이 아닌, 실제 환경의 TTFB 측정값을 기준으로 호스팅 제공업체를 평가하라.
- 서버 사이드 코드 및 데이터베이스 쿼리 최적화: 코드 실행 시간과 데이터베이스 쿼리 시간을 자주 기록하여 병목 지점을 찾고 전반적인 속도를 개선하라. 쿼리 프로파일링 및 애플리케이션 성능 모니터링(APM) 도구를 사용하여 느린 엔드포인트를 식별하라.
- 캐싱 전략 구현: 브라우저 캐싱과 서버 사이드 캐싱을 활용해 자주 액세스하는 데이터를 저장하라. 반복적인 데이터 조회를 줄이고 로딩 시간을 개선할 수 있다. 전체 페이지 캐싱을 적용하면 TTFB를 몇 초대에서 100ms 미만으로 줄일 수 있다. 자세한 내용은 캐시 유효 기간 최적화에서 확인하라.
- 개인화를 위한 클라이언트 사이드 또는 에지 렌더링: 전체 페이지 캐시 기능을 유지하려면 장바구니 수량, 로그인 상태, 간단한 메뉴 변경 등 소규모 개인화 요소에 클라이언트 사이드 또는 에지 렌더링 적용을 고려하라. 이렇게 하면 사소한 동적 요소 때문에 전체 페이지의 캐시를 무효화하는 상황을 방지할 수 있다.
- 서버 설정 최적화: 성능 향상을 위해 웹 서버 설정을 검토하고 조정하라. 여기에는 connection keep-alive 설정, 워커 프로세스 수, 메모리 할당, timeout 값이 포함된다. 서버 설정이 잘못되면 리소스가 낭비되고 응답 시간이 늘어난다.
- 콘텐츠 전송 네트워크(CDN) 사용: CDN은 웹사이트의 정적 콘텐츠를 여러 에지 노드(서버)에 분산한다. 사용자가 콘텐츠에 액세스하기 위해 도달해야 하는 물리적 거리가 줄어들어, 전 세계 사용자에게 더 빠른 로딩 시간을 제공한다. 또한 CDN은 일반적으로 자체 서버보다 더 잘 설정되어 있다. 실제 설정 방법은 Cloudflare 설정 가이드를 참조하라.
- 서버 사이드 처리 줄이기: 요청당 서버가 수행하는 작업량을 최소화하라. 비용이 많이 드는 연산은 미리 계산해 두고, 효율적인 알고리즘을 사용하며, 중요하지 않은 처리는 백그라운드 작업으로 넘겨라. 애플리케이션의 요청 라이프사이클을 분석하여 불필요한 처리 단계를 찾아 제거하라.
- HTTP/3 사용: HTTP/3는 Hypertext Transfer Protocol의 최신 버전이다. HTTP/3는 HTTP/2보다 빠르고 효율적이며, HTTP/1.1보다 훨씬 빠르다. HTTP/3로 업그레이드하면 전반적인 페이지 로딩 시간이 개선되며, 잠재적으로 세 가지 Core Web Vitals 지표(LCP, INP, CLS)가 모두 향상될 수 있다. 자세한 내용은 연결 지속 시간 최적화에서 확인하라.
- Server-Timing 헤더 설정: 이 헤더는 페이지의 여러 부분이 서버에서 처리되는 데 걸리는 시간에 대한 상세 정보를 제공한다. 이 데이터를 활용하면 병목 지점과 개선이 필요한 영역을 정확히 찾아낼 수 있으며, 특히 Largest Contentful Paint (LCP) 개선에 집중할 수 있다. Server-Timing 헤더는 Chrome DevTools Network 패널에서 확인할 수 있으며, CoreDash와 같은 RUM 도구로 수집할 수 있다.
- 느린 데이터베이스 쿼리 로깅 및 정기 최적화: 데이터베이스(MySQL, PostgreSQL, MongoDB)에서 슬로우 쿼리 로깅을 활성화하고 매주 로그를 검토하라. 인덱스 최적화, 쿼리 재구성, 자주 발생하는 쿼리를 위한 캐싱 레이어 추가는 TTFB를 크게 단축한다.
- GZIP 또는 Brotli 압축 사용: GZIP 또는 더 최신인 Brotli는 전송 전에 텍스트 기반 리소스(HTML, CSS, JavaScript)를 실시간으로 압축하여 파일 크기를 약 70% 줄인다. Brotli는 일반적으로 GZIP보다 15~20% 더 뛰어난 압축 성능을 제공한다. 파일 크기가 작아질수록 로딩 시간이 단축된다.
상호작용 최적화
Interaction to Next Paint (INP)는 사이트가 사용자 상호작용에 얼마나 빠르게 반응하는지 측정한다. 상호작용성 저하는 보통 main thread를 차단하는 장시간 실행되는 JavaScript 작업으로 인해 발생한다. INP의 세 가지 단계를 전체적으로 파악하려면 입력 지연, 처리 시간, 표시 지연 가이드를 참조하라.
- 비용이 많이 드는 스크립트에 idle-until-urgent 패턴을 구현하라: 이 방식은 중요 작업을 우선시하고, 필수적이지 않은 JavaScript 실행은 브라우저 main thread가 유휴 상태가 될 때까지 미룬다. 이를 통해 렌더링이나 사용자 상호작용 같은 중요 작업이 장시간 실행되는 스크립트에 의해 차단되지 않도록 보장한다.
requestIdleCallback을 사용해 긴급하지 않은 작업을 예약하라. 처리 시간 최적화에 대해 자세히 알아보라. - main thread에 yielding하여 long task를 분할하라: 복잡한 JavaScript 작업은 main thread를 차단하여 반응성을 지연시킬 수 있다. 이러한 작업을 더 작은 청크로 나누고 청크 사이에 main thread로 제어권을 yielding하면 브라우저가 사용자 상호작용을 처리하고 부드러운 사용자 경험을 유지할 수 있다.
scheduler.yield()(지원되는 경우) 또는setTimeout(0)을 사용해 long task를 분할하라. JavaScript 스크롤을 배제하여 INP를 개선하는 방법 가이드를 참조하라. - 입력 후 즉각적인 피드백을 제공하라: 사용자는 웹사이트와 상호작용한 후 즉각적인 반응을 기대한다. 백그라운드에서 장시간 실행되는 작업이 처리 중이더라도 시각적 피드백을 제공하거나 사용자 입력에 신속하게 반응하라. 즉각적인 시각적 피드백을 위해 CSS transition과
:active가상 클래스를 사용하라. 이는 상호작용 중이라는 느낌을 유지하고 사용자가 웹사이트가 멈췄다고 느끼는 것을 방지한다. - 스크롤 및 터치에 passive 이벤트 리스너를 사용하라: 스크롤 및 터치 이벤트 리스너에
{ passive: true }를 추가하라. passive 리스너는 핸들러가preventDefault()를 절대 호출하지 않을 것임을 브라우저에 알린다. 이를 통해 브라우저는 JavaScript를 기다리지 않고 즉시 스크롤을 시작할 수 있다. 이는 모바일 기기에서 특히 효과적이며, 스크롤 관련 상호작용의 INP를 직접 개선한다.
Core Web Vitals 모니터링
성능 저하를 조기에 발견하고 최적화가 예상한 효과를 내는지 확인하려면 Core Web Vitals를 지속적으로 모니터링해야 한다. 전체적인 상황을 파악하려면 lab 도구, field data, 실제 사용자 모니터링을 모두 활용하라.
- Lighthouse를 정기적으로 점검하라: Lighthouse는 웹 페이지의 성능 문제를 식별하는 데 도움을 주는 Google의 무료 오픈소스 감사 도구다. Lighthouse는 실제 사용자 환경에서 Core Web Vitals를 직접 측정하지는 않는다. 하지만 통제되고 표준화된 조건에서 웹사이트를 주기적으로 테스트하고 비교하기에 훌륭한 도구다. 배포 전에 성능 저하를 감지하려면 CI/CD 파이프라인에서 Lighthouse를 실행하라.
- CrUX 과거 데이터를 정기적으로 확인하라: CrUX(Chrome User Experience Report)는 실제 환경의 성능 데이터를 제공하는 Google의 공개 데이터 세트다. CrUX는 Google이 Core Web Vitals 통과 여부를 판단하는 데 사용하는 데이터 소스다. 과거 데이터를 활용해 성능 저하를 빠르게 포착하라. CrUX 데이터는 PageSpeed Insights, CrUX 대시보드, 또는 CrUX API를 통해 접근할 수 있다.
- RUM 추적을 설정하라: RUM(실제 사용자 모니터링)은 웹사이트에서 실제 사용자 경험을 추적하는 것이다. RUM 도구는 다양한 지역과 기기에서 방문자에게 페이지가 실제로 로드되는 데 걸리는 시간에 대한 데이터를 수집한다. 이는 Lighthouse 및 CrUX의 시뮬레이션 데이터를 보완하여 실제 환경의 성능에 대한 가치 있는 인사이트를 제공한다. 상세한 Core Web Vitals 기여 데이터를 위한 RUM 추적 도구로 CoreDash를 추천한다.
- 성능 예산을 설정하라: 성능 예산은 각 메트릭에 대해 구체적인 성능 목표(예: LCP 2.5초 미만, INP 200ms 미만, CLS 0.1 미만)를 설정하는 것이다. 이는 최적화 작업을 가이드하는 벤치마크 역할을 한다. 성능 예산 대비 실제 성능을 정기적으로 확인하라. 즉각적인 조치가 필요한 영역을 식별하고 최적화 우선순위를 정하는 데 도움이 된다.
- 세그멘테이션을 활용하라: 가장 가치 있는 방문자 유형과 다양한 페이지 유형을 추적하려면 세그멘테이션을 활용하라. 그렇지 않으면 대규모 트래픽이 이 중요한 그룹에만 영향을 미치는 성능 문제를 가려버릴 수 있다. 기기 유형, 연결 속도, 지역, 페이지 템플릿별로 세그먼트를 나누어 숨겨진 문제를 찾아내라.
주요 렌더링 경로 최적화
주요 렌더링 경로는 브라우저가 HTML, CSS, JavaScript를 화면의 픽셀로 변환하는 일련의 단계다. 이 경로를 최적화하면 First Contentful Paint와 LCP element render delay가 직접 개선된다. 과도한 DOM 크기를 피하는 방법도 참고하라.
- 주요 리소스 수 최소화: 모든 render-blocking 리소스(CSS 및 동기 JavaScript)는 브라우저가 페인트하기 전에 다운로드되고 처리되어야 한다. 비필수 스크립트를 지연하고 중요하지 않은 스타일시트를 비동기로 로드하여 주요 리소스 수를 줄여라.
- 리소스 로드 순서 최적화: 주요 CSS와 폰트가 먼저 로드된 다음 초기 화면 이미지가 로드되고, 그 후에 지연된 스크립트가 로드되도록 하라.
fetchpriority속성과 리소스 우선순위 지정 힌트를 사용하여 브라우저에 중요도를 전달하라. - DOM 트리 깊이 축소: DOM 트리가 깊게 중첩되면 스타일 계산 시간과 레이아웃 작업이 늘어난다. 가능하면 최대 깊이는 32단계, 전체 DOM 요소 수는 1,500개 미만을 목표로 하라. DOM 구조를 평평하게 만들면 페인트 성능과 INP presentation delay가 모두 개선된다.
- 요소 태그 및 속성보다 클래스와 ID 사용:
p.important대신.important를 사용하라. 이렇게 하면 브라우저가 스타일 매칭을 위해 해당 유형의 모든 요소를 검색할 필요가 줄어들어 스타일 재계산이 빨라진다. - 깊은 선택자 중첩 피하기: CSS 선택자를 깊게 중첩할수록 브라우저가 수행해야 하는 계산이 많아진다. 중첩을 줄이기 위해 HTML 구조를 변경하거나, 대상 요소에 더 가깝고 구체적인 클래스를 사용하라. 선택자 깊이는 최대 3단계로 제한하라.
- 하위 선택자 최소화:
.container > .content와 같은 선택자는 브라우저가 컨테이너 내부의 모든 요소를 확인하도록 만든다. 가능하면 콘텐츠 요소에 더 직접적인 클래스를 사용하여 선택자 매칭 속도를 높여라. - 동일한 스타일을 가진 선택자 병합: 여러 요소가 동일한 스타일을 공유하는 경우, 단일 클래스로 그룹화하거나 BEM(Block Element Modifier) 명명 규칙을 사용하여 유지보수성을 높이고 CSS 출력을 줄여라.
쿠키 동의 최적화
쿠키 동의 배너는 GDPR 및 유사 규정에 필수적이지만, 주의해서 구현하지 않으면 Core Web Vitals에 상당한 영향을 미칠 수 있다. 제대로 로드되지 않은 동의 배너는 LCP를 지연시키고, CLS를 유발하며, INP를 증가시킬 수 있다. 자세한 내용은 Core Web Vitals를 위한 서드파티 위젯 최적화를 읽어보라.
- 동적 페이지에는 서버 사이드 쿠키 동의를 고려하라: 동적으로 서버 사이드 렌더링되는 페이지의 경우, 초기 HTML 응답에 동의 배너를 렌더링하는 서버 사이드 솔루션을 구현하는 것이 별도의 JavaScript 기반 솔루션을 로드하는 것보다 대개 더 빠르다. 이는 추가 네트워크 요청과 스크립트 평가 오버헤드를 없애 준다.
- 캐시된 페이지에서는 쿠키 동의 스크립트를 비동기로 로드하라: 캐시된 페이지의 경우, 쿠키 동의 스크립트를 비동기로 로드하고 스크립트에
fetchpriority="high"를 추가해 사용자 상호작용 전에 표시될 만큼 충분히 일찍 로드되도록 하는 것을 고려하라. - LCP 간섭을 피하기 위해 동의 텍스트를 짧게 유지하라: 긴 쿠키 고지 텍스트는 LCP 엘리먼트를 차지할 수 있다. 브라우저가 가장 크게 보이는 텍스트 블록을 잠재적인 LCP 후보로 간주하기 때문이다. 더 짧은 텍스트를 작성하거나 텍스트를 보이는 영역이 더 작은 여러 단락으로 나누는 것을 고려하라.
- 쿠키 알림 스크립트를 자체 호스팅하라: 가능하면 쿠키 알림 스크립트와 스타일시트를 캐시하고 자체 호스팅하라. 이는 서드파티 동의 관리 플랫폼으로의 DNS 조회 및 연결 오버헤드를 없애고 로딩 동작을 완전히 제어할 수 있게 해준다.
싱글 페이지 애플리케이션 최적화
React, Vue, Angular 또는 유사한 프레임워크로 빌드한 싱글 페이지 애플리케이션(SPA)은 고유한 Core Web Vitals 문제에 직면한다. 클라이언트 사이드 렌더링은 FCP와 LCP를 모두 지연시킬 수 있으며, 하이드레이션은 INP를 차단할 수 있다.
- 항상 서버 사이드 렌더링 또는 프리렌더링을 사용하라: 클라이언트 사이드 렌더링에만 의존하는 SPA는 콘텐츠를 표시하기 전에 브라우저가 JavaScript를 다운로드, 파싱, 실행하도록 강제한다. SSR(Next.js, Nuxt, SvelteKit) 또는 정적 프리렌더링을 사용하여 브라우저가 즉시 그릴 수 있는 초기 HTML을 제공하라.
- 동적 생성보다 정적 프리렌더를 우선하라: 정적 프리렌더(빌드 시 생성됨)는 동적으로 생성된 프리렌더보다 훨씬 빠르다. 서버 측 처리 없이 CDN에서 직접 제공할 수 있기 때문이다. 요청마다 데이터가 필요하지 않은 페이지에는 정적 생성을 사용하라.
- 하이드레이션 이후에 서드파티 스크립트를 로드하라: 하이드레이션 중에 프레임워크는 페이지를 상호작용 가능하게 만들기 위해 이미 상당한 main thread 시간을 소모한다. 이와 동시에 서드파티 스크립트를 로드하면 문제가 가중되고 입력 지연이 악화된다. 필수적이지 않은 모든 스크립트는 하이드레이션 과정이 완료된 이후로 미뤄라.
과도한 DOM 크기 피하기
DOM 크기가 크면(요소 1,500개 초과 또는 깊이 32단계 초과) 메모리 사용량이 늘고 스타일 계산이 느려지며, 비용이 많이 드는 레이아웃 리플로우를 유발합니다. 이는 INP 프레젠테이션 지연과 페인트 메트릭 모두에 직접적인 영향을 미칩니다. 과도한 DOM 크기를 해결하는 방법을 확인하십시오.
- 불필요한 DOM 요소 줄이기: HTML을 감사하여 스타일이나 구조적 목적이 없는 래퍼 요소를 찾으십시오. 깊게 중첩된
<div>구조를 시맨틱 HTML 요소로 대체하십시오. 활성 DOM을 작게 유지하기 위해 react-window나 virtual-scroller 같은 라이브러리로 긴 목록을 가상화하는 것을 고려하십시오. - 효율적인 JavaScript 및 CSS 선택자 사용하기: 복잡한 CSS 선택자와 JavaScript DOM 쿼리(광범위한 패턴의
querySelectorAll등)는 DOM 크기가 커질수록 기하급수적으로 느려집니다. 가능한 한 구체적인 클래스 선택자를 사용하고 DOM 쿼리 범위를 하위 트리로 제한하십시오. - 화면 밖 콘텐츠에 content-visibility: auto 사용하기: CSS
content-visibility: auto속성은 브라우저에 화면 밖에 있는 요소가 스크롤되어 화면에 보일 때까지 렌더링을 건너뛰도록 지시합니다. 이는 콘텐츠 섹션이 긴 페이지의 초기 렌더링 작업을 크게 줄여줍니다.
API 요청 최적화
렌더링을 차단하거나 콘텐츠를 지연하는 API 요청은 LCP와 TTFB에 부정적인 영향을 미친다. 클라이언트 측 데이터 페칭은 싱글 페이지 애플리케이션에서 LCP가 느려지는 흔한 원인이다.
- API 요청 수 최소화: 각 API 요청은 전체 페이지 로드 시간을 늘린다. 웹사이트의 기능을 평가하고 초기 콘텐츠를 렌더링하는 데 필요한 API 요청 수를 줄일 수 있는 방법을 찾는다. 데이터 배칭(여러 요청을 하나로 병합)이나 GraphQL 같은 기술은 라운드 트립을 줄여준다.
- 효율적이고 최적화된 API 사용: API 자체의 설계와 구현은 성능에 영향을 미친다. 속도와 효율성에 최적화되고 잘 설계된 API를 사용한다. 자주 요청되는 데이터의 응답 시간을 줄이기 위해 API 측에 캐싱 메커니즘을 구현한다.
- 중요 API 요청 프리로드: 이미지와 같은 중요 리소스를 프리로드하는 것과 마찬가지로, 필수 API 요청을 프리로드하면 체감 성능이 크게 향상된다.
<link rel="preload" as="fetch">를 사용하여 브라우저가 중요 API를 조기에 가져오도록 지시하고, 초기 콘텐츠를 렌더링할 때 발생하는 지연을 최소화한다. 더 많은 기법은 리소스 우선순위 지정 가이드를 참고한다.
채팅 위젯 최적화
채팅 위젯은 레이아웃 이동을 유발하는 흔한 원인이다. 일찍 로드하면 LCP에도 문제를 유발할 수 있다. 단계별 구현 방법은 완벽한 Core Web Vitals로 채팅 위젯을 구현하는 방법을 읽어보라.
- 메인 콘텐츠가 로드된 후에 채팅 위젯을 로드하라: 인터넷 역사상 페이지의 메인 콘텐츠가 로드되기 전에 채팅이 필요했던 사람은 아무도 없다.
requestIdleCallback이나 스크롤 기반 트리거를 사용하여 페이지의 초기 렌더링이 완료될 때까지 채팅 위젯 초기화를 미뤄라. - 채팅 위젯의 레이아웃 이동을 방지하라: 채팅 위젯이 레이아웃 이동을 유발한다면, 페이지에 완전히 렌더링될 때까지
opacity: 0으로 숨겨두는 것이 좋다. 이렇게 하면 화면에 보이는 콘텐츠가 흔들리지 않고 백그라운드에서 위젯이 배치된다. CSS 트랜지션을 사용해 위젯을 부드럽게 페이드인하라. - 가벼운 채팅 위젯 제공업체를 선택하라: 여러 솔루션을 비교하라. 어떤 채팅 위젯은 다른 위젯보다 훨씬 가벼우며 Core Web Vitals 문제를 덜 일으킨다. 도입하기 전에 각 제공업체의 JavaScript 번들 크기, 네트워크 요청 수, 그리고 INP 영향을 비교하라.
서비스 워커 성능 최적화
서비스 워커는 자산과 전체 페이지 응답까지 캐싱하여 재방문 성능을 크게 향상시키며, 재방문자의 TTFB를 줄인다. 그러나 잘못 구현된 서비스 워커는 실제로 내비게이션을 느리게 만든다. 캐시 기간 최적화에 대해 자세히 알아보라.
- 서비스 워커에 핵심 자산 캐싱: CSS, JavaScript, 폰트, 이미지와 같은 정적 자산에는 캐시 우선 전략을 사용하라. 이를 통해 재방문자는 로컬 캐시에서 사이트를 거의 즉시 로드할 수 있다. 서비스 워커 install 이벤트 중에 가장 중요한 리소스를 사전 캐싱하라.
- 서비스 워커 코드 최적화: 서비스 워커를 가볍고 효율적으로 유지하라. 복잡한 라우팅 로직, 과도한
event.waitUntil()사용, 설치 속도를 떨어뜨리는 대규모 사전 캐싱 매니페스트를 피하라. 자주 변경되지만 즉각적인 최신 상태가 필요하지 않은 리소스에는 stale-while-revalidate 패턴을 사용하라.
동영상 콘텐츠 최적화
viewport에서 동영상 요소가 가장 크게 보이는 콘텐츠인 경우 LCP element가 될 수 있다. 최적화되지 않은 대용량 동영상은 다른 중요한 리소스와 대역폭을 두고 경쟁한다.
- 동영상 압축 및 최적화: H.264, VP9, AV1 같은 최신 코덱을 적절한 품질 설정으로 사용하라. 화면에 표시되는 최대 크기에 맞추어 동영상 해상도를 줄여라. 400px 너비로 표시되는 동영상을 1920px로 인코딩할 필요는 없다. 화질 대비 파일 크기 비율을 최적화하려면 2패스 인코딩을 사용하라.
- 동영상에 lazy loading 사용: 폴드 아래에 있는 동영상은
<iframe>요소에loading="lazy"속성을 사용하거나 Intersection Observer API로 동영상 로드를 지연시켜라. 자동 재생되는 배경 동영상은 poster 이미지로 대체하고, 사용자가 그 근처로 스크롤할 때만 동영상을 로드하라. - 빠른 CDN에 동영상 호스팅: 동영상 파일은 크기가 커서 CDN 배포의 효과를 크게 본다. 가변 비트레이트 스트리밍, 지리적 분산, 최적화된 전송을 제공하는 전용 동영상 CDN 또는 호스팅 서비스(Cloudflare Stream, Mux, Bunny.net 등)를 사용하라.
- 동영상 요소에 poster 이미지 사용:
<video>요소에는 항상poster속성을 설정하라. poster 이미지는 동영상이 로드되는 동안 브라우저가 화면을 즉시 그릴 수 있게 하며, 이는 LCP element 역할을 할 수 있다. 다른 LCP 이미지와 마찬가지로 poster 이미지를 최적화하라.

