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

웹 반응성 개선하기

kakao
PRO
December 08, 2022

웹 반응성 개선하기

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

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

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

kakao
PRO

December 08, 2022
Tweet

More Decks by kakao

Other Decks in Programming

Transcript

  1. -JHIUIPVTF6TFSGMPXܳࢎਊೠਢ߈਽ࢿѐࢶӝ
    $PQZSJHIU,BLBP$PSQ"MMSJHIUTSFTFSWFE3FEJTUSJCVUJPOPSQVCMJDEJTQMBZJTOPUQFSNJUUFEXJUIPVUXSJUUFOQFSNJTTJPOGSPN,BLBP
    ਢ߈਽ࢿѐࢶೞӝ
    ߅਷਋&UIBOFVOXV


    ஠஠য়
    JG LBLBP

    View Slide

  2. 1. 웹 성능이란?


    2. 웹 반응성


    3. 웹 반응성 지표


    4. 웹 반응성 측정 방법


    5. 웹 반응성 개선 사례

    View Slide

  3. 1. 웹 성능이란?


    2. 웹 반응성


    3. 웹 반응성 지표


    4. 웹 반응성 측정 방법


    5. 웹 반응성 개선 사례

    View Slide

  4. View Slide

  5. Real User Monitoring
    Synthetic Monitoring
    Kakao의 성능 측정

    View Slide

  6. 성능 개선 지원
    성능 개선 가이드
    성능 개선 지원

    View Slide

  7. 페이지 로드 이후

    View Slide

  8. 페이지 로드 이후
    Load Interaction

    View Slide

  9. 페이지 로드 이후
    페이지 전환 페이지 전환 페이지 전환
    인터렉션 인터렉션 인터렉션
    블로그
    지도

    View Slide

  10. 1. 웹 성능이란?


    2. 웹 반응성


    3. 웹 반응성 지표


    4. 웹 반응성 측정 방법


    5. 웹 반응성 개선 사례

    View Slide

  11. 웹 반응성
    Poor Responsiveness Good Responsiveness

    View Slide

  12. 70% 2x
    사용자 사용량
    일주일에 한번 이상


    끔찍한 반응성을 경험
    반응성이 나쁜 페이지보다


    좋은 페이지 사용량이 2배 이상
    웹 반응성 현황
    출처: Chrome Data

    View Slide

  13. 사용자 입력으로부터 100ms 이내 반응
    RAIL 모델 Response 성능 목표

    View Slide

  14. 1. 웹 성능이란?


    2. 웹 반응성


    3. 웹 반응성 지표


    4. 웹 반응성 측정 방법


    5. 웹 반응성 개선 사례

    View Slide

  15. INP
    TBT CLS
    Total Blocking Time Interaction to Next Paint Cumulative Layout Shift
    웹 반응성 지표

    View Slide

  16. 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

    View Slide

  17. 사용자 입력이 발생하고 화면의 변화가 생길때까지의 시간
    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

    View Slide

  18. 사용자 입력이 발생하고 화면의 변화가 생길때까지의 시간
    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

    View Slide

  19. 사용자 입력이 발생하고 화면의 변화가 생길때까지의 시간
    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

    View Slide

  20. 사용자 입력이 발생하고 화면의 변화가 생길때까지의 시간
    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

    View Slide

  21. 페이지 전체 수명동안 발생하는 예기치 않은 레이아웃 이동 점수
    CLS (Cumulative Layout Shift)

    View Slide

  22. 1. 웹 성능이란?


    2. 웹 반응성


    3. 웹 반응성 지표


    4. 웹 반응성 측정 방법


    5. 웹 반응성 개선 사례

    View Slide

  23. 특정 환경에서 로드 성능을 포함한 다양한 성능을 측정합니다.
    Lighthouse User
    fl
    ow

    View Slide

  24. Snapshot
    Navigation Timespan
    단일 페이지의 로드


    성능 측정
    사용자 인터렉션 후


    페이지의 상태 측정
    임의의 시간동안


    사용자 인터렉션 측정
    Lighthouse User
    fl
    ow

    View Slide

  25. - 단일 페이지 로드 분석


    - LCP와 Speed Index와 같은 페이지 로드 성능 점수 제공


    카테고리


    - Performance


    - Accessibility


    - Best Practice


    - SEO


    - PWA
    Navigation
    인터렉션 인터렉션 인터렉션
    측정범위

    View Slide

  26. - 사용자 인터렉션 이후 특정 상태의 페이지 분석


    - SPA나 복잡한 폼의 접근성 이슈 확인


    카테고리


    - Performance


    - Accessibility


    - Best Practice


    - SEO
    Snapshot
    인터렉션 인터렉션 인터렉션
    측정범위

    View Slide

  27. - 임의의 시간동안 사용자 인터렉션 분석


    - 수명이 긴 페이지, SPA 에서 성능 개선 포인트 제공


    카테고리


    - Performance


    - Best Practice


    - SEO
    Timespan
    인터렉션 인터렉션 인터렉션
    측정범위

    View Slide

  28. 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();

    View Slide

  29. 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();

    View Slide

  30. 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();

    View Slide

  31. 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();

    View Slide

  32. 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();

    View Slide

  33. Chrome Devtools Recorder

    View Slide

  34. {


    "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"


    ]


    ]

    View Slide

  35. {


    "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"


    ]


    ]

    View Slide

  36. 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 표현식

    View Slide

  37. Chrome Devtools Recorder Extension
    Puppeteer Cypress Nightwatch

    View Slide

  38. 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();

    View Slide

  39. 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();

    View Slide

  40. 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();

    View Slide

  41. 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();

    View Slide

  42. Lighthouse User
    fl
    ow 의 timespan 모드 리포트

    View Slide

  43. 1. 웹 성능이란?


    2. 웹 반응성


    3. 웹 반응성 지표


    4. 웹 반응성 측정 방법


    5. 웹 반응성 개선 사례

    View Slide

  44. TBT (Total Blocking Time)

    View Slide

  45. 챗봇 주문

    View Slide

  46. 챗봇 주문
    ◼︎
    TBT
    ◼︎
    INP ● CLS
    300 ms 350 ms 0
    TBT
    Total Blocking Time
    GOOD
    NEEDS


    IMPROVEMENT
    POOR
    200 600

    View Slide

  47. 챗봇 주문
    화면 전환시 강제 re
    fl
    ow 발생

    View Slide

  48. Re
    fl
    ow (Critical Rendering Path)
    Composite
    Paint
    Layout
    Style
    Javascript

    View Slide

  49. Re
    fl
    ow (Critical Rendering Path)
    Composite
    Paint
    Layout
    Style
    Javascript

    View Slide

  50. Javascript
    Layout
    Style
    Re
    fl
    ow (Critical Rendering Path)
    Composite
    Paint
    Layout
    Style
    Javascript
    Javascript
    Javascript

    View Slide

  51. 챗봇 주문 프로젝트 개선전/후

    View Slide

  52. 챗봇 주문
    ◼︎
    TBT
    ◼︎
    INP ● CLS
    300 ms 350 ms 0
    TBT
    Total Blocking Time
    GOOD
    NEEDS


    IMPROVEMENT
    POOR
    200 600

    View Slide

  53. 챗봇 주문
    ● TBT ● INP ● CLS
    200 ms 200 ms 0
    TBT
    Total Blocking Time
    GOOD
    NEEDS


    IMPROVEMENT
    POOR
    200 600
    33% 개선

    View Slide

  54. INP (Interaction to Next Paint)

    View Slide

  55. WPM (Web Performance Monitoring)

    View Slide

  56. 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

    View Slide

  57. 전환 업데이트
    긴급 업데이트
    WPM (Web Performance Monitoring)
    React 팀에서 정의한 상태 업데이트

    View Slide

  58. View Slide

  59. View Slide

  60. WPM 프로젝트 개선/후

    View Slide

  61. 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

    View Slide

  62. 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% 개선

    View Slide

  63. CLS (Cumulative Layout Shift)

    View Slide

  64. 카카오페이 구매

    View Slide

  65. 카카오페이 구매
    ● TBT ● INP ▲ CLS
    20 ms 140 ms 0.259
    CLS
    Cumulative Layout Shift
    GOOD
    NEEDS


    IMPROVEMENT
    POOR
    0.1 0.25

    View Slide

  66. 카카오페이 구매
    카카오페이 구매 프로젝트 개선/후

    View Slide

  67. 카카오페이 구매
    ● TBT ● INP ▲ CLS
    20 ms 140 ms 0.259
    CLS
    Cumulative Layout Shift
    GOOD
    NEEDS


    IMPROVEMENT
    POOR
    0.1 0.25

    View Slide

  68. 카카오페이 구매
    ● TBT ● INP ● CLS
    20 ms 80 ms 0
    CLS
    Cumulative Layout Shift
    GOOD
    NEEDS


    IMPROVEMENT
    POOR
    0.1 0.25
    100% 개선

    View Slide

  69. 끝으로..
    1. 로드 성능은 여전히 중요하지만 반응성도 점점 중요해진다.


    2. Lighthouse user
    fl
    ow로 측정하는 반응성


    3. 반응성 지표별 개선 사례


    - 블로킹 타임 발생시 강제 리플로우 확인


    - 전체 화면 업데이트 발생시 중요한 부분 우선 업데이트 진행


    - 레이아웃 시프트는 간단한 작업만으로 쉽게 개선 가능

    View Slide

  70. 끝으로..
    1. 로드 성능은 여전히 중요하지만 반응성도 점점 중요해진다.


    2. Lighthouse user
    fl
    ow로 측정하는 반응성


    3. 반응성 지표별 개선 사례


    - 블로킹 타임 발생시 강제 리플로우 확인


    - 전체 화면 업데이트 발생시 중요한 부분 우선 업데이트 진행


    - 레이아웃 시프트는 간단한 작업만으로 쉽게 개선 가능

    View Slide

  71. Next Step
    - 웹 반응성 측정 자동화


    - 웹 반응성 측정과 e2e 테스트 통합


    - 웹 반응성 개선 가이드

    View Slide


  72. View Slide