본문으로 건너뛰기
개발 머꼬
개발 노트React
hohyeon.dev22

검색창에 타이핑할 때마다 화면이 버벅인 이유 — 무거운 목록과 같은 우선순위

  • #Engineering Note
  • #Performance
  • #React

문제 발생

검색어를 입력하면 5천 건짜리 목록이 필터링되는 화면에서, 글자를 칠 때마다 입력이 한 박자씩 늦게 반영됐습니다. 목록 렌더링이 느린 것은 알겠는데 입력창까지 같이 느려지는 것이 문제였습니다.

const [query, setQuery] = useState("");
const results = useMemo(() => filterHuge(items, query), [items, query]);

return (
  <>
    <input value={query} onChange={(e) => setQuery(e.target.value)} />
    <HugeList items={results} />
  </>
);

원인 분석

React는 하나의 상태 갱신을 하나의 렌더로 처리합니다. setQuery 한 번에 입력창과 목록이 함께 다시 그려지고, 그 렌더가 끝나야 브라우저가 화면을 갱신합니다. 목록 렌더가 200ms 걸리면 입력 반영도 200ms 뒤입니다.

문제의 본질은 성능이 아니라 우선순위입니다. 입력창은 즉시 반응해야 하고 목록은 조금 늦어도 됩니다. 그런데 코드에는 그 차이가 표현돼 있지 않습니다.

디바운스로 미루는 방식이 흔한 우회책이지만, 지연 시간을 사람이 정해야 하고(짧으면 여전히 버벅이고 길면 반응이 굼뜹니다), 빠른 기기와 느린 기기에 같은 값이 적용됩니다.

React는 이 구분을 API로 제공합니다. useDeferredValue 는 값의 "급하지 않은 버전"을 만들고, useTransition 은 특정 갱신을 급하지 않다고 표시합니다. 급한 렌더가 들어오면 진행 중이던 급하지 않은 렌더는 중단되고 다시 시작됩니다.

해결 방안

  1. 목록에는 미뤄진 값을 넘깁니다. 입력창은 query로 즉시 갱신되고, 목록은 deferredQuery를 따라 늦게 따라옵니다.
const [query, setQuery] = useState("");
const deferredQuery = useDeferredValue(query);
const results = useMemo(() => filterHuge(items, deferredQuery), [items, deferredQuery]);

return (
  <>
    <input value={query} onChange={(e) => setQuery(e.target.value)} />
    <HugeList items={results} />
  </>
);
  1. 미뤄지는 동안임을 표시합니다. 값이 아직 따라오지 않았다는 것을 비교로 알 수 있습니다.
const isStale = query !== deferredQuery;
<div style={{ opacity: isStale ? 0.6 : 1 }}><HugeList items={results} /></div>
  1. 무거운 자식은 memo로 감쌉니다. 이게 빠지면 미뤄진 값이 같아도 부모가 리렌더될 때 자식이 매번 다시 그려져 효과가 사라집니다. useDeferredValuememo와 함께 쓸 때 제 값을 합니다.
  2. 이벤트에서 시작하는 갱신이라면 useTransition을 씁니다. 탭 전환처럼 "누른 즉시 반응은 필요하지만 내용 교체는 조금 늦어도 되는" 경우에 맞습니다.
const [isPending, startTransition] = useTransition();

function selectTab(next) {
  startTransition(() => setTab(next));   // 이 갱신은 중단 가능
}
  1. 디바운스를 대체하는 것이지 최적화를 대체하지는 않습니다. 렌더 자체가 무거우면 여전히 무겁습니다 — 가상 스크롤이나 결과 개수 제한이 먼저인 경우도 많습니다. 우선순위 분리는 "그럼에도 입력은 즉시 반응하게" 만드는 도구입니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.