ํ์ฌ ํ๋ก์ ํธ์ ๊ฒฝ์ฐ ๊ถํ ๊ด๋ฆฌ๋ฅผ recoil ๋ก ๋ค๋ฃจ๊ณ ์๋ค.
โ
Rocoil ์ด๋?
ํ์ด์ค๋ถ์์ ๋ฐํํ React ์ ์ญ ์ํ ๊ด๋ฆฌ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ด๋ค.
usestate ๋ง์ ์ฌ์ฉํ์ฌ ์ํ ๊ด๋ฆฌ๋ฅผ ํ๊ฒ ๋๋ฉด ํ๋ก๊ทธ๋จ์ด ๋ณต์กํ๊ฒ ๋ ์,
props drilling์ด ์ผ์ด๋๊ฒ ๋๋๋ฐ, Recoil์ ์ด๋ฌํ ํ์๋ฅผ ๋ฐฉ์งํ๊ฒ ํด์ค๋ค.
โ
Recoil์ ๊ฒฝ์ฐ. React ์์ ์ฌ์ฉํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์๋, ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ์๋์ ๋งํฌ์์ ๋ด์ฉ์ ํ์ธํ๋ฉด ๋๋ค.
Recoil ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ฃผ์ํ ๊ฐ๋ ์ผ๋ก๋ Atom ๊ณผ selector๊ฐ ์๋ค.
โ
Recoil ์์ ํ๋์ ์ ์ญ ์ํ๋ฅผ Atom ์ด๋ผ๊ณ ํ๋๋ฐ, Atom ์ ์๋ ์ํ ๊ฐ๋ค์ ๋ชจ๋ ์ปดํฌ๋ํธ์์ ์ ๊ทผ์ด ๊ฐ๋ฅํ๋ค. ๋ฐฑ๊ณผ์ฌ์ ๊ฐ์ ๋๋.
์์ ๊ทธ๋ฆผ์ ๋ณด๋ฉด ์ ์ ์๋ฏ Recoil์ ๋จ๋ฐฉํฅ ๋ฐ์ดํฐ ํ๋ฆ์ ๊ฐ์ง๋ค.
โ
Atom ์ ๋ํ์ฌ ์ ๋ฆฌํด ๋ณด๋ฉด
1. Atom์ ์ํ ๋จ์์ด๋ฉฐ, ์ ๋ฐ์ดํธ์ ์ฐธ์กฐ๊ฐ ๊ฐ๋ฅํจ.
2. Atom์ด ์ ๋ฐ์ดํธ ๋๋ฉด ๊ฐ๊ฐ์ ์ปดํฌ๋ํธ๋ค์ ์๋ก์ด ๊ฐ์ ๋ฐ์ํ์ฌ ๋ฆฌ๋ ๋๋ง ๋จ.
3. ์ปดํฌ๋ํธ ๋ด๋ถ ์ํ ๊ฐ ๋์ ์ฌ์ฉ๋ ์ ์์ผ๋ฉฐ, ๋์ผํ atom์ด ์ฌ๋ฌ ์ปดํฌ๋ํธ์์ ์ฌ์ฉ๋๋ ๊ฒฝ์ฐ ๊ทธ ์ปดํฌ๋ํธ๋ค์ ์ํ๋ฅผ ๊ณต์ ํ๋ค.
Atom ์์ฑ ๋ฐฉ๋ฒ
const fontSizeState = atom({
key: 'fontSizeState', // ๊ณ ์ ํ ํค ๊ฐ
default: 14
});
atom์ ํค ๊ฐ์ ์ ์ญ์ ์ผ๋ก ๊ณ ์ ํด์ผ ํ๋ค.
์ฌ๊ธฐ์ ๋ฑ๋ก๋ ์ ์ญ ์ํ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด์๋ useRecoilState(ํค๊ฐ) ์ผ๋ก ๋ถ๋ฌ์์ฃผ๋ฉด ๋๋ค.
useState ์ฒ๋ผ ์ฌ์ฉํ๋ฉด ๋์ง๋ง, ๋ค๋ฅธ ์ ์ state ๋ ๋ด๋ถ ์ํ, Recoil์ ์ ์ญ ์ํ๋ผ๋ ๊ฒ.
function FontButton() {
const [fontSize, setFontSize] = useRecoilState(fontSizeState);
return (
<button onClick={() => setFontSize((size) => size + 1)}
style={{fontSize}}>
Click to Enlarge
</button>
);
}
ํด๋น ๋ฒํผ์ ๋๋ฅด๊ฒ ๋๋ฉด, ํฐํธ ์ฌ์ด์ฆ๊ฐ 1์ฉ ์ฆ๊ฐํ๋ฉฐ, ๋ชจ๋ ํ๋ฉด์์ ์ฆ๊ฐ๋ ํฐํธ ์ฌ์ด์ฆ๊ฐ ์ ์ฉ๋๋ค.
โ
selector ์ด๋?
์ ์ญ ์ํ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ด๋ค ๊ณ์ฐ์ ํตํด ์๋ก์ด ๊ฐ์ ๋ด๋ฑ๋ ์์ํจ์.
atom์ด๋ ๋ ๋ค๋ฅธ selector๋ฅผ ํตํ์ฌ ์ ๋ ฅ์ ๋ฐ์ ์ ์๊ฒ ๋๋ค.
โ
๊ธฐ๋ณธ์ ์ด๊ณ ์ต์ํ์ ์ํ๊ฐ๋ค์ atom์ ์ ์ฅํด๋๊ณ , seletor์ ๋ช ์ํ ํจ์๋ฅผ ํตํ์ฌ ์ํ ๊ฐ์ ๋ณ๊ฒฝํ๋ฉด ๋๋ค.
selector ์ฌ์ฉ๋ฐฉ๋ฒ
const fontSizeLabelState = selector({
key: 'fontSizeLabelState', // ๊ณ ์ ํ ํค ๊ฐ
get: ({get}) => {
const fontSize = get(fontSizeState);
const unit = 'px';
return `${fontSize}${unit}`;
},
});
get ์๋ ์ํ๋ฅผ ๊ณ์ฐํ ํจ์๊ฐ ๋ด๊ฒจ์ ธ ์์ผ๋ฉฐ, get์ผ๋ก ์ ๋ฌ๋๋ ๋งค๊ฐ๋ณ์๋ฅผ ํตํ์ฌ Atom์ด๋ ๋ค๋ฅธ Selector์ ์ ๊ทผํ ์ ์์. ์์ ๊ฐ์ด ์ ์ธํด์ฃผ์์ผ๋ฉด ์๋์ฒ๋ผ ์ฌ์ฉํ๋ฉด ๋๋ค.
function FontButton() {
const [fontSize, setFontSize] = useRecoilState(fontSizeState);
const fontSizeLabel = useRecoilValue(fontSizeLabelState);
return (
<>
<div>Current font size: ${fontSizeLabel}</div>
<button onClick={setFontSize(fontSize + 1)}
style={{fontSize}}>
Click to Enlarge
</button>
</>
);
}
seletor์ ๊ฒฝ์ฐ๋ useRecoilValue(ํค๊ฐ) ์ ํํ๋ก ์ ์ธํ์ฌ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ,
๋ฒํผ ํด๋ฆญ ์ ํฐํธ ์ฌ์ด์ฆ ์ฆ๊ฐ์ ๋์์ fontSizeLabel ์ ๋ ํฐ์๋ ์ ๋ฐ์ดํธ ๋์ด ์ปดํฌ๋ํธ์ ๋ฐ์๋๋ค.
๊ถํ ๊ด๋ฆฌ ?
โ
๊ถํ ๊ด๋ฆฌ๋ฅผ ๊ตฌํํ๋ ๋ฐฉ๋ฒ์๋ ์ฌ๋ฌ๊ฐ์ง๊ฐ ์๊ฒ ์ง๋ง... Recoil ๋ก ์ ์ธํ๋ค๋ฉด
์ ์ญ ์ํ ๊ด๋ฆฌ์ธ ์ ์ ์ฌ์ฉํ์ฌ login ์ฌ๋ถ๋ฅผ Recoil์ ์ ์ธํด์ฃผ๊ณ ,
login ์๋ฃ ๋๋ฉด login์ฌ๋ถ๋ฅผ true๋ก ๋ฐ๊ฟ์ค๋ค.
๊ทธ๋ฆฌ๊ณ login ์ฌ๋ถ์ ๋ฐ๋ผ app.js ์์ ํด๋น ์ฃผ์์ง์ ์ ์ ๊ฐ๋ฅํ๊ฒ ํ ๊ฑด์ง ๋ง๊ฑด์ง๋ฅผ ๋ถ๊ธฐํ๋ ํ์์ผ๋ก ๊ตฌํํ๋ค.
// useState.ts
// ์ด๊ฑธ ์ ํ์ผ๋ช
์ state ๋ผ๊ณ ํ์ง๋ ๋ชจ๋ฅด๊ฒ ์..
import { selector } from 'recoil';
// import { ACCESSTOKEN_KEY } from '../common/constants/user';
export const getUserIsLogin = selector({
key: 'userLoginState',
get: () => {
// const accessToken = localStorage.getItem("ACCESSTOKEN_KEY");
// return !!accessToken;
return true;
},
});
์ด๋ฐ์์ผ๋ก ์ ์ธํด๋๊ณ isLogin ๊ฐ์ ํ๋ณํ์ฌ
app.js ์์ ํด๋น ๋ฃจํธ์ ์ ์ ๊ฐ๋ฅํ๊ฒ ํ ๊ฑด์ง ๋ถ๊ธฐ๋ง ํด์ฃผ๋ฉด ๋๋ค.
( ์ฌ๊ธฐ์ ๋ณดํต Private, Punblic ๋ถ๋ฆฌํ์ฌ.. ๋ก๊ทธ์ธ์ด ์๋์ด ์๋๋ฐ ํ์๋ง ์ฌ์ฉํ ์ ์๋ ํ๋ฉด์ ์ ๊ทผํ๋ ค๊ณ ํ๋ฉด login ํ๋ฉด์ผ๋ก ๊ฐ์ sending ์ํจ๋ค๋์ง์ ๊ธฐ๋ฅ์ ์ถ๊ฐํ๊ธฐ๋ ํ๊ณ , ๋ ๋์๊ฐ ๊ด๋ฆฌ์ ๊ณ์ ์ ํ๋ฉด ์ ๊ทผ ์ฌ๋ถ ๋ํ ๋ถ๋ฆฌ๊ฐ ๊ฐ๋ฅํ๋ค.)
// PrivateRouter.js
import { useEffect } from 'react';
import { Navigate, Outlet } from 'react-router-dom';
import { URL } from '../common/constants/url';
// import useSnackBar from '@/hooks/common/useSnackBar';
const PrivateRouter = ({ isAuthenticated }) => {
// const { showSnackBar } = useSnackBar();
useEffect(() => {
if (!isAuthenticated) {
// showSnackBar('๋ก๊ทธ์ธ ํ ์ฌ์ฉํด์ฃผ์ธ์');
console.log('๋ก๊ทธ์ธ ํ ์ฌ์ฉํด์ฃผ์ธ์')
}
}, [isAuthenticated]);
return isAuthenticated ? <Outlet /> : <Navigate to={URL.LOGIN} replace />;
};
export default PrivateRouter;
// PrivateRouter.js
import { useEffect } from 'react';
import { Navigate, Outlet } from 'react-router-dom';
// import useSnackBar from '@/hooks/common/useSnackBar';
const PublicRouter = ({ isAuthenticated }) => {
// const { showSnackBar } = useSnackBar();
useEffect(() => {
if (isAuthenticated) {
// showSnackBar('๋ก๊ทธ์ธ ์ํ์์ ์ด์ฉํ ์ ์๋ ์๋น์ค์
๋๋ค');
console.log("๋ก๊ทธ์ธ ์ํ์์ ์ด์ฉํ ์ ์๋ ์๋น์ค์
๋๋ค")
}
}, []);
return isAuthenticated ? <Navigate to="/" replace /> : <Outlet />;
};
export default PublicRouter;
์ง์ง ๊ถํ ๊ด๋ฆฌ ๊ฑฐ์ ์๋ง๋ค์ด๋จ๋ค...;
// app.js
import React, { Suspense } from "react";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import { useRecoilState, useRecoilValue } from "recoil";
import { getUserIsLogin } from "./store/userState";
import PublicRouter from "./router/PublicRouter";
import PrivateRouter from "./router/PrivateRouter";
const Contact = React.lazy(() => import("./components/pages/Contact"));
return (
<Suspense fallback={<Loading />}>
{/* <BrowserRouter > */}
<Routes>
<Route path="/" element={<MainLayout />}>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route element={<PrivateRouter isAuthenticated={isLogin} />}>
<Route path="/contact" element={<Contact />} />
</Route>
<Route element={<PublicRouter isAuthenticated={isLogin} />}>
<Route path={URL.LOGIN} element={<Login />} />
</Route>
</Route>
</Routes>
</Suspense>
);
};
๋์ถฉ ์ด๋ฐ์์ผ๋ก ๊ตฌ์กฐ๋ง ํ์ธํ๋ฉด ๋๊ฒ ๋ค.
๋์ค์ ๊ถํ๊ด๋ฆฌ ๋ง๋ค์ด์ง ๊ฒ ์ฝ๋ ๊ฐ์ ธ์๋ณด๋ ๊ฑธ๋ก...
โ๊ณต์ ๋ฌธ์๋ฅผ ์ฝ๊ฑฐ๋ ๊ฐ๋ฐํ๋ฉฐ ๋๊ผ๋ ๋ํผ์
๋ก ์์ฑํ๊ณ ์์ผ๋,
๊ถ๊ธํ์ ์ ์ด๋ ์๋ชป๋ ์ ์ด ์๋ค๋ฉด ๋๊ธ ๋จ๊ฒจ์ฃผ์๋ฉด ๊ฐ์ฌํ๊ฒ ์ต๋๋ค.