리액트에서 INP최적화하기

MM·2025년 4월 8일

PerformanceHigher

목록 보기
3/4
post-thumbnail

핵심 웹 바이탈(Core Web Vitals)

웹 성능 측정 지표.
라이트하우스로 측정한 LCP 외에도 INP, CLS가 있다!

INP (Interaction to Next Paint)

사용자 입력에 대한 반응 시간

사용자가 클릭/탭/키보드 입력 등 상호작용을 했을 때..
-> 그에 따른 화면 변화가 실제로 일어나는 시점까지 걸린 시간!
-> LCP가 2.5초 이하를 권장하듯, INP는 0.2초 이하를 권장한다.




1. 레이아웃 스레싱 방지하기

브라우저는 성능 최적화를 위해 DOM 업데이트를 비동기적으로 처리하려 한다! = batch처리

그래서 아래처럼 dom 레이아웃 읽기와 쓰기가 반복되면..

for (let el of elements) {
  const height = el.clientHeight; //reflow
  el.style.height = height + 10 + "px"; //repaint
}

계속해서 레이아웃을 재계산하면서..
-> CPU를 낭비하고 프레임이 드랍돼 FPS가 감소한다!
-> 이것이 바로 레이아웃 스레싱.

해결법

간단하다! 아래처럼 reflow와 repaint가 일어날 지점을 모아 묶어주면 됨.

const heights = elements.map(el => el.clientHeight); // 읽기 묶기
elements.forEach((el, i) => {
  el.style.height = heights[i] + 10 + "px"; // 쓰기 묶기
});



2. 돔구조 개선

dom 크기 줄이기

구글에서는 document.querySelectorAll("*").length 로 얻은 돔 개수가 1400개 이하일 것을 권장!

  • dynamic import로 줄이기
  • observer로 lazy loading하기
  • 작은 단위 suspense 적극 활용 (단, 첫 뷰포트에는x)



3. useEffect 주의하기

브라우저의 리렌더링 순서

  1. js 상태변경
  2. 리렌더링
  3. useLayoutEffect 실행
  4. 리페인트 = 진짜 크기
  5. useEffect 실행
    • 리페인트가 완벽하게 끝난 시점이 아님!
    • 브라우저는 렌더링 후 작업을 매우 빠르게 진행하기 때문에..!
  6. 다음 이벤트 루프 시작 -> setTimeout

따라서...

useEffect(() => {
  const element = document.getElementById("some-box");
  const height1 = element.getBoundingClientRect().height;
  console.log(`${height1}는 실제 그려진 height와 다를수도 있다!`);
  //useEffect가 실행될 시점엔 아직 리페인트가 완벽하게 끝나지 않았다!
  
  
 setTimeout(() => {
    const height2 = element.getBoundingClientRect().height;
    console.log(`${height2}가 실제 그려진 height!`);
   //다음 이벤트 루프때 실행!
  }, 0);
}, []);

setTimeout은 어따 쓰지

모든 레이아웃이 다 보여지고 난 후에 해야 하는 작업에!!

  • 성능 측정
  • 전체 레이아웃 다 생긴 이후의 스크롤 이동 <- 이거 유용할듯???
profile
중요한 건 꺾여도 그냥 하는 마음

0개의 댓글