Concurrent mode
๋์์ฑ ๋ชจ๋(concurrent mode)๋ ํ ๊ฐ์ง ์ผ์ด ๋๋ ๋ ๊น์ง ๋ฌด์กฐ๊ฑด ๋ค์ ์์ ์ ๊ธฐ๋ค๋ฆฌ๋ ๊ฒ์ด ์๋, ํ ๊ฐ์ง ์ผ์ด ์คํ ์ค์ผ ๋์ ๋ค๋ฅธ ์ผ์ ์ํํ๋ ๊ฒ์ ์๋ฏธํ๋ค. ๊ทธ๋ ๋ค๋ฉด ์ด ๋์์ฑ(concurrency)์ด ๋ณํ์ฑ(parallelism)๊ณผ ๊ฐ์ ์๋ฏธ๋ฅผ ๊ฐ์ง ๋์์ด๊ฐ ์๋๊ฐ? ๋ผ๊ณ ์๊ฐํ ์ ์๊ฒ ์ง๋ง ๋์ด ๋งํ๋ ์์ ์ ๋์์ฑ์ ์กฐ๊ธ ๋ค๋ฅด๋ค.
๋์์ฑ์ ๊ฒฝ์ฐ ํ ๊ฐ์ง ์ผ์ด ๋ฌด์กฐ๊ฑด ๋๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๋ ๊ฒ์ด ์๋๋ผ, ๊ทธ ์ฌ์ด์ ๋ค๋ฅธ ์ผ์ ํ ์ ์์ผ๋ฉด ํ๋ ๊ฒ์ด๋ค. ๋ฐ๋ฉด ๋ณํ์ฑ์ ๊ฒฝ์ฐ๋ ๋์์ ๋ ๊ฐ์ง ์ด์์ ์ผ์ ํ๋ ๊ฒ์ด๋ค. ๊ทธ๋ฌ๋ ํ ๊ฐ์ง ์ผ์ด ๋๋ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๋ ๊ฒ๊ณผ๋ ์๊ด์ด ์๊ณ ์์ ๋ง๋ค ๊ฐ๊ฐ ์์์ ์ํํ๋ ๊ฒ์ด๋ค. ๊ฐ์ฅ ์ฌ์ด ์๋ก, ์์ด ๋๊ฐ์ง๋ง ๋น ๋ฅธ ์ฌ๋๊ณผ ์์ด ์ฌ๋๊ฐ์ธ ์ฌ๋์ ์๊ฐํ๋ฉด ๋์์ฑ๊ณผ ๋ณํ์ฑ์ด ๋ฌด์์ ์๋ฏธํ๋ ๊ฒ์ธ์ง ๋๋์ด ์ฌ ๊ฒ์ด๋ค.
๋ฆฌ์กํธ์์์ ๋์์ฑ ์ฒ๋ฆฌ๋ ์ฌ๋ฌ ์์ ์ ์ฒ๋ฆฌํ ์ ์๋๋ก ์์ ๋ค์ ์์ ์กฐ๊ฐ๋ค๋ก ๋๋๊ณ , ์ค์ผ์ค๋ฌ๋ฅผ ํตํ์ฌ ๊ฐ ์์ ๋ค์ ์ค์๋์ ๋ฐ๋ฅธ ์ฐ์ ์์๋ฅผ ๋ถ์ฌํ๋ค(time-slicing).
์ ์ฐ์ ์์๋ฅผ ๋ถ๋ฆฌํ ๊น?
์๋ฌด๋ฆฌ ์์ด ๋น ๋ฅธ ์ฌ๋์ด๋ผ๊ณ ํ๋๋ผ๋ ์ฌ์ค์ ๋จ๊ณ๋ณ๋ก ํ์ง ์์ผ๋ฉด ๊ฒฐ๊ตญ ์ ์ฒด์ ์ธ ์์ ์ ๋ง๊ฐ์ง๊ธฐ ๋ง๋ จ์ด๋ค. ์๋์์น ๋ง๋๋ ๊ณผ์ ์์ ๋นต์ ๊ตฝ๋ ์์ ๊ณผ ์ผ์ ๋ฐ๋ฅด๋ ์์ ์ ํ ๋, ๋นต์ ๋จผ์ ๊ตฝ๊ณ ์ผ์ ๋ฐ๋ฅด๋ฏ์ด ์์ ์๋ ์ฐ์ ์์๋ฅผ ๋ถ์ฌํ๊ณ ์ด์ ๋ฐ๋ผ ์์ ์ ์ฒ๋ฆฌํ ํ์๊ฐ ์๋ค.
๋ฆฌ์กํธ๊ฐ ์ด๋ ๊ฒ ๋๋ ์์ ๋ค์ ์ฒ๋ฆฌํ๋ ๊ณผ์ ์์ ๋ฉ์ธ์ค๋ ๋๋ ๋ธ๋ก๋์ง ์์ผ๋ฉฐ, ๋์์ ์ฌ๋ฌ ์์ ์ ์ฒ๋ฆฌํ๋ฉด์ ์ฐ์ ์์์ ๋ฐ๋ผ ๊ฐ ์์ ๋ค ๊ฐ์ ์ ํ์ด ๊ฐ๋ฅํ๊ฒ ๋์๋ค.
์ด์ ๊ฐ์ด ๋ฆฌ์กํธ 18๋ฒ์ ๋ถํฐ๋ ๋์์ฑ ๋ ๋๋ง์ ํตํด์ ๋ ๋๋ง ์์ฒด์ ๊ฐ์ํ๊ณ , ์ด๋ฅผ ์ค๋จํ๊ฑฐ๋ ์ฌ๊ฐ, ํ๊ธฐํ๋ ๋ฑ ์์ ๋ค์ ๋จ์๋ณ๋ก ์กฐ์ ํ ์ ์๊ฒ ๋์๋ค.
๋์์ฑ์ ๋์ ๋ฐฐ๊ฒฝ
React 18 ์ด์ ์๋ ๋ ๋๋ง์ด ๋๊ธฐ์ ์ผ๋ก ์ฒ๋ฆฌ๋์๊ธฐ ๋๋ฌธ์ ์ด ์ค๊ฐ์ ์ด๋ค ๊ฒ๋ ๊ฐ์ ํ ์ ์์๋ค. ์ด๋ ๊ณง ๋ ๋๋ง์ด ์คํ๋๋ฉด ๋ ๋๋ง์ด ๋๋ ๋๊น์ง ๋ฌด์กฐ๊ฑด ๊ธฐ๋ค๋ ค์ผ ํ๋ค๋ ์ด์ผ๊ธฐ๊ธฐ๋ ํ๋ค.
๊ทธ๋์ ๋ง์ฝ ๋ ๋๋ง์ด ์ค๋ ๊ฑธ๋ฆฌ๋ ์์ ์ ๊ฒฝ์ฐ์๋, ๋ค์ ์ํํ ์์ ์ด ๋ธ๋กํน๋์ด ์ ํ๋ฆฌ์ผ์ด์ ์์ฒด๊ฐ ๋ ์ ๋จน๋ ๋ฏํ ๋ชจ์ต์ ๋ณด์ฌ์ฃผ์ด UX๊ฐ ํ์ ํ ๋จ์ด์ง๊ฒ ๋๋ค.
์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ ๊ฐ๋ฐ์๋ค์ Debounce์ Throttle ๋ฐฉ์์ ์ฌ์ฉํ์ฌ ์ด๋์ ๋ ํด์ํ ์ ์์๋ค.
Debounce ์ฌ์ฉ์์ ์ ๋ ฅ์ด ์ฐ์์ผ๋ก ๋ค์ด์ฌ ๋ ๋ง์ง๋ง ์ ๋ ฅ ํ ์ผ์ ์๊ฐ์ด ์ง๋ ๋ค์์ ๋ฌด๊ฑฐ์ด ์์ ์ ์ํํ๋ ๋ฐฉ์
Throttle ํน์ ์๊ฐ ๋์ ํ ๋ฒ๋ง ํจ์๋ฅผ ์คํํ ์ ์๋๋ก ์ ํํ๋ ๋ฐฉ์.
ํ์ง๋ง ์ด๋ฌํ ๋ฐฉ์ ๋ํ Debounce์ ๊ฒฝ์ฐ์๋ ์ฑ๋ฅ์ด ์ข์๋ ๋ชจ๋ ๊ธฐ๊ธฐ์์ ๊ฐ์ ์๊ฐ๋์ ๋๊ธฐ ํ์ ์์ ์ํ์ ํด์ผํ๊ณ , Throttle์ ๊ฒฝ์ฐ์๋ Throttle ์ฃผ๊ธฐ๋ฅผ ์งง๊ฒ ๊ฐ์ ธ๊ฐ์๋ก ์ฑ๋ฅ์ ์ ์ ๋จ์ด์ง๋ค๋ ํ๊ณ๊ฐ ๋ณด์๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ด๋ฌํ ๋๊ธฐ์ ๋ ๋๋ง์ ํ๊ณ๋ฅผ ํด์ํ๊ณ ์ ๋์์ฑ์ ํ์์ฑ์ด ๋๋๋์ด ๋์ค๊ฒ ๋์๋ค.
Concurrent Mode ์ค์
๊ธฐ์กด์ ReactDOM ํจ์์ ํ๋กํ ํ์ ํจ์๋ก ์ฌ์ฉํ๋ render๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ReactDOM์ ํ๋กํ ํ์ ํจ์์ธ createRoot๋ฅผ ํตํด์ ๊ฐ์ฒด๋ฅผ ์์ฑํ ๋ค, ํด๋น ๊ฐ์ฒด์ render ํจ์๋ฅผ ํตํด ์ํธ๋ฆฌ ํฌ์ธํธ๋ฅผ ๋ ๋๋ง์ํจ๋ค.
import ReactDOM from 'react-dom';
import App from 'App';
const container = document.getElementById('app');
// ์ด์ ๋ฒ์ (React 17)
const container = document.getElementById('app');
ReactDOM.render(<App />, container);
// Concurrent Mode ๋์
์ดํ(React 18)
// ๋ฃจํธ ์์ฑ
const root = ReactDOM.createRoot(container);
// ๋ฃจํธ ๊ฐ์ฒด์ ๋ฉ์๋๋ก ์ฑ์ ๋ ๋๋ง
root.render(<App />);{๋ฃจํธ๊ฐ์ฒด}.render๋ฅผ ํตํด์ ์ฑ์ ๋ ๋๋ง์ํค๊ฒ ๋๋ฉด ๊ฐ์ ๋ ๊ธฐ๋ฅ๋ค๊ณผ ๋์ ์ฒ๋ฆฌ๋ฅผ ์ํ startTransition, useTransition, useDeferredValue ํ
์ ์ฌ์ฉํ ์ ์๋ค.
Concurrent Mode์ ํ์ฉ
Automatic Batching(์ํ ์ผ๊ด์ฒ๋ฆฌ)
์ฌ๋ฌ ๊ฐ์ ์ํ๋ฅผ ์
๋ฐ์ดํธํ ๊ฒฝ์ฐ, ๊ธฐ์กด์๋ ํ๋์ state์ ์
๋ฐ์ดํธ -> ๋ณ๊ฒฝ๋ ์ํ๋ฅผ ๋ฆฌ๋ ๋๋ง -> ๋ค์ state์ ์
๋ฐ์ดํธ์ ๋จ๊ณ๋ก ์ํ๊ฐ ์
๋ฐ์ดํธ ๋์๊ธฐ ๋๋ฌธ์ ์ฌ๋ฌ๋ฒ ๋ฆฌ๋ ๋๋ง์ด ๋ฐ์ํ์๊ณ , ์ด์ ์ฑ๋ฅ์ ์ผ๋ก ์ข์ง ์์ ํจ๊ณผ๋ฅผ ๊ฐ์ ธ์๋ค.
๊ธฐ์กด์ React 17๊น์ง๋ Automatic Batching์ด ์ ์ฉ์ ๋์ด ์์์ง๋ง, ์ ์ฉ๋๋ ๊ณณ์ด ์ด๋ฒคํธ ํธ๋ค๋ฌ ํจ์ ๋ด๋ถ๋ก ํ์ ์ ์ธ ํน์ฑ์ ๊ฐ์ก๋ค.
๊ทธ๋ฌ๋ค๋ณด๋ ๋คํธ์ํฌ ํธ์ถ(Promise)์ ๋ํ .then ๋ฉ์๋์ ์ฝ๋ฐฑ ํจ์์ ์ฌ๋ฌ๊ฐ์ ์ํ ์
๋ฐ์ดํธ๊ฐ ๋ค์ด๊ฐ ์๋ค๊ฑฐ๋ setTimeout์ ์ฝ๋ฐฑ ํจ์ ์์ ์ฌ๋ฌ ์ํ ์
๋ฐ์ดํธ ๋ฑ์์๋ ์ํ ์
๋ฐ์ดํธ๊ฐ ์ผ๊ด์ฒ๋ฆฌ๊ฐ ์๋, ์์ฐจ์ฒ๋ฆฌ ๋ฐฉ์์ผ๋ก ๋์ํ๋ฉด์ ๋ณ๊ฒฝ๋ state์ ์๋งํผ ๋ฆฌ๋ ๋๋ง์ ์ํํ๊ฒ ๋๋ค. ๊ทธ๋ฌ๋ค๋ณด๋ ์ฑ๋ฅ์ ์ผ๋ก ๋จ์ด์ง๋ ํจ๊ณผ๋ฅผ ๊ฐ์ง๊ฒ ๋ ๊ฒ์ด๋ค.
์ด์ React18๋ถํฐ Concurrent Mode๊ฐ ํ์ฑํ๋๋ฉด, ๋ชจ๋ Promise๋ setTimeout, ์ด๋ฒคํธ ์ฝ๋ฐฑ ๋ฑ์์ ๋ค์ ๊ฐ์ ์ํ ์ ๋ฐ์ดํธ๊ฐ ์ผ๊ด๋ก ์ฒ๋ฆฌ๋๋๋ก ๋ณ๊ฒฝ๋์๋ค.
Transition ๊ด๋ จ ํ ๋ค

