Lighthouse의 User Flow


원글: Lighthouse user flows[CC BY 4.0] - Brendan Kenny (Twitter / Github)

Lighthouse는 페이지가 처음 로드 될 때의 성능과 Best Practice를 측정하는 환상적인 도구다. 하지만, 전통적으로 페이지의 삶을 분석하는 데에 Lighthouse를 사용하기는 힘들었다. 예를 들어,

  • Warm 캐시가 된 페이지의 로딩
  • Service Worker가 활성화된 페이지의 측정
  • 예상되는 사용자 인터랙션 수집하기

다시 말해, Lighthouse가 주요 정보를 놓칠 수 있다는 것이다. Core Web Vital은 페이지 로드에 국한되어 있고, 캐시가 비어있는 상황만 따지지는 않는다. 게다가, Cumulative Layout Shift(CLS) 같은 지표는 페이지가 열려있는 시간 동안 계속 측정이 가능하다.

Lighthouse는 새로운 모드인 User Flow API를 통해 페이지의 생애를 테스트할 수 있게 해줄 것이다. Puppeteer는 정해진 각본대로 페이지를 로드하고 사용자 인터랙션을 유발해 주는 역할을 했고, Lighthouse는 그런 인터랙션이 일어날 때 주요 지표를 측정하기 위해 다양하게 이용되었다. 즉, 성능은 페이지가 로드 될 때와 페이지의 인터랙션 중일 때만 측정 가능하다. 접근성 측정은 CI를 통해서 수행될 수 있다. 첫 화면은 물론이고 하나라도 놓치지 않기 위해 개발자 각자의 복잡한 순서로도 확인할 수 있다.

이제 User Flow를 위해 작성한 대부분의 Puppeteer 스크립트에 Lighthouse를 추가해서 성능과 Best Practice를 측정할 수 있다 이 튜토리얼은 새로운 Lighthouse 수정 내용인 User Flow로 내비게이션, 스냅샷, 타임 스팬 부분을 측정하는 방법을 다룰 것이다.

설치

User Flow API는 아직 프리뷰 상태이지만, 최신 Lighthouse에서는 사용해 볼 수 있다. 하단의 데모를 실행해 보려면 Node 버전 14 이상의 환경이 필요하다. 빈 디렉터리를 만들고 아래 커맨드를 실행해 보자.

# 기본적인 ES 모듈을 만든다.
echo '{"type": "module"}' > package.json

# 마법사를 사용하지 않고 npm 프로젝트를 초기화한다.
npm init -y

# 예제에서 필요한 의존성을 설치한다.
npm install lighthouse puppeteer open

꿀팁!
{"type": "module"} 라인이 없다면 npm -y는 기본적으로 모듈 대신 CommonJS를 사용한다. 아래 코드를 사용할 수는 있지만, 의존성을 가져오려면 import 대신에 require()로 바꿔 써야 할 것이다.

내비게이션

Lighthouse의 "내비게이션" 모드는 브라우저가 페이지를 최초 로드하면서 분석하는, 표준 Lighthouse 동작을 실제로 부르는 이름이다. (아직까지는 그렇다.) 이 모드는 페이지의 로딩 성능을 측정하기 위해 사용되지만, User Flow가 새로운 시각의 가능성을 열어 줄 것이다.

페이지 로드를 측정하는 Lighthouse의 순서는 다음과 같다.

  1. Puppeteer로 브라우저를 열고
  2. Lighthouse User Flow를 시작한다.
  3. 측정하고자 하는 URL로 내비게이션 한다.
import fs from 'fs';
import open from 'open';
import puppeteer from 'puppeteer';
import {startFlow} from 'lighthouse/lighthouse-core/fraggle-rock/api.js';

async function captureReport() {
  const browser = await puppeteer.launch({headless: false});
  const page = await browser.newPage();

  const flow = await startFlow(page, {name: 'Single Navigation'});
  await flow.navigate('https://web.dev/performance-scoring/');

  await browser.close();

  const report = flow.generateReport();
  fs.writeFileSync('flow.report.html', report);
  open('flow.report.html', {wait: false});
}

captureReport();

이게 가장 간단한 Flow다. 페이지를 열면, 결과지에는 한 개의 스텝만 요약되어 보일 것이다. 그 스텝을 클릭하면 전통적인 Lighthouse 내비게이션 결과지가 나타날 것이다.

라이브 리포트 확인해보기

일반적인 Lighthouse의 경우, 이 페이지는 캐시 가 없거나 local storage를 비운 채로 로드 될 것이다. 하지만 실제 사이트를 방문하는 사용자들은 cold 캐시 상태로 방문할 수도 있고, warm 캐시로 방문할 수도 있다. cold 캐시와 아직 warm 캐시 상태인 사용자 그 둘 간의 성능 차이는 꽤 클 것이다.

