웹 서비스 성능 최적화하기 - Part 2. userflow

gyomni·2023년 4월 3일
1

Performance Optimization

목록 보기
2/2
post-thumbnail

part.1의 로딩, 렌더링 성능에 이어서 이번에는 유저의 액션에 얼마나 빠르게 반응하는가! 에 대한 주제로 게시해보려한다.

웹 반응성 성능을 측정하고 개선해서 UI가 유저의 상호작용에 아주 빠르게 반응하도록하여 좋은 UX를 선사하는 서비스로 거듭나보겠다✨


성능 측정 도구

Part 1. 로드 및 렌더링 성능과 마찬가지로 lighthouse를 사용한다.

그 중에서도 TimespansSnapshots로 웹 반응성에 대한 성능을 측정한다.

참고로 Layout shift는 사용자가 쉽게 알아차릴 수 있을 것이라 생각해서 최대한 개선해 놓은 상태이다.

이번에는 회원이 생성한 고유링크에 비회원(게스트)이 들어가서 서비스를 이용하는 부분의 userflow의 성능을 측정해보겠다.

Timespans

: 유저 인터렉션을 포함한 임의의 기간을 분석한다.

Timespans에서는 Performance와 Best Practices의 점수를 볼 수 있다.

아래는 /guest/개인링크/guest-entry로 들어간 게스트가 테이프를 만드는 과정까지의 userflow를 보여주고 있다.

Performace, Best Practice가 초록색이 되도록 성능 개선을 해보자 !

1. Performance

사용자에게 시각적으로 표시되는 데 걸리는 시간을 측정하는 속도 지수 점수이다.

현재 8 / 13 으로 매겨졌다.

1) Metrics

Web Vitals중 개선해야 할 사항들에는 TBT , INP 가 있다.

🔺 Total Blocking Time (TBT) / 930 ms
Sum of all time periods between FCP and Time to Interactive, when task length exceeded 50ms, expressed in milliseconds.

TBT 점수가 현재 930 ms인데, 200 ms까지 되야 빠르다는 평가를 받을 수 있다.

TBT는 part.1의 navigation 측정에서도 시간이 오래걸려 개선해야 했던 사항이다.
part.1 게시물에서 TBT에 대한 내용은 기술했었지만, task나 메인 스레드 대한 내용은 다루지 못했었기에 여기서 추가로 정리하고 넘어가겠다.

Task

  • 브라우저가 수행하는 개별 작업이다.

  • 작업에는 렌더링, HTML / CSS 구문 분석, JS 코드 실행 및 직접 제어할 수 없는 기타 작업과 같은 것들이 포함되고, 이 모든 것 중에서 JS는 작업의 주요 소스이다.

Main Thread

  • 브라우저에서 대부분의 작업이 실행되는 곳이다.

  • 한 번에 하나의 작업만 처리할 수 있고, 작업이 50ms를 초과하면 long task로 분류된다.

  • long task 실행 동안에 사용자 - 페이지 상호 작용 시도, 중요한 렌더링 업데이트 필요한 경우 에서 브라우저는 해당 작업을 처리하는 데 지연되고,
    이로 인해 상호 작용 또는 렌더링 대기 시간이 발생한다.

    이런 경우 아래의 이미지처럼 하나의 long task를
    개별적으로 실행하는 데 시간이 덜 걸리는 더 작은 task로 나누어야 한다



    아래의 이미지처럼 task가 분할될 때 브라우저가 사용자 상호 작용을 포함하여 우선 순위가 높은 작업에 응답할 수 있는 더 많은 기회를 갖기 때문에 task를 나누는 것은 중요하다.

task, main thread은 아래와 같이 performace tab에서 확인할 수 있다.


🟧 Interaction to Next Paint (INP) / 230 ms
Interaction to Next Paint measures page responsiveness, how long it takes the page to visibly respond to user input.

INP는 페이지에서 발생하는 모든 클릭, 탭 및 키보드 상호 작용을 측정하여 페이지의 전반적인 반응성을 나타내는 것이다.

INP의 목표는 사용자가 수행하는 모든 or 대부분의 상호 작용에 대해 사용자가 상호 작용을 시작한 때부터 다음 프레임이 그려질 때까지의 시간을 가능한 한 짧게 만드는 것이다. 즉, 사용자 입력이 발생하고 빠른 피드백을 제공해야 한다.

오래 걸리는 작업에 대하여 로딩 화면을 보여주거나 동시성기능을 활용하여 개선할 수 있다.


INP의 총 시간
아래 시간의 총 합이 INP의 시간이 된다.

  • 사용자의 입력이 발생하고, 그 전에 수행하던 작업이 끝날 때 까지 발생하는 지연 시간
  • 사용자의 입력에 대한 이벤트 헨들러를 수행하는 시간
  • 사용자에게 전달할 시각적 피드백이 브라우저에서 돔에 반영되고 화면에 나타나기까지 걸리는 시간

INP의 interaction 관찰 유형

  • 마우스 클릭
  • 터치스크린 기기 탭
  • 실제 또는 화면 키보드에서 키 누르기

FID(First Input Delay)와 다른점

  • INP는 모든 페이지 상호 작용을 고려
  • FID는 첫 번째 상호 작용만 고려
    - 첫 번째 상호작용의 입력 지연 만 측정하며 이벤트 핸들러를 실행하는 데 걸리는 시간이나 다음 프레임 표시 지연은 측정하지 않음.

