์ง๋ 8์, ์ฌ๋ด ์คํฐ๋๋ก ํ ์ด ํ๋ก์ ํธ๋ฅผ ์งํํด๋ณด์๋ค. ์ด๋ค ํ๋ก์ ํธ๋ฅผ ํ๋์ง๋ ์ฌ๋ด์์ ํ ํ ์ด ํ๋ก์ ํธ๋ผ ์ธ๋ถ์ ์ผ๋ก ๊ณต๊ฐํ๊ธฐ๊ฐ ์กฐ์ฌ์ค๋ฝ๋ค. (์กฐ๋ง๊ฐ ์ด๋ ฅ์ ๋
ธ์
์๋ค๊ฐ ์ ๋ฆฌํด์ผ์ง.. ํฌํฌ)
๐ ์ด ๊ธ์ ์ด๋ฒ ์ฌ๋ด ์คํฐ๋๋ก ์งํํ ํ๋ก์ ํธ์์ ์ฌ์ฉ ํด๋ดค๋ ๊ธฐ์ ์คํ์ ๋ํ ์ ๋ฆฌ๋ฅผ ํด๋ณด๋ ๊ธ์ด๋ค.
FE ๊ฐ๋ฐ์๋ ๋ ํผ์.
๋ฐฑ์๋ ๊ฐ๋ฐ์๋ 2๋ช
. ํ ๋ถ์ api ์์
์ ๋งก์ผ์
จ๊ณ ํ ๋ถ์ ๋ฐ์ดํฐ๋ฅผ ์์งํ๋ batch ์ญํ ์ ๋งก์ผ์
จ๋ค.
์ด๋ฒ ์คํฐ๋์ ๋ชฉํ๋ ๋ฐฑ์๋ ๋ถ๊ณผ ์ํํ ์ํต์ ํ๋ฉด์ ๋ฐฑ์๋์ ์ง์๋ค์ ์กฐ๊ธ์ด๋๋ง ์ญ๋ ตํ๋ ๊ฒ์ด์๋ค. ๋๋ถ์ด ์๋ก์ด ๊ธฐ์ ์คํ(Recoil)์ ๋์ ํด๋ณด๋ฉด์ ์ด๋ค ๋งค๋ ฅ์ด ์๊ธธ๋ ํซํ ๊ธฐ์ ๋ค์ด ๋ง์ด๋ค ์ฌ์ฉํ๋ ๊ฒ์ธ์ง ๊ถ๊ตผํด์ ๊ผญ ์จ๋ณด๊ณ ์ถ์๋ค. ๊ทธ๋ฆฌ๊ณ ๋ฐฑ์๋ ๋ถ๋ค๊ณผ ๊ฐ์ด ์์ ํ๋ฉด์ batch๊ฐ ๋ฌด์์ธ์ง์ ๋ํ ๊ฐ๋ ๋ ์๊ฒ ๋์์ผ๋ฉฐ gateway๋ผ๋ ๊ฐ๋ ๋ ์๊ฒ ๋๋ค. ๋ํ ์ด๋ฒ ์คํฐ๋๋ฅผ ํตํด Poling (ํด๋ง) ์์ ๋ ํด๋ดค๋ค. ๊ฒฐ๋ก ์ ์ผ๋ก ์ ๋ง ์ฌ๋ฐ์๊ณ ์๋กญ๊ฒ ์์๊ฐ๋ ๊ฒ๋ค์ด ๋ง์์ ๊ฐ์ง ์๊ฐ์ด์๋ค.
์๋ฌด๋๋ FE ๊ฐ๋ฐ์๊ฐ ๋ ํผ์์ด๋ค๋ณด๋ ์ ํ๊ถ์ด ์์ ๋ก์ ๋ค. ๊ทธ๋์ ์์ฆ ์ฌ๋ฌ ๊ธฐ์
์์ ์์ฃผ ์ฌ์ฉ๋๋ Recoil
์ด๋ผ๋ ์ํ๊ด๋ฆฌ๋ฅผ ๋์
ํ์ฌ ์ฌ์ฉํด๋ดค๋ค. ์ฒ์ ์ฌ์ฉํด๋ณธ ์ํ๊ด๋ฆฌ์ธ๋ฐ '์ด๊ฒ ๋ง๋?' ๊ฐธ์ฐ๋ฑ ํ๋ฉด์ ์์
ํ๋ค๋ณด๋ ์ด๋์ ํ๋ก์ ํธ๋ ์์ฑ๋ ์๊ฒ ๋ง๋ฌด๋ฆฌ ์ง์ ์ ์์๋ค.
$yarn add recoil or npm install recoil
๋ฆฌ์ฝ์ผ์ ๊ทธ๋ฅ yarn add recoil
ํ๋๋ง ์ค์นํด์ฃผ๋ฉด ๋์ด๋ค.
Redux๋ Mobx์ฒ๋ผ ๋ ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ๋ถ๊ฐ์ ์ผ๋ก ์ค์นํ ํ์๊ฐ ์๋ค.
Redux๋ ๋น๋๊ธฐ ํต์ ์ ์ํ redux-thunk
๋ผ๋๊ฐ redux-saga
๋ผ๋๊ฐ ๋ถ๋ณ์ฑ ์ ์ง๋ฅผ ์ํ immer
๋ผ๋๊ฐ.. ์ด๋ฐ ์ค์นํ ๊ฒ ๋ง์์ ์ซ์๋๋ฐ.. ๋ MOBX๋ก ์น๋ฉด mobx
์ธ์ mobx-react
, mobx-react-lite
์ด๋ฐ ๊ฒ๋ค์ ์ค์นํด ์ค์ผํ๋๋ฐ recoil์ ์ง์ง ์ธ์ ์ฌํํ๋ค. โญ๏ธ
์ด๊ธฐ ์ธํ ํ๋ ๊ฒ๋ง์ ์ง์ง ์ด๊ฐ๋จํ๋ค.
- src/index.tsx
import React, { lazy, Suspense } from 'react';
import ReactDOM from 'react-dom/client';
import { Spin } from 'antd';
import { RecoilRoot } from 'recoil';
import 'antd/dist/antd.min.css';
import './index.scss';
const App = lazy(() => import('./App'));
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
// ๐ <RecoilRoot> ๋ก ๊ฐ์ธ์ !
<RecoilRoot>
<Suspense
fallback={
<main className="loading__spinner">
<Spin size="large" />
</main>
}
>
<App />
</Suspense>
</RecoilRoot>
);
์ด์ฒ๋ผ RecoilRoot
๋ก ๊ฐ์ธ๋ฉด ๋๋ค. ๊ทธ๋ผ ๋์.
Recoil, ๋ ์ฅ์ ์ด๋ผ๋ฉด atom์ด๋ selector์ ๊ฐ๋
๋ง ์๋ฉด ๋์ด๋ค ๐
๋จผ์ src/recoil
๋๋ ํ ๋ฆฌ๋ฅผ ์์ฑํ์ฌ src/recoil/atom
๊ณผ src/recoil/selector
๋ฅผ ๊ฐ๊ฐ ๋ถ๋ฆฌํ์ฌ ์ํ๊ด๋ฆฌ๋ฅผ ํ๋ค. mobx๋ Recoil์ ๋๋ ์์ ๋ก์ฐ๋ ์ค๋ฌด์์ ์ฌ์ฉํ ๋ ํจ๊ป ํ์
ํ๋ ์ฌ๋๋ค๊ณผ ์ปจ๋ฒค์
์กฐ์จํ๋ ๊ฒ์ด ํ์ ์ผ ๋ฏํ๋ค.
์ฝ๊ฒ ๋งํด ์ปดํฌ๋ํธ๋ฅผ ๊ตฌ๋ ํ์ฌ ์ํ์ ๋ณํ๋ฅผ ๋ํ๋ด์ค๋ค. ๋์ค์ ์ผ๋ก ๋ง์ด ์ ํด๋ดค์ Redux ๊ฐ๋ ์ผ๋ก ์น๋ฉด store์ ์ญํ ์ ํ๋ค.
(1) atom ์ ์ธ ์ **key๊ฐ์ ์ ๋ํฌํ ๋ช
๋ช
์ ์
๋ ฅํ๊ณ default์ ์ํ์ ์ด๊ธฐ๊ฐ์ ์
๋ ฅํ๋ค.
(2) atom์ด ์
๋ฐ์ดํธ ๋๋ฉด ํด๋น atom์ ๊ตฌ๋
ํ๊ณ ์๋ ๋ชจ๋ ์ปดํฌ๋ํธ์ ์ํ๊ฐ ๋ฆฌ๋ ๋ ๋์ด ๋ณด์ฌ์ง๋ค.
src/recoil/atom/index.ts
import { atom } from 'recoil';
// ํ์
import { TotalTypes } from 'lib/type';
import { todayCall } from 'lib/day';
// โญ๏ธ ์ด๊ฐ์ด ์์ฃผ ์ฌ์ฉ๋๋ ๊ฒ๋ค์ ๋ชจ๋ํํ์ฌ ์ฝ๋๋ฅผ ๋จ์ถ ์์ผฐ๋น
// โญ๏ธ key์ ๊ณ ์ ํ ๊ฐ ์ค์
// โญ๏ธ default์๋ ์ด๊ธฐ๊ฐ ์ ์ธ
export const daySearch = atom({
key: 'daySearchShow',
default: todayCall || ''
});
export const errorState = atom<any>({
key: 'error',
default: ''
});
atom์ผ๋ก ์ํ๋ฅผ ๊ด๋ฆฌํ ๋๋ atom
๊ทธ๋ฆฌ๊ณ atomFamily
๊ฐ ์๋๋ฐ, atomFamily๋ ๋งค๊ฐ๋ณ์ํ ํ ์ ์๋ค.
const myAtomFamily = atomFamily({
key: โMyAtomโ,
๐default: param => defaultBasedOnParam(param),
});
import React from 'react';
import './styles.scss';
import { useRecoilValue } from 'recoil';
import { daySearch } from 'recoil/atom';
import { todayCall } from 'lib/day';
const Header = () => {
// โญ๏ธ useRecoilValue ๋ฅผ ์ฌ์ฉํด์ ์ปดํฌ๋ํธ์ ์ํ ๊ตฌ๋
์์ผฐ๋ค.
const currentSearch = useRecoilValue(daySearch);
return (
<header className="board1__header">
<h1>{currentSearch === todayCall ? '(์ค์๊ฐ)' : `(${currentSearch})`}</span>
</h1>
</header>
);
};
export default Header;
atom ์์๋ ์ฌ๋ฌ ๋ฉ์๋๋ค์ ์ ๊ณตํด์ฃผ๋๋ฐ, ์ํฉ์ ๋ฐ๋ผ ์
๋ง์ ๋ง๋ ๋ฉ์๋๋ค์ ๊ณจ๋ผ ์ฐ๋ฉด ๋๋ค.
์ ์ฝ๋์์๋ useRecoilValue
๋ฅผ ์ฌ์ฉํ๋ค.
์ฝ์ ์๋ง
์๊ฒ ํ๊ณ ์ถ์ ๋ ์ฌ์ฉํ๋ค. (์ํ ์ฝ๊ธฐ๋ง ๊ฐ๋ฅ) const currentSearch = useRecoilValue(daySearch);
const [tempF, setTempF] = useRecoilState(tempFahrenheit);
const addTenCelsius = () => setTempC(tempC + 10);
return (
<div>
Temp (Celsius): {tempC}
<br />
Temp (Fahrenheit): {tempF}
<br />
<button onClick={addTenCelsius}>Add 10 Celsius</button>
<br />
<button onClick={addTenFahrenheit}>Add 10 Fahrenheit</button>
</div>
);
useSetRecoilState : setter ํจ์๋ฅผ ๋ฆฌํดํ๋ค. atom ํน์ selector๊ฐ ์ ๋ฐ์ดํธ ๋๋ฉด ๋ฆฌ๋ ๋๋ง ํด์ค๋ค. (์ฐ๊ธฐ ๊ฐ๋ฅ)
useResetRecoilState(): default ๊ฐ์ผ๋ก ๋ณ๊ฒฝ ์์ผ์ค๋ค.
export const numberState = atom<any>({
key: 'number',
default: '100'
});
์ด๋ ๊ฒ default: '100'
์ด ๊ฐ์ผ๋ก ๋ณ๊ฒฝํด์ค๋ค๋ ๋ป.
์ฃผ์ด์ง ์ํ์ ๋ณํ๋ฅผ ์ค๋ค.
๊ทธ๋ฆฌ๊ณ selector์์ api ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ ๊ฐ๋ฅํ๋ค.
export const getGlobalTotalApi = selectorFamily({
key: 'get/GlobalTotal',
get: (day: any) => async () => {
if (!day) {
return false;
}
const res = await fetcher(METHOD.GET, `/global/total?toDate=${day}`);
return res.data;
}
});
๐ ์ ํด๋น ์ฝ๋์ fetcher ๋ชจ๋์ ์ฝ๋ ๋ด์ฉ๋ฌผ์ ์๋์ ๊ฐ๋ค.
import React from 'react';
import axios from 'axios';
import { METHOD } from './type';
const apis = axios.create({
baseURL: `${process.env.REACT_APP_INDEX_SERVER_URL}` // ๊ธฐ๋ณธ ์๋ฒ ์ฃผ์ ์
๋ ฅ
});
const fetcher = async (method: METHOD, url: string) => {
try {
const res = await apis[method](url);
return res.data;
} catch (error: any) {
alert('๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค.')
location.reload();
return error;
}
};
export default fetcher;
import { daySearch } from '../atom';
export const dayValue = selector({
key: 'dayValueText',
get: ({ get }) => {
const value = get(daySearch);
if (value === todayCall) {
return null;
}
return moment(value, 'YYYY-MM-DD');
}
});
export const changeDate = selector({
key: 'get/changeDate',
get: ({ get }) => {
const clickDay = get(daySearch).replaceAll('-', '');
return clickDay;
}
});
์ด๋ ๊ฒ selector๋ ๋น๋๊ธฐ ํต์ ๋ ๊ฐ๋ฅํ๋ฉฐ atom์ผ๋ก ๊ตฌ๋
ํ ์ปดํฌ๋ํธ์ ๋ํ ์ํ๋ค์ ๋ณํ๋ ์ค ์ ์๋ค. ๋ฆฌ๋์ค๋ก ์น๋ฉด ์ก์
์ ์ญํ ์ ํด์ค๋ค๊ณ ๋ณด๋ฉด๋๋ค. selector๋ atom์ฒ๋ผ ๊ณ ์ ํ key๊ฐ์ ์ง์ ํด ์ฃผ์ด์ผํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ด๊ฒ๋ atomFamily
์ฒ๋ผ selectorFamily
๋ผ๋ ๊ฒ์ด ์๋๋ฐ ์ด๊ฒ ๋ํ ๋งค๊ฐ๋ณ์ํ ์์ผ ์ฌ์ฉํ ์ ์๋ค.
๋ณ๊ฒฝ๋ ์ํ๋ get
์ ์์ฑํ๊ณ return ๊ฐ์ผ๋ก ๋ณ๊ฒฝ๋ ๊ฐ์ ๋ฐํ์์ผ์ค๋ค.
์ง๊ธ๊น์ง ์ฌ์ฉํด๋ณธ ์ํ๊ด๋ฆฌ๋ Redux
, Mobx
, Recoil
์ด๋ ๊ฒ 3๊ฐ์ง ์๋๋ฐ ํ๋์ฉ ํน์ง๋ค๊ณผ ์ฅ๋จ์ ์ ์์ด๋ณด์๋ฉด ์ด๋ ๋ค.
Reducer + Flux
๊ฐ ํฉ์ณ Redux ๋ผ๊ณ ํ๋ค.immer
๋ผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ์๋ฆฌ๋์ค ํน์ง์ ์ค์ค์ด ์ ๋ค๋ณด๋ ๊ฑฐ์ ๋จ์ ๋ง ๋์ด๋์ ๋ฏํ.. ใ ใ
hooks
์ ๋๋์ผ๋ก ์น์ํ๊ฒ ์์
ํ ์ ์์์ฐธ๊ณ ๋ฌธ์ : https://recoiljs.org/ko/docs/introduction/installation