use๋ Promise๋ Context์ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ฐธ์กฐํ๋ React API์
๋๋ค.
const value = use(resource);๋ ํผ๋ฐ์ค
use(resource)
์ปดํฌ๋ํธ์์ Promise๋ Context์ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ์ฐธ์กฐํ๋ ค๋ฉด use๋ฅผ ์ฌ์ฉํ์ธ์.
import { use } from 'react';
function MessageComponent({ messagePromise }) {
const message = use(messagePromise);
const theme = use(ThemeContext);
// ...๋ค๋ฅธ React Hook๊ณผ ๋ฌ๋ฆฌ use๋ if์ ๊ฐ์ ์กฐ๊ฑด๋ฌธ๊ณผ ๋ฐ๋ณต๋ฌธ ๋ด๋ถ์์ ํธ์ถํ ์ ์์ต๋๋ค. ๋ค๋ง, ๋ค๋ฅธ React Hook๊ณผ ๊ฐ์ด use๋ ์ปดํฌ๋ํธ ๋๋ Hook์์๋ง ํธ์ถํด์ผ ํฉ๋๋ค.
Promise์ ํจ๊ป ํธ์ถ๋ ๋ use API๋ Suspense ๋ฐ Error Boundary์ ํตํฉ๋ฉ๋๋ค. use์ ์ ๋ฌ๋ Promise๊ฐ ๋๊ธฐPendingํ๋ ๋์ use๋ฅผ ํธ์ถํ๋ ์ปดํฌ๋ํธ๋ Suspend๋ฉ๋๋ค. use๋ฅผ ํธ์ถํ๋ ์ปดํฌ๋ํธ๊ฐ Suspense ๊ฒฝ๊ณ๋ก ๋๋ฌ์ธ์ฌ ์์ผ๋ฉด Fallback์ด ํ์๋ฉ๋๋ค. Promise๊ฐ ๋ฆฌ์กธ๋ธ๋๋ฉด Suspense Fallback์ use API๊ฐ ๋ฐํํ ์ปดํฌ๋ํธ๋ก ๋์ฒด๋ฉ๋๋ค. use์ ์ ๋ฌ๋ Promise๊ฐ Reject๋๋ฉด ๊ฐ์ฅ ๊ฐ๊น์ด Error Boundary์ Fallback์ด ํ์๋ฉ๋๋ค.
์๋ ์์๋ฅผ ์ฐธ๊ณ ํ์ธ์.
๋งค๊ฐ๋ณ์
๋ฐํ๊ฐ
use Hook์ Promise๋ Context์์ ์ฐธ์กฐํ ๊ฐ์ ๋ฐํํฉ๋๋ค.
์ฃผ์ ์ฌํญ
useAPI๋ ์ปดํฌ๋ํธ๋ Hook ๋ด๋ถ์์ ํธ์ถ๋์ด์ผ ํฉ๋๋ค.- ์๋ฒ ์ปดํฌ๋ํธ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ฌ ๋๋
use๋ณด๋คasync๋ฐawait์ ์ฌ์ฉํฉ๋๋ค.async๋ฐawait์await์ด ํธ์ถ๋ ์์ ๋ถํฐ ๋ ๋๋ง์ ์์ํ๋ ๋ฐ๋ฉด,use๋ ๋ฐ์ดํฐ๊ฐ ๋ฆฌ์กธ๋ธ๋ ํ ์ปดํฌ๋ํธ๋ฅผ ๋ฆฌ๋ ๋๋งํฉ๋๋ค. - ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์์ Promise๋ฅผ ์์ฑํ๋ ๊ฒ๋ณด๋ค ์๋ฒ ์ปดํฌ๋ํธ์์ Promise๋ฅผ ์์ฑํ์ฌ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์ ์ ๋ฌํ๋ ๊ฒ์ด ์ข์ต๋๋ค. ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์์ ์์ฑ๋ Promise๋ ๋ ๋๋งํ ๋๋ง๋ค ๋ค์ ์์ฑ๋ฉ๋๋ค. ์๋ฒ ์ปดํฌ๋ํธ์์ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๋ก ์ ๋ฌ๋ Promise๋ ๋ฆฌ๋ ๋๋ง ์ ๋ฐ์ ๊ฑธ์ณ ์์ ์ ์ ๋๋ค. ์์๋ฅผ ํ์ธํ์ธ์.
์ฌ์ฉ๋ฒ
use๋ฅผ ์ฌ์ฉํ์ฌ Context ์ฐธ์กฐํ๊ธฐ
Context๊ฐ use์ ์ ๋ฌ๋๋ฉด useContext์ ์ ์ฌํ๊ฒ ์๋ํฉ๋๋ค. useContext๋ ์ปดํฌ๋ํธ์ ์ต์์ ์์ค์์ ํธ์ถํด์ผ ํ์ง๋ง, use๋ if์ ๊ฐ์ ์กฐ๊ฑด๋ฌธ์ด๋ for์ ๊ฐ์ ๋ฐ๋ณต๋ฌธ ๋ด๋ถ์์ ํธ์ถํ ์ ์์ต๋๋ค. use๋ ์ ์ฐํ๋ฏ๋ก useContext๋ณด๋ค ์ ํธ๋ฉ๋๋ค.
import { use } from 'react';
function Button() {
const theme = use(ThemeContext);
// ...use๋ ์ ๋ฌํ Context์ Context Value๋ฅผ ๋ฐํํฉ๋๋ค. Context ๊ฐ์ ๊ฒฐ์ ํ๊ธฐ ์ํด React๋ ์ปดํฌ๋ํธ ํธ๋ฆฌ๋ฅผ ํ์ํ๊ณ ์์์ ๊ฐ์ฅ ๊ฐ๊น์ด Context Provider๋ฅผ ์ฐพ์ต๋๋ค.
Context๋ฅผ Button์ ์ ๋ฌํ๋ ค๋ฉด Button ๋๋ ์์ ์ปดํฌ๋ํธ ์ค ํ๋๋ฅผ Context Provider๋ก ๋ํํฉ๋๋ค.
function MyPage() {
return (
<ThemeContext value="dark">
<Form />
</ThemeContext>
);
}
function Form() {
// ... ๋ฒํผ ๋ ๋๋ง ...
}Provider์ Button ์ฌ์ด์ ์ผ๋ง๋ ๋ง์ ์ปดํฌ๋ํธ๊ฐ ์๋์ง๋ ์ค์ํ์ง ์์ต๋๋ค. Form ๋ด๋ถ์ ์ด๋ ๊ณณ์ด๋ Button์ด use(ThemeContext)๋ฅผ ํธ์ถํ๋ฉด "dark"๋ฅผ ๊ฐ์ผ๋ก ๋ฐ์ต๋๋ค.
useContext์ ๋ฌ๋ฆฌ, use๋ if์ ๊ฐ์ ์กฐ๊ฑด๋ฌธ๊ณผ ๋ฐ๋ณต๋ฌธ ๋ด๋ถ์์ ํธ์ถํ ์ ์์ต๋๋ค.
function HorizontalRule({ show }) {
if (show) {
const theme = use(ThemeContext);
return <hr className={theme} />;
}
return false;
}use๋ if ๋ด๋ถ์์ ํธ์ถ๋๋ฏ๋ก Context์์ ์กฐ๊ฑด๋ถ๋ก ๊ฐ์ ์ฐธ์กฐํ ์ ์์ต๋๋ค.
import { createContext, use } from 'react'; const ThemeContext = createContext(null); export default function MyApp() { return ( <ThemeContext value="dark"> <Form /> </ThemeContext> ) } function Form() { return ( <Panel title="Welcome"> <Button show={true}>Sign up</Button> <Button show={false}>Log in</Button> </Panel> ); } function Panel({ title, children }) { const theme = use(ThemeContext); const className = 'panel-' + theme; return ( <section className={className}> <h1>{title}</h1> {children} </section> ) } function Button({ show, children }) { if (show) { const theme = use(ThemeContext); const className = 'button-' + theme; return ( <button className={className}> {children} </button> ); } return false }
์๋ฒ์์ ํด๋ผ์ด์ธํธ๋ก ๋ฐ์ดํฐ ์คํธ๋ฆฌ๋ฐํ๊ธฐ
์๋ฒ ์ปดํฌ๋ํธ์์ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๋ก Promise Prop์ ์ ๋ฌํ์ฌ ์๋ฒ์์ ํด๋ผ์ด์ธํธ๋ก ๋ฐ์ดํฐ๋ฅผ ์คํธ๋ฆฌ๋ฐํ ์ ์์ต๋๋ค.
import { fetchMessage } from './lib.js';
import { Message } from './message.js';
export default function App() {
const messagePromise = fetchMessage();
return (
<Suspense fallback={<p>waiting for message...</p>}>
<Message messagePromise={messagePromise} />
</Suspense>
);
}ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๋ Prop์ผ๋ก ๋ฐ์ Promise๋ฅผ use API์ ์ ๋ฌํฉ๋๋ค. ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๋ ์๋ฒ ์ปดํฌ๋ํธ๊ฐ ์ฒ์์ ์์ฑํ Promise์์ ๊ฐ์ ์ฝ์ ์ ์์ต๋๋ค.
// message.js
'use client';
import { use } from 'react';
export function Message({ messagePromise }) {
const messageContent = use(messagePromise);
return <p>Here is the message: {messageContent}</p>;
}Message๋ Suspense๋ก ๋ํ๋์ด ์์ผ๋ฏ๋ก Promise๊ฐ ๋ฆฌ์กธ๋ธ๋ ๋๊น์ง Fallback์ด ํ์๋ฉ๋๋ค. Promise๊ฐ ๋ฆฌ์กธ๋ธ๋๋ฉด use Hook์ด ๊ฐ์ ์ฐธ์กฐํ๊ณ Message ์ปดํฌ๋ํธ๊ฐ Suspense Fallback์ ๋์ฒดํฉ๋๋ค.
"use client"; import { use, Suspense } from "react"; function Message({ messagePromise }) { const messageContent = use(messagePromise); return <p>Here is the message: {messageContent}</p>; } export function MessageContainer({ messagePromise }) { return ( <Suspense fallback={<p>โDownloading message...</p>}> <Message messagePromise={messagePromise} /> </Suspense> ); }
์์ธํ ์ดํด๋ณด๊ธฐ
Promise๋ ์๋ฒ ์ปดํฌ๋ํธ์์ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๋ก ์ ๋ฌํ ์ ์์ผ๋ฉฐ use API๋ฅผ ํตํด ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์์ ๋ฆฌ์กธ๋ธ๋ฉ๋๋ค. ๋ํ ์๋ฒ ์ปดํฌ๋ํธ์์ await์ ์ฌ์ฉํ์ฌ Promise๋ฅผ ๋ฆฌ์กธ๋ธํ๊ณ ๋ฐ์ดํฐ๋ฅผ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ์ Prop์ผ๋ก ์ ๋ฌํ๋ ๋ฐฉ๋ฒ๋ ์กด์ฌํฉ๋๋ค.
export default async function App() {
const messageContent = await fetchMessage();
return <Message messageContent={messageContent} />
}ํ์ง๋ง ์๋ฒ ์ปดํฌ๋ํธ์์ await์ ์ฌ์ฉํ๋ฉด await ๋ฌธ์ด ์๋ฃ๋ ๋๊น์ง ๋ ๋๋ง์ด ์ฐจ๋จ๋ฉ๋๋ค. ์๋ฒ ์ปดํฌ๋ํธ์์ ํด๋ผ์ด์ธํธ ์ปดํฌ๋ํธ๋ก Promise๋ฅผ Prop์ผ๋ก ์ ๋ฌํ๋ฉด Promise๊ฐ ์๋ฒ ์ปดํฌ๋ํธ์ ๋ ๋๋ง์ ์ฐจ๋จํ๋ ๊ฒ์ ๋ฐฉ์งํ ์ ์์ต๋๋ค.
๊ฑฐ๋ถ๋ Promise ์ฒ๋ฆฌํ๊ธฐ
๊ฒฝ์ฐ์ ๋ฐ๋ผ use์ ์ ๋ฌ๋ Promise๊ฐ ๊ฑฐ๋ถ๋ ์ ์์ต๋๋ค. ๊ฑฐ๋ถ๋ ํ๋ก๋ฏธ์ค๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ 2๊ฐ์ง๊ฐ ์กด์ฌํฉ๋๋ค.
Error Boundary๋ฅผ ์ฌ์ฉํ์ฌ ์ค๋ฅ ํ์ํ๊ธฐ
Promise๊ฐ ๊ฑฐ๋ถ๋ ๋ ์ค๋ฅ๋ฅผ ํ์ํ๊ณ ์ถ๋ค๋ฉด Error Boundary๋ฅผ ์ฌ์ฉํฉ๋๋ค. Error Boundary๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด use API ๋ฅผ ํธ์ถํ๋ ์ปดํฌ๋ํธ๋ฅผ Error Boundary๋ก ๋ํํฉ๋๋ค. use์ ์ ๋ฌ๋ Promise๊ฐ ๊ฑฐ๋ถ๋๋ฉด Error Boundary์ ๋ํ Fallback์ด ํ์๋ฉ๋๋ค.
"use client"; import { use, Suspense } from "react"; import { ErrorBoundary } from "react-error-boundary"; export function MessageContainer({ messagePromise }) { return ( <ErrorBoundary fallback={<p>โ ๏ธSomething went wrong</p>}> <Suspense fallback={<p>โDownloading message...</p>}> <Message messagePromise={messagePromise} /> </Suspense> </ErrorBoundary> ); } function Message({ messagePromise }) { const content = use(messagePromise); return <p>Here is the message: {content}</p>; }
Promise.catch๋ก ๋์ฒด ๊ฐ ์ ๊ณตํ๊ธฐ
use์ ์ ๋ฌ๋ Promise๊ฐ ๊ฑฐ๋ถ๋ ๋ ๋์ฒด ๊ฐ์ ์ ๊ณตํ๋ ค๋ฉด Promise์ catch ๋ฉ์๋๋ฅผ ์ฌ์ฉํฉ๋๋ค.
import { Message } from './message.js';
export default function App() {
const messagePromise = new Promise((resolve, reject) => {
reject();
}).catch(() => {
return "no new message found.";
});
return (
<Suspense fallback={<p>waiting for message...</p>}>
<Message messagePromise={messagePromise} />
</Suspense>
);
}Promise์ catch ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด Promise ๊ฐ์ฒด์์ catch๋ฅผ ํธ์ถํฉ๋๋ค. catch๋ ์ค๋ฅ ๋ฉ์์ง๋ฅผ ์ธ์๋ก ๋ฐ๋ ํจ์๋ฅผ ์ธ์๋ก ๋ฐ์ต๋๋ค. catch์ ์ ๋ฌ๋ ํจ์๊ฐ ๋ฐํํ๋ ๊ฐ์ ๋ชจ๋ Promise์ ๋ฆฌ์กธ๋ธ ๊ฐ์ผ๋ก ์ฌ์ฉ๋ฉ๋๋ค.
๋ฌธ์ ํด๊ฒฐ
โSuspense Exception: This is not a real error!โ
React ์ปดํฌ๋ํธ ๋๋ Hook ํจ์ ์ธ๋ถ์์, ํน์ try-catch ๋ธ๋ก์์ use๋ฅผ ํธ์ถํ๊ณ ์๋ ๊ฒฝ์ฐ์
๋๋ค. try-catch ๋ธ๋ก ๋ด์์ use๋ฅผ ํธ์ถํ๋ ๊ฒฝ์ฐ ์ปดํฌ๋ํธ๋ฅผ Error Boundary๋ก ๋ํํ๊ฑฐ๋ Promise์ catch๋ฅผ ํธ์ถํ์ฌ ์ค๋ฅ๋ฅผ ๋ฐ๊ฒฌํ๊ณ Promise๋ฅผ ๋ค๋ฅธ ๊ฐ์ผ๋ก ๋ฆฌ์กธ๋ธํฉ๋๋ค. ์ด๋ฌํ ์์๋ค์ ํ์ธํ์ธ์.
function MessageComponent({messagePromise}) {
function download() {
// โ `use`๋ฅผ ํธ์ถํ๋ ํจ์๊ฐ ์ปดํฌ๋ํธ๋ Hook์ด ์๋๋๋ค.
const message = use(messagePromise);
// ...๋์ , ์ปดํฌ๋ํธ ํด๋ก์ ์ธ๋ถ์์ use๋ฅผ ํธ์ถํ์ธ์. ์ฌ๊ธฐ์ use๋ฅผ ํธ์ถํ๋ ํจ์๋ ์ปดํฌ๋ํธ ๋๋ Hook์
๋๋ค.
function MessageComponent({messagePromise}) {
// โ
`use`๋ฅผ ์ปดํฌ๋ํธ์์ ํธ์ถํ๊ณ ์์ต๋๋ค.
const message = use(messagePromise);
// ...