2) Diagnostics

각각의 가이드에 따라 개선을 해보자.

Minimize work during key interaction - 230 ms spent on event 'mousedown'
next paint와의 상호 작용 측정 중에 발생하는 스레드 차단 작업
이라고 한다. 즉 INP와 관련있다.

performace tab에서 자세하게 살펴보자.

위 과정에서 "작성완료" 버튼을 클릭 후 다음 피드백까지 233.4 ms의 시간이 소요 되고 있는데, 200 ms 내로 피드백을 제공해주도록 해야한다.

버튼을 누르고 난 뒤 로딩중 일때를 바로 사용자에게 알려주는 로딩중 애니메이션을 넣었다.

스크린샷 처럼 "작성완료" 를 클릭하면 다음 작업이 이뤄지기 전까지 스피너로 로딩중을 유저가 알 수 있게 되면서 INP는 180ms로 좋은 범위에 들어오게 되었다.


3) 결과

2. Best Practices


콘솔 창에 에러가 뜨면서 생긴 가이드이다.

react-dom.production.min.js:164 Uncaught Error: Minified React error #418; visit https://reactjs.org/docs/error-decoder.html?invariant=418 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.
    at l7 (react-dom.production.min.js:164:367)
    at up (react-dom.production.min.js:242:182)
    at s (react-dom.production.min.js:350:298)
    at sp (react-dom.production.min.js:333:138)
    at react-dom.production.min.js:331:376
    at sd (react-dom.production.min.js:331:384)
    at i9 (react-dom.production.min.js:323:192)
    at E (scheduler.production.min.js:14:181)
    at MessagePort.L (scheduler.production.min.js:15:101)

https://reactjs.org/docs/error-decoder.html?invariant=418 에 들어가보니 hydration과 관련된 에러였다.

_app.tsx

  <QueryClientProvider client={queryClient}>
     <Suspense fallback={<Custom404 />}>
          <ThemeProvider theme={theme}>
            <Global styles={global} />
            <Layout>
              <Component {...pageProps} />
            </Layout>
            <div id="modal" />
          </ThemeProvider>
        </Suspense>
      </QueryClientProvider>

<Suspense>는 클라이언트 측 데이터 가져오기와 함께 사용하도록 설계되었기 때문에 서버 측 렌더링과 함께 사용하면 문제가 발생할 수 있다고 한다.

그래서 hydrated 라는 상태를 boolean값으로 생성하고
useEffect에서 setState 함수로 false값을 true로 바뀐 뒤,
Suspense 위치를 아래와 같이 바꾼다면 에러가 나지 않을 것이라 생각이 들었다.


        {hydrated && (
          <Suspense fallback={<Custom404 />}>
          <ThemeProvider theme={theme}>
            <Global styles={global} />
            <Layout>
              <Component {...pageProps} />
            </Layout>
            <div id="modal" />
          </ThemeProvider>
		</Suspense>
        )}

하지만 이렇게 하게 되면 초기 렌더링시에도 클라인어트측의 렌더링이 완료된 후 받아오기 때문에 next.js를 사용하는 이유가 없어질 것이라 생각했다.

Suspense를 제거해봤을 때 현재는 전과 다른 부분이 없었기에 아래와 같이 <suspense fallback={<Custom404 />}>...</suspense> 부분을 제거하는 선택을 했다.


  <QueryClientProvider client={queryClient}>
          <ThemeProvider theme={theme}>
            <Global styles={global} />
            <Layout>
              <Component {...pageProps} />
            </Layout>
            <div id="modal" />
          </ThemeProvider>
      </QueryClientProvider>

결과

Snapshots

: 유저가 상호작용 한 후 특정 상태의 페이지를 분석한다.

1) BEST PRACTICES

사용자가 화면 확대 축소 못하도록 막은 user-scalable=no 속성 때문에 생긴 에러는 part.1 에서도 언급했듯이 의도한 것이므로 나두기로 했다.

2) CONTRAST

배경색과 전경색의 대비 비율이 충분하지 않다는 가이드가 나와있다. 배경과 글씨의 대비가 적은 경우 사용자가 읽기 불편하다는 안내이다.
이 부분을 향상시킨다면 가독성을 향상시킬 수 있다.

색상 대비가 중요한 이유
낮은 대조도를 경험하는 사람들은 모든 항목이 거의 동일한 밝기로 나타나는 경향이 있다고 한다.
그래서 윤곽선, 테두리, 가장자리 및 세부 정보를 구별하기가 어렵고 배경에 너무 가까운 휘도(밝기)의 텍스트는 읽기가 힘들다고 한다.

성공 기준
텍스트가 이미지의 일부인 경우에도 작은 텍스트의 경우 최소 4.5:1, 큰 텍스트(24px or 19px(bold))의 경우 최소 3:1의 색 대비를 유지

Text elements must have sufficient color contrast against the background 여기에 들어가보면 컬러 대비 분석이 가능하다.

컬러 분석을 해보니 2.18:1 이 나왔다. 대비가 확실히 떨어지긴한다. 현재 디자이너의 디자인 대로 구현한 것이므로 일단은... 놔두었다. 나중에 기회가 된다면 이 부분에 대해 의견을 나눠봐야겠다.


참고자료

profile
Front-end developer 👩‍💻✍

0개의 댓글