Upgrade to Pro — share decks privately, control downloads, hide ads and more …

웹 반응성 개선하기

kakao
December 08, 2022

웹 반응성 개선하기

#성능 #Responsiveness #Lighthouse #UserFlow #웹성능

웹 성능은 주로 로드 성능에 집중되었지만 웹의 역할이 커지면서 사용자 상호작용에 대한 성능도 중요해졌습니다. 웹이 로드 된 이후 사용자와 상호작용에 관련된 성능을 측정하고 개선하는 방법을 알아봅니다.

발표자 : ethan.eunwu
카카오 FE플랫폼팀에서 FE개발자를 위한 개발을 하는 에단입니다.

kakao

December 08, 2022
Tweet

More Decks by kakao

Other Decks in Programming

Transcript

  1. 1. 웹 성능이란? 2. 웹 반응성 3. 웹 반응성 지표

    4. 웹 반응성 측정 방법 5. 웹 반응성 개선 사례
  2. 1. 웹 성능이란? 2. 웹 반응성 3. 웹 반응성 지표

    4. 웹 반응성 측정 방법 5. 웹 반응성 개선 사례
  3. 1. 웹 성능이란? 2. 웹 반응성 3. 웹 반응성 지표

    4. 웹 반응성 측정 방법 5. 웹 반응성 개선 사례
  4. 70% 2x 사용자 사용량 일주일에 한번 이상 끔찍한 반응성을 경험

    반응성이 나쁜 페이지보다 좋은 페이지 사용량이 2배 이상 웹 반응성 현황 출처: Chrome Data
  5. 1. 웹 성능이란? 2. 웹 반응성 3. 웹 반응성 지표

    4. 웹 반응성 측정 방법 5. 웹 반응성 개선 사례
  6. INP TBT CLS Total Blocking Time Interaction to Next Paint

    Cumulative Layout Shift 웹 반응성 지표
  7. TBT (Total Blocking Time) - Long Task : 메인 스레드에서

    50ms 이상 실행되는 작업 #MPDLJOH5JNF-POH5BTL઺NTܳઁ৻ೠݫੋझۨ٘੼ਬदр 아래 그림에서 Long Task는 총 3개, TBT는 200 + 40 + 105 = 345 Main thread timline (task blocking time) 200 40 105
  8. 사용자 입력이 발생하고 화면의 변화가 생길때까지의 시간 INP (Interaction to

    Next Paint) pointerup mouseup click Blocking tasks Procesing time Render Paint Compositing, GPU, and raster Procesing time Frame presented! Input received Input delay INP
  9. 사용자 입력이 발생하고 화면의 변화가 생길때까지의 시간 INP (Interaction to

    Next Paint) pointerup mouseup click Blocking tasks Procesing time Render Paint Compositing, GPU, and raster Procesing time Frame presented! Input received Input delay
  10. 사용자 입력이 발생하고 화면의 변화가 생길때까지의 시간 INP (Interaction to

    Next Paint) pointerup mouseup click Blocking tasks Procesing time Render Paint Compositing, GPU, and raster Procesing time Frame presented! Input received Input delay
  11. 사용자 입력이 발생하고 화면의 변화가 생길때까지의 시간 INP (Interaction to

    Next Paint) pointerup mouseup click Blocking tasks Procesing time Render Paint Compositing, GPU, and raster Procesing time Frame presented! Input received Input delay
  12. 1. 웹 성능이란? 2. 웹 반응성 3. 웹 반응성 지표

    4. 웹 반응성 측정 방법 5. 웹 반응성 개선 사례
  13. Snapshot Navigation Timespan 단일 페이지의 로드 성능 측정 사용자 인터렉션

    후 페이지의 상태 측정 임의의 시간동안 사용자 인터렉션 측정 Lighthouse User fl ow
  14. - 단일 페이지 로드 분석 - LCP와 Speed Index와 같은

    페이지 로드 성능 점수 제공 카테고리 - Performance - Accessibility - Best Practice - SEO - PWA Navigation 인터렉션 인터렉션 인터렉션 측정범위
  15. - 사용자 인터렉션 이후 특정 상태의 페이지 분석 - SPA나

    복잡한 폼의 접근성 이슈 확인 카테고리 - Performance - Accessibility - Best Practice - SEO Snapshot 인터렉션 인터렉션 인터렉션 측정범위
  16. - 임의의 시간동안 사용자 인터렉션 분석 - 수명이 긴 페이지,

    SPA 에서 성능 개선 포인트 제공 카테고리 - Performance - Best Practice - SEO Timespan 인터렉션 인터렉션 인터렉션 측정범위
  17. import puppeteer from 'puppeteer'; import {startFlow} from 'lighthouse/lighthouse-core/fraggle-rock/api.js'; const browser

    = await puppeteer.launch({headless: false}); const page = await browser.newPage(); const flow = await startFlow(page, {name: 'daum'}); await flow.startTimespan({stepName: 'routing'}); await page.goto("https://m.daum.net/"); await page.click("#mArticle > nav:nth-child(2) > li.news1 > a > span") await page.click("#mArticle > nav:nth-child(2) > li.sports > a > span") await page.click("#mArticle > nav:nth-child(2) > li.enter > a > span") await flow.endTimespan(); await browser.close(); const report = await flow.generateReport(); const json = await flow.createFlowResult();
  18. import puppeteer from 'puppeteer'; import {startFlow} from 'lighthouse/lighthouse-core/fraggle-rock/api.js'; const browser

    = await puppeteer.launch({headless: false}); const page = await browser.newPage(); const flow = await startFlow(page, {name: 'daum'}); await flow.startTimespan({stepName: 'routing'}); await page.goto("https://m.daum.net/"); await page.click("#mArticle > nav:nth-child(2) > li.news1 > a > span") await page.click("#mArticle > nav:nth-child(2) > li.sports > a > span") await page.click("#mArticle > nav:nth-child(2) > li.enter > a > span") await flow.endTimespan(); await browser.close(); const report = await flow.generateReport(); const json = await flow.createFlowResult();
  19. import puppeteer from 'puppeteer'; import {startFlow} from 'lighthouse/lighthouse-core/fraggle-rock/api.js'; const browser

    = await puppeteer.launch({headless: false}); const page = await browser.newPage(); const flow = await startFlow(page, {name: 'daum'}); await flow.startTimespan({stepName: 'routing'}); await page.goto("https://m.daum.net/"); await page.click("#mArticle > nav:nth-child(2) > li.news1 > a > span") await page.click("#mArticle > nav:nth-child(2) > li.sports > a > span") await page.click("#mArticle > nav:nth-child(2) > li.enter > a > span") await flow.endTimespan(); await browser.close(); const report = await flow.generateReport(); const json = await flow.createFlowResult();
  20. import puppeteer from 'puppeteer'; import {startFlow} from 'lighthouse/lighthouse-core/fraggle-rock/api.js'; const browser

    = await puppeteer.launch({headless: false}); const page = await browser.newPage(); const flow = await startFlow(page, {name: 'daum'}); await flow.startTimespan({stepName: 'routing'}); await page.goto("https://m.daum.net/"); await page.click("#mArticle > nav:nth-child(2) > li.news1 > a > span") await page.click("#mArticle > nav:nth-child(2) > li.sports > a > span") await page.click("#mArticle > nav:nth-child(2) > li.enter > a > span") await flow.endTimespan(); await browser.close(); const report = await flow.generateReport(); const json = await flow.createFlowResult();
  21. import puppeteer from 'puppeteer'; import {startFlow} from 'lighthouse/lighthouse-core/fraggle-rock/api.js'; const browser

    = await puppeteer.launch({headless: false}); const page = await browser.newPage(); const flow = await startFlow(page, {name: 'daum'}); await flow.startTimespan({stepName: 'routing'}); await page.goto("https://m.daum.net/"); await page.click("#mArticle > nav:nth-child(2) > li.news1 > a > span") await page.click("#mArticle > nav:nth-child(2) > li.sports > a > span") await page.click("#mArticle > nav:nth-child(2) > li.enter > a > span") await flow.endTimespan(); await browser.close(); const report = await flow.generateReport(); const json = await flow.createFlowResult();
  22. { "title": "daum", "steps": [ { "type": "navigate", "url": "https://m.daum.net/"

    }, { "type": "click", "target": "main", "selectors": [ [ "aria/઱ਃ ࢲ࠺झ ݫ׏ ಟ஖ӝ नӏ ࣗधঌܿ", "aria/[role=\"generic\"]" ], [ "#mArticle > nav:nth-child(2) > div > button > span" ] ]
  23. { "title": "daum", "steps": [ { "type": "navigate", "url": "https://m.daum.net/"

    }, { "type": "click", "target": "main", "selectors": [ [ "aria/઱ਃ ࢲ࠺झ ݫ׏ ಟ஖ӝ नӏ ࣗधঌܿ", "aria/[role=\"generic\"]" ], [ "#mArticle > nav:nth-child(2) > div > button > span" ] ]
  24. Chrome Devtools Recorder Type Property Required Description click, doubleClick offsetX,

    offsetY O 요소의 좌상단 기준 change value O 최종 값 keyDown, keyUp key O 키 이름 scroll x, y 절대 스크롤 x, y 위치값 navigate url O 대상 URL waitForElement operator >= | == (default) | <= waitForElement count 선택기로 식별되는 요소의 수 waitForExpression expression O Javascript 표현식
  25. import puppeteer from 'puppeteer'; import {startFlow} from 'lighthouse/lighthouse-core/fraggle-rock/api.js'; const browser

    = await puppeteer.launch({headless: false}); const page = await browser.newPage(); const flow = await startFlow(page, {name: 'daum'}); await flow.startTimespan({stepName: 'routing'}); await page.goto("https://m.daum.net/"); await page.click("#mArticle > nav:nth-child(2) > li.news1 > a > span") await page.click("#mArticle > nav:nth-child(2) > li.sports > a > span") await page.click("#mArticle > nav:nth-child(2) > li.enter > a > span") await flow.endTimespan(); await browser.close(); const report = await flow.generateReport(); const json = await flow.createFlowResult();
  26. import puppeteer from 'puppeteer'; import {startFlow} from 'lighthouse/lighthouse-core/fraggle-rock/api.js'; const browser

    = await puppeteer.launch({headless: false}); const page = await browser.newPage(); const flow = await startFlow(page, {name: 'daum'}); await flow.startTimespan({stepName: 'routing'}); await flow.endTimespan(); await browser.close(); const report = await flow.generateReport(); const json = await flow.createFlowResult();
  27. import puppeteer from 'puppeteer'; import {startFlow} from 'lighthouse/lighthouse-core/fraggle-rock/api.js'; import {createRunner,

    PuppeteerRunnerExtension} from '@puppeteer/replay'; const browser = await puppeteer.launch({headless: false}); const page = await browser.newPage(); const flow = await startFlow(page, {name: 'daum'}); await flow.startTimespan({stepName: 'routing'}); await flow.endTimespan(); await browser.close(); const report = await flow.generateReport(); const json = await flow.createFlowResult();
  28. import puppeteer from 'puppeteer'; import {startFlow} from 'lighthouse/lighthouse-core/fraggle-rock/api.js'; import {createRunner,

    PuppeteerRunnerExtension} from '@puppeteer/replay'; const browser = await puppeteer.launch({headless: false}); const page = await browser.newPage(); const flow = await startFlow(page, {name: 'daum'}); await flow.startTimespan({stepName: 'routing'}); const runner = await createRunner(readJSON(‘/recording.json'), new PuppeteerRunnerExtension(browser, page)); await runner.run(); await flow.endTimespan(); await browser.close(); const report = await flow.generateReport(); const json = await flow.createFlowResult();
  29. 1. 웹 성능이란? 2. 웹 반응성 3. 웹 반응성 지표

    4. 웹 반응성 측정 방법 5. 웹 반응성 개선 사례
  30. 챗봇 주문 ◼︎ TBT ◼︎ INP • CLS 300 ms

    350 ms 0 TBT Total Blocking Time GOOD NEEDS IMPROVEMENT POOR 200 600
  31. Javascript Layout Style Re fl ow (Critical Rendering Path) Composite

    Paint Layout Style Javascript Javascript Javascript
  32. 챗봇 주문 ◼︎ TBT ◼︎ INP • CLS 300 ms

    350 ms 0 TBT Total Blocking Time GOOD NEEDS IMPROVEMENT POOR 200 600
  33. 챗봇 주문 • TBT • INP • CLS 200 ms

    200 ms 0 TBT Total Blocking Time GOOD NEEDS IMPROVEMENT POOR 200 600 33% 개선
  34. WPM (Web Performance Monitoring) ▲ TBT ▲ INP • CLS

    960 ms 1,040 ms 0.008 INP Interaction to Next Paint GOOD NEEDS IMPROVEMENT POOR 200 500
  35. WPM (Web Performance Monitoring) ▲ TBT ▲ INP • CLS

    960 ms 1,040 ms 0.008 INP Interaction to Next Paint GOOD NEEDS IMPROVEMENT POOR 200 500
  36. WPM (Web Performance Monitoring) INP Interaction to Next Paint GOOD

    NEEDS IMPROVEMENT POOR 200 500 • TBT • INP • CLS 130 ms 80 ms 0.01 92% 개선
  37. 카카오페이 구매 • TBT • INP ▲ CLS 20 ms

    140 ms 0.259 CLS Cumulative Layout Shift GOOD NEEDS IMPROVEMENT POOR 0.1 0.25
  38. 카카오페이 구매 • TBT • INP ▲ CLS 20 ms

    140 ms 0.259 CLS Cumulative Layout Shift GOOD NEEDS IMPROVEMENT POOR 0.1 0.25
  39. 카카오페이 구매 • TBT • INP • CLS 20 ms

    80 ms 0 CLS Cumulative Layout Shift GOOD NEEDS IMPROVEMENT POOR 0.1 0.25 100% 개선
  40. 끝으로.. 1. 로드 성능은 여전히 중요하지만 반응성도 점점 중요해진다. 2.

    Lighthouse user fl ow로 측정하는 반응성 3. 반응성 지표별 개선 사례 - 블로킹 타임 발생시 강제 리플로우 확인 - 전체 화면 업데이트 발생시 중요한 부분 우선 업데이트 진행 - 레이아웃 시프트는 간단한 작업만으로 쉽게 개선 가능
  41. 끝으로.. 1. 로드 성능은 여전히 중요하지만 반응성도 점점 중요해진다. 2.

    Lighthouse user fl ow로 측정하는 반응성 3. 반응성 지표별 개선 사례 - 블로킹 타임 발생시 강제 리플로우 확인 - 전체 화면 업데이트 발생시 중요한 부분 우선 업데이트 진행 - 레이아웃 시프트는 간단한 작업만으로 쉽게 개선 가능
  42. Next Step - 웹 반응성 측정 자동화 - 웹 반응성

    측정과 e2e 테스트 통합 - 웹 반응성 개선 가이드