상태 변화의 우선순위를 지정하기 위해
useTransition
훅이 새로 도입되었습니다.
useTransition
은 비동기 작업을 처리할 때 사용되며, 특히, 복잡한 상태 업데이트나 UI 렌더링이 필요할 때 해당 작업을 우선순위가 낮은 작업으로 처리하여 사용자 인터페이스가 끊김 없이 반응할 수 있도록 도와줍니다. 이를 통해 성능을 향상시키고, 사용자가 어떤 작업을 했을 때 즉각적인 반응을 볼 수 있게 해줍니다.
isPending
: 상태 업데이트가 지연 중인지 여부를 나타내는 불리언 값.startTransition
: 긴급하지 않은 상태 업데이트를 실행하는 함수.const [isPending, startTransition] = useTransition();
const handleClick = () => {
startTransition(() => {
// 긴급하지 않은 상태 업데이트
setState(newValue);
});
};
useTransition
의 내부 동작 원리는 React의 동시성 모드(Concurrent Mode)와 관련이 깊습니다. React 18에서 도입된 이 동시성 모드를 통해, React는 상태 업데이트와 렌더링 작업을 우선순위에 따라 관리할 수 있게 되었습니다. useTransition
은 이러한 동시성 모드의 기능을 활용하여 상태 업데이트의 우선순위를 조절하고, 긴급하지 않은 작업을 비동기적으로 처리하는 방식으로 성능을 최적화합니다.
1 ) 상태 업데이트 우선순위 분리
useTransition
을 사용하면 상태 업데이트 작업을 두 가지 우선순위로 나누어 처리할 수 있습니다.
이러한 작업을 우선순위에 따라 분리함으로써 UI가 중요한 작업(예: 입력 처리)을 방해받지 않도록 하고, 덜 중요한 작업은 나중에 처리하게 됩니다.
2 ) startTransition 함수
startTransition
은 긴급하지 않은 작업을 감싸서 처리하는 역할을 합니다. 이를 호출하면 React는 해당 상태 업데이트를 낮은 우선순위 작업으로 분류하여, 동시성 모드에서 처리할 수 있게 됩니다. 내부적으로 startTransition
은 이 작업을 비동기적으로 스케줄링합니다.
useTransition
은 상태 업데이트가 즉시 처리될 필요가 없는 경우, 즉 긴급하지 않은 작업을 낮은 우선순위로 처리합니다. 예를 들어, 대량의 데이터 필터링 작업이나 애니메이션은 우선순위가 낮은 작업으로 처리되어 사용자 상호작용(클릭, 입력 등)과 같은 긴급한 작업에 영향을 미치지 않습니다.useTransition
은 비동기적으로 상태를 업데이트합니다. 즉, UI에서 즉각적인 반응이 필요한 작업을 먼저 처리한 후, 나머지 상태 업데이트를 나중에 백그라운드에서 처리합니다. 이를 통해 UI의 성능을 유지하면서도 작업을 효율적으로 처리할 수 있습니다.useTransition
을 사용하면 긴급하지 않은 상태 업데이트가 백그라운드에서 처리되므로, 사용자가 상호작용하는 중요한 작업이 먼저 처리됩니다. 즉, 사용자 입력에 즉각적으로 반응할 수 있어 UI가 끊김 없이 동작합니다.useTransition
은 중요한 작업(예: 클릭, 입력 등)이 차질 없이 처리되도록 해 주어 매끄러운 사용자 경험을 제공합니다. 사용자는 필터링, 대량의 데이터 렌더링 등 무거운 작업 중에도 입력 또는 UI 상호작용의 즉각적인 반응을 경험할 수 있습니다.isPending
값을 이용해 로딩 상태를 처리할 수 있습니다. 이를 통해 사용자는 작업이 진행 중임을 인지하고 기다릴 수 있게 됩니다.useTransition
을 사용하여 긴급하지 않은 작업을 백그라운드에서 처리함으로써 복잡한 UI도 부드럽게 렌더링할 수 있습니다.useTransition
을 남용하거나 잘못 사용하면 애플리케이션의 복잡도가 증가할 수 있습니다. 상태 업데이트를 어느 시점에 우선순위를 낮출 것인지 결정하는 것은 개발자의 몫이며, 잘못 설정된 우선순위로 인해 기대하지 않은 성능 문제가 발생할 수 있습니다.isPending
)를 보여주는 것이 중요합니다.useTransition
은 브라우저의 성능에 따라 다르게 동작할 수 있습니다. 특히, 브라우저가 리소스가 부족할 경우 낮은 우선순위 작업이 매우 느리게 처리될 수 있어 기대했던 만큼의 성능 향상을 얻지 못할 수도 있습니다.useTransition
을 사용해 검색과 같은 작업을 처리할 때, 검색어 입력은 즉시 반영되지만 결과 목록을 업데이트하는 작업은 우선순위를 낮춰서 UI가 끊기지 않게 처리하는 경우입니다.
import { useEffect, useState, useTransition } from "react";
function SearchComponent() {
const [keyword, setKeyword] = useState(""); // 입력값은 즉시 반영
const [list, setList] = useState([]);
const [isPending, startTransition] = useTransition();
const items = Array.from({ length: 5000 }, (_, i) => `Item ${i + 1}`);
const filterItems = (query) => {
return items.filter((item) => item.includes(query));
};
const handleChange = (e) => {
const value = e.target.value;
setKeyword(value);
};
useEffect(() => {
// 검색 결과 업데이트는 낮은 우선순위로 처리
startTransition(() => {
const filteredList = filterItems(keyword);
setList(filteredList);
});
}, [keyword]);
return (
<div>
<input
type="text"
value={keyword}
onChange={handleChange}
placeholder="Search..."
/>
{/* 검색 결과가 지연될 때 로딩 메시지 표시 */}
{isPending && <p>Updating list...</p>}
<ul>
{list.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}
export default SearchComponent;
startTransition
: 검색 결과 목록을 업데이트하는 작업을 낮은 우선순위로 처리합니다.isPending
: 트랜지션이 진행 중일 때 UI에 "Updating list..."를 보여줌으로써 로딩 상태를 보여줍니다.
useTransition
은 함수 실행의 우선순위를 지정하는 반면,useDeferredValue
는 값의 업데이트 우선순위를 지정합니다. 우선순위가 높은 작업을 실행하는 동안useMemo
와 유사하게 이전 값을 계속 들고 있으면서 업데이트를 지연시킵니다.
이 훅은 useMemo
와 함께 사용하면 더 효과가 좋다. 종속된 값들을 memoize 시키면 불필요한 재 랜더링을 막으면서 하위 컴포넌트나 상태의 업데이트를 지연시킬 수 있습니다.
useDeferredValue
가 값을 지연시키는 내부 동작은 React의 스케줄링 시스템과 우선순위 기반 작업 처리에 기반을 둡니다. 이 시스템은 React 18에서 도입된 Concurrent Mode(동시성 모드)에서 작동하며, 이는 React가 렌더링 작업을 더 유연하게 관리할 수 있도록 돕습니다. 구체적으로 useDeferredValue
는 값의 업데이트를 지연시켜 중요한 작업을 먼저 처리하고, 덜 중요한 렌더링 작업을 나중에 처리하는 방식으로 작동합니다.
1 ) 스케줄링 작업
React는 상태나 값이 변경될 때마다 해당 변경을 작업 단위로 분리하여 처리합니다. 각 작업은 우선순위(priority)를 가지고 있습니다. 예를 들어, 사용자의 클릭이나 입력과 같은 상호작용은 높은 우선순위를 가지며, 데이터 처리나 값 필터링 등의 작업은 낮은 우선순위로 처리될 수 있습니다.
2 ) 현재 상태 유지
useDeferredValue
는 값이 변경되더라도, 즉시 새로운 값으로 렌더링하지 않고 이전 값을 계속해서 사용합니다. 즉, 값 자체는 바로 변경되었지만, 렌더링이 지연되어 화면에 반영되는 것을 늦추는 방식입니다.
3 ) 값 업데이트 지연
React는 중요한 작업이 끝나거나 렌더링에 여유가 생겼을 때, 비로소 지연된 값을 사용하여 렌더링을 수행합니다. 이때 새로운 값이 기존 값과 달라졌다면, 그때서야 컴포넌트가 다시 렌더링됩니다. 중요한 점은 이 렌더링이 사용자의 인터페이스 상호작용을 방해하지 않도록, 낮은 우선순위로 처리된다는 것입니다.
4 ) 렌더링 최적화
useDeferredValue
는 값이 자주 변하는 상황에서 이를 즉시 반영하지 않기 때문에, 불필요한 렌더링을 줄일 수 있습니다. 렌더링이 지연되면 React는 해당 작업을 백그라운드에서 처리하고, 중요한 UI 상호작용에 집중할 수 있습니다.
useDeferredValue
는 값이 변하더라도 즉시 렌더링되지 않고 지연시켜 성능을 최적화합니다. 즉, 사용자가 입력하거나 값을 변경해도 해당 값이 바로 렌더링되지 않고, React가 여유가 있을 때 새로운 값으로 렌더링을 처리합니다.useMemo
와의 조합: useDeferredValue
는 useMemo
와 함께 ****사용하면 더 효과적입니다. 자주 변하는 값들을 useDeferredValue
로 지연시키고, 메모이제이션을 통해 종속된 값들을 메모이즈하면 불필요한 연산과 렌더링을 더 효과적으로 줄일 수 있습니다.useDeferredValue
를 사용하면 값의 변화를 지연시켜 덜 중요한 부분을 비동기적으로 처리하여, 복잡한 UI에서도 성능 저하 없이 자연스러운 사용자 경험을 제공합니다.useDeferredValue
를 잘못 사용하거나, 즉시 반영되어야 할 중요한 작업에 적용하면 사용자 경험을 저해할 수 있습니다. 예를 들어, 중요한 상호작용이나 즉각적인 반응이 필요한 경우에도 렌더링이 지연되면 사용자가 지연을 인지하게 되고, UI가 느리게 반응하는 것처럼 느껴질 수 있습니다.useDeferredValue
가 적합하지 않을 수 있습니다. 이 경우에는 값을 즉시 반영해야 사용자 경험이 저하되지 않습니다.useDeferredValue
를 적절히 활용하기 위해서는 코드에 추가적인 복잡성이 발생할 수 있습니다. 언제 값을 지연시킬지, 지연된 값을 어떻게 처리할지에 대한 전략을 잘 짜야 하며, 잘못된 사용은 성능을 저하시키거나 예상치 못한 동작을 초래할 수 있습니다.useDeferredValue
는 입력 필드의 값이 변경될 때, 해당 값을 즉시 반영하지 않고 지연시켜 불필요한 리렌더링을 방지하는 데 사용할 수 있습니다. 아래 예시에서는 사용자가 검색어를 입력할 때, 검색어 업데이트를 지연시켜 성능을 최적화하는 상황입니다.
import { useState, useDeferredValue, useEffect } from "react";
function App() {
const [keyword, setKeyword] = useState("");
const [list, setList] = useState([]);
const deferredInput = useDeferredValue(keyword); // 지연된 값
const items = Array.from({ length: 5000 }, (_, i) => `Item ${i + 1}`);
const filterItems = (query) => {
// 가상 데이터 필터링 로직 (예: 5000개의 항목을 필터링)
return items.filter((item) =>
item.toLowerCase().includes(query.toLowerCase())
);
};
useEffect(() => {
setList(filterItems(deferredInput));
}, [deferredInput]);
return (
<div>
<input
type="text"
value={keyword}
onChange={(e) => setKeyword(e.target.value)}
placeholder="Search..."
/>
<ul>
{list.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
}
export default App;
useDeferredValue
는 keyword
값을 지연시켜 성능을 최적화합니다. 즉, 사용자가 빠르게 입력할 때 매번 즉시 필터링하는 것이 아니라, 일정 시간 지연된 후 마지막 값으로 필터링 작업을 수행합니다. 이를 통해 UI 성능을 개선할 수 있습니다.useTransition
과 useDeferredValue
는 React에서 성능 최적화를 위해 사용되는 두 가지 훅이지만, 그 목적과 동작 방식이 다릅니다.
useTransition
은 상태 업데이트의 우선순위를 낮춰, 긴급하지 않은 작업(예: 필터링, 애니메이션 등)을 비동기적으로 처리하여 UI가 끊기지 않고 부드럽게 작동하도록 돕습니다. 이를 통해 중요한 작업(예: 사용자 입력)과 덜 중요한 작업을 구분하여 UI 응답성을 유지하는 데 중점을 둡니다.
반면, useDeferredValue
는 값의 렌더링을 지연시켜 사용자가 느끼는 지연을 최소화하면서 덜 중요한 값의 변화를 비동기적으로 반영합니다. 즉, 값 자체는 즉시 업데이트되지만, 렌더링이 지연되기 때문에 UI 성능에 부담이 적습니다. 주로 값이 자주 변하는 상황에서 성능을 개선하는 데 사용됩니다.
이 두 훅은 각각 다른 용도에 맞게 사용되며, 애플리케이션의 성능과 사용자 경험을 크게 향상시킬 수 있습니다.
useTransition | useDeferredValue | |
---|---|---|
주요 목적 | 긴급하지 않은 상태 업데이트를 낮은 우선순위로 처리하여 성능 최적화 | 자주 변하는 값의 렌더링을 지연시켜 불필요한 리렌더링을 방지 |
사용 위치 | 상태 업데이트 시, startTransition 으로 감싸서 비동기 처리 | 값의 업데이트가 빈번할 때, 값을 useDeferredValue 로 감쌈 |
응답성 | 중요한 상태 업데이트와 덜 중요한 작업을 구분해 UI가 끊김 없이 반응하도록 지원 | 값의 변경을 지연시켜 UI 성능을 유지하면서 부드러운 동작을 제공 |
렌더링 | 상태 업데이트가 비동기적으로 처리되어 UI의 즉각적 반응 보장 | 렌더링이 지연되므로 값의 변화에 따른 불필요한 렌더링 최소화 |
사용 예시 | 필터링, 대규모 데이터 처리, 애니메이션 등 긴급하지 않은 작업 | 검색어 입력 시 렌더링 지연, 빠른 입력에서 발생하는 불필요한 렌더링 방지 |