๐ React 18 ๋ณ๊ฒฝ์
๐ useId
โ
useId
๋ ํด๋ผ์ด์ธํธ์ ์๋ฒ๊ฐ์ hydration์ mismatch๋ฅผ ํผํ๋ฉด์ ์ ๋ํฌํ ์์ด๋๋ฅผ ์์ฑํ ์ ์๋ ์๋ก์ด hook์
๋๋ค. ์ด๋ ์ฃผ๋ก ๊ณ ์ ํ id
๊ฐ ํ์ํ ์ ๊ทผ์ฑ API์ ์ฌ์ฉ๋๋ ์ปดํฌ๋ํธ์ ์ ์ฉํ ๊ฒ์ผ๋ก ๊ธฐ๋๋ฉ๋๋ค.
์์ด๋๋ ๊ธฐ๋ณธ์ ์ผ๋ก ํธ๋ฆฌ ๋ด๋ถ์ ๋ ธ๋์ ์์น๋ฅผ ๋ํ๋ด๋ base 32 ๋ฌธ์์ด์ ๋๋ค. ํธ๋ฆฌ๊ฐ ์ฌ๋ฌ children์ผ๋ก ๋ถ๊ธฐ๋ ๋๋ง๋ค, ํ์ฌ ๋ ๋ฒจ์์ ์์ ์์ค์ ๋ํ๋ด๋ ๋นํธ๋ฅผ ์ํธ์ค ์ผ์ชฝ์ ์ถ๊ฐํ๊ฒ ๋ฉ๋๋ค,.
useId
๋ ๋ชฉ๋ก์์ ํค๋ฅผ ์์ฑํ๊ธฐ ์ํ ๊ฒ์ด ์๋๋๋ค. ํค๋ ๋ฐ์ดํฐ์์ ์์ฑ๋์ด์ผ ํฉ๋๋ค.
๐ useTransition
โ
์ด ๋ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ฉด ์ผ๋ถ ์ํ ์ ๋ฐ์ดํธ๋ฅผ ๊ธด๊ธํ์ง ์์ ๊ฒ(not urgent)๋ก ํ์ํ ์ ์์ต๋๋ค. ์ด๊ฒ์ผ๋ก ํ์๋์ง ์์ ์ํ ์ ๋ฐ์ดํธ๋ ๊ธด๊ธํ ๊ฒ์ผ๋ก ๊ฐ์ฃผ๋ฉ๋๋ค. ๊ธด๊ธํ ์ํ ์ ๋ฐ์ดํธ๊ฐ ๊ธด๊ธํ์ง ์์ ์ํ ์ ๋ฐ์ดํธ์ ์ค๋จํ ์ ์์ต๋๋ค.
์ํ ์ ๋ฐ์ดํธ๋ฅผ ๊ธด๊ธํ ๊ฒ๊ณผ ๊ธด๊ธํ์ง ์์ ๊ฒ์ผ๋ก ๋๋์ด ๊ฐ๋ฐ์์๊ฒ ๋ ๋๋ง ์ฑ๋ฅ์ ํ๋ํ๋๋ฐ ๋ง์ ์์ ๋ฅผ ์ฃผ์๋ค๊ณ ๋ณผ ์ ์์ต๋๋ค.
function App() {
const [resource, setResource] = useState(initialResource)
const [isPending, startTransition] = useTransition({ timeoutMs: 3000 })
return (
<>
<button
disabled={isPending}
onClick={() => {
startTransition(() => {
const nextUserId = getNextId(resource.userId)
setResource(fetchProfileData(nextUserId))
})
}}
>
Next
</button>
{isPending ? 'Loading...' : null}
<ProfilePage resource={resource} />
</>
)
}
startTransition
๋ ํจ์๋ก, ๋ฆฌ์กํธ์ ์ด๋ค ์ํ๋ณํ๋ฅผ ์ง์ฐ์ํค๊ณ ์ถ์์ง ์ง์ ํ ์ ์์ต๋๋ค.isPending
์ ์งํ ์ฌ๋ถ๋ก, ํธ๋์ง์ ์ด ์งํ์ค์ธ์ง ์ ์ ์์ต๋๋ค.timeoutMs
ํ๋กํผํฐ๋ ํธ๋์ง์ ์ด ์๋ฃ๋ ๋๊น์ง ์ผ๋ง๋ ์ค๋ซ๋์ ๊ธฐ๋ค๋ฆด ๊ฒ์ธ์ง ๊ฒฐ์ ํฉ๋๋ค.{timeoutMs: 3000}
๋ฅผ ์ ๋ฌํ๋ค๋ฉด โ๋ค์ ํ๋กํ์ ๋ถ๋ฌ์ค๋ ๋ฐ 3์ด๋ณด๋ค ์ค๋ ๊ฑธ๋ฆฐ๋ค๋ฉด ๋ก๋ฉ ์ํ๋ฅผ ๋ณด์ฌ์ฃผ๊ณ ๊ทธ์ ๊น์ง ๊ณ์ ์ด์ ํ๋ฉด์ ๋ณด์ฌ์ค๋ ๊ด์ฐฎ์โ๋ผ๋ ์๋ฏธ์ ๋๋ค.
useTransition
๊ฐ์ API๋ฅผ ์ฌ์ฉํ๋ฉด ์ํ๋ ์ฌ์ฉ์ ๊ฒฝํ์ ์ด์ ์ ๋ง์ถ ์ ์๊ณ ์ด๋ป๊ฒ ๊ตฌํํ๋์ง ์๊ฐํ์ง ์์๋ ๋ฉ๋๋ค.
const initialResource = fetchUserAndPosts();
function ProfilePage() {
const [resource, setResource] = useState(initialResource);
function handleRefreshClick() {
setResource(fetchUserAndPosts());
}
return (
<Suspense fallback={<h1>Loading profile...</h1>}>
<ProfileDetails resource={resource} />
<button onClick={handleRefreshClick}>
Refresh
</button>
<Suspense fallback={<h1>Loading posts...</h1>}>
<ProfileTimeline resource={resource} />
</Suspense>
</Suspense>
);
}
์ด ์์์์ ํ์ด์ง๊ฐ ๋ก๋๋๊ฑฐ๋ โRefreshโ ๋ฒํผ์ ๋๋ฅผ ๋ ๋ง๋ค ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
fetchUserAndPosts()
์ ๋ฐํ๊ฐ์ ์ํ์ ์ ์ฅํ์ฌ ํ์ ์ปดํฌ๋ํธ๋ค์ด ์ ์ฒญ์์ ๊ฐ์ ธ์จ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ ์ ์๊ฒ ํ๊ฒ ์ต๋๋ค.
<ProfileDetails>
๋ฐ <ProfileTimeline>
์ปดํฌ๋ํธ๋ ์๋ก์ด ๋ฐ์ดํฐ๋ฅผ ๋ํ๋ด๋ ์๋ก์ด ๋ฆฌ์์ค prop์ ์์ ํ๊ณ ์์ง ์๋ต์ด ์๊ธฐ ๋๋ฌธ์ "suspend"๋๊ณ fallback์ด ํ์๋ฉ๋๋ค.
ํ์ง๋ง ์ ๊ฒฝํ์ ์์ฐ์ค๋ฝ์ง ์์ต๋๋ค. ์ฐ๋ฆฌ๋ ํ ํ์ด์ง๋ฅผ ๋ธ๋ผ์ฐ์งํ๊ณ ์์๋๋ฐ ๋ฒํผ์ ํด๋ฆญํ ์งํ์ ๋ฐ๋ก ๋ก๋ฉ ์ํ๋ก ์ ํ๋์ด ์ฌ์ฉ์๋ฅผ ํผ๋์ค๋ฝ๊ฒ ํฉ๋๋ค. ์ด์ ์ฒ๋ผ, ์๋์น ์์ ๋ก๋ฉ ์ํ๋ฅผ ์จ๊ธฐ๊ธฐ ์ํด์ ์ํ ๊ฐฑ์ ์ ํธ๋์ง์
์ ๋ํํ ์ ์์ต๋๋ค.
function ProfilePage() {
const [isPending, startTransition] = useTransition({
// Wait 10 seconds before fallback
timeoutMs: 10000
});
const [resource, setResource] = useState(initialResource);
function handleRefreshClick() {
startTransition(() => {
setResource(fetchProfileData());
});
}
return (
<Suspense fallback={<h1>Loading profile...</h1>}>
<ProfileDetails resource={resource} />
<button
onClick={handleRefreshClick}
disabled={isPending}
>
{isPending ? "Refreshing..." : "Refresh"}
</button>
<Suspense fallback={<h1>Loading posts...</h1>}>
<ProfileTimeline resource={resource} />
</Suspense>
</Suspense>
);
}
โRefreshโ ๋ฒํผ์ ํด๋ฆญํด๋ ์ฐ๋ฆฌ๊ฐ ๋ธ๋ผ์ฐ์งํ๊ณ ์๋ ํ์ด์ง๊ฐ ์ฌ๋ผ์ง์ง ์์ต๋๋ค. ์ฐ๋ฆฌ๋ ์ธ๋ผ์ธ์ผ๋ก ๋ญ๊ฐ ๋ก๋ฉ๋๊ณ ์๋ค๋ ๊ฒ์ ๋ณด๊ณ ๋ฐ์ดํฐ๊ฐ ์ค๋น๋ ์ดํ์ ์๋ก์ด ๋ฐ์ดํฐ๊ฐ ๋ณด์ ๋๋ค.
์ด์ useTransition
์ ํ์์ฑ์ด ๋งค์ฐ ์ผ๋ฐ์ ์ด๋ผ๋ ๊ฑธ ์ ์ ์์ต๋๋ค. ์ฌ์ฉ์๊ฐ ์ํธ์์ฉํ๋ ๋์์ ์ค์๋ก ์จ๊ธฐ์ง ์๋๋ก ์ปดํฌ๋ํธ๋ฅผ ์์คํ๋ ์ํ๋ก ๋ง๋ค ์ ์๋ ๋๋ถ๋ถ ๋ฒํผ ํด๋ฆญ์ด๋ ์ํธ์์ฉ์ useTransition
์ผ๋ก ๋ํํด์ผ ํฉ๋๋ค.
์ ์์
์ ์ปดํฌ๋ํธ ์ฌ์ด์ ๋ง์ ๋ฐ๋ณต์ ์ธ ์ฝ๋ ์์ฐ์ผ๋ก ์ด์ด์ง ์ ์์ต๋๋ค. ์ด๊ฒ์ด ์ผ๋ฐ์ ์ผ๋ก ๋์์ธ ์์คํ
์ useTransition
์ฌ์ฉํ๋ ๊ฒ์ ์ถ์ฒํ๋ ์ด์ ์
๋๋ค. ์๋ฅผ ๋ค์ด ํธ๋์ง์
๋ก์ง์ ์ปค์คํ
<Button>
์ปดํฌ๋ํธ๋ก ์ถ์ถํ ์ ์์ต๋๋ค.
function Button({ children, onClick }) {
const [isPending, startTransition] = useTransition({
timeoutMs: 10000
});
function handleClick() {
startTransition(() => {
onClick();
});
}
const spinner = (
<span className="DelayedSpinner">
{/* ... */}
</span>
);
return (
<>
<button
onClick={handleClick}
disabled={isPending}
>
{children}
</button>
{isPending ? spinner : null}
</>
);
}
๋ช
์ฌํ์ธ์. ๋ฒํผ์ ์ด๋ค ์ํ๋ฅผ ๊ฐฑ์ ํ๋์ง ๊ด์ฌํ์ง ์์ต๋๋ค. ์ด๊ฒ์ onClick
์ด๋ฒคํธ ํธ๋ค๋ฌ์์ ๋ฐ์ํ๋ ๋ชจ๋ ์ํ ๊ฐฑ์ ์ transition
์ ํฌํจํฉ๋๋ค. ์ด์ <Button>
์ด ํธ๋์ง์
์ค์ ์ ๋์ ํด ์ฃผ๊ธฐ ๋๋ฌธ์ <ProfilePage>
์ปดํฌ๋ํธ์ ํธ๋์ง์
์ค์ ์ ํด์ค ํ์๊ฐ ์์ต๋๋ค.
function ProfilePage() {
const [resource, setResource] = useState(initialResource);
function handleRefreshClick() {
setResource(fetchProfileData());
}
return (
<Suspense fallback={<h1>Loading profile...</h1>}>
<ProfileDetails resource={resource} />
<Button onClick={handleRefreshClick}>
Refresh
</Button>
<Suspense fallback={<h1>Loading posts...</h1>}>
<ProfileTimeline resource={resource} />
</Suspense>
</Suspense>
);
}
๋ฒํผ์ ํด๋ฆญํ๋ฉด ํธ๋์ง์
์ด ์์๋๊ณ ๊ทธ ์์ props.onClick()
์ด ํธ์ถ๋์ <ProfilePage>
์ปดํฌ๋ํธ์์ handleRefreshClick
ํจ์๊ฐ ์คํ๋ฉ๋๋ค. ์๋ก์ด ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๊ธฐ ์์ํ์ง๋ง ํธ๋์ง์
๋ด๋ถ๋ผ์ ํด๋ฐฑ์ด ๋ณด์ฌ์ง์ง ์์ผ๋ฉฐ useTransition
ํธ์ถ์ ์ง์ ๋ 10์ด๊ฐ ์ง๋์ง ์์์ต๋๋ค. ํธ๋์ง์
์ด ๋ณด๋ฅ์ค์ธ ๋์ ๋ฒํผ์ ์ธ๋ผ์ธ์ผ๋ก ๋ก๋ฉ ์ธ๋์ผ์ดํฐ๋ฅผ ๋ด
๋๋ค.
์ด์ ์ปจ์ปค๋ฐํธ ๋ชจ๋๊ฐ ์ปดํฌ๋ํธ์ ๊ฒฉ๋ฆฌ ์์ค ๋ฐ ๋ชจ๋์ฑ์ ํฌ์ํ์ง ์๊ณ ๋ ์ฐ์ํ ์ฌ์ฉ์ ๊ฒฝํ์ ๋ง๋๋์ง ๋ฐฐ์ ์ต๋๋ค. React๋ ํธ๋์ง์ ์ ์กฐ์ ํฉ๋๋ค.
๐ useDeferredValue
โ
useDeferredValue
๋ฅผ ์ฌ์ฉํ๋ฉด, ํธ๋ฆฌ์์ ๊ธํ์ง ์์ ๋ถ๋ถ์ ์ฌ๋๋๋ง์ ์ง์ฐํ ์ ์์ต๋๋ค. ์ด๋ debounce
์ ๋น์ทํ์ง๋ง, ๋ช๊ฐ์ง ๋ ์ฅ์ ์ด ์์ต๋๋ค. ๊ณ ์ ๋ ์ง์ฐ์๊ฐ์ด ์์ผ๋ฏ๋ก, ๋ฆฌ์กํธ๋ ์ฒซ๋ฒ์งธ ๋ ๋๋ง์ด ๋ฐ์๋๋ ์ฆ์ ์ง์ฐ ๋ ๋๋ง์ ์๋ํฉ๋๋ค. ์ด ์ง์ฐ๋ ๋ ๋๋ง์ ์ธํฐ๋ฝํธ๊ฐ ๊ฐ๋ฅํ๋ฉฐ, ์ฌ์ฉ์ ์
๋ ฅ์ ์ฐจ๋จํ์ง ์์ต๋๋ค.
const deferredValue = useDeferredValue(value);
useDeferredValue
๊ฐ์ ์๋ฝํ๊ณ ๋ ๊ธด๊ธํ ์
๋ฐ์ดํธ๋ฅผ ์ฐ๊ธฐํ ๊ฐ์ ์ ๋ณต์ฌ๋ณธ์ ๋ฐํํฉ๋๋ค. ํ์ฌ ๋ ๋๋ง์ด ์ฌ์ฉ์ ์
๋ ฅ๊ณผ ๊ฐ์ ๊ธด๊ธ ์
๋ฐ์ดํธ์ ๊ฒฐ๊ณผ์ธ ๊ฒฝ์ฐ React๋ ์ด์ ๊ฐ์ ๋ฐํํ ๋ค์ ๊ธด๊ธ ๋ ๋๋ง์ด ์๋ฃ๋ ํ ์ ๊ฐ์ ๋ ๋๋งํฉ๋๋ค.
์ด hook์ ๋๋ฐ์ด์ฑ ๋๋ throttling์ ์ฌ์ฉํ์ฌ ์ ๋ฐ์ดํธ๋ฅผ ์ฐ๊ธฐํ๋ user-space hooks์ ์ ์ฌํฉ๋๋ค.
useDeferredValue
์ฌ์ฉ์ ์ด์ ์ React๊ฐ ๋ค๋ฅธ ์์
์ด ์๋ฃ๋๋ ์ฆ์ ์
๋ฐ์ดํธ ์์
์ ์ํํ๊ณ (์์์ ์๊ฐ์ ๊ธฐ๋ค๋ฆฌ๋ ๋์ ) startTransition
๊ณผ ๊ฐ์ด ์ง์ฐ๋ ๊ฐ์ด ๊ธฐ์กด ์ฝํ
์ธ ์ ๋ํ ์๊ธฐ์น ์์ ๋์ฒด๋ฅผ ํธ๋ฆฌ๊ฑฐํ์ง ์๊ณ ์ผ์ ์ค๋จ๋ ์ ์๋ค๋ ๊ฒ์
๋๋ค.