Warm 로드를 측정하기

이 스크립트에 두 번째 내비게이션을 추가할 수도 있다. 이번에는 Lighthouse의 내비게이션에서 기본적으로 하는 동작인 캐시와 storage를 비우기를 꺼볼 것이다. 이번 예제는 캐시의 이점이 얼마나 되는지 확인하기 위해 web.dev의 그 아티클을 한 번 더 로드할 것이다.

async function captureReport() {
  const browser = await puppeteer.launch({headless: false});
  const page = await browser.newPage();

  const testUrl = 'https://web.dev/performance-scoring/';
  const flow = await startFlow(page, {name: 'Cold and warm navigations'});
  await flow.navigate(testUrl, {
    stepName: 'Cold navigation'
  });
  await flow.navigate(testUrl, {
    stepName: 'Warm navigation',
    configContext: {
      settingsOverrides: {disableStorageReset: true},
    },
  });

  await browser.close();

  const report = flow.generateReport();
  fs.writeFileSync('flow.report.html', report);
  open('flow.report.html', {wait: false});
}

captureReport();

Flow의 결과지는 다음과 같을 것이다.

A Lighthouse flow report showing two navigations, one cold and one warm, which has a higher performance score

라이브 리포트 확인해보기

Cold와 Warm 로드의 조합으로 실제 사용자들의 경험에 가까운 테스트를 할 수 있다. 만약, 여러분의 사이트에 사용자들이 한 번의 방문했을 때 여러 페이지를 로드하는 경향이 있다면, 이 예제에서 사용한 방법이 실제 사용자들이 여러분의 사이트에서 겪는 모습에 더 가까운 성능을 측정할 수 있게 해줄 것이다.

스냅샷

스냅샷은 Lighthouse audit의 한 시점에 실행되는 새로운 모드다. 일반적인 Lighthouse 실행과 다르게 페이지는 다시 로드되지 않는다. 스냅샷을 사용해서 페이지를 설정하고 페이지가 특정 상태를 가지고 있는 상황을 테스트할 수 있다. 예를 들어 드롭다운이 열려있거나, form 요소가 일부만 채워져 있다거나 하는 것 말이다.

모든 Lighthouse audit이 스냅샷 모드로 실행되는 건 아니다. 현재 대부분의 성능 지표는 페이지 로드와 함께 시작되도록 정의되어 있기 때문에 이런 지표들에는 스냅샷을 적용할 수 없다. 하지만 접근성 audit과 대부분의 성능 Best Practice 중요한 체크는 가능하다.

Squoosh에 추가한 고급 설정 UI가 자동화된 Lighthouse 검사를 통과하는지 확인해 보고 싶다고 가정해 보자.

(Squoosh 고급 설정 메뉴)

Pupeteer로 위 프로세스를 스크립트로 작성할 수 있고, 각 스텝마다 Lighthouse 스냅샷을 만들 수 있다.

async function captureReport() {
  const browser = await puppeteer.launch({headless: false});
  const page = await browser.newPage();

  const flow = await startFlow(page, {name: 'Squoosh snapshots'});

  await page.goto('https://squoosh.app/', {waitUntil: 'networkidle0'});

  // 첫 번째 데모 이미지 버튼을 기다렸다가 연다.
  const demoImageSelector = 'ul[class*="demos"] button';
  await page.waitForSelector(demoImageSelector);
  await flow.snapshot({stepName: 'Page loaded'});
  await page.click(demoImageSelector);

  // UI의 고급 설정 버튼이 나타날때까지 기다렸다가 연다.
  const advancedSettingsSelector = 'form label[class*="option-reveal"]';
  await page.waitForSelector(advancedSettingsSelector);
  await flow.snapshot({stepName: 'Demo loaded'});
  await page.click(advancedSettingsSelector);

  await flow.snapshot({stepName: 'Advanced settings opened'});

  browser.close();

  const report = flow.generateReport();
  fs.writeFileSync('flow.report.html', report);
  open('flow.report.html', {wait: false});
}

captureReport();

결과지를 보니 나쁘지 않은 결과로 보인다. 하지만 접근성 기준은 여러분이 직접 확인해 봐야 할 것 같다.

Lighthouse flow 결과지로 생성된 스냅샷들이 있다

라이브 리포트 확인해보기

타임 스팬(Timespan)

현업(CrUX[역: CrUX는 Chrome User Experience Report를 줄인 말])과 연구실(Lighthouse)의 성능 측정 결과의 가장 큰 차이 중 하나는 사용자 인터랙션이 없다는 것이다. 마지막 모드인 타임 스팬이 그 부분을 보완할 수 있다.

