Core Web Vitals

SPA의 Core Web Vitals: 오해 없이 라우트 전환 성능 측정하기

결과를 해석하기 전에 탐색 방식과 측정 구간을 먼저 정의하세요.

브라우저 안의 여러 라우트 패널과 그 아래 표시된 타임라인
같은 문서를 유지하면서 라우트가 전환되는 모습을 표현한 개념도입니다.

단일 페이지 애플리케이션의 성능을 평가하려면 최초 문서 로드, 이후의 라우트 전환, 그 사이의 상호작용을 측정해야 합니다. 서로 연결된 경험이지만 측정 경계는 다릅니다. 클릭에 빨리 반응했다고 목적지 콘텐츠가 로드되었다고 볼 수는 없습니다. 시작 페이지의 좋은 점수도 사용자가 이후에 방문하는 모든 라우트의 성능을 설명하지는 않습니다.

보고서가 실제로 무엇을 측정하는지 정의하세요

전체 탐색은 새 문서를 만듭니다. 일반적인 SPA 라우트 전환은 현재 문서를 갱신합니다. History API로 주소를 바꾸었다는 사실만으로 모든 브라우저와 보고 도구에서 새로운 성능 측정이 시작되는 것은 아닙니다.

문서 기준 수집과 소프트 내비게이션을 명시적으로 지원하는 수집을 구분하세요. 지원 여부는 브라우저, 수집기, 보고서 종류에 따라 달라집니다. 소프트 내비게이션 측정을 제공한다면 그 구분을 유지하고 기존 페이지 로드 결과와 설명 없이 섞지 마세요. 애플리케이션이 만든 라우트 타이머도 유용하지만 자동으로 LCP, INP, CLS가 되지는 않습니다.

Google의 SPA 측정 안내는 탐색 모델이 중요한 이유를 설명합니다. 결과를 비교하기 전에 도구가 제공하는 데이터셋의 정의를 확인하세요. 특정 라우트의 측정값이 없다는 것은 관측 범위가 부족하다는 뜻이지 즉시 완료되었다는 뜻이 아닙니다.

진입 라우트는 따로 진단하세요

새 세션에서 중요한 목적지를 직접 여세요. 브라우저가 핵심 콘텐츠를 발견하고 리소스를 내려받으며 표시에 필요한 코드를 실행하는 과정을 관찰합니다. 서버 렌더링이나 사전 렌더링은 콘텐츠를 더 일찍 발견하게 할 수 있습니다. 다만 하이드레이션이 상호작용을 위한 작업을 추가할 수 있다는 점은 남아 있습니다.

네트워크 대기, 코드 실행, 렌더링을 나누어 보세요. 다운로드가 적어도 JavaScript를 실행하는 동안 화면이 멈출 수 있습니다. 반대로 피할 수 있는 의존 작업이 끝난 후에야 시작하는 API 요청은 스크립트 실행량만 줄여서는 해결되지 않습니다. 프레임워크만으로 결과를 설명하지 말고 LCP 개선 안내를 따라 핵심 콘텐츠가 보이기까지의 경로를 조사하세요.

라우트가 준비되었다는 기준을 정하세요

상품 화면이라면 상품 식별 정보, 현재 가격, 주요 동작을 사용할 수 있는 상태를 준비 완료로 정의할 수 있습니다. 기사 화면이라면 제목과 본문이 문서에 반영된 상태가 될 수 있습니다. 사용자의 작업과 관련된 기준을 선택하고 일관되게 적용하세요. 다음 동작을 사용할 수 없다면 로딩 표시가 사라진 것만으로는 충분하지 않습니다.

탐색 의도를 나타내는 라우터 이벤트에서 시작 시간을 기록하고, 선택한 조건이 충족될 때 완료를 기록하세요. 취소, 실패, 리디렉션은 성공한 전환과 구분해야 합니다. 나중에 시작한 탐색이 앞선 탐색을 대체했다면 이전 작업의 늦은 완료를 새 라우트의 결과로 처리하지 마세요.

애플리케이션의 시간 신호를 추가하세요

다음 예제는 완료된 라우트 전환을 위한 타이머입니다. 탐색을 시작할 때 beginRouteTiming()을 호출하고 준비 조건이 충족되면 반환된 함수를 호출하세요. 개인정보가 포함된 URL 대신 article처럼 정규화된 라우트 이름을 사용합니다.

