13.1 useTransition ๊ฐ์ 13.1.1 useTransition ์ด๋?13.1.2 useTransition ์ ์ฌ์ฉํ๋ ์ด์ ?13.2 useTransition ๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ13.2.1 useTransition์ ๊ธฐ๋ณธ ๊ตฌ์กฐ13.2.2 useTransiton์ ์๋ ๋ฐฉ์13.2.3 useTransiton์ ์ฃผ์์ฌํญ13.3 (์ค์ต1) useTransition์ ํตํด ๊ฒ์ ๋ฏธ๋ฆฌ๋ณด๊ธฐ UX ๊ฐ์ ํ๊ธฐ13.3.1 useTransition์ ์ฌ์ฉํ์ง ์์์ ๊ฒฝ์ฐ 13.3.2 useTransition์ ์ ์ฉํด๋ณด๊ธฐ13.4 (์ค์ต2) useTransition์ ํตํด ํ์ด์ง ์ด๋ UX ๊ฐ์ ํ๊ธฐ13.4.1 useTransition์ ์ฌ์ฉํ์ง ์์์ ๊ฒฝ์ฐ 13.4.2 useTransition ์ ์ฉํด๋ณด๊ธฐ
ย
13.1 useTransition ๊ฐ์
ย
13.1.1 useTransition ์ด๋?
useTransition์ ๋๋ฆฐ ์ปดํฌ๋ํธ์ ์ฑ๋ฅ์ ํฅ์์ํค๋ Hook์ผ๋ก ํ๋ฒ ๋ ๋๋ง ์ฐ์ฐ์ด ์์๋๋ฉด ๋ฉ์ถ ์ ์๋ ๋ ๋๋ง ๋ธ๋กํน ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํ์ฌ ์ฌ์ฉ๋ฉ๋๋ค. ๋ํ ๋ค์ ํ๋ฉด์ผ๋ก ์ ํ ์์
์ ํ๊ธฐ ์ ์ ์ปจํ
์ธ ๊ฐ ๋ก๋ ๋ ๋๊น์ง ๋๊ธฐํจ์ผ๋ก์จ ์ปดํฌ๋ํธ๊ฐ ๋ฐ๋์งํ์ง ์์ ๋ก๋ฉ ์ํ๋ฅผ ํผํ ์ ์๊ฒ ํด์ฃผ๋ฉฐ, ๋ ์ค์ํ ์
๋ฐ์ดํธ๋ฅผ ์ปดํฌ๋ํธ๊ฐ ์ฆ์ ๋ ๋๋ง ํ ์ ์๋๋ก ํ์ ๋ ๋๋ง๊น์ง ๋ฐ์ดํฐ ๊ฐ์ ธ์ค๊ธฐ๋ฅผ ์ง์ฐ์ํฌ ์ ์์ต๋๋ค. useTransition Hook์ ๋ฐฐ์ด์์
startTransition
๊ณผ isPending
๋ ๊ฐ์ ๊ฐ์ ๋ฐํํฉ๋๋ค.ย
13.1.2 useTransition ์ ์ฌ์ฉํ๋ ์ด์ ?
์ถ๊ฐ์ ์ผ๋ก useTransition์ ์ค๋ช
ํ๊ธฐ์ ์์ ๊ธด๊ธ ์
๋ฐ์ดํธ์ ์ ํ ์
๋ฐ์ดํธ์ ๋ํด ์์๋ณด๊ฒ ์ต๋๋ค. React 18 ์ ๊น์ง๋ ์๋์ ์ฝ๋์ ๊ฐ์ด ๋ชจ๋ ์
๋ฐ์ดํธ๊ฐ ๊ธด๊ธํ๊ฒ ๋ ๋๋ง๋์์ต๋๋ค. ์ด๋ ๊ฒ ๋ชจ๋ ์
๋ฐ์ดํธ๊ฐ ๋์์ ๋ ๋๋ง ๋๋ ๊ฒฝ์ฐ, ์ฌ์ฉ์์์ ์ํธ์์ฉ์ ์ฐจ๋จํ๊ฒ ๋์ด ๋ ๋๋ง์ด ๋๋ ๋์ ํ์ด์ง๋ ์ง์ฐ ํ์์ด ๋ฐ์ํ์ฌ ์๋ตํ์ง ์๋ ๊ฒ์ฒ๋ผ ๋๊ปด์ง ์ ์๋ค๋ ๋จ์ ์ด ์์์ต๋๋ค.
ย
setInputValue(input); // ๊ธด๊ธ ์ ๋ฐ์ดํธ : ์ ๋ ฅํ ๊ฐ setSearchQuery(input); // ๊ธด๊ธํ์ง ์์ ์ ๋ฐ์ดํธ: ๊ฒฐ๊ณผ ๊ฐ
ํ์ง๋ง ์ต๊ทผ React 18์ ์ถ๊ฐ๋ useTransition์ ํตํด React์๊ฒ ๊ธด๊ธํ ์
๋ฐ์ดํธ์ ๊ทธ๋ ์ง ์์ ์
๋ฐ์ดํธ๋ฅผ ์๋ ค์ค์ผ๋ก์จ ์ฌ์ฉ์์ ์ํธ์์ฉ์ ๋น ๋ฅด๊ฒ ์ ์งํ ์ ์์๊ณผ ๋์์ ๋ถํ์ํ ๋ ๋๋ง์ ์ค์ผ ์ ์๋ ํจ๊ณผ๋ ์ป์ ์ ์๊ฒ ๋์์ต๋๋ค.
ย
- ๊ธด๊ธ ์ ๋ฐ์ดํธ: ์ ๋ ฅ, ํด๋ฆญ๊ณผ ๊ฐ์ ์ง์ ์ ์ธ ์ฌ์ฉ์์์ ์ํธ์์ฉ์ ๋ฐ์ํ๋ ์ ๋ฐ์ดํธ๋ก, ์ฆ๊ฐ์ ์ผ๋ก ์๋น์ค๊ฐ ๋์ง ์์ผ๋ฉด ์ฌ์ฉ์๊ฐ ๋ถํธํจ์ ๋๋ ์ ์๋ ์์ญ
- ์ ํ ์ ๋ฐ์ดํธ: ํ๋์ ๋ทฐ์์ ๋ค๋ฅธ ๋ทฐ๋ก UI๋ฅผ ์ ํํ๋ ์ ๋ฐ์ดํธ๋ก, ํ๋ฉด์ ์ฆ์ ๋ํ๋์ง ์์๋ ์ฌ์ฉ์๊ฐ ๋ถํธํจ์ ๋๋ผ์ง ์๋ ์์ญ
ย
์๋ฅผ ๋ค์ด ์ฌ์ฉ์๊ฐ ์
๋ ฅ์ฐฝ์ ๊ฒ์์ด๋ฅผ ์
๋ ฅํ๋ฉด, API์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ ํ๋ฉด์ ๊ฒ์ ๊ฒฐ๊ณผ ๋ชฉ๋ก์ ๋ฏธ๋ฆฌ ๋ณด์ฌ์ฃผ๋ ์ดํ๋ฆฌ์ผ์ด์
์ด ์๋ค๊ณ ๊ฐ์ ํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค. ์ฌ์ฉ์๊ฐ ์
๋ ฅ์ฐฝ์ ๊ฒ์์ด๋ฅผ ์
๋ ฅ(๊ธด๊ธ ์
๋ฐ์ดํธ)์ ํ๊ณ ์์์๋ ๋ง์ ์์ ๊ฒ์๊ฒฐ๊ณผ(์ ํ ์
๋ฐ์ดํธ)๊ฐ ๋ ๋๋ง์ด ๋์ด๋ฒ๋ฆฐ๋ค๋ฉด ๋ ๋๋ง ๋ธ๋กํน ๋ฌธ์ ๋ก ์ธํด ์
๋ ฅ์ฐฝ์ด ๋ฒ๋ฒ
๊ฑฐ๋ฆฌ๋ ํ์์ด ๋ํ๋ ์ฌ์ฉ์์๊ฒ ๋งค์ฐ ๋ถ๋ง์กฑ์ค๋ฌ์ด ๊ฒฝํ์ด ๋ ๊ฒ์
๋๋ค.
ย
import { startTransition } from 'react' setInputValue(input) // ๊ธด๊ธ ์ ๋ฐ์ดํธ : ์ ๋ ฅํ ๊ฐ startTransition(() => { setSearchQuery(input) // ์ ํ ์ ๋ฐ์ดํธ: ๊ฒฐ๊ณผ ๊ฐ
ย
์์ ์์ ์ ๊ฐ์ด
startTransition
์ ์ฌ์ฉํ์ฌ ๊ธด๊ธํ ์
๋ฐ์ดํธ๋ฅผ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค. ํด๋ฆญ ํน์ ํค ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ๋ ๊ฒฝ์ฐ startTransition
์ผ๋ก ๊ฐ์ธ์ธ ๊ธด๊ธํ ์ด๋ฒคํธ๋ฅผ ๋จผ์ ์ฒ๋ฆฌํ ์ดํ ์๋ฃ๋์ง ์์ ์ค๋๋ ๋ ๋๋ง์ ํ๊ธฐํ๊ณ ์ต์ ์
๋ฐ์ดํธ๋ก ๋ ๋๋ง์ ํ๊ฒ ๋๋ ๊ฒ ์
๋๋ค.ย
startTransition์ด๋? ๋ฆฌ์กํธ์ ์ด๋ค ์ํ๋ณํ๋ฅผ ์ง์ฐํ๊ณ ์ถ์์ง ์ง์ ํ ์ ์๋ ํจ์์
๋๋ค.
ย
์ด์ ๋ฒ์ ์์๋ ๊ฒ์ ๊ฒฐ๊ณผ ๋ฑ์์ ์ฌ์ฉํ๋
Debounce
, Throttle
๊ธฐ๋ฅ์ ์ฌ์ฉํ์ฌ ์ผ์ ์๊ฐ์ ๊ธฐ๋ค๋ฆฌ๋ ๊ฒ์ผ๋ก ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ์์ต๋๋ค.Debounce
์ Throttle
์ ๊ฒฝ์ฐ ์ผ์ ์๊ฐ์ ๊ธฐ๋ค๋ฆฌ๋ ๊ฒ์ผ๋ก ๋ฌธ์ ๋ฅผ ์ ์ ๋ฏธ๋ฃจ๋ ๋ฐฉ์์ด์๋ค๋ฉด, useTranstion์ ๋ ๋๋ง ์ค์๋ ์ฐ์ ์์๊ฐ ๋ ๋์ ์์
์ด ์๊ธด๋ค๋ฉด ๋จผ์ ์ฒ๋ฆฌํ ์ ์๊ฒ ํด์ค์ผ๋ก์จย ์ฌ์ฉ์ ๊ฒฝํ์ ํฅ์์ํฌ ์ ์๋ค๋ ์ฐจ๋ณ์ ์ ๊ฐ์ง๊ณ ์์ต๋๋ค. ์ฆ ๊ธด๊ธ ์
๋ฐ์ดํธ ํจ์๋ฅผ ์ ํ ์
๋ฐ์ดํธ ํจ์๋ณด๋ค ์ฐ์ ์์๋ฅผ ๋๊ฒ ์ค์ ํ์ฌ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๊ฒ์ด๋ผ ๋ณผ ์ ์๊ฒ ์ต๋๋ค.ย
13.2 useTransition ๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
ย
13.2.1 useTransition์ ๊ธฐ๋ณธ ๊ตฌ์กฐ
ย
const [isPending, startTransition] = useTransition();
ย
useTransition์ ๋ ๊ฐ์ ์ธ์๋ฅผ ๋ฐ์ ๋ฐฐ์ด๋ก ๋ฐํํฉ๋๋ค. ์ฒซ ๋ฒ์งธ ์ธ์
isPending
์ โ๋๊ธฐโ, โ๋ณด๋ฅ ์คโ ์ํ์ ์์
์ด ์๋์ง ํ๋จํ๋ Boolean ๊ฐ์ ๋ฐํํฉ๋๋ค. isPending
์ true์ผ ๋, ๋ก๋ฉ ์ค์์ ํ์ํ์ฌ ์ฌ์ฉ์์๊ฒ ์๋ฆฌ๊ฑฐ๋ ์์ฑ์ ๋นํ์ฑํํ๋ ์ฉ๋๋ก ์ฌ์ฉํ์ฌ UI๋ก ๋ณด์ฌ์ค๋๋ค.
๋ ๋ฒ์งธ ์ธ์ startTransition
์ ๋ฎ์ ์ฐ์ ์์๋ก ์คํํด๋ ๋๋ ์
๋ฐ์ดํธ ํจ์๋ฅผ ๊ฐ์๋๋ค.ย
๋ค์์ useTransition์ ์ฌ์ฉํ ๊ธฐ๋ณธ ์์ ์ฝ๋์
๋๋ค. count ์ํ ๊ฐ์ 2์ฉ ๋ํด์ฃผ๋ setCount ํจ์๋ฅผ
startTransition
์ผ๋ก ๊ฐ์ธ์ฃผ์์ผ๋ฉฐ, ์์์ ๊ฐ์๋ ๋ณด๋ฅ ์ค์ธ ์์
์ด ๋จ์์์ ๋ Loadingโฆ ๋ฌธ๊ตฌ๋ฅผ ํ๋ฉด์ ๋ํ๋
๋๋ค.function App() { const [isPending, startTransition] = useTransition(); const [count, setCount] = useState(0); function handleClick() { startTransition(() => { setCount(c => c + 2); }) } return ( <div> {isPending && "Loading..."} <button onClick={handleClick}>{count}</button> </div> ); }
ย
13.2.2 useTransiton์ ์๋ ๋ฐฉ์
React 18์์๋ concurrency(๋์์ฑ)์ด ๊ฐ์ฅ ์ค์ํ ํค์๋๋ก ์
๋ฐ์ดํธ๋์์ต๋๋ค. ๋์์ฑ์ โ๋์์ ์คํํ๋คโ์๋ ๋ค๋ฅธ ์๋ฏธ์
๋๋ค. JavaScript๋ ์ฑ๊ธ ์ค๋ ๋์ด๊ธฐ ๋๋ฌธ์, ํ๋์ ์ค๋ ๋ ์์์ ์ฌ๋ฌ ์์
๋ค์ ์์ ๋จ์๋ก ์ชผ๊ฐ์ด โ๋น ๋ฅด๊ฒ ์์
๋ค์ ๋ฐ๊ฟ๊ฐ๋ฉฐ ์คํํ๋ฏ๋ก ์ฌ๋ฌ ๊ฐ์ง ์ผ์ ์ฒ๋ฆฌํ๋ ๊ฒ์ฒ๋ผ ๋ณด์ธ๋คโ๋ ์๋ฏธ๋ฅผ ๋ด๊ณ ์์ต๋๋ค.
ย
ย
๋ค์์ React ๊ณต์ ๋ฌธ์์์ ๋ฐ์ทํ ๋ด์ฉ์
๋๋ค.
In a concurrent render, this is not always the case. โฆ To do this, it waits to perform DOM mutations until the end, once the entire tree has been evaluated. With this capability, React can prepare new screens in the background without blocking the main thread. This means the UI can respond immediately to user input even if itโs in the middle of a large rendering task, creating a fluid user experience.
React์์์ ๋์์ฑ์ ๋ฉ์ธ ์ค๋ ๋์ ๋์์ ๋ง์ง ์๊ณ ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์๋ก์ด ํ๋ฉด์ ์ค๋นํ๋ค๋ ์๋ฏธ๋ก, ๋๊ท๋ชจ ๋ ๋๋ง ์์
์ค ์ฌ์ฉ์ ์
๋ ฅ์ ์ฆ์ ์๋ตํ ์ ์์ต๋๋ค.
ย
13.2.3 useTransiton์ ์ฃผ์์ฌํญ
์ฒซ ๋ฒ์งธ, ์ ๊ทผ์ด ๊ฐ๋ฅํ state์ setํจ์์๋ง
startTransiton
์ผ๋ก ๊ฐ์ ์ ์์ต๋๋ค. ๋ง์ฝ props ๋๋ ์ปค์คํ
ํ
์ ๋ฐํ ๊ฐ์ ์ ํํด์ผ ํ๋ค๋ฉด, useDefferedValue
๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค.๋ ๋ฒ์งธ,
startTransition
์ผ๋ก ๊ฐ์ผ ํจ์๋ ๋๊ธฐ์ ์ผ๋ก ์คํ๋๋, ๋ฎ์ ์ฐ์ ์์๋ก ์ ํํ๊ธฐ ๋๋ฌธ์ ๋ ๋๋ง ๋๋ ๋์ ๋์ ์ฐ์ ์์์ ํจ์๊ฐ ์
๋ฐ์ดํธ๋๋ฉด ๋ ๋๋ง์ ์ค๋จํ๊ณ , ์ฐ์ ์์ ์ฒ๋ฆฌ ํ ๋ ๋๋ง ์์
ํฉ๋๋ค.๋ง์ง๋ง์ผ๋ก, useTransiton์ Hook์ด๊ธฐ ๋๋ฌธ์ ์ปดํฌ๋ํธ ์ธ๋ถ์์ ํธ์ถํ ์ ์์ต๋๋ค. ํ์ง๋ง
startTransiton
ํจ์๋ฅผ ๋จ๋
์ผ๋ก ์ฌ์ฉํ์ฌ ๊ฐ์ผ๋ค๋ฉด Hook ์์ด๋ ์ ํ ์์
์ผ๋ก ๋ฎ์ ์ฐ์ ์์์ ๋ ์ ์์ต๋๋ค.ย
import { startTransiton } from "react"
ย
13.3 (์ค์ต1) useTransition์ ํตํด ๊ฒ์ ๋ฏธ๋ฆฌ๋ณด๊ธฐ UX ๊ฐ์ ํ๊ธฐ
10,000๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋ ๋๋งํ๋ ๊ณผ์ ์์์ ์ง์ฐ์๊ฐ์ผ๋ก ์ธํด input ์์์ ํ์ดํํ ๊ฒฐ๊ณผ๊ฐ ํ๋ฉด์์ ๋ฐ๋ก ๋ณด์ด์ง ์์ต๋๋ค. 10,000๊ฐ์ ์์๊ฐ ํ๋ฉด์์ ๋ ๋๋ง์ด ๋จผ์ ์ฒ๋ฆฌ๊ฐ ๋๊ณ ๋ ํ์ ๋ฐ์์ด ๋๊ธฐ ๋๋ฌธ์
๋๋ค.
์ด๋ฅผย useTransitionย Hook์ ์ฌ์ฉํ์ฌ ์์
์ ์ฐ์ ์์๋ฅผ ๋ณ๊ฒฝํ์ ๋ ์ด๋ค ์ฐจ์ด๊ฐ ์๋์ง ํ์ธํด๋ณด๊ฒ ์ต๋๋ค.
App.js, Page.js, Result.js ์ธ ๊ฐ์ง ํ์ผ์ ๋ง๋ค์ด ์งํํ๊ฒ ์ต๋๋ค.
13.3.1 useTransition์ ์ฌ์ฉํ์ง ์์์ ๊ฒฝ์ฐ
import { useState } from "react"; import Page from "./pages/Page"; function App() { const [keyword, setKeyword] = useState(); return ( <div> <Page keyword={keyword} setKeyword={setKeyword}/> </div> ); } export default App;
๊ฐ์ฅ ์์ ์ปดํฌ๋ํธ์ธ App.js์์๋ input ๊ฐ์ ์
๋ ฅ๋๋ value๋ฅผ ๋ด์ state๋ฅผ ์ ์ธํ๊ณ props๋ก ํ์ ์ปดํฌ๋ํธ๋ก ๋๊ฒจ์ฃผ์์ต๋๋ค.
ย
import React from 'react' import Result from '../components/Result' function Page({keyword, setKeyword}) { return ( <div> <input type="text" onKeyUp={(e) => {setKeyword(e.target.value)}} /> <Result keyword={keyword}/> </div> ) } export default Page
Page.js์์ input ์์์ value๋ฅผ setKeyword๋ฅผ ์ด์ฉํ์ฌ state์ ๊ฐ์ ์ ์ฅํ๋ onKeyUp ์ด๋ฒคํธ ํจ์๋ฅผ ์์ฑํ์ต๋๋ค.
ย
import React from "react"; function Result({keyword}) { const bigWork = new Array(10000).fill(0); return ( <div> { bigWork.map(()=> { return <div>{keyword} {keyword} {keyword}</div> }) } </div> ) } export default Result
state๋ฅผ ๋ฐ์ div์์์ ์ธ๋ฒ ๋ฐ๋ณตํ๋ ๊ฒฐ๊ณผ๊ฐ์ ์ถ๋ ฅํ๋๋ก ์์ฑํ์ต๋๋ค. bigWork๋ผ๊ณ ์ ์ธ๋ ๋ฐฐ์ด์ด map ํจ์๋ฅผ ํตํด 10,000๊ฐ์ ์์๋ฅผ ๋ ๋๋งํ๋ ๊ณผ์ ์์ ์ง์ฐ์๊ฐ์ด ๋ฐ์ํฉ๋๋ค. ์ด๋ฌํ ์ง์ฐ์๊ฐ์ผ๋ก ์ธํด ์ฌ์ฉ์๋ input ์์์ value๋ฅผ ์
๋ ฅํ ๋์ ๊ฒฐ๊ณผ๊ฐ ์ฆ๊ฐ์ ์ผ๋ก ๋ณด์ฌ์ง์ง ์์ ๋ถํธํจ์ ๋๋ ์ ์์ต๋๋ค.
ย
์ง์ ๋ธ๋ผ์ฐ์ ์์ ์คํํด๋ณด๋ฉด input์ ๊ฐ๋๋ค๋ผ๋ฅผ ๋น ๋ฅด๊ฒ ์
๋ ฅํ์ง๋ง, ์ง์ฐ์๊ฐ์ผ๋ก ์ธํด input์ ์
๋ ฅํ๋ ์ฆ๊ฐ์ ์ธ ๋ ๋๋ง ๋ฐ์์๋๊ฐ ๋งค์ฐ ๋๋ฆฐ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
ย
ํฌ๋กฌ ๋ธ๋ผ์ฐ์ ์์ React Developer Tools ์ต์คํ
์
์ ์ค์นํ๋ฉด Profiler๋ก ์ฑ๋ฅ์ ์ธก์ ํ ์ ์์ต๋๋ค. ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด
useTransition
์ ์ฌ์ฉํ๊ธฐ ์ ์๋ ์ธ๊ฐ์ ํ์ผ์ด ๊ฑฐ์ ๋์์ ๋ ๋๋ง๋๋ ๋ชจ์ต์ ๋ณผ ์ ์์ผ๋ฉฐ ๋ ๋๋ง ์์ ์๊ฐ์ด 74.4ms ์ธ ๊ฒ์ ๋ณผ ์ ์์ต๋๋ค.ย
13.3.2 useTransition์ ์ ์ฉํด๋ณด๊ธฐ
import { useTransition } from "react"; import { useState } from "react"; import Page from "./pages/Page"; function App() { const [keyword, setKeyword] = useState(); const [isPending, startTransition] = useTransition(); return ( <div> <Page keyword={keyword} setKeyword={setKeyword} isPending={isPending} startTransition={startTransition}/> </div> ); } export default App;
๊ฐ์ฅ ์์ ์ปดํฌ๋ํธ์ธ App.js ์์ useTransition์ ์ ์ธํ๊ณ ํ์ ์ปดํฌ๋ํธ๋ก ๋ด๋ ค์ฃผ๊ฒ ์ต๋๋ค.
ย
import React from 'react' import Result from '../components/Result' function Page({keyword, setKeyword, isPending, startTransition}) { return ( <div> <input type="text" onKeyUp={(e) => { startTransition(()=>{ setKeyword(e.target.value) }) }} /> <Result keyword={keyword} isPending={isPending}/> </div> ) } export default Page
์ด๋ฒคํธ ํจ์๋ก ๋์ํ๋ setKeyword๋ฅผ
startTransition
๋ก ๋ํํ๋ฉด ๋ ๋๋ง ์ ์ฐ์ ์์๊ฐ ์ค์ ๋์ด setKeyword ํจ์๋ ๋ค๋ฅธ ์ฝ๋๋ค๋ณด๋ค ๋์ค์ ์ฒ๋ฆฌ๋ฉ๋๋ค. ๋๋ฌธ์ ์ฌ์ฉ์๊ฐ input์ ์
๋ ฅํ๋ ์ฆ์ ๋ฐ์ํด์ผ ํ๋ ๋ ๋๋ง์ ์ฐ์ ์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.ย
import React from "react"; function Result({keyword, isPending}) { const bigWork = new Array(10000).fill(0); return ( <div> { isPending ? ( <div>Loading...</div> ) : ( bigWork.map(() => { return <div>{keyword} {keyword} {keyword}</div> }) ) } </div> ) } export default Result
isPending
์ ์ด์ฉํ์ฌ startTransition
์ผ๋ก ๊ฐ์ผ setKeyword ์
๋ฐ์ดํธ๊ฐ ์งํ๋ ๋ Loading ๋ฌธ์๊ฐ ๋ ๋๋ง ๋ ์ ์๋๋ก ์ฒ๋ฆฌํ์ต๋๋ค. useTransition์ ์ฌ์ฉํ๋ฉด ์์
์ ์ฒ๋ฆฌ ์์๋ฅผ ๋ณ๊ฒฝํ๊ธฐ์ ์ด์ ๋ณด๋ค ํ์คํ input์ ํ์ดํํ ๋ ์ฆ๊ฐ์ ์ผ๋ก ๋ฐ์ํ๊ณ , isPending
์ ์ด์ฉํ์ฌ ์์
์ฒ๋ฆฌ์ค์ด๋ผ๋ ์๋ฏธ์ ๋ก๋ฉํ๋ฉด๋ ์ฝ๊ฒ ๋ณด์ฌ์ค ์ ์๋ ์ฅ์ ์ด ์์ต๋๋ค.ย useTransition์ ์ฌ์ฉํ๋ฉด ๋ ๋๋ง์ ์ฐ์ ์์๊ฐ ํ์คํ ๋๋ฌ๋๋ฉฐ ๋ฌด๊ฑฐ์ด ์์
์ด ์๋ Result ์ปดํฌ๋ํธ๊ฐ ๋ ๋๋ง ์ฐ์ ์์๊ฐ ๊ฐ์ฅ ๋ฎ์ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค. ๋๋ฌธ์ Page์ปดํฌ๋ํธ์ ์๋ input ์ ํ์ดํ์ ๋ํ ๋ ๋๋ง์ด ์ฐ์ ๋๊ธฐ์ ์ฌ์ฉ์๋ ๋ฒ๋ฒ
์์ด ์ค์ด๋ ๊ฒ ์ฒ๋ผ ๋๋ ์ ์์ต๋๋ค.
useTransition์ ์ฌ์ฉํ๋ฉด ๋ฌด๊ฑฐ์ด ์์
์ด ๋๋ฐ๋์ด ์๋ ์ํฉ์์๋ ์ฌ์ฉ์๊ฐ input์ ์
๋ ฅํ๋ ๋ ๋๋ง ๊ฒฐ๊ณผ๊ฐ ์ฐ์ ์ ์ผ๋ก ์ฒ๋ฆฌ๋ฉ๋๋ค. ์ฌ๊ธฐ์ ์ฃผ์ํ ์ ์ย useTransition์ด ๋ฌด๊ฑฐ์ด ์์
์ผ๋ก ์ธํ ์ง์ฐ์๊ฐ ์์ฒด๋ฅผ ์ค์ด๋ ๊ฒ์ ์๋๋ผ๋ ๊ฒ์
๋๋ค.
ย
ย
13.4 (์ค์ต2) useTransition์ ํตํด ํ์ด์ง ์ด๋ UX ๊ฐ์ ํ๊ธฐ
ย
13.4.1 useTransition์ ์ฌ์ฉํ์ง ์์์ ๊ฒฝ์ฐ
๋ค์์ useTransition์ ์ฌ์ฉํ๊ธฐ ์ ์์ ์
๋๋ค. App.js, PageOne.js, PageTwo.js ์ธ ๊ฐ์ง ํ์ผ์ ๋ง๋ค์ด ์งํํ๊ฒ ์ต๋๋ค.
App.js์์๋ setPage๊ฐ ์ง์ ํ๋ state ๊ฐ์ ๋ฐ๋ผ ํด๋นํ๋ ์ปดํฌ๋ํธ๋ฅผ ํ๋ฉด์ ๋ ๋๋งํฉ๋๋ค. App.js์์ ๋๋ฅด๋ ๋ฒํผ์ state ๊ฐ์ ๋ฐ๋ผ ๊ฐ๊ฐ PageOne๊ณผ PageTwo์ปดํฌ๋ํธ๊ฐ ํ๋ฉด์ ๊ทธ๋ ค์ง๋๋ฐ, PageOne์ 30,000๊ฐ๊ฐ๋์ ๋ฐ์ดํฐ๊ฐ ๋ ๋๋ง ๋๋๋ก ํ์ฌ ๋ฐ์ดํฐ ํธ์ถ ์ ๋ก๋ฉ ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฌ๋ ์ํฉ์ ๊ฐ์ ํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค. ์๋์ ์ผ๋ก PageTwo๋ ๋ ๋๋งํ ์์๊ฐ ์ ๊ธฐ ๋๋ฌธ์ ํ๋ฉด์ ๋ฐ๋ก ๊ทธ๋ ค์ง๋๋ค.
ย
import { useState } from "react"; import PageOne from "./PageOne.js"; import PageTwo from "./PageTwo.js"; function Router() { const [page, setPage] = useState("/"); function navigate(url) { setPage(url); } let content; if (page === "/") { content = ( <> <button onClick={() => navigate("/page-one")}> ๐๏ธโโ๏ธ ๋ฐ์ดํฐ ๋ก๋๊ฐ ๋ง์ ํ์ด์ง </button> <button onClick={() => navigate("/page-two")}>๐ ์ผ๋ฐ ํ์ด์ง</button> </> ); } else if (page === "/page-one") { content = <PageOne />; } else if (page === "/page-two") { content = <PageTwo />; } return ( <> <h1>{`ํ`}</h1> <main>{content}</main> </> ); } export default function App() { return <Router />; }
import { Suspense, useEffect } from "react"; export default function PageOne() { return ( <> <h1>๋ฐ์ดํฐ ๋ก๋๊ฐ ๋ง์ ํ์ด์ง์ ๋๋ค.</h1> <Suspense fallback={<h2>Loading...</h2>}> {Array(30000) .fill() .map((v, i) => ( <div key={i}>๋ถ๋ฌ์จ ๋ฐ์ดํฐ</div> ))} </Suspense> </> ); }
import React from "react"; function PageTwo() { return <div>์ผ๋ฐ ํ์ด์ง์ ๋๋ค.</div>; } export default PageTwo;
ย
ย
ย
ย
ํด๋น ์ฝ๋๋ฅผ ์คํํ์ ๋, ํ์์ ๋ฐ์ดํฐ ๋ก๋๊ฐ ๋ง์ ํ์ด์ง๋ก ์ด๋ํ๋ ๋ฒํผ์ ๋๋ฅธ๋ค๋ฉด ๋ฐ์ดํฐ๊ฐ ๋ก๋๊ฐ ์๋ฃ๋๊ธฐ ์ ๊น์ง ํ๋ฉด์ด ๋ฉ์ถ๋ ๊ฒ๊ณผ ๊ฐ์ ํ์์ด ๋ํ๋ฉ๋๋ค. ๋ํ ๋ ๋๋ง ์์ฒญ์ด ๋๋๊ธฐ ์ ๊น์ง๋ ์ผ๋ฐ ํ์ด์ง๋ฅผ ํด๋ฆญํ๋ ๋ฑ์ ๋ค๋ฅธ ์์
์ ํ ์ ์์ต๋๋ค.
13.4.2 useTransition ์ ์ฉํด๋ณด๊ธฐ
useTransition Hook์ ์ ์ฉํด๋ณด๊ธฐ์ ์์ React 18์ ๋์
๋
Suspense
๊ฐ๋
์ ๋ํด ๊ฐ๋จํ ์์๋ณด๊ฒ ์ต๋๋ค. Suspense๋ ? ์ปดํฌ๋ํธ๊ฐ ๋น๋๊ธฐ์ ์ผ๋ก ๋ ๋๋ง๋๋ ๊ฒฝ์ฐ ์ฌ์ฉํฉ๋๋ค. ๋ฐ์ดํฐ๊ฐ ์ ๋ถ ๋ก๋๋์ง ์์ ์ํ๋ฅผ ๋ฆฌ์กํธ์๊ฒ ์๋ ค์ฃผ๋ ๊ธฐ๋ฅ์ผ๋ก, Suspense ๋ด๋ถ์ ๊ตฌ์ฑ ์์๊ฐ ๋ฐ์ดํฐ๋ฅผ ๋ก๋ํ๋ ๋์์๋ ๋ ๋๋ง์ด ์ผ์ ์ค๋จ๋๋ฉฐ fallback ํ๋กํผํฐ์ ์ง์ ๋ ์ปดํฌ๋ํธ๊ฐ ์ฌ์ฉ์์๊ฒ ๋ณด์ฌ์ง๋๋ค.
ย
Suspense
๋ ์๋์ ๊ฐ์ด ์ฌ์ฉํฉ๋๋ค.<Suspense fallback={<Spinner/>}> <Component/> {/* ๋น๋๊ธฐ์ ์ผ๋ก ๋ ๋๋ง๋๋ ์ปดํฌ๋ํธ */} </Suspense>
ย
์ฌ๊ธฐ์ fallback ์ปจํ
์ธ ๊ฐ ๋ ๋๋ง ๋ ๋์๋ ์ฌ์ฉ์์๊ฒ ๊ธฐ์กด์ UI ๋ ๋๋ง์ด ์ผ์์ ์ผ๋ก ์ค๋จ๋๋ ๊ฒ์ฒ๋ผ ๋ณด์ฌ์ง๋๋ค. ์ด ๋ ํ์ด์ง๋ฅผ ๋ณด๋ค ๋ ๋ถ๋๋ฝ๊ฒ ์ ํํ๊ธฐ ์ํ์ฌ useTransition ์
Suspense
์ ๊ฒฐํฉํ์ฌ ์ฌ์ฉํฉ๋๋ค. useTransition ๋ด๋ถ์ ์ง์ ํ ํจ์๋ก ์ธํ ์
๋ฐ์ดํธ์ ๋ ๋๋ง์ด ์ค๋จ๋๋ ๊ฒฝ์ฐ, Suspense
๋ fallback์ ์ง์ ๋ ์ปดํฌ๋ํธ๋ฅผ ๋ณด์ฌ์ฃผ๋ ๋์ UI๋ฅผ ์ ์งํ๋ฉฐ ์์
์ ์งํํฉ๋๋ค.์ด์ ์ฝ๋์์ ์ด๋ฅผ ๊ตฌํํ๊ธฐ ์ํด App.js์์ ๋น๋๊ธฐ ์์
์ด ์ผ์ด๋๋ Router ์ปดํฌ๋ํธ๋ฅผ
Suspense
๋ก ๊ฐ์ธ์ฃผ๊ฒ ์ต๋๋ค. ์ด์ด์ useTranisiton Hook์ ์ ์ฉํ์ฌ ์ฐ์ ์์๋ฅผ ์ค์ ํด๋ณด๊ฒ ์ต๋๋ค. startTransition
์ผ๋ก setPage๋ฅผ ๊ฐ์ธ ํด๋น ํจ์๊ฐ ๋ฐ์ดํฐ ๋ก๋๋ฅผ ์๋ฃํ ๋๊น์ง ๊ธฐ๋ค๋ฆฌ๋๋ก React์๊ฒ ์๋ ค์ฃผ๋ ๊ฒ์
๋๋ค. ย
import { useState, Suspense, useTransition } from "react"; function Router() { const [page, setPage] = useState("/"); const [isPending, startTransition] = useTransition(); function navigate(url) { startTransition(() => { setPage(url); }); } //... export default function App() { return ( <Suspense fallback={<h2>๐ค Loading...</h2>}> <Router /> </Suspense> ); }
์ฌ๊ธฐ์ ์ฌ์ฉ์๊ฐ โ๐๏ธโโ๏ธ๋ฐ์ดํฐ ๋ก๋๊ฐ ๋ง์ ํ์ด์งโ ๋ฒํผ์ ๋๋ฅธ๋ค๋ฉด ์ด๋ป๊ฒ ๋ ๊น์?
Suspense
๋ก ๊ฐ์ผ Router ์ปดํฌ๋ํธ์ ์
๋ฐ์ดํธ๊ฐ ์ผ์ด๋ ๋ fallback์ ์ง์ ๋ โ๐ค Loading...โ์ด ๋ ๋๋ง๋ ๊ฒ์ด๋ผ๊ณ ์์ํ ์๋ ์์ต๋๋ค. ํ์ง๋ง React๋ startTransition
์ผ๋ก ๊ฐ์ผ setPageํจ์๊ฐ ์ฐ์ ์์๊ฐ ๋ฎ๋ค๊ณ ์ธ์งํ๊ธฐ ๋๋ฌธ์, UI์ ๋ณ๊ฒฝ์ ์์ ํด๋น ํ์ด์ง์ ๋ฐ์ดํฐ ๋ก๋ ์์
์ด ๋ฐฑ๊ทธ๋ผ์ด๋์์ ๋จผ์ ์งํ๋๋ฉฐ ํ์ด์ง ์ปดํฌ๋ํธ ๋ ๋๋ง์ ํ์์๋ก ์งํ๋ฉ๋๋ค. ย
์ฌ๊ธฐ์
startTransition
๊ณผ ํจ๊ป ์ ๋ฌ๋๋ ํ๋กํผํฐ์ธ isPendig
์ ์ํ๋ฅผ ํตํด ํ๋ฉด ์ ํ์ด ์ผ์ด๋ ๋์ ๋ก๋ฉ ์ํ๋ฅผ ์๊ฐ์ ์ผ๋ก ํํํ ์ ์์ต๋๋ค. isPending
์ ์ํ์ ๋ฐ๋ผ ์ถ๋ ฅ๋๋ ๋ฌธ๊ตฌ๋ฅผ ๋ฐ๊ฟ๋ณด๊ฒ ์ต๋๋ค.return ( <> <h1>{isPending ? `โณ ๋ก๋ฉ์ค(isPending...)` : `ํ`}</h1> <main>{content}</main> </> );
ย
ย
๋ฐ์ดํฐ๊ฐ ๋ก๋ ์ค์ผ ๋
Suspense
์ fallback ์ปจํ
์ธ ๋ฅผ ๋ณด์ฌ์ฃผ๋ ๋์ , ์์ ๊ฐ์ด ์ ํ ์
๋ฐ์ดํธ์ isPending
์ ์ํ์ ๋ฐ๋ผ ์กฐ๊ฑด ์ฒ๋ฆฌ๋ฅผ ํ์ฌ ๋ก๋ฉ ์ธ๋์ผ์ดํฐ ์ญํ ์ ํ๋ UI๋ฅผ ๊ตฌํํ ์ ์์ต๋๋ค.์ฌ๊ธฐ์ ์ง๊ณ ๊ฐ์ผ ํ useTransition์ ํน์ง์
startTransition
์ผ๋ก ๊ฐ์ผ ์ ํ ์์
์ด ์ค๋จ๋ ์ ์๋ค๋ ๊ฒ์
๋๋ค. isPending
์ด true ์ํ์ผ ๋, ์ผ๋ฐ ํ์ด์ง๋ฅผ ํด๋ฆญํ ๊ฒฝ์ฐ PageOne ์ปดํฌ๋ํธ๋ฅผ ๋ ๋๋งํ๋ ์์
์ด ์ค๋จ๋๊ณ PageTwo๋ก์ ๋ ๋๋ง ์ ํ์ด ์ผ์ด๋ฉ๋๋ค. ๊ธฐ์กด์ ์ฝ๋์ console.log๋ฅผ ์ถ๊ฐํ์ฌ ํ์ธํด๋ณด๊ฒ ์ต๋๋ค. PageOne.js์๋ useEffect
Hook์ ์ด์ฉํ์ฌ ๋ฐ์ดํฐ ๋ก๋๊ฐ ์๋ฃ๋์์ ๋๋ฅผ ํ์ธํ ์ ์๋ console.log๋ ์ถ๊ฐํ๊ฒ ์ต๋๋ค.ย
import { Suspense, useEffect } from "react"; export default function PageOne() { console.log("๐๏ธโโ๏ธ ๋ฐ์ดํฐ ๋ก๋๊ฐ ๋ง์ ํ์ด์ง"); useEffect(() => { console.log("๋ง์ดํธ: ์ปดํฌ๋ํธ ์ฒ์ ๊ทธ๋ ค์ง"); }, []); return ( <> <h1>๋ง์ ๋ด์ฉ์ด ์๋ ํ์ด์ง</h1> <Suspense fallback={<h2>Loading...</h2>}> {Array(30000) .fill() .map((v, i) => ( <div key={i}>๋ถ๋ฌ์จ ๋ฐ์ดํฐ</div> ))} </Suspense> </> ); }
import React from "react"; function PageTwo() { console.log("๐ ์ผ๋ฐ ํ์ด์ง"); return <div>PageTwo</div>; } export default PageTwo;
ย
ย
ํด๋น ์ฝ๋๋ฅผ ์ ์ฉํ ํ, ๋ฐ์ดํฐ ๋ก๋๊ฐ ๋ง์ ํ์ด์ง ๋ฒํผ์ ๋๋ฌ pageOne ์ปดํฌ๋ํธ์ ๋ ๋๋ง์ด ๋๋๊ธฐ ์ ์ ์ผ๋ฐ ํ์ด์ง ๋ฒํผ์ ๋๋ ์ ๊ฒฝ์ฐ์
๋๋ค. ์ฝ์์๋ ์์ ๊ฐ์ด ๋ฐ์ดํฐ ๋ก๋๊ฐ ๋ง์ ํ์ด์ง, ์ผ๋ฐ ํ์ด์ง๊ฐ ๋๋ํ ์ฐํ๋ฉฐ, ์ผ๋ฐ ํ์ด์ง๋ก ์ด๋ํฉ๋๋ค.
์ด๋ฅผ ํตํด
startTransition
์ผ๋ก ์ง์ ๋ ์ ํ ์
๋ฐ์ดํธ ์งํ ์ค์ ์ฌ์ฉ์ ํด๋ฆญ์ด๋ผ๋ ๊ธด๊ธ ์
๋ฐ์ดํธ ๋ฐ์ ์ ๊ธฐ์กด์ ์ ํ ์
๋ฐ์ดํธ ์์
์ด ์ค๋จ๋๋ฉฐ ๋ง์ง๋ง ์
๋ฐ์ดํธ๊ฐ ๋ ๋๋ง ๋๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.ย
๋ง์ฝ ์ ํ ์์
์ด ์ค๋จ๋๊ธฐ ์ ํ์ด์ง ๋ก๋๊ฐ ์๋ฃ๋์๋ค๋ฉด ์์ ๊ฐ์ด ๋ ๋๋ง์ด ์๋ฃ๋์ด ์ปดํฌ๋ํธ๊ฐ ๊ทธ๋ ค์ก์ ๊ฒ์
๋๋ค.
์ด๋ ๊ฒ useTransition์ ์ ํ ์์
์ด ์ค๋จ๋๋ค๋ ์ฑ์ง์ ์ด์ฉํ๋ฉด, ์ฌ์ฉ์๋ ๋ฌด๊ฑฐ์ด ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋ ํ์ด์ง๋ฅผ ํด๋ฆญํ๊ณ ๋์ ๋ฐ์ดํฐ ๋ก๋๊ฐ ์๋ฃ๋๊ธฐ๊น์ง ๊ธฐ๋ค๋ฆฌ์ง ์์๋ ๋ฉ๋๋ค. ์ด๊ฒ์ ์ฌ์ฉ์๊ฐ ์๋น์ค ์ด์ฉ ์ค ๋ฐ์ดํฐ ๋ก๋์ ์ ์ฝ๋ฐ์ง ์๊ฒ ํ์ฌ ํ์ด์ง์ UX ์ฑ๋ฅ ๊ฐ์ ์ ๋์์ ์ค ์ ์์ต๋๋ค.
์ฌ๊ธฐ๊น์ง useTransition์ ์ด์ฉํ์ฌ UX ๊ฐ์ ์ ๋ง์น App.js์ ์ต์ข
์ฝ๋์
๋๋ค.
ย
import { Suspense, useState, useTransition } from "react"; import PageOne from "./PageOne.js"; import PageTwo from "./PageTwo.js"; function Router() { const [page, setPage] = useState("/"); const [isPending, startTransition] = useTransition(); function navigate(url) { startTransition(() => { setPage(url); }); } let content; if (page === "/") { content = ( <> <button onClick={() => navigate("/page-one")}> ๐๏ธโโ๏ธ ๋ฐ์ดํฐ ๋ก๋๊ฐ ๋ง์ ํ์ด์ง </button> <button onClick={() => navigate("/page-two")}>๐ ์ผ๋ฐ ํ์ด์ง</button> </> ); } else if (page === "/page-one") { content = <PageOne />; } else if (page === "/page-two") { content = <PageTwo />; } return ( <> <h1>{isPending ? `โณ ๋ก๋ฉ์ค(isPending...)` : `ํ`}</h1> <main>{content}</main> </> ); } export default function App() { return ( <Suspense fallback={<h2>๐ค Loading...</h2>}> <Router /> </Suspense> ); }
ย