본문 바로가기
  • 하고 싶은 일을 하자
dev

useDeferredValue 는 언제, 어떻게 쓰는걸까?

by 박빵떡 2023. 11. 5.
반응형

useDeferredValue 는 UI의 일부분의 update (리렌더링)을 연기할 수 있는 Hook 이다.

const deferredValue = useDeferredValue(value)

 

useDeferredValue를 해석해보자면 연기된 값을 사용한다는 뜻이다.

value가 변하면서 컴포넌트가 리렌더링 될 때

바뀐 최신 value 를 쓰는 게 아니라

처음의 value를 갱신하는 것을 연기하여 계속 쓴다는 의미이다.

 

그렇다면 언제 리렌더링을 연기하여 deferredValue를 쓰면 좋을까?

 

첫 번째 예시

 

 

React 공식 문서의 예시를 가져왔다.

input에 검색할 앨범명을 입력하여 api 요청하고

그 결과를 유저에게 보여준다.

 

예를 들어, 유저가 abcde 이렇게 다섯 글자를 연속으로 입력한다.

이 때 마지막 입력이 끝난 abcde 에 대한 결과만 보여주고 싶을 경우

useDeferredValue 를 사용하면 된다.

 

어떻게 이렇게 동작하는 걸까?

공식 문서에 따르면 useDeferredValue 가 new value 를 받으면, background 에서 new value 를 이용한 re-render 가 발생한다. 이 background re-render는 interrupt 가 가능하다. 즉, ab를 입력하고 있을 때 a에 대해 리렌더링이 끝나지 않았더라도 b가 입력되었을 때 interrupt 가 발생한다. 그 결과 a의 리렌더링은 보여지지 않고 b의 리렌더링 결과만 보여지게 된다.

 

그림을 대충 그려봤는데

value가 a 일 때 리렌더링 하는 중에

b가 입력되어 interrupt 되어 b가 리렌더링 되고 ...

마지막에 e가 입력되었을 때 interrupt 되는 리렌더링이 없어서 e가 보여지게 되는 것이다.

 

렌더링 중이던 a는 어떻게 되나?

background 에서는 a와 ab 둘 다 리렌더링이 시도된다.

그러나 b가 interrupt 할 경우, 이 렌더링은 버려지게 되고, 데이터가 로드되었을 때 최신의 value 로 리렌더링을 하게 된다.

 

출처: React useDeferredValue 공식 문서

재밌는 것은 유저가 입력하여 받아온 response가 cache 로 저장되어

backspace로 이전에 호출했던 input 값인 경우

api 호출을 하지 않고 바로 사용한다고 한다.

 

그래서 공식 문서의 예시에 abc 를 입력하고 backspace 로 c를 지우면

로딩 없이 바로 ab의 결과를 보게 된다.

 

cache에 저장하지 않고 매번 호출하게 하는 방법도 있을까?

나중에 찾아보겠다.

 

※ Throttle이나 Debounce 와는 다르게 api 호출을 막진 않는다.

출처: React useDeferredValue 공식 문서

따라서 abcde 로 입력할 경우

a, ab, abc, abcd, abcde 로 api 호출은 발생한다.

UI에서 보여지는 것만 a, ab, abc, abd 를 안보여주고

abcde 만 보여주게 하는 것이다.

즉, api 호출에 대한 최적화를 위해서는 Throttle과 Debounce를 써야할 것이고

UI에 대한 최적화는 useDeferredValue 를 쓰면 된다.

 

두 번째 예시 : useDeferredValue 를 이용한 최적화

마찬가지로 공식 문서의 에시를 가져왔다.

[App.js]
import { useState, useDeferredValue } from 'react';
import SlowList from './SlowList.js';

export default function App() {
  const [text, setText] = useState('');
  const deferredText = useDeferredValue(text);

  return (
    <>
      <input value={text} onChange={e => setText(e.target.value)} />
      <SlowList text={deferredText} />
    </>
  );
}
[SlowList.js]
import { memo } from 'react';

const SlowList = memo(function SlowList({ text }) {
  // Log once. The actual slowdown is inside SlowItem.
  console.log('[ARTIFICIALLY SLOW] Rendering 250 <SlowItem />');

  let items = [];
  for (let i = 0; i < 250; i++) {
    items.push(<SlowItem key={i} text={text} />);
  }
  return (
    <ul className="items">
      {items}
    </ul>
  );
});

function SlowItem({ text }) {
  let startTime = performance.now();
  while (performance.now() - startTime < 1) {
    // Do nothing for 1 ms per item to emulate extremely slow code
  }

  return (
    <li className="item">
      Text: {text}
    </li>
  )
}

export default SlowList;

 

이 예시에서는 text 에 입력하여 SlowList 가 리렌더링 될 경우

의도적으로 딜레이를 줘서 리렌더링이 오래 걸리는 예시이다.

실제로 테스트 해보면 useDeferredValue 가 없을 경우 text 입력에 큰 딜레이가 발생하게 된다.

 

useDeferredValue를 사용하면 abcd 를 입력했을 때의 리렌더링 시간을 단축할 수 있다.

 

빨간색은 useDeferredValue 가 없을 경우이다.

a를 입력했을 때 SlowList 렌더링이 오래 걸려 b를 입력하지 못하고 오래 기다렸다가 입력해야한다.

 

파란색이 useDeferredValue를 사용한 경우인데

a를 입력하고 b를 바로 입력했을 때

a를 리렌더링 하는 중에 b가 interrupt 로 입력되어

b의 리렌더링을 수행하게 된다.

이로인해 input 을 곧바로 입력할 수 있게 되어 더 좋은 UX를 개발할 수 있다.

 

공식 문서 링크:

https://react.dev/reference/react/useDeferredValue

 

useDeferredValue – React

The library for web and native user interfaces

react.dev

 

반응형

댓글