javascript
function beginRouteTiming(routeName) {
  const id = crypto.randomUUID();
  const start = `route:${id}:start`;
  const end = `route:${id}:end`;
  const name = `route:${id}:ready`;
  performance.mark(start);

  return function finishRouteTiming() {
    performance.mark(end);
    const result = performance.measure(name, {
      start,
      end,
      detail: { route: routeName }
    });
    performance.clearMarks(start);
    performance.clearMarks(end);
    performance.clearMeasures(name);
    return result.duration;
  };
}

반환되는 시간의 단위는 밀리초입니다. 이 도우미는 애플리케이션 신호 사이의 간격을 측정할 뿐, 픽셀이 실제 화면에 표시되었는지 또는 Core Web Vitals 기준을 만족했는지 확인하지 않습니다. 실제 라우터와 렌더링 생명주기에 연결하고, 취소 시 정리와 오류 보고를 추가하세요. 고유한 이름은 동시에 진행된 성공 측정을 구분하고, 정리 코드는 완료된 항목이 타임라인에 계속 남는 것을 방지합니다.

프레임워크의 커밋이나 requestAnimationFrame 콜백도 유용한 콘텐츠가 모두 표시되었다는 증거는 아닙니다. 지표의 의미를 정하기 전에 브라우저 성능 기록과 실제 화면 동작을 시간 신호와 대조하세요.

반응성과 목적지 로드를 구분하세요

INP는 사용자 상호작용에서 다음 화면 갱신까지의 응답을 다룹니다. 클릭 직후 로딩 상태가 표시되어도 목적지는 여전히 준비되지 않았을 수 있습니다. 상호작용은 빠르게 반응하지만 라우트는 느릴 수 있다는 뜻입니다. 라우트 전체 소요 시간을 INP라고 부르지 말고 두 관찰을 각각 유지하세요.

비용이 큰 이벤트 핸들러, 동기 렌더링, 반복되는 레이아웃 계산을 살펴보세요. 즉각적인 응답에 필요하지 않은 작업은 미루고 현재 상태를 정확히 보여 주는 진행 표시를 제공하세요. 클릭 이후 메인 스레드가 바쁘다면 네트워크 설정을 바꾸기 전에 성능 기록을 확인해야 합니다. INP 개선 안내는 원인을 좁히는 데 도움이 됩니다.

필요한 것을 로드하면서 화면을 안정적으로 유지하세요

라우트별 코드 분할은 진입 시 불필요한 작업을 줄일 수 있습니다. 하지만 서로 의존하는 요청의 연속 단계가 추가되면 목적지가 늦어질 수 있습니다. 구조상 가능하다면 독립적인 데이터 요청과 코드 로드를 함께 시작하세요. 추측에 따른 미리 읽기는 현재 사용 중인 라우트와 경쟁할 수 있으므로 선택적으로 적용해야 합니다.

이미지와 비동기 패널에 필요한 공간을 미리 확보하세요. 탐색 영역을 안정적으로 유지하고 사용자가 읽는 내용 위에 늦게 도착한 콘텐츠를 삽입하지 마세요. 최근 입력 때문에 특정 이동이 CLS에서 제외되더라도 전환 과정의 움직임이 사용자에게 불편할 수 있습니다. 지표뿐 아니라 보이는 경험도 평가해야 합니다.

같은 조건의 여정과 데이터셋을 비교하세요

직접 진입, 내부 탐색, 반복 탐색, 뒤로 가기와 앞으로 가기, 데이터가 늦게 도착하는 전환을 테스트하세요. 실패한 요청과 중단된 전환도 포함합니다. 변경 사항을 비교할 때 캐시 상태와 기기 조건을 맞추세요. 그렇지 않으면 미리 준비된 라우트가 단지 이전 작업의 혜택을 받은 것인데도 새로운 개선처럼 보일 수 있습니다.

양호 기준은 LCP 2.5 s 이하, INP 200 ms 이하, CLS 0.1 이하이며 모바일과 데스크톱을 나눈 75번째 백분위수에서 평가합니다. 이는 정해진 Core Web Vitals 측정에 적용하는 기준이지 임의의 애플리케이션 타이머에 적용하는 기준이 아닙니다. 보고서가 URL, 오리진, 문서 로드, 식별된 소프트 내비게이션 중 무엇을 나타내는지 확인하세요.

라우트 문맥은 해당 상호작용이나 전환이 발생했을 때 기록하세요. 텔레메트리를 보낼 때 우연히 활성화된 URL을 붙이는 방식은 피해야 합니다. 로컬에서 성공한 성능 기록을 전체 방문자의 경험으로 해석하기 전에 필드 데이터와 실험실 데이터의 차이를 확인하세요. 유용한 보고서는 느린 여정을 식별하고 개선 효과를 검증할 수 있게 합니다.

관련 가이드