๐ Context API๋ ๋ฌด์์ธ๊ฐ?
๋ฆฌ์กํธ ๊ณต์๋ฌธ์์์๋ Context๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ์๊ฐํ๊ณ ์์ต๋๋ค.
context๋ฅผ ์ด์ฉํ๋ฉด ๋จ๊ณ๋ง๋ค ์ผ์ผ์ด props๋ฅผ ๋๊ฒจ์ฃผ์ง ์๊ณ ๋
์ปดํฌ๋ํธ ํธ๋ฆฌ ์ ์ฒด์ ๋ฐ์ดํฐ๋ฅผ ์ ๊ณตํ ์ ์์ต๋๋ค.์ผ๋ฐ์ ์ธ React ์ ํ๋ฆฌ์ผ์ด์ ์์ ๋ฐ์ดํฐ๋ ์์์ ์๋๋ก
(์ฆ, ๋ถ๋ชจ๋ก๋ถํฐ ์์์๊ฒ) props๋ฅผ ํตํด ์ ๋ฌ๋์ง๋ง,
์ ํ๋ฆฌ์ผ์ด์ ์์ ์ฌ๋ฌ ์ปดํฌ๋ํธ๋ค์ ์ ํด์ค์ผ ํ๋ props์ ๊ฒฝ์ฐ
(์๋ฅผ ๋ค๋ฉด ์ ํธ ๋ก์ผ์ผ, UI ํ ๋ง) ์ด ๊ณผ์ ์ด ๋ฒ๊ฑฐ๋ก์ธ ์ ์์ต๋๋ค.context๋ฅผ ์ด์ฉํ๋ฉด, ํธ๋ฆฌ ๋จ๊ณ๋ง๋ค ๋ช ์์ ์ผ๋ก props๋ฅผ ๋๊ฒจ์ฃผ์ง ์์๋
๋ง์ ์ปดํฌ๋ํธ๊ฐ ์ด๋ฌํ ๊ฐ์ ๊ณต์ ํ๋๋ก ํ ์ ์์ต๋๋ค.
๋ฆฌ์กํธ์์๋ ์ปดํฌ๋ํธ ๊ฐ prop drilling์ ๋ฐฉ์งํ๊ธฐ ์ํด context๋ฅผ ๋ด๋์์ต๋๋ค.
context๋ฅผ ์ด์ฉํ๋ค๋ฉด ๋ถ๋ชจ ์ปดํฌ๋ํธ์ ์์ ์ปดํฌ๋ํธ๋ฅผ ๋ฐ์ด๋์ด ๊ฐ์ ์ ๋ฌํ ์ ์์ต๋๋ค.
๊ทธ๋ผ ์ฌ์ฉ๋ฒ ๋จผ์ ์์๋ณด๊ฒ ์ต๋๋ค.
๐ Context Api ์ฌ์ฉ๋ฒ
๐ React.createContext
const NewContext = React.createContext(defaultValue);
// ํ์
์ง์ ๋ ๊ฐ๋ฅํฉ๋๋ค.
const NewContext2 = React.createContext<{value:string}>({value:''});
์ฐ์ React.createContext
๋ฅผ ํตํด context ๊ฐ์ฒด๋ฅผ ๋ง๋ค์ด์ค๋๋ค.
defaultValue
๋ ํธ๋ฆฌ ์์์ ์ ์ ํ Provider
๋ฅผ ์ฐพ์ง ๋ชปํ ๋๋ง ์ฐ์ด๋ ๊ธฐ๋ณธ๊ฐ์
๋๋ค.
Provider์ value๊ฐ undefined์ด์ฌ๋ defaultValue๋ฅผ ์ ๊ณตํ์ง๋ ์์ต๋๋ค.
๐ React.Provider
const DisplayContext = React.createContext<{mode:'light'|'dark'}>({mode:'light'});
...
return (
<DisplayContext.Provider value={mode:'light'}>
<App />
</DisplayContextProvider>
)
Provider
๋ ๋ฐ์ value
๊ฐ์ ํ์์ ์์นํ ์ปดํฌ๋ํธ์๊ฒ ์ ๋ฌํ๋ ์ญํ ์ ํฉ๋๋ค.
Provider์์ Provider๋ฅผ ์ ์ธํ ์ ์์ผ๋ฉฐ ์ด๋ด ๊ฒฝ์ฐ ๊ฐ์ฅ ๊ฐ๊น์ด ๋ถ๋ชจ Provider์ ๊ฐ์ด ์ฐ์ ๋ฉ๋๋ค.
๊ทธ๋ฆฌ๊ณ context๋ฅผ ๊ตฌ๋ ํ๊ณ ์๋ ํ์ ์ปดํฌ๋ํธ๋ค์ Provider์ ๊ฐ์ด ๋ณ๊ฒฝ๋๋ฉด ์ ๋ถ ๋ ๋๋ง๋ฉ๋๋ค.
๊ทธ๋ ๊ธฐ์ ํ context ์์ ์ฐ๊ด์ฑ์ด ์๋ ๊ฐ๋ค์ ํจ๊ป ๋ฃ์ ๊ฒฝ์ฐ ์ฑ๋ฅ ์ด์๊ฐ ๋ฐ์ํ ์ ์๊ธฐ ๋๋ฌธ์
์ฐ๊ด์ฑ ์๋ ๊ฐ๋ค๋ผ๋ฆฌ ๋ถ๋ฆฌํ์ฌ ๋ค๋ฅธ context๋ก ๋ถ๋ฆฌํด์ค ์๋ ์์ต๋๋ค.
๐ React.useContext
const App = () => {
const { mode } = React.useContext(DisplayContext);
return (
<div style={{background : mode === 'light' ? 'white' : 'black'}}>
Hello
</div>
)
}
ํจ์ํ ์ปดํฌ๋ํธ์์๋ useContext๋ฅผ ์ด์ฉํด context์ ๋ฃ์ด์ค ๊ฐ์ ๊บผ๋ด์ฌ ์ ์์ต๋๋ค.
return (
<DisplayContext.Consumer>
{value => (
<div style={{background : value.mode === 'light' ? 'white' : 'black'}}>
Hello
</div>
)}
</DisplayContext.Consumer>
)
๊ธฐ์กด์๋ Consumer๋ฅผ ์ด์ฉํด ๊ฐ์ ๊บผ๋ด์๋๋ฐ,
์ด๋ ์ฃผ๋ก ํด๋์คํ ์ปดํฌ๋ํธ์์ ์ฌ์ฉํ๋ render props ๋ฐฉ์์ผ๋ก์จ
ํจ์ํ ์ปดํฌ๋ํธ์์๋ ํ
์ ์ด์ฉํ๋ ๊ฒ์ด ํจ์ฌ ๊ฐ๊ฒฐํ๊ณ ์ฌ์ฉํ๊ธฐ์ ํธ๋ฆฌํฉ๋๋ค.
๋ถ๋ชจ์๊ฒ์ props
๋ฅผ ๋ด๋ ค๋ฐ์ง ์๊ณ ๊ทธ ๊ณผ์ ์ ๊ฑด๋ ๋ฐ์ด ๊ฐ์ ๊ณต์ ํ๋ค๋ ๊ฒ์ ๋ณด์ํ๋
์ ์ญ์ผ๋ก ์ํ๋ฅผ ๊ด๋ฆฌํ๋ค๋ ๋ง๊ณผ๋ ๋น์ทํ๊ฒ ๋ค๋ฆฝ๋๋ค.
ํ์ง๋ง ์ด๋ฌํ ๋ง์ด ๋ฌด์ํ๊ฒ๋ Context
๋ ์ํ ๊ด๋ฆฌ ํด์ด ์๋๋๋ค.
๐ Context Api๋ ์ ์ํ ๊ด๋ฆฌ ํด์ด ์๋๊ฐ?
๐ ๋ง์ Context ๊ฐ์ ์ฌ์ฉํ๋ค๋ฉด?
<Context1.Provider value={...}>
<Context2.Provider value={...}>
<Context3.Provider value={...}>
<Context4.Provider value={...}>
...
</Context4.Provider>
</Context3.Provider>
</Context2.Provider>
</Context1.Provider>
์ฐ์ context๋ฅผ ์ผ๋ฐ์ ์ธ ์ํ ๊ด๋ฆฌ๋๊ตฌ๋ก์จ ์ฌ์ฉํ๋ค๋ฉด
์์ ๊ฐ์ด ๋ถ์ด๋๋ Provider๋ค์ ๊ณ์ํด์ ์ค์ฒฉ๋์ด ์์ฌ๊ฐ ๊ฒ์ด๊ณ ,
ํน์ ์ปดํฌ๋ํธ์ ์ฌ์ฉํ๋ ๊ฐ์ ์ํด Provider๋ฅผ ์ฌ๊ธฐ์ ๊ธฐ ํฉ๋ฟ๋ ค ๋์ผ๋ฉด ๋ณ๊ฒฝ์ด ์ผ์ด๋ฌ์ ๋
๊ทธ๊ฒ์ ๋์ํ๋ ๋์ด๋๋ ๋์ด๋๋ context ๋งํผ ์ด๋ ค์์ง ๊ฒ๋๋ค.
๊ทธ๋ ๊ธฐ์ Context๋ Redux๋ Recoil ๊ฐ์ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ฌ์ฉ์ ๋ฐฐ์ ํ์ง ์๊ณ ,
์ค์ ๋ก๋ ์ ์ญ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ธฐ ์ํด์๋ ์ฌํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค.
๐ ์ํ ๊ด๋ฆฌ ๋๊ตฌ์ ์กฐ๊ฑด
์ํ๋ฅผ ๊ด๋ฆฌํ๋ ๋๊ตฌ๊ฐ ๋๋ ค๋ฉด ๋ค์๊ณผ ๊ฐ์ ์กฐ๊ฑด์ด ํ์ํฉ๋๋ค.
- ๊ฐ ์ ์ฅ
- ๊ฐ ์ฝ๊ธฐ
- ๊ฐ ๋ณ๊ฒฝ
Context๋ ์์ฒด์ ์ผ๋ก ์ด๋๊ฐ์ ๊ฐ์ ์ ์ฅํ์ง๋ ์๊ณ useState
์ setState
์ฒ๋ผ ๊ฐ์ ๋ณ๊ฒฝํ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ์ง๋ ์์ต๋๋ค.
๊ทธ์ Provider์ ๋ฃ์ด์ค value๋ฅผ ํ์ Children ์๋ฌด๊ณณ์์๋useContext
๋ฅผ ์ฌ์ฉํด ๊บผ๋ด์ฐ๊ธฐ๋ง ํ๋ ๊ฒ๋๋ค.
์ฆ, Context๋ ์๋ฌด๊ฒ๋ ๊ด๋ฆฌํ์ง ์๊ณ ์ค๊ฐ๋ค๋ฆฌ์ ์ญํ ๋ง ํ๋ค๋ ๊ฒ์ ์๋ฏธํฉ๋๋ค.
๊ทธ๋ ๊ธฐ์ ํน์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๊ธฐ๋ฅ์ ์ธํฐํ์ด์ค๋ก ์ถ์ํํ ํ
์ธํฐํ์ด์ค๋ฅผ ํ์ฅํ ๊ฐ์ฒด์ ์ธ์คํด์ค๋ฅผ Context์ ์ฃผ์
ํ๊ณ ,
์ด๋ฅผ ์ฌ์ฉํ ๋๋ Context๋ฅผ ์ฌ์ฉํ๋ฉฐ ๊ทธ ๊ธฐ๋ฅ์ ์ง์ ์ ์ผ๋ก ์ฌ์ฉํ๋ ๊ฒ์ด ์๋
์ถ์ํํ ์ธํฐํ์ด์ค๋ฅผ ๊ฐ๋ฅดํค๊ฒ ํ์ฌ ์ฝ๊ฒ ๊ต์ฒด ๊ฐ๋ฅํ๊ฒํจ์ผ๋ก์จ
์์กด์ฑ ์ฃผ์
์ ๊ฐ๋
์ ๊ฐ์ก๋ค๊ณ ํ ์ ์์ต๋๋ค.
๐ Context์ ์์กด์ฑ ์ฃผ์
Context๋ฅผ ๊ณ์ธต ์ค๊ฐ์ ๋ฐฐ์นํ๋ค๋ฉด ๊ณ์ธต๊ฐ์ ๊ฒฐํฉ์ ๋์ด๋ผ ์ ์์ต๋๋ค.
์์ ๋ก ํ์ธํด ๋ณด๊ฒ ์ต๋๋ค.
const getAxiosData = useCallback(async () => {
const data = await axios.get<any>(
"https://jsonplaceholder.typicode.com/posts"
);
return data;
}, []);
๊ธฐ์กด์๋ ์๋ฒ์์ ํต์ ์ ํ ๋ ํน์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ง์ ํธ์ถํ๋ ๋ฐฉ์์ผ๋ก ์ฌ์ฉํ๊ณค ํ์ต๋๋ค.
์ด๋ฐ ๊ฒฝ์ฐ์๋ ์๋ฒ์์ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์์ค๋ ๋ชจ๋ ์ฝ๋์๋ axios๋ฅผ ์ง์ ์ ์ผ๋ก ํธ์ถํ ๊ฒ์ด๊ณ ,
๋ง์ฝ axios์ ๋ฌธ์ ๊ฐ ์์๋ค๊ฑฐ๋, ํน์ ๋ค๋ฅธ ๊ธฐ์ ์ ๋ฉ๋ฆฌํธ๊ฐ ์ปค๋ณด์ฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๊ต์ฒดํ๋ ค๊ณ ํ๋ค๋ฉด
๊ธฐ์กด์ ์์ฑํ๋ ๋ชจ๋ ์ฝ๋๋ฅผ ์์ ํด์ผ ํ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํฉ๋๋ค.
์ด๋ฌํ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ํน์ ๊ธฐ๋ฅ ์์ฒด๋ฅผ context์ ์ฃผ์ ํ์ฌ ์ฌ์ฉํด๋ณด๊ฒ ์ต๋๋ค.
๐ ํ์ ์ ์
export interface FetchInterface {
get<T>(url: string, params?: any | any[]): Promise<T>;
post<T>(url: string, params?: any | any[]): Promise<T>;
}
๊ธฐ๋ณธ์ ์ผ๋ก rest api๋ก ์๋ฒ์ ํต์ ํ ๋์๋ ์ฃผ๋ก get, post, patch, update, delete๊ฐ ์ฌ์ฉ๋ฉ๋๋ค.
ํธ์๋ฅผ ์ํด get๊ณผ post์ ๊ธฐ๋ฅ๋ง ์ถ์ํํด ํ์ ์ผ๋ก ์ ์ํ์ต๋๋ค.
๐ ๊ฐ์ฒด ์์ฑ
export class Axios implements FetchInterface {
async get<T>(url: string, params?: any | any[]): Promise<T> {
const { data } = await axios.get<T>(url, { params });
return data;
}
async post<T>(url: string, params?: any | any[]): Promise<T> {
const { data } = await axios.post(url, { params });
return data;
}
}
export class Fetch implements FetchInterface {
async get<T>(url: string, params?: any | any[]): Promise<T> {
const data = await fetch(url, {
method: "GET",
body: JSON.stringify(params)
}).then((response) => response.json());
return data;
}
async post<T>(url: string, params?: any | any[]): Promise<T> {
const data = await fetch(url, {
method: "POST",
body: JSON.stringify(params)
}).then((response) => response.json());
return data;
}
}
์์์ ์ ์ํ ํ์ ์ ํด๋์ค์์ ํ์ฅํด ํ์ํ ๊ธฐ๋ฅ์ ๊ธฐ์ ํ ํ์๋๋ก ๊ฐ์ ํ ์ ์์ต๋๋ค.
๐ Context ์ ์
// context ๊ฐ์ฒด ์์ฑ
const FetchContext = React.createContext<FetchInterface>(new Fetch());
// Provider์ ํด๋์ค ์ธ์คํด์ค๋ฅผ value๋ก ๋ฃ์ด์ค๋ค.
// ์ถํ fetching ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ณ๊ฒฝ ์ ์ธํฐํ์ด์ค ์ ์๋๋ก ํด๋์ค๋ฅผ ์์ฑ ํ,
// value์ ์ธ์คํด์ค๋ง ๊ฐ์ ๋ผ์์ฃผ๋ฉด ๋๋ค.
export const FetchContextProvider = ({
children
}: {
children: JSX.Element;
}) => {
return (
<FetchContext.Provider value={new Axios()}>
{children}
</FetchContext.Provider>
);
};
// useContext๋ฅผ custom hook ํํ๋ก ํ๋ฒ ๋ wrapping ํด์
// ์ปดํฌ๋ํธ์์ ์ด ํ
๋ง ํธ์ถํ์ฌ ์ฌ์ฉํ ์ ์๋ค.
export const useFetchContext = () => React.useContext(FetchContext);
// Provider์ value ๊ต์ฒด๋ก ๊ธฐ์ ์์ฒด๋ฅผ ๋ณ๊ฒฝ
return (
<FetchContext.Provider value={new Fetch()}>
{children}
</FetchContext.Provider>
)
์ด์ context๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ์ค๋นํด์ค๋๋ค.
์ฌ๊ธฐ์ ์ค์ํ ๋ถ๋ถ์ Provider์ value์ ์์์ ์์ฑํ ๊ฐ์ฒด์ ์ธ์คํด์ค๋ง ๋ฃ์ด์ค๋ค๋ฉด
๋ค๋ฅธ ๋ถ๋ถ์ ์์ ์์ด ๊ธฐ์ ์ ๊ต์ฒด๊ฐ ์ด๋ค์ง๋ค๋ ๋ถ๋ถ์
๋๋ค.
๐ Context ํธ์ถ
export default function App() {
const [state, setState] = useState<any>();
const { get } = useFetchContext();
const getServerData = useCallback(async () => {
const data = await get<any>("https://jsonplaceholder.typicode.com/posts");
return data;
}, [get]);
useEffect(() => {
getServerData().then((response) => setState(response));
}, [getServerData]);
return (
<FetchContextProvider>
<div>{state && JSON.stringify(state)}</div>
</FetchContextProvider>
);
}
์ฌ์ฉํ๋ ๊ณณ์์๋ ํน์ ๊ธฐ์ ์ ์ง์ ํธ์ถํ์ง ์๊ณ context๋ก ์ ์ํ ํจ์๋ฅผ ์ด์ฉํ๊ธฐ ๋๋ฌธ์
๋ก์ง๊ณผ ํน์ ๊ธฐ์ ์ ๋ถ๋ฆฌ๊ฐ ๊ฐ๋ฅํด์ง๋๋ค.
์ด๋ ๊ฒ ํน์ ๊ธฐ๋ฅ์ด๋ ๊ธฐ์ ์ ์ ํด์ง ํ์๋๋ก ์ ์ํด ๋์ผ๋ฉด
์ด๋ฅผ ์ฌ์ฉํ๋ ์ปดํฌ๋ํธ๋ ๋ถํ์ํ ๋ณ๊ฒฝ์ ์์ ๋ก์์ง ๋ฟ๋ง ์๋๋ผ
ํ
์คํธํ ๋๋ ์์์์ mocking Provider๋ง ์ ๊ณตํ๊ธฐ๋ง ํ๋ฉด ๋๋ค๋ ์ฅ์ ๋ํ ์กด์ฌํฉ๋๋ค.
์ ์ฒด ์์ ์ฝ๋๋ ์๋์์ ํ์ธํด์ฃผ์ธ์.
๐ ๋ง์น๋ฉฐ
์๊ฐ๋ณด๋ค ์ดํดํ๊ธฐ ์ฝ์ง ์์ ๋ด์ฉ์ด์ง๋ง ๋ชจ๋ฌ ๊ฐ์ ๊ธฐ๋ฅ์ด๋ Data Fetching ๊ฐ์ ๊ธฐ์ ๋ค์
context๋ฅผ ํตํด ์ฌ์ฉํ๋ค๋ฉด ์ถํ ๊ธฐ๋ฐ ๊ธฐ์ ์ ๋ณ๊ฒฝํ ์ผ์ด ์์ด๋ ์ฌ์ฌ์ฉ ํ๊ธฐ ์ฌ์์ง๊ฒ ๋ค๊ณ ์๊ฐํ์ต๋๋ค.
์์ง๊น์ง ํ๋ก์ ํธ์ ์ ์ฉํ๊ธฐ์ ์ดํด๋๊ฐ ๋ถ์กฑํ๋ค๊ณ ๋๋ผ์ง๋ง ์ธ์ ๊ฐ ๊ผญ ์ ์ฉํด๋ณด๋ ค ํฉ๋๋ค.
'React' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[React+TS] styled-components, ThemeProvider ํ์ ์ ์ํ๊ธฐ (0) | 2023.05.28 |
---|---|
ํ๋ก ํธ์๋, ํจ์ํ ํ๋ก๊ทธ๋๋ฐ(FP)์ด ์ ํธ ๋๋ ์ด์ (0) | 2023.05.16 |
[React] ๋ฆฌ์กํธ์ Side Effect (0) | 2023.04.18 |
[React] react-query, ๋ฆฌ์กํธ ์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํด์ผ ํ๋ ์ด์ (2) | 2023.04.16 |
[React] Suspense์ ์ฌ์ฉํด ์ ์ธ์ ์ผ๋ก ๋ก๋ฉ ํ๋ฉด ๊ตฌํํ๊ธฐ (4) | 2023.03.23 |
๋๊ธ