타임 스팬은 Lighthouse audit을 일정 기간 동안 실행하는 것을 말한다. 내비게이션 될 수도 있고 아닐 수도 있다. 이를 통해 페이지 인터랙션 도중에 어떤 일이 일어나는지 측정할 수 있다. Lighthouse는 기본적으로 CLS를 페이지 로딩 도중에 측정한다. 하지만 실제로 CLS는 내비게이션 초기부터 페이지가 닫힐 때까지 측정되어야 한다. 사용자 인터랙션이 CLS를 유발하는 경우, 이전까지의 Lighthouse는 이를 잡아내지 못할뿐더러 이를 고치는 데 아무 도움도 주지 못한다.

이를 시연하기 위해, 아티클이 스크롤 되는 도중에 원래 없었다가 갑자기 광고가 추가되는 예제 사이트를 준비했다. 긴 카드의 행렬 사이에서 가끔가다 한 번씩 빨간 사각형이 뷰 포트에 들어오는 순간에 나타날 것이다. 비록 빨간 사각형의 공간이 미리 준비되지 않았지만 말이다. 그 이유로 아래에 있는 카드들은 아래로 밀려나고, 이렇게 Layout Shift가 일어난다.

일반적인 Lighthouse 내비게이션으로는 CLS가 0일 것이다. 하지만 스크롤이 일어나면 페이지는 대대적인 Layout Shift가 일어나게 되고, CLS 값은 증가할 것이다.

데모 사이트 영상

데모 사이트 확인해보기

아래 코드는 두 동작 사이의 차이를 보여주는 결과지를 생성하기 위한 스크립트다.

async function captureReport() {
  const browser = await puppeteer.launch({headless: false});
  const page = await browser.newPage();
  // 페이지로 프로토콜 커맨드를 보내기 위해 세션 핸들을 받아온다.
  const session = await page.target().createCDPSession();

  const testUrl = 'https://pie-charmed-treatment.glitch.me/';
  const flow = await startFlow(page, {name: 'CLS during navigation and on scroll'});

  // 일반적인 Lighthouse 내비게이션
  await flow.navigate(testUrl, {stepName: 'Navigate only'});

  // 내비게이션과 스크롤 타임 스팬
  await flow.startTimespan({stepName: 'Navigate and scroll'});
  await page.goto(testUrl, {waitUntil: 'networkidle0'});
  // 사용자처럼 스크롤 해야 한다. 이를 위한 Puppeteer 함수는 없지만, DevTools Protocol과 Input.synthesizeScrollGesture 이벤트를 사용할 수 있다. 반복 횟수와 지연 시간 파라미터로 스크롤 제스처를 자연스럽게 따라 할 수 있다.
  // https://chromedevtools.github.io/devtools-protocol/tot/Input/#method-synthesizeScrollGesture
  await session.send('Input.synthesizeScrollGesture', {
    x: 100,
    y: 0,
    yDistance: -2500,
    speed: 1000,
    repeatCount: 2,
    repeatDelayMs: 250,
  });
  await flow.endTimespan();

  await browser.close();

  const report = flow.generateReport();
  fs.writeFileSync('flow.report.html', report);
  open('flow.report.html', {wait: false});
}

captureReport();

이 스크립트로 "일반 내비게이션"과 "내비게이션 및 스크롤"을 비교하는 결과지를 생성할 수 있다.

A Lighthouse flow report showing a set of snapshots taken

라이브 리포트 확인해보기

이제 스텝들을 파헤쳐 보자. 내비게이션 전용 스텝을 보니 CLS가 0이다. 아주 좋은 사이트라고 할 수 있다.

The Lighthouse report covering only page navigation with all green metrics

하지만 "내비게이션 및 스크롤" 스텝은 다른 결과가 나왔다. 현재는 타임 스팬에서는 Total Blocking Time과 Cumulative Layout Shift만 측정할 수 있다. 하지만 이 페이지의 LazyLoading 한 콘텐츠가 CLS 덩어리임에는 틀림없다.

The Lighthouse report covering page navigation and scrolling with a failing CLS

이전의 Lighthouse는 실제 사용자 경험에서는 명확하게 나타나는 문제더라도 위와 같은 문제가 되는 CLS를 인식하지 못했다. 스크립트화된 인터랙션으로 성능을 테스트할 수 있게 되어 연구를 더 현실화할 수 있었다.

피드백을 기다리는 중

새로운 Lighthouse의 User Flow API는 더 많은 것을 해볼 수 있다. 하지만 사용자가 겪는 것 같은 시나리오를 측정하는 일은 어려울 수도 있다.

어떤 질문이라도 좋으니 Lighthouse 토의 포럼에 와서 질문해 주길 바란다. 버그 제보나 이슈 트래커에 기능 제안도 환영이다.