React Transition ํบ์๋ณด๊ธฐ! ๐
React์์ useTransition๊ณผ ๊ฐ์ Transition ๊ธฐ๋ฅ์ ์ฌ์ฉ์ ๊ฒฝํ(UX)์ ๋ถ๋๋ฝ๊ฒ ์ ์งํ๋ฉด์ ์ํ ์ ๋ฐ์ดํธ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ๊ด๋ฆฌํ๋ ๋๊ตฌ์ ๋๋ค. ๋ฒ๋ฒ ๊ฑฐ๋ฆผ ์์ด ๋งค๋๋ฝ๊ฒ ๋์ํ๊ฒ ํ๋ ๊ฒ์ด ๊ณ ๋ฏผ์ด๋ผ๋ฉด ๊ผญ ์์๋ณด๋ฉด ์ข์ ๊ธฐ๋ฅ์ ๋๋ค.
Transition์ ์ดํดํ๋ ค๋ฉด ๊ทธ ๋จ๊ณ์ React์ ๋ด๋ถ ๋์ ์๋ฆฌ๋ฅผ ์ดํด๋ณด๋ ๊ฒ์ด ํ์ํ๋ฐ์, Rendering, Pending, ๊ทธ๋ฆฌ๊ณ Completed๋จ๊ณ๋ค์ ์ค์ฌ์ผ๋ก ํบ์๋ณด๋๋ก ํ ๊ป์.
๐ Transition์ ๋จ๊ณ์ ํ๋ฆ
React Transition์ ์ํ ์ ๋ฐ์ดํธ๋ฅผ ๐ด ๊ธด๊ธ ์์ (Urgent)๊ณผ ๐ต ๋น๊ธด๊ธ ์์ (Non-Urgent)์ผ๋ก ๋๋์ด ์ฒ๋ฆฌํฉ๋๋ค.
์ด ๊ณผ์ ์์ ์ฃผ์ ๋จ๊ณ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
1๏ธโฃ Transition Start: ์์ ์์
2๏ธโฃ Pending State: ๋๊ธฐ ์ค
3๏ธโฃ Render: ์๋ก์ด ์ํ ๋ ๋๋ง
4๏ธโฃ Completed State: ์๋ฃ
1๏ธโฃ Transition Start: ์์ ์์
์ฌ์ฉ์ ์ํธ์์ฉ:
์ฌ์ฉ์๊ฐ ๋ฒํผ์ ํด๋ฆญํ๊ฑฐ๋ ์ ๋ ฅ์ ํ ๋, React๊ฐ ์ด๋ฒคํธ๋ฅผ ๊ฐ์งํด ์์ ์ ์์ํฉ๋๋ค. (์ด๋ฒคํธ ํธ๋ฆฌ๊ฑฐ์ ์: ๋ฒํผ ํด๋ฆญ, ์ ๋ ฅ ๋ฑ)
React ์ค์ผ์ค๋ง
์ฌ์ฉ์๊ฐ ์ด๋ฒคํธ๋ฅผ ํธ๋ฆฌ๊ฑฐํ๋ฉด, React๋ startTransition์ผ๋ก ๋ํ๋ ์์ ์ ๐ต ๋น๊ธด๊ธ์ผ๋ก ์์ฝํฉ๋๋ค.
(๐ด ๊ธด๊ธ ์์ ๊ณผ ๐ต ๋น๊ธด๊ธ ์์ ์ ๊ตฌ๋ถํ์ฌ ํจ์จ์ ์ผ๋ก ์ฒ๋ฆฌํ๊ธฐ ์ํจ)
์ฆ๊ฐ์ ์ธ UI ์ ๋ฐ์ดํธ
๐ด ๊ธด๊ธ ์ํ๋ ์ฆ์ ์ ๋ฐ์ดํธ๋์ด ์ฌ์ฉ์ ์ ๋ ฅ์ ์ ์ํ๊ฒ ๋ฐ์ํฉ๋๋ค.
(์ฆ, ๋ฒํผ ํด๋ฆญ ํ ๋ฐ๋ก ํผ๋๋ฐฑ์ ์ ๊ณตํ์ฌ ์ฌ์ฉ์๊ฐ ์ํธ์์ฉํ๊ณ ์๋ค๋ ๋๋์ ๋ฐ์ ์ ์๋๋ก ํ๋ ๊ฒ)
startTransition(() => {
// ๋น๊ธด๊ธ ์ํ ์
๋ฐ์ดํธ
setData(newData);
});
2๏ธโฃ Pending State: ๋๊ธฐ ์ค
Transition์ด ์งํ ์ค์ด๋ฉด
React๋ isPending์ true๋ก ์ค์ ํ์ฌ ํ์ฌ Transition์ด ์งํ ์ค์์ ๋ํ๋ ๋๋ค.
์ด ์ํ์์ ์ฌ์ฉ์๋ ์ฌ์ ํ UI์ ์ํธ์์ฉํ ์ ์๊ณ , React๋ ๋๊ธฐ ์ํ๋ฅผ ๊ด๋ฆฌํฉ๋๋ค.
๊ธด๊ธ ์์ ์ฒ๋ฆฌ ํ ๋น๊ธด๊ธ ์์ ์์
์ด ๋จ๊ณ์์๋ ๋ชจ๋ ๐ด ๊ธด๊ธ ์์ ์ด ์๋ฃ๋ ํ ๐ต ๋น๊ธด๊ธ ์์ ์ด ์์๋ฉ๋๋ค.
์ด ์์ ์ ๋ก๋ฉ UI๋ฅผ ํ์ํ์ฌ ์ฌ์ฉ์์๊ฒ ํ์ฌ ์ํ๋ฅผ ์๋ฆด ์ ์์ต๋๋ค.
{isPending && <p>Loading...</p>}
3๏ธโฃ Render: ์๋ก์ด ์ํ ๋ ๋๋ง
๋น๊ธด๊ธ ์ํ ์ ๋ฐ์ดํธ๊ฐ ์๋ฃ
๋น๊ธด๊ธ ์ํ ์ ๋ฐ์ดํธ๊ฐ ์๋ฃ๋๋ฉด, React๋ ์๋ก์ด ์ํ๋ก ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํฉ๋๋ค.
์ด ๊ณผ์ ์์ React๋ ์ด์ ์ํ์ ์ ์ํ๋ฅผ ๋น๊ต(diffing)ํ์ฌ ๋ณ๊ฒฝ๋ ๋ถ๋ถ๋ง DOM์ ๋ฐ์ํฉ๋๋ค. (์ฑ๋ฅ์ ๊ทน๋ํํ๋ ์ค์ํ ๋จ๊ณ)
Rendering ์ค ํน์ง:
- Concurrent Rendering
- React๋ UI๋ฅผ ๋ฉ์ถ์ง ์๊ณ ์์ ์ ๋ณ๋ ฌ๋ก ์ฒ๋ฆฌํ์ฌ ์ต์ ์ ์ฑ๋ฅ์ ์ ์งํจ
- ์ฌ์ฉ์๋ ์ค๊ฐ์ UI๊ฐ ๋ฉ์ถ๋ ๊ฒ์ ๊ฒฝํํ์ง ์๊ณ ์์ฐ์ค๋ฌ์ด ํ๋ฆ์ ์ ์งํ ์ ์์
- Batching
- ์ฌ๋ฌ ์ํ ์ ๋ฐ์ดํธ๋ฅผ ํ ๋ฒ์ ๋ ๋๋ง ์์ ์ผ๋ก ๋ณํฉํ์ฌ ํจ์จ์ฑ์ ๋์(๋ถํ์ํ ๋ ๋๋ง ๐บ, ์ฑ๋ฅ๐ป)
4๏ธโฃ Completed State: ์๋ฃ
Transition์ด ์๋ฃ
Transition์ด ์๋ฃ๋๋ฉด isPending์ด false๋ก ๋ณ๊ฒฝ๋ฉ๋๋ค.
์ด ์์ ์์ ๋ก๋ฉ UI๋ ์ฌ๋ผ์ง๊ณ , ์๋ก์ด ์ํ๊ฐ ๋ฐ์๋ ํ๋ฉด์ด ์ฌ์ฉ์์๊ฒ ๋ณด์ฌ์ ธ ๊ฒฐ๊ณผ๋ฅผ ์ฆ์ ํ์ธํ ์ ์์ต๋๋ค.
{!isPending && <ul>{data.map(item => <li key={item}>{item}</li>)}</ul>}
๐ ๋ก๋ฉ์ด ๋๋ ํ ์ฌ์ฉ์์๊ฒ ์๋ก์ด ๋ฐ์ดํฐ๋ฅผ ๋ฆฌ์คํธ ํํ๋ก ๋ณด์ฌ์ค๋๋ค.
๐ Transition ๋์ ์๋ฆฌ
React Transition์ ์ค์ผ์ค๋ง๊ณผ ์ฐ์ ์์ ๊ด๋ฆฌ๋ฅผ ํตํด ๋์ํฉ๋๋ค. ์ด๋ฅผ ์ดํดํ๊ธฐ ์ํด ๋ด๋ถ ๊ตฌ์กฐ๋ฅผ ์ดํด๋ด ์๋ค.
1๏ธโฃ ๐ด ๊ธด๊ธ ์์ vs ๐ต ๋น๊ธด๊ธ ์์
React๋ Transition์์ ๐ด ๊ธด๊ธ ์์ ๊ณผ ๐ต ๋น๊ธด๊ธ ์์ ์ ๊ตฌ๋ถํฉ๋๋ค.
- ๐ด ๊ธด๊ธ ์์
- ์ฌ์ฉ์ ์ ๋ ฅ, ํด๋ฆญ ๋ฑ ์ฆ๊ฐ์ ์ธ ๋ฐ์์ด ํ์ํ ์ํ ์ ๋ฐ์ดํธ(๋ฒํผ์ ํด๋ฆญํ๊ฑฐ๋ ํผ์ ๋ฐ์ดํฐ๋ฅผ ์ ๋ ฅํ ๋ ๋ฆฌ์กํธ๋ ์ด ์์ ์ ์ต์ฐ์ ์ผ๋ก ์ฒ๋ฆฌํ๋ค.)
- ๐ต ๋น๊ธด๊ธ ์์
- ๋๊ท๋ชจ ๋ฐ์ดํฐ ํํฐ๋ง, ๋ ๋๋ง๊ณผ ๊ฐ์ ์์ (์ฆ๊ฐ์ ์ธ ๋ฐ์์ด ํ์ํ์ง ์๊ธฐ ๋๋ฌธ์, ์ฌ์ฉ์๊ฐ ์ํธ์์ฉํ๋ ๋์ ๋ฐฐ๊ฒฝ์์ ์ฒ๋ฆฌํ๋ค.)
setImmediateState("urgent"); // ๊ธด๊ธ
startTransition(() => setNonImmediateState("non-urgent")); // ๋น๊ธด๊ธ
2๏ธโฃ Concurrent Rendering
React๋ ๋๊น ์๋ ์ธํฐํ์ด์ค๊ฐ ์ ๊ณต๋๋๋ก ๐ต ๋น๊ธด๊ธ ์์ ์ ์ค์ผ์ค๋งํ์ฌ UI๊ฐ ๋ฉ์ถ์ง ์๋๋ก ๋ณด์ฅํฉ๋๋ค.
๐ ๊ธด๊ธ ์์ ์ฐ์ ์ฒ๋ฆฌ ์ดํ ๋จ์ ์๊ฐ ๋์โณ ๋น๊ธด๊ธ ์์ ์ฐจ๋ก๋ก ์ํ ์ด๋ UI๊ฐ ์์ฐ์ค๋ฝ๊ฒ ์ ๋ฐ์ดํธ ๋๊ณ ์ฆ์ ํ์ธ์ด ๊ฐ๋ฅํฉ๋๋ค.
๐ ์ํ๋ณ React์ ํ๋ ์์ฝ
์ํ | React ํ๋ |
Initial | Transition ์์ ์ . ๊ธฐ์กด ์ํ๋ฅผ ์ ์งํ๋ฉฐ ์๋ก์ด ์์ ์ ์์ฝํฉ๋๋ค. |
Pending | ๋น๊ธด๊ธ ์ํ ์ ๋ฐ์ดํธ ์งํ ์ค. UI๋ ๊ธฐ์กด ์ํ๋ฅผ ์ ์งํ๋ฉฐ ๋ก๋ฉ ์ํ๋ฅผ ํ์ํ ์ ์์ต๋๋ค. |
Rendering | ์๋ก์ด ์ํ๋ก ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํฉ๋๋ค. ๋น๊ธด๊ธ ์์ ์ด ๋ชจ๋ ์๋ฃ๋๋ฉด ์งํ๋ฉ๋๋ค. |
Completed | Transition ์๋ฃ. ์๋ก์ด ์ํ๋ฅผ ์ฌ์ฉ์์๊ฒ ํ์ํ๋ฉฐ ๋ก๋ฉ ์ํ๋ฅผ ์ ๊ฑฐํฉ๋๋ค. |
๐ Redendering ์ค ๋ฐ์ํ ์ ์๋ ์ํฉ๊ณผ ๋ฌธ์
Transition ์์ ์ค ์ฃผ์ํด์ผ ํ ๋ช ๊ฐ์ง ์ฌ๋ก์ ๋ฌธ์ ๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
1๏ธโฃ ์ฌ์ฉ์ ์ํธ์์ฉ ์ค๋จ ๋ฌธ์
๐ ๋ธ๋กํน ์ด์:
Transition ์ค ๊ธด๊ธ ์ํ ์ ๋ฐ์ดํธ๊ฐ ๋ธ๋กํน๋๋ฉด ์ฌ์ฉ์ ๊ฒฝํ์ด ๋๋น ์ง ์ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด, ์ฌ์ฉ์๊ฐ ๋ฒํผ์ ํด๋ฆญํ์ ๋ ๋ฐ์์ด ๋๋ฆฌ๊ฑฐ๋, ํ๋ฉด์ด ๋ฉ์ถ ๊ฒ ๊ฐ์ ๋๋์ ๋ฐ์ ์ ์์ต๋๋ค.
๐ก ํด๊ฒฐ์ฑ :
startTransition ์ผ๋ก ๋น๊ธด๊ธ ์์ ์ ์์ฝํ์ฌ ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํฉ๋๋ค.
(๊ธด๊ธ ์์ ์ด ์ฐ์ ์ฒ๋ฆฌ, ๋น๊ธด๊ธ ์์ ์ ์ฌ์ฉ์ ์ํธ์์ฉ์ด ์๋ฃ๋ ํ์ ์ฒ๋ฆฌ ๋๋๋ก ํด์ค)
2๏ธโฃ isPending ์ํ ๊ด๋ฆฌ
โ ๏ธ ๊ฒน์น๋ Transition ๋ฌธ์ :
์ฌ๋ฌ Transition ์์ ์ด ๋์์ ๋ฐ์ํ๋ฉด, ๋ก๋ฉ ์ํ๊ฐ ์ฌ๋ฐ๋ฅด๊ฒ ๊ด๋ฆฌ๋์ง ์์ ์ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด, ์ฌ์ฉ์๊ฐ ์ฌ๋ฌ ๋ฒํผ์ ๋น ๋ฅด๊ฒ ํด๋ฆญํ๋ฉด, ๊ฐ ํด๋ฆญ์ ๋ํ ๋ก๋ฉ ์ํ๊ฐ ๋๋ฆฌ๊ฐ ๋ ์ ์์ต๋๋ค.
๐ ํด๊ฒฐ์ฑ :
์ํ๋ณ๋ก isPending ํ๋๊ทธ๋ฅผ ๊ตฌ๋ถํ๊ฑฐ๋ ๊ณ ์ ์์ ID๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
const [isPending1, startTransition1] = useTransition();
const [isPending2, startTransition2] = useTransition();
๐ผ ์ค์ฉ์ ์ธ Transition ์์
1๏ธโฃ ํํฐ๋ง ์์ ์์์ ํ์ฉ
function FilterExample() {
const [query, setQuery] = useState("");
const [results, setResults] = useState([]);
const [isPending, startTransition] = useTransition();
const handleInputChange = (e) => {
const input = e.target.value;
setQuery(input);
startTransition(() => {
const filtered = mockData.filter(item => item.includes(input));
setResults(filtered);
});
};
return (
<div>
<input type="text" value={query} onChange={handleInputChange} />
{isPending ? <p>Loading...</p> : <ul>{results.map(r => <li key={r}>{r}</li>)}</ul>}
</div>
);
}
2๏ธโฃ ๋ชจ๋ฌ ์ฐฝ์ ํตํ Transition ์์
import React, { useState, useTransition } from 'react';
const Modal = ({ onClose }) => {
return (
<div style={modalStyles}>
<h2>๋ฟ
</h2>
<p>์ด๊ณณ์ Transition์ ํตํด ์ด๋ฆฌ๋ ๋ชจ๋ฌ์
๋๋!</p>
<button onClick={onClose}>๋ซ๊ธฐ</button>
</div>
);
};
const App = () => {
const [isOpen, setIsOpen] = useState(false);
const [isPending, startTransition] = useTransition();
const openModal = () => {
startTransition(() => {
setIsOpen(true);
});
};
const closeModal = () => {
startTransition(() => {
setIsOpen(false);
});
};
return (
<div>
<h1>React Transition ์์ </h1>
<button onClick={openModal}>๋ชจ๋ฌ ์ด๊ธฐ</button>
{isOpen && <Modal onClose={closeModal} />}
{isPending && <p>๋ชจ๋ฌ์ ์ฌ๋ ๋...</p>}
</div>
);
};
export default App;
์ค๋ฌด์์์ ํ ๐
1๏ธโฃ ๋ก๋ฉ UI๋ฅผ ๋ช ํํ
๐ ์ฌ์ฉ์ ํผ๋๋ฐฑ ์ ๊ณต:
Transition ์ค์๋ isPending์ ํ์ฉํ์ฌ ๋ก๋ฉ ์ํ๋ฅผ ์ฌ์ฉ์์๊ฒ ๋ช ํํ ๋ณด์ฌ์ฃผ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
์ฌ์ฉ์๊ฐ ์ด๋ค ์์ ์ ์ํํ๊ณ ์๋์ง, ๊ทธ๋ฆฌ๊ณ ๊ทธ ์์ ์ ์งํ ์ํฉ์ด ์ด๋ป๊ฒ ๋๋์ง๋ฅผ ์ ์ ์๋๋ก ํด์ผ ํฉ๋๋ค.
์๋ฅผ ๋ค์ด, ๋ชจ๋ฌ์ ์ฌ๋ ๋์ "๋ก๋ฉ ์ค..." ๋ฉ์์ง๋ ์ ๋๋ฉ์ด์ ์ ํ์ํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
๐จ ์๊ฐ์ ์์ ํ์ฉ
๋ก๋ฉ UI๋ ๋จ์ํ ํ ์คํธ ๋ฉ์์ง๋ฟ๋ง ์๋๋ผ, ์คํผ๋, ํ๋ก๊ทธ๋ ์ค ๋ฐ ๋ฑ ๋ค์ํ ์ง๊ด์ ์๊ฐ์ ์์๋ฅผ ํ์ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค.
2๏ธโฃ ๊ธด๊ธ ์ํ ์ฐ์ ์ฒ๋ฆฌ
โก๏ธ ๋น ๋ฅธ ๋ฐ์์ฑ ์ ์ง:
startTransition์ ์ ์ฌ์ ์์ ์ฌ์ฉํ์ฌ ๊ธด๊ธ ์ํ๊ฐ ์ง์ฐ๋์ง ์๋๋ก ์ค๊ณํ๋ ๊ฒ์ด ์ข์ต๋๋ค..
์ฌ์ฉ์๊ฐ ๋ฒํผ์ ํด๋ฆญํ๊ฑฐ๋ ์ ๋ ฅ์ ํ ๋์ ๊ฐ์ ๊ธด๊ธ ์์ ์ ์ฆ์ ๋ฐ์ํด์ผ ํฉ๋๋ค.
๊ผญ! ๋น๊ธด๊ธ ์์
์ด ๊ธด๊ธ ์์
์ ๋ธ๋กํนํ์ง ์๋๋ก ์ค๊ณํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
๐งฉ ์์ ํ๋ฆ ์ต์ ํ
๊ธด๊ธ ์์ ์ ์ฐ์ ์ผ๋ก ์ฒ๋ฆฌํ๊ณ , ๋น๊ธด๊ธ ์์ ์ ์ดํ์ ์ํํ๋๋ก ์ค์ผ์ค๋งํ๋ ๊ฒ์ด ํ๋์ ๊ฟํ์ ๋๋ค.
์๋ฅผ ๋ค์ด, ๋ฐ์ดํฐ ํํฐ๋ง ์์ ์ด ์์ ๋๋ ์ฌ์ฉ์์ ์ ๋ ฅ์ด ์๋ฃ๋ ํ์ ์ฒ๋ฆฌํ๋๋ก ํ์ฌ UI์ ๋ฐ์์ฑ์ ์ ์งํ๋ฉด ๋ถ๋๋ฝ๊ฒ ๋ณด์ ๋๋ค!
3๏ธโฃ ํฐ ์์ ์ ๋ฐฐ์น๋ก ๋๋๊ธฐ
๐ฆ ๋น๊ธด๊ธ ์์ ์ธ๋ถํ
๋น๊ธด๊ธ ์์ ์ด ํด ๊ฒฝ์ฐ, ์ด๋ฅผ ์์ ์์ ์ผ๋ก ์ ์ ํ ๋๋ ์ ์คํํ๋ ๊ฒ์ด ์ฑ๋ฅ ์ ํ๋ฅผ ๋ฐฉ์ง์ ๋์์ด ๋ฉ๋๋ค.
์ด๋ ํ ๋ฒ์ ๋ง์ ์์ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ๋ฉด ๋ ๋๋ง ์ฑ๋ฅ์ด ์ ํ๋ ์ ์๊ธฐ ๋๋ฌธ์ ๋๋ค.
๐ ์์
์๋ฅผ ๋ค์ด, ๋๊ท๋ชจ ๋ฐ์ดํฐ์ ์ ํํฐ๋งํ ๋, ๋ชจ๋ ๋ฐ์ดํฐ๋ฅผ ํ ๋ฒ์ ์ฒ๋ฆฌํ๊ธฐ๋ณด๋ค๋
๋ช ๊ฐ์ ์์ ๋ฐฐ์น๋ก ๋๋์ด ์ฒ๋ฆฌํ๋ฉด UI๊ฐ ๋ ๋งค๋๋ฝ๊ฒ ๋ฐ์ํ ์ ์์ต๋๋ค.
์ด๋ ๊ฒ ํ๋ฉด ์ฌ์ฉ์๋ ๋๊ธฐ ์๊ฐ์ ์ต์ํํ๊ณ , ์ ํ๋ฆฌ์ผ์ด์ ์ ์ ๋ฐ์ ์ฑ๋ฅ์ ๋์ผ ์ ์๋ต๋๋ค.
๐ท์ ์ค์ ๊ฐ๋ฐ์๊ฐ ๋์ด๋ด ์๋น! ๐ท