Transition ๊ด๋ จ ํ
๋ค์ ์ ๋ถ ์ฐ์ ์์๋ฅผ ์ง์ ๊ด๋ฆฌํ์ฌ ๋ ๋๋ง ๊ณผ์ ์์ ์ฑ๋ฅ์ ๊ฐ์ ํ๊ธฐ ์ํด ์๋กญ๊ฒ ์ถ๊ฐ๋ ํ
๋ค์ด๋ค.
์์์ ๋งํ ๊ฒ๊ณผ ๊ฐ์ด ์ด์ ์ ๋ ๋๋ง ๋ฐฉ์์ ๋๊ธฐ์ ์ผ๋ก ๊ณ์ํด์ UI์ ๋ํ ์
๋ฐ์ดํธ ์คํ -> ๋ฆฌ๋ ๋๋ง์ ๋ฐ๋ณต์ด์๋ค.

ํ์ง๋ง ๋์์ฑ ๋ ๋๋ง ๋ฐฉ์์ผ๋ก ๋ณ๊ฒฝ๋๋ฉด์ ์ฒซ ์ํ ์
๋ฐ์ดํธ๋ถํฐ ์ต์ข
์ ์ผ๋ก ๋ณด์ฌ์ผ ํ๋ ํ๋ฉด์ ๋ ๋๋ง๊น์ง ๊ฐ๋ ๊ณผ์ ์์ ์ค๊ฐ์ ๊ณ์ํด์ ์ฐ์ ์์๊ฐ ๋ฎ์, ์ฆ ๊ฐ๋ฒผ์ด ์
๋ฐ์ดํธ๋ฅผ ๋ผ์ ๋น๊ต์ ๊ฐ๋ฒผ์ด ์
๋ฐ์ดํธ -> ๋ฌด๊ฑฐ์ด ์
๋ฐ์ดํธ์์ผ๋ก ์
๋ฐ์ดํธ๋ฅผ ์ํฌ ์ ์๋๋ก ์ฒ๋ฆฌํ์ฌ UI blocking ์์ด ๋์์ ๋ค๋ฅธ ์์
์ด ์ํ๋๋ ๊ฒ๊ณผ ๊ฐ์ ์ฌ์ฉ์ ๊ฒฝํ์ ์ ๊ณตํ ์ ์๊ฒ ๋์๋ค.
useTransition
useTransition์ ์ด๋ฌํ ๋์์ฑ์ ๊ตฌํํ๊ธฐ ์ํด ํ์ํ ํ
์ด๋ค. ์ด ํ
์ ๋ฆฌ์กํธ ์ปดํฌ๋ํธ ์์์ ๋์์ฑ ๋ชจ๋์ ์ ๊ทผํ ์ ์๋๋ก ํด์ค๋ค.
import { useState } from 'react';
export function FilterList({ names }) {
const [query, setQuery] = useState('');
const changeHandler = ({ target: { value } }) => setQuery(value);
return (
<div>
<input onChange={changeHandler} value={query} type="text" />
{names.map((name, i) => (
<ListItem key={i} name={name} highlight={query} />
))}
</div>
);
}
function ListItem({ name, highlight }) {
const index = name.toLowerCase().indexOf(highlight.toLowerCase());
if (index === -1) {
return <div>{name}</div>;
}
return (
<div>
{name.slice(0, index)}
<span className="highlight">
{name.slice(index, index + highlight.length)}
</span>
{name.slice(index + highlight.length)}
</div>
);
}ํด๋น ์ฝ๋๋ฅผ ๋ณด๋ฉด input์ ๋ด์ฉ์ด ๋ฐ๋ ๋๋ง๋ค setState๋ฅผ ์คํํ๊ณ ์๊ณ , state๊ฐ ๋ฐ๋๊ฒ ๋๋ฉด ๋ฆฌ๋ ๋๋ง์ด ์ด๋ฃจ์ด์ง๋ฉฐ, names๋ฅผ mapํ๋ ํจ์๊ฐ ๋ฆฌ๋ ๋๋ง ๋๋ง๋ค ์คํ๋๋ฉฐ UI๋ฅผ ๋ค์๊ธ ํ๋ฉด์ ๋์ด๋ค.
ํ์ง๋ง ์ด๋ฌํ name๋ค์ด ์ ์ ๋ง์์ง ์๋ก, input์ ๋น ๋ฅด๊ฒ ์ ๋ ฅํ๊ฒ ๋๋ค๋ฉด ๋ฆฌ๋ ๋๋ง ์๋๊ฐ input ์ด๋ฒคํธํธ๋ค๋ฌ์ setState๊ฐ ์คํ๋๋ ์๋๋ฅผ ๋ฐ๋ผ๊ฐ์ง ๋ชปํ๊ฒ ๋๊ณ , ๊ฒฐ๊ทน input์ value๊ฐ ๋น ๋ฅด๊ฒ ์ ๋ ฅ๋ ์ ์๋ ๋ฌธ์ ๋ฅผ ๊ฐ์ง๊ฒ ๋๋ค. ์์ ๋ณด๊ธฐ
์ด๋ด๋, ๋ฌด๊ฑฐ์ด ์์ ์ List์ ๋ฆฌ๋ ๋๋ง์ด ๋ ๊ฒ์ด๊ณ , ๋น๊ต์ ๊ฐ๋ฒผ์ด ์์ ์ input์ eventHandler ์์ ๋ค์ด์๋ setState์ ๋ํ input์ value ๋ฆฌ๋ ๋๋ง์ด๋ค. ๊ทธ๋ ๋ค๋ฉด ์ด input์ ๋ฆฌ๋ ๋๋ง์ ๋ํด์ ์ฐ์ ์์๋ฅผ ๋์ฌ input๋จผ์ ๊ณ์ ๋จผ์ ๋ฆฌ๋ ๋๋ง ๋ ์ ์๊ฒ๋ง ํ๋ค๋ฉด input์ด ๋ฐ๋ฆฌ๊ฒ ๋๋ ํ์์ ๋ฐฉ์งํ ์ ์๋ ๊ฒ์ด๋ค. ๊ทธ๋ด ๋ ์ฌ์ฉํ๋ ํ ์ด useTransition ํ ์ด๋ค.
[isPending, startTransition] = useTransition()useTransition์ ์ฌ์ฉํ๋ฉด isPending, startTranstion์ ๋ฆฌํด๊ฐ์ ๋ฐ๋๋ค.
- isPending: transition์ด pending ์ํ์ธ์ง ์๋ ค์ฃผ๋ boolean ๊ฐ
- startTransition(callbackFn): UI ์ ๋ฐ์ดํธ์ ๊ดํ ๋ก์ง์ ์ฝ๋ฐฑ ํจ์๋ก ๋๊ฒจ์ค ์ด startTransition์ ํตํด์ ์ฝ๋ฐฑํจ์์ setState๋ฅผ ํตํ ๋ฆฌ๋ ๋๋ง ๋ก์ง์ ์ฐ์ ์์๊ฐ ๋ฎ๋๋ก ์ค์ ํ์ฌ ํ์์๋ก ๋ ๋๋ง์ด ์ด๋ฃจ์ด์ง ์ ์๋๋ก ํ๋ฉด์ ์ฑ๋ฅ์ ๊ฐ์ ํ๋ค.
`startTransition` lets you update the state without blocking the UI.
๋ผ๊ณ React์ ๊ณต์๋ฌธ์์์ ๋์ ์๋ ์ค๋ช ๊ณผ ๊ฐ์ด, UI๋ฅผ ๋ฐ๋ก ๋ธ๋กํนํ์ง ์๊ณ ์ํ๋ฅผ ์ ๋ฐ์ดํธ ํ๋๋ก ์ํด์ผ๋ก์จ ๋น๊ต์ ๋ฌด๊ฑฐ์์ ๋ค๋ฅธ UI์ ๋ ๋๋ง์ ๋ง๋ ์์ ๋ค์ ์๋์ ์ผ๋ก ์ง์ฐ์ํฌ ์ ์๋ค.
import { startTransition } from 'react';
function TabContainer() {
const [tab, setTab] = useState('about');
function selectTab(nextTab) {
startTransition(() => {
setTab(nextTab);
});
}
// ...
}์ด๋ฐ ์์ผ๋ก setState๊ฐ ์๋ ๋ถ๋ถ์ ๊ฒฝ์ฐ ๋ ๋๋ง ๊ณผ์ ์์ ๋งํ์ง ์๊ณ ๋ฐ๋ก ์งํ๋๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฒผ์ด UI์ ์ ๋ฐ์ดํธ ๋ฑ์ด ๋งํ์ง ์๊ณ ๋ฐ๋ก๋ฐ๋ก ๋ ๋๋ง์ด ๋ ์ ์๋ ํ๊ฒฝ์ ์ ๊ณตํ๋ค.
useDeferredValue
useDeferredValue๋ ์ํ์ ์
๋ฐ์ดํธ ์ฐ์ ์์๋ฅผ ๋ฎ์ถ๋ค๋ ์ ์์ useTransition๊ณผ ์ ์ฌํ๊ฒ ๋์ํ๋ ๋ฉด์ด ์๋ค.
ํ์ง๋ง startTransition์ ์ฝ๋ฐฑํจ์ ๋ด๋ถ์ setState๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ์์ด๊ณ , useDeferredValue๋ state๊ฐ์ ์ธ์๋ก ๋ฐ์์ ์ง์ฐ๋ ๊ฐ์ ๋ฐํํ๋ ํจ์์ด๋ค. ๋ฐ๋ผ์ useTransition์ ์ํ๋ฅผ ๋ณํ์ํค๋ ํ๋ ์์ฒด๋ฅผ ๋ํํ๋ ๊ฒ์ด๊ณ , useDeferredValue์ ๊ฐ ์์ฒด๋ฅผ ๋ํํด์ ์ฌ์ฉํ๋ ํํ์ด๋ค.
useDeferredValue๋ก ๋ํํ ์ํ์ ๊ฒฝ์ฐ์๋ ๋ค๋ฅธ ์ํ๊ฐ์ด ๋ชจ๋ ์ํ ๋ณ๊ฒฝ์ด ์ด๋ฃจ์ด์ง ์ดํ์ ์์ ์ด ๋ฐ๋๊ฒ ๋๋ค. ๊ฐ ๋ณํ์ ์ฐ์ ์์๊ฐ ๋ฎ์์ง๊ธฐ ๋๋ฌธ์ ๋ค๋ฅธ ์ํ๋ค์ ์ ๋ฐ์ดํธ ์ดํ์ ์คํ๋๋ ๊ฒ์ด๋ค.
๊ทธ๋ ๊ธฐ์ ์ฌ์ฉํ๋ ๋ฐฉ์์ ๋ฐ๋ผ useTransition๊ณผ useDeferredValue๋ฅผ ์ทจ์ฌ ์ ํํ์ฌ ์ฌ์ฉํ๋ค.
- useTransition : ์ํ ๋ณ๊ฒฝ์ ๋ํ ๋ฆฌ๋ ๋๋ง์ด ๋ชจ๋ ์ด๋ฃจ์ด์ง ํ ์ฝ๋ฐฑ ํจ์ ์คํ
- ์ฝ๋ฐฑ ํจ์ ์์ setState๋ฅผ ๋ฃ์ด ์ฌ์ฉ
- useDeferredValue: ์ํ ๋ณ๊ฒฝ์ ๋ํ ๋ฆฌ๋ ๋๋ง์ด ๋ชจ๋ ์ด๋ฃจ์ด์ง ํ์ ๋ณํ ๊ฐ์ ๋ํ ๋ฆฌ๋ ๋๋ง
์ถ๊ฐ์ ์ผ๋ก useDeferredValue๋ Suspense์ ํจ๊ป ์ฌ์ฉ์ด ๊ฐ๋ฅํ๋ค .
๋ง์ฝ useDeferredValue์ ์ธ์๋ก ์ค์ ํ ์ํ๊ฐ ๋ณ๊ฒฝํ๊ฒ ๋๋ฉด ์๋ก์ด ๊ฐ์ผ๋ก ์ธํ ๋ฐฑ๊ทธ๋ผ์ด๋ ์
๋ฐ์ดํธ ๋์ ์ด์ ์ ์ํ๋ฅผ ๋ณด์ฌ์ค๋ค. ์
๋ฐ์ดํธ ์ด์ ์ ์ํ๋ฅผ ๋ณด์ฌ์ฃผ๋ฉด์ ๋ค์ ์ปดํฌ๋ํธ๋ค์ ๋ ๋๋ง์ด ๋ชจ๋ ์ด๋ฃจ์ด์ง ํ์ ํด๋น ์ํ๋ฅผ ์ฐธ์กฐํ๋ ์ปดํฌ๋ํธ์์ ๋ค์๊ธ ๋ ๋๋ง์ ์๋ํ๋ค. ์ด ๋ ๋๋ง์ ์๋ํ๋ ๊ณผ์ ์์ ๊ธฐ์กด์๋ ํ
์ ์ฌ์ฉํ์ฌ ์๋กญ๊ฒ ๋ฐ์ดํฐ๋ฅผ fetchingํ๋ ๋์ ๋์ฌ ui๋ฅผ ์ค์ ํ๊ฑฐ๋ Suspense๋ฅผ ํ์ฉํ์ฌ fallback UI๋ฅผ ๋ณด์ฌ์ค์ผ๋ก์จ ์ฌ์ฉ์์๊ฒ ๋ก๋ฉ์ค์์ ๋ณด์ฌ์ค ์ ์๋ค.
Suspense ์ฝํ ์ธ ๊ฐ ๋ ๋๋งํ ์ค๋น๊ฐ ๋๊ธฐ ์ ๊น์ง ๋์ฒด UI๋ฅผ ๋ณด์ฌ์ฃผ๋ ํ๊ทธ
export default function App() {
const [query, setQuery] = useState('');
return (
<>
<label>
Search albums:
<input value={query} onChange={e => setQuery(e.target.value)} />
</label>
<Suspense fallback={<h2>Loading...</h2>}>
<SearchResults query={query} />
</Suspense>
</>
);
}
๊ธฐ๋ณธ์ ์ผ๋ก ์ปดํฌ๋ํธ๊ฐ ์ผ์ ์ค๋จ๋์ ๋ lazy loading์ด๋ use, Next.js์ ๊ฐ์ suspense ์ง์ํ๋ ํ๋ ์์ํฌ์ ๋ฐ์ดํฐ ํ์นญ ๋ฑ์ ์ฌ์ฉํ๊ฒ ๋๋ฉด ๊ฐ์ฅ ๊ฐ๊น์ด ์์ Suspense ์ปดํฌ๋ํธ๊ฐ fallback ui๋ฅผ ๋์์ฃผ๊ฒ ๋๋ค.
import { Suspense, useState, useDeferredValue } from 'react';
import SearchResults from './SearchResults.js';
export default function App() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
return (
<>
<label>
Search albums:
<input value={query} onChange={e => setQuery(e.target.value)} />
</label>
<Suspense fallback={<h2>Loading...</h2>}>
<SearchResults query={deferredQuery} />
</Suspense>
</>
);
}์ฌ๊ธฐ์ useDeferredValue๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ฉด ๊ธฐ์กด ์ํ๋ฅผ ์
๋ฐ์ดํธํ ๊ฐ์ ๋ํด์ ์ง์ฐ๋ ๋ ๋๋ง์ด ์ด๋ฃจ์ด์ง๊ณ , ๊ทธ ๋ ๋๋ง์ด ๋ค์๊ธ ์ด๋ฃจ์ด์ง๋ ๋์ ์ด์ ์ ๊ฐ์ด ์์ ๊ฒฝ์ฐ ์ด๋ฅผ ํ์ํจ์ผ๋ก์จ ์ด์ ์ ๊ฐ์ ๋ํ๋ผ ์ ์๋ค.
๋ํ ์ด ๋ฐฉ์์ ์กฐ๊ธ ๋ ํ์ฉํ์ฌ ๊ธฐ์กด์ ๊ฐ๊ณผ ๋ง์ฝ ๊ฐ์ด ๋ฐ๋์๋ค๋ฉด ์ด๋ฅผ ๋น๊ตํ๋ ๋ณ์ ํ๋๋ฅผ ๋ง๋ค์ด refetchingํ๊ณ ์๋ ์ํ๋ฅผ ์ ์ ์๋ ui๋ก ๋ณด์ฌ์ค ์๋ ์๋ค.
import { Suspense, useState, useDeferredValue } from 'react';
import SearchResults from './SearchResults.js';
export default function App() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
const isStale = query !== deferredQuery;
return (
<>
<label>
Search albums:
<input value={query} onChange={e => setQuery(e.target.value)} />
</label>
<Suspense fallback={<h2>Loading...</h2>}>
<div style={{
opacity: isStale ? 0.5 : 1,
transition: isStale ? 'opacity 0.2s 0.2s linear' : 'opacity 0s 0s linear'
}}>
<SearchResults query={deferredQuery} />
</div>
</Suspense>
</>
);
}์ ์์์์๋ ์๋กญ๊ฒ ์
๋ฐ์ดํธ๋ deferredQuery์ ์ด์ ์ ๊ฐ์ด์ด๋ query๋ฅผ ๋น๊ตํ ๋ณ์์ธ isStale์ ๋ฐ๋ก ์ ์ธํ์ฌ ์ด์ ๋ฐ๋ผ transition์ ์ฃผ๋ฉฐ refetching์ค์ธ ์ํ๋ฅผ ๋ํ๋ด์๋ค. ์ด๋ฅผ ๋น๊ตํ์ฌ ui๋ฅผ ๋์ธ ๋๋ suspense์ fallback ui๋ณด๋ค๋ ๊ธฐ์กด์ ๊ฐ์ ๋์ฐ๋ ๋ฐฉ์์ผ๋ก๋ ์ฌ์ฉํ ์ ์๋ค.
https://dmitripavlutin.com/react-usetransition/ https://velog.io/@heelieben/React-18-Concurrent-Rendering