recharts ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ด์ฉํ bar, area ๊ฐ ํผํฉ๋ ์ฐจํธ ๋ง๋ค๊ธฐ
<div style={{ width: 2000, height: 800 }}>
<h3>์ฐจํธ</h3>
<ResponsiveContainer width="100%" height="100%">
<ComposedChart
width={600}
height={400}
data={chartData}
margin={{
top: 20,
right: 20,
bottom: 20,
left: 20,
}}
>
recharts ๊ณต์ ๋ฌธ์์ ์๋ ์์ ์์ ๊ฐ์ ธ์๋๋ฐ ์ ์ฐจํธ๊ฐ ์๋์ค๋์ง ๋ชฐ๋ผ์ ๋นํฉํ๋ค. ์ฌ๊ธฐ์ ResponsiveContainer
๊ฐ ๋ถ๋ชจ ์์์ 100%์ด๊ธฐ ๋๋ฌธ์ ์์ ํ๊ทธ์์ ํฌ๊ธฐ๋ฅผ ์ฃผ์ด์ผ ํ๋ค.
<Area
type="monotone"
dataKey="value_area"
fill="#8884d8"
stroke="#8884d8"
yAxisId="left"
/>
<Bar dataKey="value_bar" barSize={20} yAxisId="right" fill="pink" />
๋ถํ์ bar ์ ๊ฐ๋ ค์ ธ area ๋ถ๋ถ์ด ์ ๋ณด์ด์ง ์์๋ค. ์๋ ์ด๋ฐ๊ฑธ๊น? ํ์ง๋ง ๊ทธ๋ด๋ฆฌ ์์ง
<Bar dataKey="value_bar" barSize={20} yAxisId="right" fill="pink" />
<Area
type="monotone"
dataKey="value_area"
fill="#8884d8"
stroke="#8884d8"
yAxisId="left"
/>
Bar
์ Area
์ ์์๋ง ๋ฐ๊ฟ์ฃผ๋ฉด ๊ณ ์น ์ ์๋ค.
hover ์์ ๊ฐ๊ฐ์ ์ ๋ณด๋ฅผ ๋ํ๋ด ์ฃผ๋ ๊ฒ์ ํดํ์ด๋ผ๊ณ ํ๋ค.
recharts ์์๋ ๊ธฐ๋ณธ ํดํ์ ์ ๊ณตํด์ฃผ์ง๋ง ๋ฌํ๊ฒ? ๋ชป์๊ฒผ๋ค.
๊ณต์๋ฌธ์์ ์ปค์คํ
ํ๋ ๋ฐฉ์๋ ์ ๋์์๋ค. id
๋ก ๋ฐ๋ ๊ตฌ ์ด๋ฆ ์๋๋ก map
๋ฐ๋ณต๋ฌธ์ ์ฌ์ฉํด flex
๋ฅผ ์ฃผ๊ณ ์์น๋ฅผ ๊ฐ๊ฐ์ bar์ area ์ปฌ๋ฌ๋ก ์ค์ ํ๋ค.
๊ทธ๋ฆฌ๊ณ focus๋๋ฉด ์ฌ์ง์ฒ๋ผ ์๋์ผ๋ก ๋ณด๋๊ฐ ์๊ธฐ๋๋ฐ,
.recharts-tooltip-wrapper:focus-visible {
outline: none;
}
์ด ์์ฑ์ ์ฃผ๋ฉด ์์จ ์ ์๋ค.
์ฒ์์ ํ์ผ์ ๋ง๋ค๋ ์ค์๋ก jsx
๋ก ๋ง๋ค์ด ์ค๋ฅ ํ๋ ์์ด ์ ๋๊ฐ๋ค๊ฐ, ์ฌ์ค์ ๊นจ๋ซ๊ณ tsx
๋ก ๋ฐ๊พธ์ ๋ง์ ๋นจ๊ฐ์ค ํํฐ๊ฐ ๋ฌ๋ค.. ๐ญ
const CustomTooltip = ({
active,
payload,
}: {
active: boolean;
payload: any[];
}) => {
if (active && payload && payload.length) {
return (
payload๋ ์ปค์๋ฅผ ์ฐจํธ์ ๊ฐ๋ค๋๋ฉด ๋์ค๋ ์ ๋ณด์ธ๋ฐ ์ด๋ป๊ฒ ์ฒ๋ฆฌํด์ค์ผ ํ ์ง ๋ชฐ๋ผ any[] ๋ก ๋์๋ค.
import { TooltipProps } from 'recharts';
const CustomTooltip = ({
active,
payload,
}: TooltipProps<number, string>): JSX.Element | null => {
if (active && payload && payload.length) {
return (
์๊ณ ๋ณด๋ recharts ์์ ์ ๊ณตํ๋ TooptipProps ์ด ์์ด์ ์ ์ฉ์์ผ ์คฌ๋ค.
Type 'Set<any>' is not an array type or a string type. Use compiler option '--downlevelIteration' to allow iterating of iterators.
์ด ์ค๋ฅ๋ ES6๋ฅผ ์ง์ํ์ง ์๋ ํ๊ฒฝ์์ ES6 ์ด๊ฑฐํ(Iterable) ๊ฐ์ฒด๋ฅผ ์ด๊ฑฐํ๋ ค๊ณ ํ ๋ ๋ฐ์ํ๋ค. ์๋ฅผ๋ค์ด Set ํน์ Map ๊ฐ์ฒด๋ ES6์์ ์ถ๊ฐ๋ ๊ฐ์ฒด์ธ๋ฐ tsconfig.json
์ target
์ ES5
๋ก ์ค์ ํ ์ํ์์ Set์ ์ด๊ฑฐํ๋ ค๊ณ ํ๋ฉด ์ด๋ฌํ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ ๊ฒ์ด์๋ค.
ํด๊ฒฐํ๋ ๋ฐ์๋ ๋๊ฐ์ง ์ค๋ฅํด๊ฒฐ ๋ฐฉ๋ฒ์ด ์๋๋ฐ ๋๋ ๊ฐ๋จํ --downlevelIteration
์ ์ฌ์ฉํ์ฌ TypeScript๋ฅผ ๋น๋ํ๋ค.
bar
๊ณผ area
์ ๊ฐ์ useState
๋ก ๊ด๋ฆฌํ๋๋ฐ, ํํฐ๋ง ๊ธฐ๋ฅ์ ์ถ๊ฐํ๊ฒ ๋๋ฉด์ ๋ฆฌํฉํ ๋ง์ ํ๊ธฐ๋ก ํ๋ค. ์๋ก๊ณ ์นจํด๋ ํํฐ๋ง ์ ๋ณด๊ฐ ์ฌ๋ผ์ง์ง ์๊ฒ ํ๊ธฐ ์ํด react-router-dom
์์ ์ ๊ณตํ๋ useSearchParams
๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ์ ์ ์ฅํด์ฃผ๊ธฐ๋ก ํ๋ค.
์ด๊ธฐ๊ฐ์ ๋ฃ์ด์ฃผ์ง ์์์ ์ฒซ ํ๋ฉด์ด ๋น ํ๋ฉด์ผ๋ก ๋ํ๋ฌ๋ค. useEffect ์์ ์ด๊ธฐ๊ฐ์ด ์์ ๋์ ์ ์ฒด ์ ํ์ ํด์ค๋ค๋ ์ฝ๋๋ฅผ ์์ฑํ์ฌ ์ฒซ ํ๋ฉด์์ district ์ category์ ์ ์ฒด๊ฐ์ผ๋ก ์ ํํด์ฃผ๊ฒ ๋ง๋ค์๋ค.
useEffect(() => {
if (!district && !category) {
searchParams.set('district', '์ ์ฒด');
searchParams.set('category', '์ ์ฒด');
}
}, []);
์ฃผ์์ฐฝ์ ๋ณด๋ฉด ํด๋ฆญ์ ๋ฐ๋ผ category ์ district ๊ฐ ๋ณํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค!