
상태 변화의 우선순위를 지정하기 위해
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의 즉각적 반응 보장 | 렌더링이 지연되므로 값의 변화에 따른 불필요한 렌더링 최소화 |
| 사용 예시 | 필터링, 대규모 데이터 처리, 애니메이션 등 긴급하지 않은 작업 | 검색어 입력 시 렌더링 지연, 빠른 입력에서 발생하는 불필요한 렌더링 방지 |