์ทจ์
์ดํ๋ก๋ ์ฒ์ ์์ฑํด๋ณด๋ ์ฌ์ด๋ ํ๋ก์ ํธ ํ๊ณ ๋ก ์
๋๋ค.
์ฌ๋ด์์ ๋์ ธ์ค ์ฃผ์ ๋ก ์์
ํ ์คํฐ๋์ฉ ํ๋ก์ ํธ์ด๋ฉฐ ์ ๊ฐ ์์
ํ ์ฝ๋์ ์ผ๋ถ๋ง ๊ณต๊ฐํ์ฌ ํธ๋ ์ด๋ ์คํ(Trade-Off)์ ๊ธฐ์ ๊ณต์ ์ ๋ชฉ์ ์ผ๋ก ํฌ์คํ
ํด๋ณผ๊น ํฉ๋๋ค.
์๋ฌด๋๋ ์ ๋ฌด๊ฐ ๋จผ์ ์ธ์ง๋ผ ์คํฐ๋์ฉ ํ๋ก์ ํธ์ธ ์ด๊ฒ์ ๋ฌํํ๊ฒ ์ก์ 6์ฃผ ์ ๋ ์์๋๋ค. ๋งค์ฃผ ํ์์ผ ๋ชฉ์์ผ 2ํ ์ฉ ์คํฌ๋ผ ํ๋ฉด์ ๊ฐ์ ์ ๋ฌด ์งํ๋๋ฅผ ํ์ ํ๊ณ ์๋์ด ๋ถ์ ์ฝ๋ ๋ฆฌ๋ทฐ๋ฅผ ํตํด ์ฝ๋ ๊ฐ์ ์ ์ ๋ํ ํผ๋๋ฐฑ๋ ๋ฐ์ํ๋ค.
๋์ด์ผ๋ณด๋ฉด ์ ๋ฌด์ ์คํฐ๋ ๋ ๋ค ๋ณํํ๋๋ผ ํ๋ค์์ง๋ง ๋ด๊ฐ ํ๊ณ ์ถ์ ๊ธฐ์ ์ด๋ฉฐ ์๋ก์ด ๊ฒ๋ค ์์๊ฐ๋ ์ฌ๋ฏธ๊ฐ ์ ์ ํ๋ค.
๊ฐ๋ฐ์ด ๋๋ ๋ค, ์ธ์ ๋ฐํ๋ฅผ ํตํด ๊ฐ๋ฐ๊ทธ๋ฃน๊ณผ ๊ธฐํ๊ทธ๋ฃน ์ผ์๋ค์๊ฒ UX ์ ์ผ๋ก ๊ฐ์ ํ ์ ์ ์ดํํ๊ณ ์ฝ๋๋ ์ ์ด๋ ๊ฒ ์งฐ๋์ง ๋ถ๊ฐ ์ค๋ช ๊ณผ Recoil์ด๋ผ๋ ๊ธฐ์ ์คํ์ ๋ํด ๊ธฐ์ ์ ํ(?)ํ๋ ์๊ฐ์ ๊ฐ์ก๋ค.
์ด ๋ชฉํ๋ค ์ค์ฌ์ผ๋ก ์์ ์ ์๋ฃ์์ผฐ๋ค. ์คํฐ๋ ์งํํ๋ ์๊ฐ๋์ ๋ฐฑ์๋ ๋ถ๋ค๊ณผ ์๋ก ์์์ธ ์๊ฐ์ด ๋์์ ์ข์ ๊ฒ ๊ฐ์ ๊ธฐ์ ๋๋๊ฐ์ ๋๋์ผ๋ก๋ค๊ฐ ์คํฐ๋ ์ด๋ฐ์ ๋ฆฌ์กํธ ํ๋ก์ ํธ ์ด๊ธฐ ํ๊ฒฝ ์ธํ ๋ถํฐ ๊ธฐ๋ณธ์ ์ธ FE ๊ฐ๋ ๋ค ์๋ฃ๋ฅผ ์ค๋นํด์ ๊ฐ๋ตํ๊ฒ ์ค๋ช ํ๊ณค ํ๋ค.
(์ฌ์ค ๊ทธ๋ฃน์ฅ๋๊ป์ ํ๋ก ํธ์ ๋ง ๊ด์ฌ ๊ฐ๋ ์๊ธฐ์ จ์...! ์ ๋น๋ฐ ๐คซ)
๐ ๊ทธ ๋น์์ ์ค๋นํ๋ ์๋ฃ๋ค, React ์ด๊ธฐ, ํ๋ก์ ํธ ํ๊ฒฝ Setting
https://velog.io/@leemember/FE-React-ํ๋ก์ ํธ-์ด๊ธฐ-Setting
๋ด๊ฐ ์ ํ ๊ธฐ์ ์คํ์ ์ด๋ฌํ๋ค.
๊ทธ๋์ ์ํ๊ด๋ฆฌ๋ Redux, Mobx ๋ฐ์ ์ธ ์ค ๋ชฐ๋์ง๋ง ์ด ๊ธฐํ์ Recoil์ ์จ๋ณด๋ฉด์ ์์ผ๋ก Recoil๋ง ์ฐ๊ณ ์ถ์ ์ ๋๋ก ํธํ๋ค ๐๐ฎ
Redux vs Mobx vs Recoil ๋น๊ต๊ธ + Recoil ์ฌ์ฉ๋ฒ์ ์ ๋ฆฌํด๋ณธ ๊ธ์ ๋๋ค ! ๐
https://velog.io/@leemember/%EB%A6%AC%EC%95%A1%ED%8A%B8-%EC%83%81%ED%83%9C%EA%B4%80%EB%A6%AC-Recoil%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC
๐ ์ด ํ์ด์ง๋ฅผ ํด๋ก ์ฝ๋ฉ ํ ๊ฑฐ๋ค. ์ด๋ป๊ฒ?
๋จผ์ , ๋ฆฌ์กํธ ์ค๋ฝ๊ฒ ์ปดํฌ๋ํธํ ์ํค๊ธฐ ์ํด ํ๋ฉด์ ๋ณด๊ณ ๋ถ์ํด์ ์ชผ๊ฐ๋ดค๋ค ๐ง ๋ฐ์ ํ๋ ๊ธฐ์กด์๋ <table>
ํ๊ทธ๋ก ์์
๋ผ์๋ ๊ฒ์ ๋ฐ์ํ์ผ๋ก๋ ๊ตฌํํด๋ณด๊ณ ์ถ์ด์ <div>
ํ๊ทธ๋ก ๋ถ๋ฆฌํ์ฌ ์์
ํ๋ค.
ํ๋ฉด์ ๋ณด๊ณ ์ด๋ป๊ฒ ์ปดํฌ๋ํธ๋ฅผ ๋๋์ง ๊ณ ๋ฏผํ ๋ค, ์ ํ๋ฉด์ฒ๋ผ ๋ฐ์ค ํ์ ํด๋ ๊ฒ์ ๊ธฐ์ค์ผ๋ก ์๋์ ๊ฐ์ ํ์ผ ๊ฒฝ๋ก๋ก ์ปดํฌ๋ํธ๋ฅผ ์ชผ๊ฐฐ๋ค.
๐ฆ src
โฃ ๐ pages
โ โฃ ๐ domestic.tsx ๐(๊ตญ๋ด ๊ฐ์์์ฐ ์ฝ์ธ๊ฑฐ๋์)
โ โฃ ๐ global.tsx ๐(๊ตญ๋ด ๋ฐ ๊ตญ์ ๊ฐ์์์ฐ ์ฝ์ธ๊ฑฐ๋์)
โฃ ๐ lib
โ โฃ ๐ type.ts ๐ (๋ฐ์ดํฐ ํ์
)
โ โฃ ๐ api.ts ๐ (api ์์ฒญ ๋ชจ๋)
โ โฃ ๐ day.ts ๐ (์ค๋ ๋ ์ง)
โฃ ๐ component
โ โฃ ๐ Common
โ โ โฃ ๐ Search.tsx ๐(๋ ์ง ์
๋ ฅ DatePicker)
โ โฃ ๐ Global ๐(๊ตญ๋ด ๋ฐ ๊ตญ์ ๊ฐ์์์ฐ ์ฝ์ธ๊ฑฐ๋์ ํ์ด์ง์๋ง ์ฐ์ผ ์ปดํฌ๋ํธ๋ค)
โ โ โฃ ๐ DomesticSummary.tsx ๐(๊ตญ๋ด ์์)
โ โ โฃ ๐ GlobalSummary.tsx ๐(๊ธ๋ก๋ฒ ์์)
โ โ โฃ ๐ Header.tsx ๐(ํค๋์ ์๋ ๋ด์ฉ๋ค)
โ โ โฃ ๐ List.tsx ๐(๊ฐ ๊ฑฐ๋์๋ค์ ๊ฑฐ๋์ก๊ณผ ๊ธ๋ก๋ฒ ์ ์ ์จ)
โ โฃ ๐ Domestic
โ โ โฃ ๐ *.tsx (๊ธ๋ก๋ฒ๊ณผ ๋์ผ)
์ค์๊ฐ์ผ๋ก ์ด์๋๋ ๊ฑฐ๋์ ์ฅ์ 1๋ถ ๊ฐ๊ฒฉ์ผ๋ก HTTP ์์ฒญ์ ๋ ๋ ค ์ฃผ๊ธฐ์ ์ผ๋ก ์ ๋ฐ์ดํธ ์์ผฐ๋ค. ์ด๊ฒ์ Polling(ํด๋ง)์ด๋ผ๊ณ ํ๋ค.
// api ์ํ๊ด๋ฆฌ
/* โญ๏ธ useRecoilValue๋ Recoil์์ ์ ๊ณต๋๋ ๋ฉ์๋๋ก ์ฝ๊ธฐ ์ ์ฉ์ ๊ธฐ๋ฅ์ ํฉ๋๋ค.
๊ทธ๋์ useRecoilValue๋ฅผ ํตํด ์ด๊ธฐ api ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋๋ฐ,
์ฌ๊ธฐ์ parameter ๊ฐ์ todayCall์ ์ค๋์ ๋ ์ง๋ฅผ ๊ฐ๊ณต์ํจ ํจ์์
๋๋ค.
*/
const domesticRankings: Rank[] = useRecoilValue(getGlobalDomeRankListApi(todayCall));
const globalRankingList: Rank[] = useRecoilValue(getGlobalRankListApi(todayCall));
const globalTotal: TotalTypes = useRecoilValue(getGlobalTotalApi(todayCall));
const globalCoinList: [CoinList][] = useRecoilValue(getGlobalCoinListApi(todayCall));
// โญ๏ธ ๋ฐ์ดํฐ ํผ์ปค์ ๋ ์ง ์ ํ ์, ๊ทธ ๋ ์ง์ ๋ฐ์ดํฐ ๊ฐ์ ธ์ฌ ์ ์๋๋ก ์ํ๋ณ๊ฒฝ
const clickDay = useRecoilValue(daySearch);
// โญ๏ธ ๋ญํน ๋ฆฌ์คํธ
// โญ๏ธ ํ์ด์ง ์ง์
ํ๊ณ ๋ ๋๋ง ํ์๋ง์ ๋ฐ์ดํฐ๋ค ๋ณด์ฌ์ง๋๋ก
const [domeRanking, setDomeRanking] = useState<Rank[]>(domesticRankings);
const [globalRanking, setGlobalRanking] = useState<Rank[]>(globalRankingList);
const [total, setTotal] = useState<TotalTypes>(globalTotal);
const [coinList, setCoinList] = useState<[CoinList][]>(globalCoinList);
// โญ๏ธ ๋ฐ์ดํฐํผ์ปค์ ์๋ ๋ ์ง๋ฅผ ํด๋ฆญํ ๋
const changeDay = clickDay.replaceAll('-', '');
// ๐api๋ฅผ ํธ์ถํด์ฃผ๋ ํจ์๋ค. dayParams์๋ ๋ ์ง๋ฅผ ๋ฐ์์ค๋๋ก ํ๋ค.
const globalState = async (dayParams: {}) => {
const domesticRank = await fetcher(METHOD.GET, `/global/domesticRank?toDate=${dayParams}`);
const globalRank = await fetcher(METHOD.GET, `/global/globalRank?toDate=${dayParams}`);
const globalTotal = await fetcher(METHOD.GET, `/global/total?toDate=${dayParams}`);
const coinList = await fetcher(METHOD.GET, `/global/coinList?toDate=${dayParams}`);
setDomeRanking(domesticRank.data);
setGlobalRanking(globalRank.data);
setTotal(globalTotal.data);
setCoinList(coinList.data);
};
useEffect(() => {
// ๐setInterval์ ํตํด 1๋ถ ๊ฐ๊ฒฉ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์
๋ฐ์ดํธ (=Polling)
let polling = setInterval(() => {
globalState(todayCall);
}, 60000);
// ๐ ๋ง์ผ์ ๋ฐ์ดํฐํผ์ปค์ ์ ํํ ๋ ์ง๊ฐ ์ค๋ ๋ ์ง๊ฐ ์๋๋ผ๋ฉด polling์ด ๋๊ธฐ๋๋กํ๊ณ , ์ ํํ ๋ ์ง์ ๋ฐ์ดํฐ๋ค์ด ๋ณด์ฌ์ง๋๋ก ์กฐ๊ฑด๋ฌธ์ ๊ฑธ์๋ค.
if (clickDay.replaceAll('-', '') !== todayCall) {
clearInterval(polling);
globalState(changeDay);
} else {
globalState(changeDay);
}
// ๐ ํ์ด์ง์ ๋ฒ์ด๋ ๊ฒฝ์ฐ์๋ polling X
return () => {
clearInterval(polling);
};
}, [clickDay]);
์ด์ฒ๋ผ setInterval
์ ์ฌ์ฉํ์ฌ 1๋ถ(=60000) ๊ฐ๊ฒฉ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์๋กญ๊ฒ ๋ถ๋ฌ์๋ค. ๋ฌธ์ ๋ setInterval
๋ง์ ์ฌ์ฉํด์ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๊ฒ ๋๋ค๋ฉด ์ฒ์ ํ์ด์ง์ ์ด๊ธฐ ์ง์
ํ์ ๋ ์๋ฌด๋ฐ ๋ฐ์ดํฐ๊ฐ ์๋ ์ํ์์ 1๋ถ ๋ค์ ๋ฐ์ดํฐ๊ฐ ๋์ค๊ฒ ๋ ๊ฒ์ด๋ค. ๊ทธ๋์ ํด๊ฒฐ์ฑ
์ผ๋ก ์ฒ์ ํ์ด์ง ์ง์
ํ์ ๋๋ถํฐ ๋ฐ์ดํฐ๋ค์ด ๋ ๋๋ง ๋๋๋ก ํ๊ธฐ ์ํด Recoil๋ก ์ด๊ธฐ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ด์คฌ๋ค.
์ด๋ ๊ฒ ํจ์ผ๋ก์จ ํ์ด์ง ์ด๊ธฐ ์ง์ ์ ๋ ๋๋ง ๋ ๋ ๋ฐ๋ก ๋ฐ์ดํฐ๋ค์ด ๋ณด์ฌ์ง๋ ์ํ์์, setInterval๋ก ํ์ฌ๊ธ 1๋ถ ๋ง๋ค ์๋ก์ด ๋ฐ์ดํฐ๋ค์ด ๋ณด์ฌ์ง ์ ์๊ฒ ๊ตฌํํ๋ค.
ํ์ง๋ง, ๋ฐ์ดํฐํผ์ปค๋ก ๋ค๋ฅธ ๋ ์ง๋ฅผ ์ ํํ์ ๋๋ polling ๋์ด์ง๋๋ก ์กฐ๊ฑด๋ฌธ์ ๋ฌ์์. (์๋ํ๋ฉด ๊ณผ๊ฑฐ ๋ ์ง๋ ์ด๋ฏธ ์ฅ์ด ๋๋ฌ๊ธฐ ๋๋ฌธ)
์์ฉํ ํ ์ ์๋ ์ฝ๋๋ฅผ ๋ชจ๋ํ ์์ผ ์ฝ๋๋ฅผ ๊ฐ๊ฒฐํ๊ฒ ํ๋ค.
(1) API fetcher ๋ชจ๋ -> API ์์ฒญ ๊ธฐ๋ฅ
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๋ก ๋์ฐ๊ณ ํ์ด์ง๋ฅผ ๋ฆฌ๋ก๋
alert('๋ฐ์ดํฐ๊ฐ ์์ต๋๋ค.')
location.reload();
return error;
}
};
export default fetcher;
์ฌ์ฉ๋ฒ์ ์ด๋ฐ์์ผ๋ก ์ธ ์ ์๋ค.
const domesticRank = await fetcher(METHOD.GET, `/global/domesticRank?toDate=${dayParams}`);
๊ทธ๋ฆฌ๊ณ baseURL์ ๋ด๊ธด env ๋ก์ปฌ ์ฃผ์๋ฅผ ๋ถ๋ฌ์ค๊ธฐ ์ํด์๋, yarn add env-cmd
๋ฅผ ์ค์นํ๊ณ ํ๋ก์ ํธ ๊ฐ์ฅ ์์ ํด๋์ .env-*
ํด๋๋ฅผ ์์ฑํ๋ค. ๊ทธ๋ฆฌ๊ณ baseURL์ ๊ฐ์ ธ์ค๊ณ ์ ํ๋ ๊ฐ์ ๋ฃ์ด์ค๋ค.
๊ทธ๋ฆฌ๊ณ package.json์ ์์ ํด์ค์ผ ํจ !
"scripts": {
"start": "env-cmd -f .env-local react-scripts start",
"build": "react-scripts build",
"build:dev": "env-cmd -f .env-dev react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
์ด๋ ๊ฒ scripts ์์ ์ฐ์ ์์๋ก ๋ถ๋ฌ์ค๊ณ ์ ํ๋ env ํ๊ฒฝ ํ์ผ ์์๋๋ก ์ ์ธํด์ฃผ๋ฉด ๋๋ค. ๊ทธ๋ฆฌ๊ณ package.json ํ์ผ ์ฝ๋ ๊ฐ์ฅ ํ๋จ์ "proxy": "api url ์ฃผ์"
๋ฅผ ๋คํธ์ํฌ ํต์ ํ api ์ฃผ์๋ฅผ ๊ธฐ์
ํ๊ธฐ
(2) ๊ฐ ๊ฑฐ๋์์ ๊ฑฐ๋์ก ํฉ๊ณ๋ด๊ธฐ
const coinSum = (arrName: any) => {
const arrNum = _.map(arrName, 'volume');
return <>{_.sum(arrNum).toLocaleString()}</>;
};
lodash
๋ผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด์ ๋ชจ๋ ๋ฐฐ์ด์ ์๋ฅผ ํฉ๊ณ๋ด๋ ์์
์ ํ๋ค. (3) ์ค๋ ๋ ์ง ํจ์
const today = new Date();
const year = today.getFullYear();
const month = ('0' + (today.getMonth() + 1)).slice(-2);
const day = ('0' + today.getDate()).slice(-2);
๐ export const todayCall = year + month + day;
Today ํจ์๋ api ์ฝ ํ ๋ ์๊ธฐ์ฃ ๊ธฐ ์์ฃผ ์ฐ์ฌ์ ๋ชจ๋ํ ํ๋ค.
๊ธฐ์กด์ ์์ ๋ ํ์ด์ง๋ฅผ ๋ณด๋ฉด ๋ ์ง๋ฅผ ์ ํํ๋ ๊ธฐ๋ฅ์ ์๋ค. ๊ธฐ์กด์๋ ๋ ์ง๋ฅผ ์ง์ ์ ๋ ฅํด์ผ ํ๊ณ ๋๋ฌด๋๋ ๋ถํธํ๋ ๊ฒ ์ซ์์ (-) ํ์ดํ๋ ์ง์ ๋ฃ์ด์ ๋ฐ์ดํฐ๋ฅผ ์กฐํํด์ผ ๋๋ ์์คํ ์ด๋ค. (ex) 2023-01-01 ์ด๋ฐ์์ผ๋ก ์ซ์์ ํ์ดํ ์์ด๊ฐ๋ฉฐ ๋ ์ง๋ฅผ ์ ๋ ฅํ ๋ค, ๋ ํ๋ฒ '์กฐํ' ๋ผ๋ ๋ฒํผ์ ํด๋ฆญํด์ผ ํด๋น ๋ ์ง์ ๋ฐ์ดํฐ๊ฐ ๋ณด์ฌ์ง๋ค.
๊ทธ๋์ ๋๋ ์ด๋ฌํ ๋ถํธํจ์ ์์ ๊ณ ์ฌ๋ฏธ์ฑ๊ณผ ํธ๋ฆฌ์ฑ ๋ ๋ค ์ก๊ธฐ ์ํด UI/UX์ ์ผ๋ก ๊ฐ์ ์ํค๊ณ ์ antd
๋ผ๋ ์ปดํฌ๋ํธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ DataPicker
๋ฅผ ๊ฐ์ ธ์จ ๋ค ์ปค์คํ
ํ์๋ค.
์ด๋ป๊ฒ ์ปค์คํ ํ๋๋ฉด, 1๏ธโฃ ์กฐํํ๊ณ ์ ํ๋ ๋ ์ง๋ฅผ ํด๋ฆญํ์ ๋ ๋ฐ๋ก ๊ทธ ๋ ์ง์ ๋๋ ์ฅ์ ๋ฐ์ดํฐ๋ค์ด ๋ณด์ฌ์ง๋๋ก api url ํ๋ผ๋ฏธํฐ ๊ฐ์ Recoil atom์ผ๋ก ์ํ๊ฐ์ ์ ์ดํ๋ฉฐ ๋ฐ์ดํฐ๋ฅผ ์กฐํ์์ผฐ๊ณ 2๏ธโฃ ์ค๋ ๋ ์ง ์ดํ๋ก ๋ฏธ๋ ๋ ์ง๋ ์ ํํ ์ ์๋๋ก ๋นํ์ฑํํ๋ค. ๊ทธ๋ฆฌ๊ณ ์ค๋ ์ฅ์ ์ค์๊ฐ ๋ฒํผ ํด๋ฆญ ์ ๋ณด์ฌ์ง๋๋ก ๊ตฌํํ๋ค.
๐ Search.tsx (DatePicker ์ปค์คํ ์ปดํฌ๋ํธ)
import React from 'react';
import moment from 'moment';
import { useRecoilValue, useSetRecoilState } from 'recoil';
import { daySearch } from 'recoil/atom';
import { dayValue } from 'recoil/selector';
import type { DatePickerProps } from 'antd';
import { DatePicker } from 'antd';
import { todayCall } from 'lib/day';
import './styles.scss';
const Search = () => {
// ๐ useSetRecoilState๋ ๋ฆฌ์ฝ์ผ์์ ์ํ๋ฅผ ๋ณ๊ฒฝํด์ฃผ๋ setter ์ญํ ์ด๋ค.
const setCurrent: any = useSetRecoilState(daySearch);
const value = useRecoilValue(dayValue);
// ๐ setCurrent์ ๋ด๊ธด daySearch ๊ฐ์ ํด๋ฆญํ dateString์ ๊ฐ์ผ๋ก ๋ณ๊ฒฝํ๋ค.
const onChangeDate: DatePickerProps['onChange'] = (date, dateString) => {
setCurrent(`${dateString}`);
};
// ์ค์๊ฐ
const reloadButton = () => {
setCurrent(todayCall);
};
return (
<section className="search">
<DatePicker
onChange={onChangeDate}
className="dataInput"
// ๐ ๋ ์ง ํ์
format="YYYY-MM-DD"
placeholder="๋ ์ง๋ฅผ ์ ํํด์ฃผ์ธ์."
allowClear={false}
value={value}
// ๐ ์ค๋ ๋ ์ง ์ดํ๋ก ๋ฏธ๋ ๋ ์ง๋ฅผ ํด๋ฆญํ์ง ๋ชปํ๊ฒ ๋นํ์ฑํ ์ํค๋ ๋ฐฉ๋ฒ
disabledDate={(current: any) => {
// ์ด ๋ ๋ถํฐ ~ ์ด ๋ ๊น์ง๋ง ์ ํ ๊ฐ๋ฅ
return current.year() < 2020 || current >= moment().subtract(1, 'days').toDate();
}}
/>
<button className="btn-now" id="searchNowBtn" type="button" onClick={reloadButton}>
์ค์๊ฐ
</button>
</section>
);
};
export default Search;
โญ๏ธ ์ฐธ๊ณ ์๋ฃ โญ๏ธ
antd ๐ https://ant.design/components/date-picker#header
์คํ์ค๋ฒํ๋ก์ฐ ๐https://stackoverflow.com/questions/46358603/disable-date-and-time-for-antd-datepicker
import React from 'react';
import { CoinList, Rank } from 'lib/type';
import CoinNameList from './CoinNameList';
import ExchangeList from './ExchangeList';
import './styles.scss';
interface Props {
coinList: [CoinList][];
rankingList: Rank[];
}
const List = ({ coinList, rankingList }: Props) => {
// ๊ฑฐ๋์ ์ฝ์ธ ๋ฆฌ์คํธ
const arrDealList = () => {
const list = [];
for (let i = 0; i < coinList.length; i++) {
list.push(<ExchangeList key={i} globalCoinList={coinList[i]} globalRank={rankingList[i]} />);
}
return list;
};
return (
<div className="coinListBox">
<CoinNameList coinName={coinList[0]} />
{arrDealList()}
</div>
);
};
export default List;
๋ถ๋ชจ ์ปดํฌ๋ํธ์์ ๋ฟ๋ ค์ฃผ๋ ๋ฐ์ดํฐ๋ฅผ ๋ฐ์ List ์ปดํฌ๋ํธ์ ๋ฐ๋ณต๋ฌธ์ ๋๋ ค ์ฝ์ธ๋ฆฌ์คํธ์ length ๋งํผ ๋ฐ์ดํฐ๋ฅผ ๋ถ๋ฌ์ค๋๋ก ํ๋ค.
1๏ธโฃ ์ ๊ณต๋๋ ๋ฐ์ดํฐ ์์น(๋ฐฐ์ด, ๊ฐ์ฒด)๋ค์ ํธ๋ค๋ง ํ๋๋ฐ lodash๋ผ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํด์ ๋น ๋ฅธ ์์ ๊ณผ ๊ฐ๊ฒฐํ ์ฝ๋๋ฅผ ๊ตฌํํ ์ ์์๋ค. ๊ทธ๋ฆฌ๊ณ ์ด๊ฒ์ ํผํฌ๋จผ์ค ์ธก๋ฉด์์๋ native๋ณด๋ค ๋ ๋์ ์ฑ๋ฅ์ ๊ฐ์ก๋ค๊ณ ํ๋ค.
๐ ์ฐธ๊ณ ์๋ฃ https://lodash.com/docs/4.17.15
2๏ธโฃ ์ด ์คํฐ๋๋ฅผ ํ๋ฉด์ ๋ด ๋ชฉํ์๋ ๋ฐฑ์๋ ๊ฐ๋ฐ์์ ์ํํ ์ํต์ ์ํ ๋ฐฑ์๋ ๊ด๋ จ ๊ฐ๋ ์ฉ์ด๋ค์ ์์งํ ์ ์์๋ค. Batch ๋ผ๋ ๊ฒ์ ์๋กญ๊ฒ ์๊ฒ ๋๋๋ฐ ์ด๊ฒ์ ๊ณผ๊ฑฐ์ ๋ฐ์ดํฐ๋ค์ ์ง๊ณํ๋ ๊ฐ๋ ์ด๋ค. ๊ทธ๋์ ์ด๊ฑธ๋ก ํ์ฌ๊ธ ๊ฑฐ๋์์ ๊ณผ๊ฑฐ ๋ฐ์ดํฐ๋ค์ ์์งํ ์ ์์๋ ๊ฒ์ด์๋ค. ๊ทธ๋์ ๊ฐ ๋ ์ง ๋ณ๋ก ๋ฐ์ดํฐ๋ฅผ ์๋ต ๋ฐ์ ๋๋ ํ๋ผ๋ฏธํฐ ํน์ ์ฟผ๋ฆฌ์คํธ๋ง์ผ๋ก ๋ ์ง ๊ตฌ๊ฐ์ ์ํ๊ด๋ฆฌ atom์ผ๋ก ๋ฐ์ดํฐํผ์ปค๋ก ์ ํํ ๋ ์ง๋ฅผ ๋ฐ์ ๋ฐ์ดํฐ๋ฅผ ์๋ต๋ฐ์ ์ ์์๋ค. ๊ทธ๋ฆฌ๊ณ Swagger์ PostMan ํ์ฉํ๋ ๋ฒ๋ ์ ๋๋ก ํฐ๋ํ ์ ์์์ ๐คฉ
3๏ธโฃ ์ํ๊ด๋ฆฌ ๊ธฐ์ ์คํ์ผ๋ก Recoil์ด๋ผ๋ ๊ฒ์ ์ฌ์ฉํด๋ณธ ์ .
4๏ธโฃ env-cmd ๋ฅผ ํ์ฉํ์ฌ ํ๊ฒฝ๋ณ์๋ฅผ ์ค์ ํ๋ค. ๊ทธ๋์ ์ด๋ฅผ ํตํด ๊ฐ๋ฐ, QA ๋ฑ๋ฑ์ ํ๊ฒฝ์์ ์ฌ์ฉํ ํ๊ฒฝ๋ณ์ ํ์ผ์ ์์ฑํ์ฌ ์ํฉ์ ๋ง๊ฒ package.json์ ์คํฌ๋ฆฝํธ ๋ช ๋ น์ด ๋ณ๋ก env ํ์ผ ์ฐ์ ์์๋ฅผ ์ ํ ์ ์๋ค.
5๏ธโฃ ๋ฆฌ์กํธ๋ก ๊ฐ๋ฐํ ๋ ๊ฐ๋ฐ ์๋ฒ ์ ์ ์ฃผ์๋ ๊ธฐ๋ณธ์ ์ผ๋ก localhost:3000์ด๋ค. ๋ง์ฝ api ์๋ฒ ์ฃผ์๊ฐ localhostL5000 ์ด๋ผ๊ณ ํ๋ฉด, ๋ฐ๋ก CORS ์ค์ ์ ํด๋์ง ์์ ์ด์ api request๋ฅผ ๋ ๋ฆฌ๋ฉด CORS ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค. ๊ทธ๋์ packages.json์์ Proxy ๊ธฐ๋ฅ์ ์ฌ์ฉํ๋ฉด CORS ์ ์ฑ ์ ์ฐํํ ์ ์๋ค.
6๏ธโฃ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ธฐ์ ์ผ๋ก ์์ฒญํ๊ณ ์๋ต๋ฐ์ ์ ์๋ Polling์ ์ด์ฉํ์ฌ ์ฝ์ธ ์ฅ์ ๋ฐ์ดํฐ๋ค์ ์ค์๊ฐ์ผ๋ก ๋ฐ์๋ผ ์ ์์๋ค.
๋ ๐ถ
ํ์ฌ ์ ์ธ๊ณ์๋ 200์ฌ ๊ฐ ์ด์์ ์ํธํํ ๊ฑฐ๋์๊ฐ ์์ต๋๋ค. ์ด ์ค์์ ์ฐ๋ฆฌ์ ์ฌ๊ฑด์ ์ ํฉํ ๊ฑฐ๋์๋ฅผ ์ฐพ๊ธฐ ์ํด์๋ ๋ง์ ๋ ธ๋ ฅ์ด ํ์ํ ๊ฒ๋๋ค. ๋จ์ํ ์ง์๋๋ ํ ํฐ์ ์๋์ด๋, ์์ , ์์๋ฃ๋ฅผ ๊ณ ๋ คํ๋ ๊ฒ ์ด์ธ์๋, ๊ณ ๊ฐ ์๋น์ค ํ์ง, ์ง๊ฐ ๋ณด์, ๊ทธ๋ฆฌ๊ณ ์ต์ ์์น๊ธ์ ๋ํด์๋ ์์์ผ ํ ๊ฒ์ ๋๋ค. McKesson Ordering