From 74ba65a8f2dc2a41cb419fb4118f4717293a7b08 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 18:22:58 +0900 Subject: [PATCH 01/41] =?UTF-8?q?feat:=20=EC=B0=A8=ED=8A=B8=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=97=94=ED=8B=B0=ED=8B=B0=20=EB=B0=8F=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/entities/chart/index.ts | 21 +++++++++++++++++++++ src/entities/chart/types/index.ts | 1 + src/entities/chart/types/risk-chart.type.ts | 4 ++++ src/entities/index.ts | 3 +++ 4 files changed, 29 insertions(+) create mode 100644 src/entities/chart/index.ts create mode 100644 src/entities/chart/types/index.ts create mode 100644 src/entities/chart/types/risk-chart.type.ts diff --git a/src/entities/chart/index.ts b/src/entities/chart/index.ts new file mode 100644 index 0000000..98cee81 --- /dev/null +++ b/src/entities/chart/index.ts @@ -0,0 +1,21 @@ +/** + * 차트 관련 entities + */ + +// 차트 라인 데이터 +export interface ChartLine { + key: string; + name: string; +} + +// 차트 데이터 포인트 +export interface ChartDataPoint { + [key: string]: string | number; +} + +// 차트 데이터 +export interface ChartData { + title: string; + lines: ChartLine[]; + data: ChartDataPoint[]; +} diff --git a/src/entities/chart/types/index.ts b/src/entities/chart/types/index.ts new file mode 100644 index 0000000..b927d53 --- /dev/null +++ b/src/entities/chart/types/index.ts @@ -0,0 +1 @@ +export * from './risk-chart.type'; diff --git a/src/entities/chart/types/risk-chart.type.ts b/src/entities/chart/types/risk-chart.type.ts new file mode 100644 index 0000000..62e3a78 --- /dev/null +++ b/src/entities/chart/types/risk-chart.type.ts @@ -0,0 +1,4 @@ +export type RiskFactor = { + name: string; + percentage: number; +}; diff --git a/src/entities/index.ts b/src/entities/index.ts index e69de29..5a0bd7b 100644 --- a/src/entities/index.ts +++ b/src/entities/index.ts @@ -0,0 +1,3 @@ +export * from './auth'; +export * from './chart'; +export * from './risk-analysis'; From b045e2dd352cb11d139c0e82a95d8d242627d8a0 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 18:24:39 +0900 Subject: [PATCH 02/41] =?UTF-8?q?feat:=20=EC=B0=A8=ED=8A=B8=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(ChartAxis,=20ChartGrid,=20ChartLineItem,=20LegendL?= =?UTF-8?q?ine,=20Needle)=20=EB=B0=8F=20ChartBox=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/components/common/ChartAxis.tsx | 18 +++ .../main/components/common/ChartGrid.tsx | 16 +++ .../main/components/common/ChartLineItem.tsx | 18 +++ .../main/components/common/LegendLine.tsx | 25 ++++ .../main/components/common/Needle.tsx | 21 ++++ src/features/main/components/common/index.ts | 5 + .../components/features/chart/ChartBox.tsx | 119 +++--------------- 7 files changed, 120 insertions(+), 102 deletions(-) create mode 100644 src/features/main/components/common/ChartAxis.tsx create mode 100644 src/features/main/components/common/ChartGrid.tsx create mode 100644 src/features/main/components/common/ChartLineItem.tsx create mode 100644 src/features/main/components/common/LegendLine.tsx create mode 100644 src/features/main/components/common/Needle.tsx diff --git a/src/features/main/components/common/ChartAxis.tsx b/src/features/main/components/common/ChartAxis.tsx new file mode 100644 index 0000000..b7dd6e3 --- /dev/null +++ b/src/features/main/components/common/ChartAxis.tsx @@ -0,0 +1,18 @@ +import { XAxis, YAxis } from 'recharts'; + +import { useChartConfig } from '../../hooks'; + +type Props = { + dataKey: string; +}; + +export const ChartAxis = ({ dataKey }: Props) => { + const { xAxisConfig, yAxisConfig } = useChartConfig(); + + return ( + <> + + + + ); +}; diff --git a/src/features/main/components/common/ChartGrid.tsx b/src/features/main/components/common/ChartGrid.tsx new file mode 100644 index 0000000..0b021b7 --- /dev/null +++ b/src/features/main/components/common/ChartGrid.tsx @@ -0,0 +1,16 @@ +import { CartesianGrid } from 'recharts'; + +import { useChartConfig } from '../../hooks'; + +export const ChartGrid = () => { + const { gridConfig } = useChartConfig(); + + return ( + <> + {/* 가로선 - 실선 */} + + {/* 세로선 - 점선 */} + + + ); +}; diff --git a/src/features/main/components/common/ChartLineItem.tsx b/src/features/main/components/common/ChartLineItem.tsx new file mode 100644 index 0000000..53d2696 --- /dev/null +++ b/src/features/main/components/common/ChartLineItem.tsx @@ -0,0 +1,18 @@ +import { Line } from 'recharts'; + +import type { ChartLine } from '@/entities'; + +import { useChartConfig } from '../../hooks'; + +type Props = { + line: ChartLine; + index: number; +}; + +export const ChartLineItem = ({ line, index }: Props) => { + const { getLineConfig } = useChartConfig(); + + const config = getLineConfig(line, index); + + return ; +}; diff --git a/src/features/main/components/common/LegendLine.tsx b/src/features/main/components/common/LegendLine.tsx new file mode 100644 index 0000000..c0dde2f --- /dev/null +++ b/src/features/main/components/common/LegendLine.tsx @@ -0,0 +1,25 @@ +import type { ChartLine } from '@/entities'; + +import { useChartConfig } from '../../hooks'; + +type Props = { + lines: ChartLine[]; +}; + +export const LegendLine = ({ lines }: Props) => { + const { getLegendItemConfig } = useChartConfig(); + + return ( +
    + {lines.map((line, index) => { + const config = getLegendItemConfig(line, index); + return ( +
  • +
    + {config.name} +
  • + ); + })} +
+ ); +}; diff --git a/src/features/main/components/common/Needle.tsx b/src/features/main/components/common/Needle.tsx new file mode 100644 index 0000000..3cea4cb --- /dev/null +++ b/src/features/main/components/common/Needle.tsx @@ -0,0 +1,21 @@ +import { NeedleIcon } from '@/shared/components/common'; +import { type GaugeData } from '@/shared/utils/chart/risk-chart'; + +type Props = { + gaugeData: GaugeData; +}; + +export const Needle = ({ gaugeData }: Props) => { + return ( +
+
+ + +
+ ); +}; diff --git a/src/features/main/components/common/index.ts b/src/features/main/components/common/index.ts index e69de29..e70a782 100644 --- a/src/features/main/components/common/index.ts +++ b/src/features/main/components/common/index.ts @@ -0,0 +1,5 @@ +export * from './ChartAxis'; +export * from './ChartGrid'; +export * from './ChartLineItem'; +export * from './LegendLine'; +export * from './Needle'; diff --git a/src/features/main/components/features/chart/ChartBox.tsx b/src/features/main/components/features/chart/ChartBox.tsx index 225d833..42c960f 100644 --- a/src/features/main/components/features/chart/ChartBox.tsx +++ b/src/features/main/components/features/chart/ChartBox.tsx @@ -1,87 +1,24 @@ -import { - CartesianGrid, - Legend, - Line, - LineChart, - ResponsiveContainer, - Tooltip, - XAxis, - YAxis, -} from 'recharts'; +import { Legend, LineChart, ResponsiveContainer, Tooltip } from 'recharts'; -import { getChartColor, getChartColorClass } from '@/shared'; +import type { ChartData } from '@/entities'; -type DataPoint = { - year: string; - [key: string]: string | number; -}; +import { ChartAxis, ChartGrid, ChartLineItem, LegendLine } from '../../common'; type Props = { - data: DataPoint[]; - lines: { - key: string; - name: string; - showDot?: boolean; - }[]; - title?: string; - height?: number; - yAxisFormatter?: (value: number) => string; - tooltipFormatter?: (value: number, name: string) => [string, string]; - labelFormatter?: (label: string) => string; + chartData: ChartData; }; -export const ChartBox = ({ - data, - lines, - title, - yAxisFormatter = (value) => value.toFixed(2), - tooltipFormatter = (value, name) => [`${value}%`, name], - labelFormatter = (label) => `20${label}년`, -}: Props) => { +export const ChartBox = ({ chartData }: Props) => { + const { title, lines, data } = chartData; + return ( -
- {title && ( -

{title}

- )} +
+

{title}

- - {/* 가로선 - 실선 */} - - {/* 세로선 - 점선 */} - - - - + + + + { if (!payload || payload.length === 0) return null; - // lines 배열의 순서대로 범례를 생성 - return ( -
    - {lines.map((line, index) => ( -
  • -
    - {line.name} -
  • - ))} -
- ); + return ; }} /> - {lines.map((line, index) => { - const color = getChartColor(index); - return ( - - ); - })} + {lines.map((line, index) => ( + + ))}
From 68ff9ca95f09c3665f01522c241f9e7e39149208 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 18:25:08 +0900 Subject: [PATCH 03/41] =?UTF-8?q?feat:=20=EC=B0=A8=ED=8A=B8=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EA=B4=80=EB=A0=A8=20hook(useChartConfig)=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/main/hooks/index.ts | 1 + src/features/main/hooks/useChartConfig.ts | 95 +++++++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 src/features/main/hooks/useChartConfig.ts diff --git a/src/features/main/hooks/index.ts b/src/features/main/hooks/index.ts index e69de29..6eb5c2e 100644 --- a/src/features/main/hooks/index.ts +++ b/src/features/main/hooks/index.ts @@ -0,0 +1 @@ +export * from './useChartConfig'; diff --git a/src/features/main/hooks/useChartConfig.ts b/src/features/main/hooks/useChartConfig.ts new file mode 100644 index 0000000..96bfb5b --- /dev/null +++ b/src/features/main/hooks/useChartConfig.ts @@ -0,0 +1,95 @@ +/** + * 차트 설정 관련 hook + */ +import { getChartColor, getChartColorClass } from '@/shared/utils/chart/chart-colors'; + +import type { ChartLine } from '@/entities'; + +export const useChartConfig = () => { + // 차트 기본 설정 + const chartConfig = { + margin: { top: 5, right: 30, left: 0, bottom: 5 }, + height: 300, + }; + + // X축 설정 + const xAxisConfig = { + stroke: '#BDBDBF', + fontSize: 14, + tickLine: false, + axisLine: true, + tickMargin: 10, + }; + + // Y축 설정 + const yAxisConfig = { + stroke: '#BDBDBF', + fontSize: 12, + tickLine: false, + axisLine: false, + }; + + // 그리드 설정 + const gridConfig = { + horizontal: { + horizontal: true, + vertical: false, + strokeDasharray: '0', + stroke: '#BDBDBF', + }, + vertical: { + horizontal: false, + vertical: true, + strokeDasharray: '3 3', + stroke: '#f0f0f0', + }, + }; + + // 범례 설정 + const legendConfig = { + verticalAlign: 'top' as const, + align: 'left' as const, + height: 36, + wrapperStyle: { + paddingBottom: '10px', + marginLeft: '30px', + marginTop: '-20px', + }, + }; + + // 라인 설정 생성 함수 + const getLineConfig = (line: ChartLine, index: number) => ({ + key: line.key, + type: 'monotone' as const, + dataKey: line.key, + stroke: getChartColor(index), + strokeWidth: 2, + dot: { + fill: getChartColor(index), + strokeWidth: 2, + r: 4, + }, + activeDot: { + r: 6, + stroke: getChartColor(index), + strokeWidth: 2, + }, + }); + + // 범례 아이템 설정 생성 함수 + const getLegendItemConfig = (line: ChartLine, index: number) => ({ + key: line.key, + colorClass: getChartColorClass(index), + name: line.name, + }); + + return { + chartConfig, + xAxisConfig, + yAxisConfig, + gridConfig, + legendConfig, + getLineConfig, + getLegendItemConfig, + }; +}; From d6b469ee82c7083357f6ea2962c166f554a053ba Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 18:25:57 +0900 Subject: [PATCH 04/41] =?UTF-8?q?feat:=20=EC=95=84=EC=9D=B4=EC=BD=98=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/common/icons/NeedleIcon.tsx | 27 +++++++++++++++++++ src/shared/components/common/icons/index.ts | 1 + src/shared/components/common/index.ts | 1 + 3 files changed, 29 insertions(+) create mode 100644 src/shared/components/common/icons/NeedleIcon.tsx create mode 100644 src/shared/components/common/icons/index.ts diff --git a/src/shared/components/common/icons/NeedleIcon.tsx b/src/shared/components/common/icons/NeedleIcon.tsx new file mode 100644 index 0000000..ff52d89 --- /dev/null +++ b/src/shared/components/common/icons/NeedleIcon.tsx @@ -0,0 +1,27 @@ +import { type SVGProps } from 'react'; + +type NeedleIconProps = { + className?: string; +} & SVGProps; + +export const NeedleIcon = ({ className = '', ...props }: NeedleIconProps) => { + return ( + + {/* 바늘 몸체 (삼각형 모양) */} + + {/* 바늘 베이스 (원형) */} + + + ); +}; diff --git a/src/shared/components/common/icons/index.ts b/src/shared/components/common/icons/index.ts new file mode 100644 index 0000000..5964166 --- /dev/null +++ b/src/shared/components/common/icons/index.ts @@ -0,0 +1 @@ +export { NeedleIcon } from './NeedleIcon'; diff --git a/src/shared/components/common/index.ts b/src/shared/components/common/index.ts index 4790b7f..f26d5b1 100644 --- a/src/shared/components/common/index.ts +++ b/src/shared/components/common/index.ts @@ -1,2 +1,3 @@ export * from './logo-section'; export * from './image-preloader'; +export * from './icons'; From 724a13a33611ebb813fbf0128ff39cb45049c13b Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 18:29:49 +0900 Subject: [PATCH 05/41] =?UTF-8?q?feat:=20=EC=B0=A8=ED=8A=B8=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EB=AA=A9=EB=A1=9D=EC=9D=84=20CHART=5FDATA?= =?UTF-8?q?=5FLIST=EB=A1=9C=20=EB=B3=80=EA=B2=BD=ED=95=98=EA=B3=A0=20?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/main/ui/ChartSection.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/features/main/ui/ChartSection.tsx b/src/features/main/ui/ChartSection.tsx index d9698b2..a9c8737 100644 --- a/src/features/main/ui/ChartSection.tsx +++ b/src/features/main/ui/ChartSection.tsx @@ -1,13 +1,13 @@ import { ChartBox } from '../components'; -import { CHART_DATA_CONFIGS } from '../mock'; +import { CHART_DATA_LIST } from '../mock'; export const ChartSection = () => { return (
-
- {CHART_DATA_CONFIGS.map((config) => ( -
- +
+ {CHART_DATA_LIST.map((chartData, index) => ( +
+
))}
From 41baf304a863b25ebac050496a701afd94b4eeb8 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 18:40:36 +0900 Subject: [PATCH 06/41] =?UTF-8?q?fix:=20=EC=B0=A8=ED=8A=B8=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EA=B5=AC=EC=A1=B0=20=EA=B0=9C=EC=84=A0=20?= =?UTF-8?q?=EB=B0=8F=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=ED=95=AD=EB=AA=A9=20?= =?UTF-8?q?=EB=B0=8F=20=EC=A0=9C=EB=AA=A9=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/main/mock/chart-data.mock.ts | 135 +++++++++++----------- 1 file changed, 65 insertions(+), 70 deletions(-) diff --git a/src/features/main/mock/chart-data.mock.ts b/src/features/main/mock/chart-data.mock.ts index f42fc2d..024ef33 100644 --- a/src/features/main/mock/chart-data.mock.ts +++ b/src/features/main/mock/chart-data.mock.ts @@ -1,84 +1,79 @@ -// 임의의 데이터 생성 (2020-2025년) +/** + * 차트 임시 데이터 + */ +import type { ChartData } from '@/entities'; -// 최근 6개월 가격 지수 변동률 - -const CHART_DATA = { - // 최근 6개월 가격 지수 변동률 - RECENT_PRICE_FLUCTUATIONS: [ - { year: '20', 기준치: 0, 변동률: 2.5 }, - { year: '21', 기준치: 0, 변동률: 1.8 }, - { year: '22', 기준치: 0, 변동률: 0.9 }, - { year: '23', 기준치: 0, 변동률: 3.2 }, - { year: '24', 기준치: 0, 변동률: 1.5 }, - { year: '25', 기준치: 0, 변동률: 2.1 }, - ], - // LTV 전세금, 매매가 그래프 - REAL_ESTATE_PRICE_DATA: [ - { year: '20', 전세금: 2.8, 매매가: 3.2, 임대료: 1.5 }, - { year: '21', 전세금: 3.1, 매매가: 3.5, 임대료: 1.8 }, - { year: '22', 전세금: 2.5, 매매가: 2.9, 임대료: 1.2 }, - { year: '23', 전세금: 3.8, 매매가: 4.1, 임대료: 2.1 }, - { year: '24', 전세금: 3.0, 매매가: 3.3, 임대료: 1.6 }, - { year: '25', 전세금: 3.4, 매매가: 3.7, 임대료: 1.9 }, - ], - // 해당 지역 대위변제 발생 빈도 및 증가율 - REGIONAL_INCREASE_RATE: [ - { year: '20', 증가율: 1.2 }, - { year: '21', 증가율: 1.8 }, - { year: '22', 증가율: 0.9 }, - { year: '23', 증가율: 2.5 }, - { year: '24', 증가율: 1.6 }, - { year: '25', 증가율: 2.1 }, - ], -}; - -export const CHART_DATA_CONFIGS = [ +// 차트 데이터 배열 +export const CHART_DATA_LIST: ChartData[] = [ { - title: '최근 6개월 가격 지수 변동률', - data: CHART_DATA.RECENT_PRICE_FLUCTUATIONS, + title: '부동산 가격 변동 추이', lines: [ - { - key: '기준치', - name: '기준치', - showDot: false, - }, - { - key: '변동률', - name: '변동률', - showDot: true, - }, + { key: '전세금', name: '전세금' }, + { key: '매매가', name: '매매가' }, + { key: '임대료', name: '임대료' }, + ], + data: [ + { month: '1월', 전세금: 400, 매매가: 800, 임대료: 50 }, + { month: '2월', 전세금: 420, 매매가: 820, 임대료: 52 }, + { month: '3월', 전세금: 450, 매매가: 850, 임대료: 55 }, + { month: '4월', 전세금: 480, 매매가: 880, 임대료: 58 }, + { month: '5월', 전세금: 500, 매매가: 900, 임대료: 60 }, + { month: '6월', 전세금: 520, 매매가: 920, 임대료: 62 }, + { month: '7월', 전세금: 550, 매매가: 950, 임대료: 65 }, + { month: '8월', 전세금: 580, 매매가: 980, 임대료: 68 }, + { month: '9월', 전세금: 600, 매매가: 1000, 임대료: 70 }, + { month: '10월', 전세금: 620, 매매가: 1020, 임대료: 72 }, + { month: '11월', 전세금: 650, 매매가: 1050, 임대료: 75 }, + { month: '12월', 전세금: 680, 매매가: 1080, 임대료: 78 }, ], }, { - title: 'LTV 전세금, 매매가 그래프', - data: CHART_DATA.REAL_ESTATE_PRICE_DATA, + title: '지역별 가격 변동률', lines: [ - { - key: '전세금', - name: '전세금', - showDot: true, - }, - { - key: '매매가', - name: '매매가', - showDot: true, - }, - { - key: '임대료', - name: '임대료', - showDot: true, - }, + { key: '강남구', name: '강남구' }, + { key: '서초구', name: '서초구' }, + { key: '마포구', name: '마포구' }, + { key: '송파구', name: '송파구' }, + ], + data: [ + { month: '1월', 강남구: 2.1, 서초구: 1.8, 마포구: 1.5, 송파구: 1.9 }, + { month: '2월', 강남구: 2.3, 서초구: 2.0, 마포구: 1.7, 송파구: 2.1 }, + { month: '3월', 강남구: 2.5, 서초구: 2.2, 마포구: 1.9, 송파구: 2.3 }, + { month: '4월', 강남구: 2.8, 서초구: 2.5, 마포구: 2.1, 송파구: 2.6 }, + { month: '5월', 강남구: 3.0, 서초구: 2.7, 마포구: 2.3, 송파구: 2.8 }, + { month: '6월', 강남구: 3.2, 서초구: 2.9, 마포구: 2.5, 송파구: 3.0 }, + { month: '7월', 강남구: 3.5, 서초구: 3.1, 마포구: 2.7, 송파구: 3.2 }, + { month: '8월', 강남구: 3.7, 서초구: 3.3, 마포구: 2.9, 송파구: 3.4 }, + { month: '9월', 강남구: 3.9, 서초구: 3.5, 마포구: 3.1, 송파구: 3.6 }, + { month: '10월', 강남구: 4.1, 서초구: 3.7, 마포구: 3.3, 송파구: 3.8 }, + { month: '11월', 강남구: 4.3, 서초구: 3.9, 마포구: 3.5, 송파구: 4.0 }, + { month: '12월', 강남구: 4.5, 서초구: 4.1, 마포구: 3.7, 송파구: 4.2 }, ], }, { - title: '해당 지역 대위변제 발생 빈도 및 증가율', - data: CHART_DATA.REGIONAL_INCREASE_RATE, + title: '대위변제 발생률 추이', lines: [ - { - key: '증가율', - name: '증가율', - showDot: true, - }, + { key: '발생률', name: '발생률' }, + { key: '평균', name: '평균' }, + ], + data: [ + { month: '1월', 발생률: 1.2, 평균: 1.5 }, + { month: '2월', 발생률: 1.4, 평균: 1.5 }, + { month: '3월', 발생률: 1.1, 평균: 1.5 }, + { month: '4월', 발생률: 1.6, 평균: 1.5 }, + { month: '5월', 발생률: 1.3, 평균: 1.5 }, + { month: '6월', 발생률: 1.8, 평균: 1.5 }, + { month: '7월', 발생률: 1.5, 평균: 1.5 }, + { month: '8월', 발생률: 1.7, 평균: 1.5 }, + { month: '9월', 발생률: 1.4, 평균: 1.5 }, + { month: '10월', 발생률: 1.9, 평균: 1.5 }, + { month: '11월', 발생률: 1.6, 평균: 1.5 }, + { month: '12월', 발생률: 1.3, 평균: 1.5 }, ], }, ]; + +// 개별 차트 데이터 (기존 코드와의 호환성을 위해 유지) +export const REAL_ESTATE_PRICE_DATA = CHART_DATA_LIST[0]; +export const REGIONAL_PRICE_CHANGE_DATA = CHART_DATA_LIST[1]; +export const SUBROGATION_RATE_DATA = CHART_DATA_LIST[2]; From d59ccf1dc38f085a820f05b167cff48e56d4a1c6 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 18:40:50 +0900 Subject: [PATCH 07/41] =?UTF-8?q?feat:=20=EC=9C=84=ED=97=98=EB=8F=84=20?= =?UTF-8?q?=EC=B0=A8=ED=8A=B8=20=EA=B5=AC=EA=B0=84=20=EC=A0=95=EC=9D=98=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B4=80=EB=A0=A8=20=ED=83=80=EC=9E=85=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/shared/constants/index.ts | 1 + src/shared/constants/risk-chart-segments.ts | 54 +++++++++++++++++++++ 2 files changed, 55 insertions(+) create mode 100644 src/shared/constants/risk-chart-segments.ts diff --git a/src/shared/constants/index.ts b/src/shared/constants/index.ts index 194fd2d..0a8f9ed 100644 --- a/src/shared/constants/index.ts +++ b/src/shared/constants/index.ts @@ -1 +1,2 @@ export * from './router-path'; +export * from './risk-chart-segments'; diff --git a/src/shared/constants/risk-chart-segments.ts b/src/shared/constants/risk-chart-segments.ts new file mode 100644 index 0000000..b03d42a --- /dev/null +++ b/src/shared/constants/risk-chart-segments.ts @@ -0,0 +1,54 @@ +/** + * 위험도 차트 구간 정의 + */ + +// 타입 정의 +export type RiskLevel = '위험' | '다소 위험' | '양호' | '다소안전' | '안전'; + +export interface RiskSegment { + name: RiskLevel; + value: number; + fill: string; +} + +// 색상 상수 +const COLORS = { + RED: '#ff6f6f', + ORANGE: '#ffba6f', + YELLOW: '#ffe93f', + YELLOW_GREEN: '#d9ff41', + GREEN: '#2cdf44', +} as const; + +// 값 상수 +const VALUES = { + RISK: 20, + MODERATE_RISK: 20, + GOOD: 20, + MODERATE_SAFE: 20, + SAFE: 20, +} as const; + +// 이름 상수 +const NAMES = { + RISK: '위험' as const, + MODERATE_RISK: '다소 위험' as const, + GOOD: '양호' as const, + MODERATE_SAFE: '다소안전' as const, + SAFE: '안전' as const, +} as const; + +// 위험도 구간 정의 +export const RISK_CHART_SEGMENTS: readonly RiskSegment[] = [ + { name: NAMES.RISK, value: VALUES.RISK, fill: COLORS.RED }, + { name: NAMES.MODERATE_RISK, value: VALUES.MODERATE_RISK, fill: COLORS.ORANGE }, + { name: NAMES.GOOD, value: VALUES.GOOD, fill: COLORS.YELLOW }, + { name: NAMES.MODERATE_SAFE, value: VALUES.MODERATE_SAFE, fill: COLORS.YELLOW_GREEN }, + { name: NAMES.SAFE, value: VALUES.SAFE, fill: COLORS.GREEN }, +]; + +// 구간 인덱스 타입 +export type RiskSegmentIndex = 0 | 1 | 2 | 3 | 4; + +// 점수 범위 타입 +export type RiskScore = number & { __brand: 'RiskScore' }; From 21f97ec81607b41c00d30c0126f090edae69716f Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 18:41:12 +0900 Subject: [PATCH 08/41] =?UTF-8?q?feat:=20=EC=9C=84=ED=97=98=20=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20=EC=9A=94=EC=95=BD=20=EC=84=B9=EC=85=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/ui/RiskAnalysisSummarySection.tsx | 60 +++++++++++++++++++ src/features/main/ui/index.ts | 1 + 2 files changed, 61 insertions(+) create mode 100644 src/features/main/ui/RiskAnalysisSummarySection.tsx diff --git a/src/features/main/ui/RiskAnalysisSummarySection.tsx b/src/features/main/ui/RiskAnalysisSummarySection.tsx new file mode 100644 index 0000000..3b06f8f --- /dev/null +++ b/src/features/main/ui/RiskAnalysisSummarySection.tsx @@ -0,0 +1,60 @@ +import { RiskChartBox, RiskFactorsBox } from '../components'; +import { DEFAULT_RISK_ANALYSIS_DATA, TEMP_RISK_ANALYSIS_DATA, getRiskGradeColor } from '../mock'; + +export const RiskAnalysisSummarySection = () => { + // 현재 사용할 임시 데이터 + const currentData = DEFAULT_RISK_ANALYSIS_DATA; // 사진과 일치하는 기본 데이터 + + return ( +
+ {/* 임시 데이터 정보 표시 */} +
+

임시 데이터

+

+ 현재 데이터:{' '} + + {currentData.data.riskSummary.grade} 구간 ({currentData.data.riskSummary.score}점) + +

+

다른 데이터를 보려면 currentData를 변경하세요

+
+ + {/* 메인 위험도 분석 섹션 */} +
+ {/* 왼쪽: 위험도 게이지 */} + + + {/* 오른쪽: 핵심 위험 요인 */} + +
+ + {/* 모든 임시 데이터 미리보기 */} +
+

모든 임시 데이터 미리보기

+
+ {TEMP_RISK_ANALYSIS_DATA.map((data, index) => ( +
+

+ {data.data.riskSummary.grade} 구간 ({data.data.riskSummary.score}점) +

+

임대인: {data.data.landlord.name}

+

+ 신뢰도: {data.data.landlordTrust.grade} ({data.data.landlordTrust.trustScore}점) +

+
+

주요 위험 요인:

+
    + {data.data.riskSummary.factors.slice(0, 2).map((factor, factorIndex) => ( +
  • + {factor.name}: {factor.percent}% +
  • + ))} +
+
+
+ ))} +
+
+
+ ); +}; diff --git a/src/features/main/ui/index.ts b/src/features/main/ui/index.ts index 724d6dc..d8dd51d 100644 --- a/src/features/main/ui/index.ts +++ b/src/features/main/ui/index.ts @@ -1,3 +1,4 @@ export * from './ChartSection'; export * from './InputSection'; export * from './MapSection'; +export * from './RiskAnalysisSummarySection'; From 3b411d1ac38a2444800e46ea47bc9b9564fdd81d Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 18:41:36 +0900 Subject: [PATCH 09/41] =?UTF-8?q?feat:=20=EC=9C=84=ED=97=98=20=EA=B5=AC?= =?UTF-8?q?=EA=B0=84=20=EC=A0=95=EC=9D=98=20=EB=B0=8F=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EB=AA=A8=EB=8D=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/main/model/data/index.ts | 1 + src/features/main/model/data/risk-segment.ts | 8 ++++++++ src/features/main/model/index.ts | 1 + 3 files changed, 10 insertions(+) create mode 100644 src/features/main/model/data/index.ts create mode 100644 src/features/main/model/data/risk-segment.ts create mode 100644 src/features/main/model/index.ts diff --git a/src/features/main/model/data/index.ts b/src/features/main/model/data/index.ts new file mode 100644 index 0000000..7c78729 --- /dev/null +++ b/src/features/main/model/data/index.ts @@ -0,0 +1 @@ +export * from './risk-segment'; diff --git a/src/features/main/model/data/risk-segment.ts b/src/features/main/model/data/risk-segment.ts new file mode 100644 index 0000000..86345ac --- /dev/null +++ b/src/features/main/model/data/risk-segment.ts @@ -0,0 +1,8 @@ +// 5개 구간 정의 (각각 20점씩) - 왼쪽부터 안전/다소안전/양호/다소위험/위험 +export const RISK_SEGMENTS = [ + { name: '위험', value: 20, fill: '#ef4444' }, + { name: '다소 위험', value: 20, fill: '#f97316' }, + { name: '양호', value: 20, fill: '#fbbf24' }, + { name: '다소안전', value: 20, fill: '#34d399' }, + { name: '안전', value: 20, fill: '#10b981' }, +]; diff --git a/src/features/main/model/index.ts b/src/features/main/model/index.ts new file mode 100644 index 0000000..3707679 --- /dev/null +++ b/src/features/main/model/index.ts @@ -0,0 +1 @@ +export * from './data'; From 5045033c7e491286935960a1798aeb20bd83a8eb Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 18:56:14 +0900 Subject: [PATCH 10/41] =?UTF-8?q?feat:=20=EC=9C=84=ED=97=98=20=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20=EB=AA=A8=EB=93=88=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/main/mock/index.ts | 1 + src/features/main/mock/risk-analysis.mock.ts | 310 +++++++++++++++++++ 2 files changed, 311 insertions(+) create mode 100644 src/features/main/mock/risk-analysis.mock.ts diff --git a/src/features/main/mock/index.ts b/src/features/main/mock/index.ts index 67bf2e5..5627d49 100644 --- a/src/features/main/mock/index.ts +++ b/src/features/main/mock/index.ts @@ -1 +1,2 @@ export * from './chart-data.mock'; +export * from './risk-analysis.mock'; diff --git a/src/features/main/mock/risk-analysis.mock.ts b/src/features/main/mock/risk-analysis.mock.ts new file mode 100644 index 0000000..ca66f25 --- /dev/null +++ b/src/features/main/mock/risk-analysis.mock.ts @@ -0,0 +1,310 @@ +/** + * 위험도 분석 임시 데이터 + */ +import type { RiskAnalysisResponse } from '@/entities'; + +// 서버 응답 형태의 임시 데이터들 +export const TEMP_RISK_ANALYSIS_DATA: RiskAnalysisResponse[] = [ + { + data: { + riskSummary: { + score: 0, + grade: '안전', + factors: [ + { name: '전세가율', percent: 0 }, + { name: '가격하락', percent: 0 }, + { name: '미분양(재고)', percent: 0 }, + { name: '정책/규제', percent: 0 }, + { name: '법적 리스크', percent: 0 }, + ], + }, + landlord: { + landlordId: 1, + name: '홍길동', + normalizedKey: '부산-사업자번호-해시', + ownedCount: 1, + grade: 'A', + createdAt: '2025-08-21T02:26:22.055226', + updatedAt: '2025-08-21T02:26:22.055226', + }, + landlordTrust: { + trustScore: 95, + subrogationCount: 0, + arrearsCount: 0, + litigationCount: 0, + ownedUnsoldCount: 0, + grade: 'A', + }, + landlordPlaces: [ + { + placeId: 1, + label: '부산광역시 해운대구 센텀동로 45', + address: '부산광역시 해운대구 센텀동로 45', + addressDetail: '101동 1001호', + }, + ], + }, + status: 'SUCCESS', + serverDateTime: '2025-08-21T02:28:11.398139', + errorCode: null, + errorMessage: null, + }, + { + data: { + riskSummary: { + score: 15, + grade: '위험', + factors: [ + { name: '전세가율', percent: 20 }, + { name: '가격하락', percent: 33 }, + { name: '미분양(재고)', percent: 13 }, + { name: '정책/규제', percent: 20 }, + { name: '법적 리스크', percent: 13 }, + ], + }, + landlord: { + landlordId: 2, + name: '김철수', + normalizedKey: '서울-사업자번호-해시', + ownedCount: 3, + grade: 'B', + createdAt: '2025-08-21T02:26:22.055226', + updatedAt: '2025-08-21T02:26:22.055226', + }, + landlordTrust: { + trustScore: 75, + subrogationCount: 1, + arrearsCount: 0, + litigationCount: 0, + ownedUnsoldCount: 1, + grade: 'B', + }, + landlordPlaces: [ + { + placeId: 2, + label: '서울특별시 강남구 테헤란로 123', + address: '서울특별시 강남구 테헤란로 123', + addressDetail: '201동 1501호', + }, + ], + }, + status: 'SUCCESS', + serverDateTime: '2025-08-21T02:28:11.398139', + errorCode: null, + errorMessage: null, + }, + { + data: { + riskSummary: { + score: 35, + grade: '다소 위험', + factors: [ + { name: '전세가율', percent: 15 }, + { name: '가격하락', percent: 25 }, + { name: '미분양(재고)', percent: 10 }, + { name: '정책/규제', percent: 15 }, + { name: '법적 리스크', percent: 10 }, + ], + }, + landlord: { + landlordId: 3, + name: '이영희', + normalizedKey: '대구-사업자번호-해시', + ownedCount: 2, + grade: 'B', + createdAt: '2025-08-21T02:26:22.055226', + updatedAt: '2025-08-21T02:26:22.055226', + }, + landlordTrust: { + trustScore: 65, + subrogationCount: 0, + arrearsCount: 1, + litigationCount: 0, + ownedUnsoldCount: 1, + grade: 'C', + }, + landlordPlaces: [ + { + placeId: 3, + label: '대구광역시 수성구 동대구로 456', + address: '대구광역시 수성구 동대구로 456', + addressDetail: '301동 2001호', + }, + ], + }, + status: 'SUCCESS', + serverDateTime: '2025-08-21T02:28:11.398139', + errorCode: null, + errorMessage: null, + }, + { + data: { + riskSummary: { + score: 55, + grade: '양호', + factors: [ + { name: '전세가율', percent: 10 }, + { name: '가격하락', percent: 18 }, + { name: '미분양(재고)', percent: 8 }, + { name: '정책/규제', percent: 12 }, + { name: '법적 리스크', percent: 8 }, + ], + }, + landlord: { + landlordId: 4, + name: '박민수', + normalizedKey: '인천-사업자번호-해시', + ownedCount: 1, + grade: 'A', + createdAt: '2025-08-21T02:26:22.055226', + updatedAt: '2025-08-21T02:26:22.055226', + }, + landlordTrust: { + trustScore: 85, + subrogationCount: 0, + arrearsCount: 0, + litigationCount: 0, + ownedUnsoldCount: 0, + grade: 'A', + }, + landlordPlaces: [ + { + placeId: 4, + label: '인천광역시 연수구 송도동로 789', + address: '인천광역시 연수구 송도동로 789', + addressDetail: '401동 3001호', + }, + ], + }, + status: 'SUCCESS', + serverDateTime: '2025-08-21T02:28:11.398139', + errorCode: null, + errorMessage: null, + }, + { + data: { + riskSummary: { + score: 75, + grade: '다소 안전', + factors: [ + { name: '전세가율', percent: 5 }, + { name: '가격하락', percent: 12 }, + { name: '미분양(재고)', percent: 5 }, + { name: '정책/규제', percent: 8 }, + { name: '법적 리스크', percent: 5 }, + ], + }, + landlord: { + landlordId: 5, + name: '최지영', + normalizedKey: '부산-사업자번호-해시2', + ownedCount: 1, + grade: 'A', + createdAt: '2025-08-21T02:26:22.055226', + updatedAt: '2025-08-21T02:26:22.055226', + }, + landlordTrust: { + trustScore: 90, + subrogationCount: 0, + arrearsCount: 0, + litigationCount: 0, + ownedUnsoldCount: 0, + grade: 'A', + }, + landlordPlaces: [ + { + placeId: 5, + label: '부산광역시 해운대구 센텀동로 67', + address: '부산광역시 해운대구 센텀동로 67', + addressDetail: '501동 4001호', + }, + ], + }, + status: 'SUCCESS', + serverDateTime: '2025-08-21T02:28:11.398139', + errorCode: null, + errorMessage: null, + }, + { + data: { + riskSummary: { + score: 95, + grade: '매우 안전', + factors: [ + { name: '전세가율', percent: 2 }, + { name: '가격하락', percent: 5 }, + { name: '미분양(재고)', percent: 2 }, + { name: '정책/규제', percent: 3 }, + { name: '법적 리스크', percent: 2 }, + ], + }, + landlord: { + landlordId: 6, + name: '정수진', + normalizedKey: '서울-사업자번호-해시2', + ownedCount: 1, + grade: 'A', + createdAt: '2025-08-21T02:26:22.055226', + updatedAt: '2025-08-21T02:26:22.055226', + }, + landlordTrust: { + trustScore: 98, + subrogationCount: 0, + arrearsCount: 0, + litigationCount: 0, + ownedUnsoldCount: 0, + grade: 'A', + }, + landlordPlaces: [ + { + placeId: 6, + label: '서울특별시 강남구 테헤란로 456', + address: '서울특별시 강남구 테헤란로 456', + addressDetail: '601동 5001호', + }, + ], + }, + status: 'SUCCESS', + serverDateTime: '2025-08-21T02:28:11.398139', + errorCode: null, + errorMessage: null, + }, +]; + +// 기본 위험도 데이터 (사진과 일치) +export const DEFAULT_RISK_ANALYSIS_DATA: RiskAnalysisResponse = TEMP_RISK_ANALYSIS_DATA[0]; + +// 위험도 구간별 설명 +export const RISK_LEVEL_DESCRIPTIONS = { + SAFE: '매우 안전한 전세 계약입니다.', + MODERATE_SAFE: '안전한 전세 계약입니다.', + GOOD: '양호한 전세 계약입니다.', + MODERATE_RISK: '주의가 필요한 전세 계약입니다.', + RISK: '위험한 전세 계약입니다.', +} as const; + +// 위험도 점수별 등급 매핑 +export const getRiskGrade = (score: number): string => { + if (score <= 20) return '매우 안전'; + if (score <= 40) return '안전'; + if (score <= 60) return '양호'; + if (score <= 80) return '다소 위험'; + return '위험'; +}; + +// 위험도 등급별 색상 +export const getRiskGradeColor = (grade: string): string => { + switch (grade) { + case '매우 안전': + case '안전': + return 'text-green-600'; + case '양호': + return 'text-blue-600'; + case '다소 위험': + return 'text-yellow-600'; + case '위험': + return 'text-red-600'; + default: + return 'text-gray-600'; + } +}; From 67a9580586e6c00d1e6541053cd429718e83fd81 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 18:59:18 +0900 Subject: [PATCH 11/41] =?UTF-8?q?feat:=20=EC=9C=84=ED=97=98=EB=8F=84=20?= =?UTF-8?q?=EC=B0=A8=ED=8A=B8=20=EA=B4=80=EB=A0=A8=20=EC=9C=A0=ED=8B=B8?= =?UTF-8?q?=EB=A6=AC=ED=8B=B0=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=EB=AA=A8?= =?UTF-8?q?=EB=93=88=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/shared/utils/chart/index.ts | 1 + src/shared/utils/chart/risk-chart.ts | 90 ++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 src/shared/utils/chart/risk-chart.ts diff --git a/src/shared/utils/chart/index.ts b/src/shared/utils/chart/index.ts index e616679..951d09b 100644 --- a/src/shared/utils/chart/index.ts +++ b/src/shared/utils/chart/index.ts @@ -1 +1,2 @@ export * from './chart-colors'; +export * from './risk-chart'; diff --git a/src/shared/utils/chart/risk-chart.ts b/src/shared/utils/chart/risk-chart.ts new file mode 100644 index 0000000..c88613d --- /dev/null +++ b/src/shared/utils/chart/risk-chart.ts @@ -0,0 +1,90 @@ +/** + * 위험도 게이지 차트 관련 유틸리티 + */ +import { + RISK_CHART_SEGMENTS, + type RiskScore, + type RiskSegment, + type RiskSegmentIndex, +} from '../../constants'; + +// 게이지 데이터 타입 정의 +export interface GaugeData { + data: RiskSegment[]; + color: string; + label: string; + angle: number; +} + +// 상수 정의 +const MAX_SCORE = 100; +const MAX_ANGLE = 180; +const SEGMENT_SIZE = 20; + +// 점수 범위 검증 함수 +export const isValidRiskScore = (score: number): score is RiskScore => { + return score >= 0 && score <= 100; +}; + +// 안전한 점수 변환 함수 +export const toRiskScore = (score: number): RiskScore => { + return Math.max(0, Math.min(100, score)) as RiskScore; +}; + +/** + * 위험도 점수에 따른 구간 인덱스를 계산 + * @param score - 위험도 점수 (0-100) + * @returns 구간 인덱스 (0-4) + */ +export const getSegmentIndex = (score: RiskScore): RiskSegmentIndex => { + const segmentIndex = Math.floor(score / SEGMENT_SIZE); + return Math.min(segmentIndex, 4) as RiskSegmentIndex; +}; + +/** + * 위험도 점수에 따른 구간 정보를 반환 + * @param score - 위험도 점수 (0-100) + * @returns 현재 구간 정보 + */ +export const getRiskSegment = (score: number): RiskSegment => { + const normalizedScore = toRiskScore(score); + const segmentIndex = getSegmentIndex(normalizedScore); + return RISK_CHART_SEGMENTS[segmentIndex]; +}; + +/** + * 위험도 점수에 따른 각도 계산 + * @param score - 위험도 점수 (0-100) + * @returns 바늘 각도 (0-180도) + */ +export const getGaugeAngle = (score: number): number => { + const normalizedScore = toRiskScore(score); + return (normalizedScore / MAX_SCORE) * MAX_ANGLE; +}; + +/** + * 위험도 점수에 따른 게이지 데이터를 생성 + * @param score - 위험도 점수 (0-100) + * @returns 게이지 차트에 필요한 데이터 + */ +export const getGaugeData = (score: number): GaugeData => { + const normalizedScore = toRiskScore(score); + const currentSegment = getRiskSegment(normalizedScore); + const angle = getGaugeAngle(normalizedScore); + + return { + data: RISK_CHART_SEGMENTS as RiskSegment[], + color: currentSegment.fill, + label: currentSegment.name, + angle, + }; +}; + +/** + * 위험도 점수가 유효한지 검증 + * @param score - 검증할 점수 + * @returns 유효성 여부 + */ +export const validateRiskScore = (score: number): boolean => { + return isValidRiskScore(score); +}; From 2e5bddc09cd99e062436aba8ec526e1dad4f19c4 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 18:59:56 +0900 Subject: [PATCH 12/41] =?UTF-8?q?feat:=20=EC=9C=84=ED=97=98=20=EC=9A=94?= =?UTF-8?q?=EC=9D=B8=20=EB=B0=95=EC=8A=A4=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../features/chart/RiskFactorsBox.tsx | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/features/main/components/features/chart/RiskFactorsBox.tsx diff --git a/src/features/main/components/features/chart/RiskFactorsBox.tsx b/src/features/main/components/features/chart/RiskFactorsBox.tsx new file mode 100644 index 0000000..2e83e19 --- /dev/null +++ b/src/features/main/components/features/chart/RiskFactorsBox.tsx @@ -0,0 +1,22 @@ +import type { RiskFactor } from '@/entities'; + +type Props = { + riskFactors: RiskFactor[]; +}; + +export const RiskFactorsBox = ({ riskFactors }: Props) => { + return ( +
+

핵심 위험 요인

+
    + {riskFactors.map((factor, index) => ( +
  • +
    + {factor.name} + {factor.percent}% +
  • + ))} +
+
+ ); +}; From ebd241e57bc7985dc1f9bbb8d5802ee32a745221 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 19:00:16 +0900 Subject: [PATCH 13/41] =?UTF-8?q?feat:=20=EC=9C=84=ED=97=98=EB=8F=84=20?= =?UTF-8?q?=EC=B0=A8=ED=8A=B8=20=EB=B0=95=EC=8A=A4=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../features/chart/RiskChartBox.tsx | 57 +++++++++++++++++++ .../main/components/features/chart/index.ts | 4 +- 2 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 src/features/main/components/features/chart/RiskChartBox.tsx diff --git a/src/features/main/components/features/chart/RiskChartBox.tsx b/src/features/main/components/features/chart/RiskChartBox.tsx new file mode 100644 index 0000000..32596f1 --- /dev/null +++ b/src/features/main/components/features/chart/RiskChartBox.tsx @@ -0,0 +1,57 @@ +import { Pie, PieChart, ResponsiveContainer } from 'recharts'; + +import { type GaugeData, getGaugeData } from '@/shared'; + +import { Needle } from '../../common'; + +// import NeedleIcon from '../../../_assets/needle.svg'; + +type Props = { + riskScore: number; +}; + +export const RiskChartBox = ({ riskScore }: Props) => { + const gaugeData: GaugeData = getGaugeData(riskScore); + + return ( +
+

전세 계약 최종 위험도

+

+ 위험 점수 : + {riskScore} + +

+ +
+ + + + + + + {/* --- 게이지 바늘 --- */} + + + {/* 위험도 레이블 */} +
+ {gaugeData.label} +
+
+
+ ); +}; diff --git a/src/features/main/components/features/chart/index.ts b/src/features/main/components/features/chart/index.ts index a08fef3..a51f5bc 100644 --- a/src/features/main/components/features/chart/index.ts +++ b/src/features/main/components/features/chart/index.ts @@ -1 +1,3 @@ -export * from './ChartBox'; +export { ChartBox } from './ChartBox'; +export { RiskChartBox } from './RiskChartBox'; +export { RiskFactorsBox } from './RiskFactorsBox'; From 10cf55869e60172f48692248b6303b9a719ada13 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 19:00:48 +0900 Subject: [PATCH 14/41] =?UTF-8?q?feat:=20=EC=9C=84=ED=97=98=20=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20=EC=9A=94=EC=95=BD=20=EC=84=B9=EC=85=98=EC=9D=84=20?= =?UTF-8?q?=EB=A9=94=EC=9D=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=EC=97=90=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/main/MainPage.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pages/main/MainPage.tsx b/src/pages/main/MainPage.tsx index c56903f..1fd1df3 100644 --- a/src/pages/main/MainPage.tsx +++ b/src/pages/main/MainPage.tsx @@ -1,4 +1,4 @@ -import { ChartSection, InputSection, MapSection } from '@/features'; +import { ChartSection, InputSection, MapSection, RiskAnalysisSummarySection } from '@/features'; export default function MainPage() { return ( @@ -7,6 +7,9 @@ export default function MainPage() {
+
+ +
From e89e231ae480d2f732eef81c3b4892a7c4ed966e Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 19:48:32 +0900 Subject: [PATCH 15/41] =?UTF-8?q?refactor:=20=EC=9C=84=ED=97=98=20?= =?UTF-8?q?=EC=9A=94=EC=9D=B8=20=EB=B0=95=EC=8A=A4=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=8A=A4=ED=83=80=EC=9D=BC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../features/chart/RiskFactorsBox.tsx | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/features/main/components/features/chart/RiskFactorsBox.tsx b/src/features/main/components/features/chart/RiskFactorsBox.tsx index 2e83e19..d469249 100644 --- a/src/features/main/components/features/chart/RiskFactorsBox.tsx +++ b/src/features/main/components/features/chart/RiskFactorsBox.tsx @@ -6,17 +6,22 @@ type Props = { export const RiskFactorsBox = ({ riskFactors }: Props) => { return ( -
-

핵심 위험 요인

-
    +
    +

    주요 위험 요인

    +
    {riskFactors.map((factor, index) => ( -
  • -
    - {factor.name} - {factor.percent}% -
  • +
    +
    + + {factor.name} +
    + {factor.percent} + % +
    +
    +
    ))} -
+
); }; From dbe46ec0d735323d0b67c8c6c8466c72d379adfb Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 19:49:38 +0900 Subject: [PATCH 16/41] =?UTF-8?q?fix:=20Needle=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EC=97=90=EC=84=9C=20GaugeData=EC=99=80=20Nee?= =?UTF-8?q?dleIcon=20=EC=9E=84=ED=8F=AC=ED=8A=B8=20=EA=B2=BD=EB=A1=9C=20?= =?UTF-8?q?=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/main/components/common/Needle.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/features/main/components/common/Needle.tsx b/src/features/main/components/common/Needle.tsx index 3cea4cb..519b5d4 100644 --- a/src/features/main/components/common/Needle.tsx +++ b/src/features/main/components/common/Needle.tsx @@ -1,5 +1,4 @@ -import { NeedleIcon } from '@/shared/components/common'; -import { type GaugeData } from '@/shared/utils/chart/risk-chart'; +import { type GaugeData, NeedleIcon } from '@/shared'; type Props = { gaugeData: GaugeData; From 3cb65080b0f55a886950d69b921d4f233ef8e31d Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 19:49:54 +0900 Subject: [PATCH 17/41] =?UTF-8?q?style:=20=EC=9C=84=ED=97=98=EB=8F=84=20?= =?UTF-8?q?=EC=B0=A8=ED=8A=B8=20=EB=B0=95=EC=8A=A4=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EC=9D=98=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/main/components/features/chart/RiskChartBox.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/main/components/features/chart/RiskChartBox.tsx b/src/features/main/components/features/chart/RiskChartBox.tsx index 32596f1..0084f10 100644 --- a/src/features/main/components/features/chart/RiskChartBox.tsx +++ b/src/features/main/components/features/chart/RiskChartBox.tsx @@ -14,7 +14,7 @@ export const RiskChartBox = ({ riskScore }: Props) => { const gaugeData: GaugeData = getGaugeData(riskScore); return ( -
+

전세 계약 최종 위험도

위험 점수 : From 69af24e034bee7c908a50fc5d017c0d33be87779 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 19:50:05 +0900 Subject: [PATCH 18/41] =?UTF-8?q?style:=20InputSection=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=EC=9D=98=20=ED=85=8D=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/main/ui/InputSection.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/main/ui/InputSection.tsx b/src/features/main/ui/InputSection.tsx index 193c5ec..bcd4819 100644 --- a/src/features/main/ui/InputSection.tsx +++ b/src/features/main/ui/InputSection.tsx @@ -5,8 +5,8 @@ export const InputSection = () => {

- -

계약 

+ +

계약

하려고 하는 그 집

안심할 수 있는지 확인해봐요! From 041668b489e04af39a61f23891aa201723799970 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 19:50:21 +0900 Subject: [PATCH 19/41] =?UTF-8?q?feat:=20=EC=9C=84=ED=97=98=EB=8F=84=20?= =?UTF-8?q?=EB=B6=84=EC=84=9D=20=EA=B4=80=EB=A0=A8=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20=EB=B0=8F=20=EC=9D=91=EB=8B=B5=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EB=AA=A8=EB=8D=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/entities/risk-analysis/index.ts | 59 +++++++++++++++++++ .../main/ui/RiskAnalysisSummarySection.tsx | 46 +-------------- 2 files changed, 62 insertions(+), 43 deletions(-) create mode 100644 src/entities/risk-analysis/index.ts diff --git a/src/entities/risk-analysis/index.ts b/src/entities/risk-analysis/index.ts new file mode 100644 index 0000000..c2eba79 --- /dev/null +++ b/src/entities/risk-analysis/index.ts @@ -0,0 +1,59 @@ +/** + * 위험도 분석 관련 entities + */ + +// 위험 요인 +export interface RiskFactor { + name: string; + percent: number; +} + +// 위험도 요약 +export interface RiskSummary { + score: number; + grade: string; + factors: RiskFactor[]; +} + +// 임대인 정보 +export interface Landlord { + landlordId: number; + name: string; + normalizedKey: string; + ownedCount: number; + grade: string; + createdAt: string; + updatedAt: string; +} + +// 임대인 신뢰도 +export interface LandlordTrust { + trustScore: number; + subrogationCount: number; + arrearsCount: number; + litigationCount: number; + ownedUnsoldCount: number; + grade: string; +} + +// 임대인 소유 매물 +export interface LandlordPlace { + placeId: number; + label: string; + address: string; + addressDetail: string; +} + +// 위험도 분석 응답 데이터 +export interface RiskAnalysisResponse { + data: { + riskSummary: RiskSummary; + landlord: Landlord; + landlordTrust: LandlordTrust; + landlordPlaces: LandlordPlace[]; + }; + status: string; + serverDateTime: string; + errorCode: string | null; + errorMessage: string | null; +} diff --git a/src/features/main/ui/RiskAnalysisSummarySection.tsx b/src/features/main/ui/RiskAnalysisSummarySection.tsx index 3b06f8f..7c6bd14 100644 --- a/src/features/main/ui/RiskAnalysisSummarySection.tsx +++ b/src/features/main/ui/RiskAnalysisSummarySection.tsx @@ -1,60 +1,20 @@ import { RiskChartBox, RiskFactorsBox } from '../components'; -import { DEFAULT_RISK_ANALYSIS_DATA, TEMP_RISK_ANALYSIS_DATA, getRiskGradeColor } from '../mock'; +import { DEFAULT_RISK_ANALYSIS_DATA } from '../mock'; export const RiskAnalysisSummarySection = () => { // 현재 사용할 임시 데이터 const currentData = DEFAULT_RISK_ANALYSIS_DATA; // 사진과 일치하는 기본 데이터 return ( -
- {/* 임시 데이터 정보 표시 */} -
-

임시 데이터

-

- 현재 데이터:{' '} - - {currentData.data.riskSummary.grade} 구간 ({currentData.data.riskSummary.score}점) - -

-

다른 데이터를 보려면 currentData를 변경하세요

-
- +
{/* 메인 위험도 분석 섹션 */} -
+
{/* 왼쪽: 위험도 게이지 */} {/* 오른쪽: 핵심 위험 요인 */}
- - {/* 모든 임시 데이터 미리보기 */} -
-

모든 임시 데이터 미리보기

-
- {TEMP_RISK_ANALYSIS_DATA.map((data, index) => ( -
-

- {data.data.riskSummary.grade} 구간 ({data.data.riskSummary.score}점) -

-

임대인: {data.data.landlord.name}

-

- 신뢰도: {data.data.landlordTrust.grade} ({data.data.landlordTrust.trustScore}점) -

-
-

주요 위험 요인:

-
    - {data.data.riskSummary.factors.slice(0, 2).map((factor, factorIndex) => ( -
  • - {factor.name}: {factor.percent}% -
  • - ))} -
-
-
- ))} -
-
); }; From d186abe0f9900006c3311cca7805a5e5c97261b0 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 20:06:09 +0900 Subject: [PATCH 20/41] =?UTF-8?q?style:=20=EC=9C=84=ED=97=98=EB=8F=84=20?= =?UTF-8?q?=EC=B0=A8=ED=8A=B8=20=EB=B0=95=EC=8A=A4=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EC=9D=98=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=81=AC=EA=B8=B0=20=EB=B0=8F=20=EB=A0=88=EC=9D=B4=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EC=88=98=EC=A0=95,=20=EA=B2=8C=EC=9D=B4=EC=A7=80?= =?UTF-8?q?=20=EB=B0=94=EB=8A=98=20=EC=9C=84=EC=B9=98=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/features/chart/RiskChartBox.tsx | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/features/main/components/features/chart/RiskChartBox.tsx b/src/features/main/components/features/chart/RiskChartBox.tsx index 0084f10..b93c3ac 100644 --- a/src/features/main/components/features/chart/RiskChartBox.tsx +++ b/src/features/main/components/features/chart/RiskChartBox.tsx @@ -4,8 +4,6 @@ import { type GaugeData, getGaugeData } from '@/shared'; import { Needle } from '../../common'; -// import NeedleIcon from '../../../_assets/needle.svg'; - type Props = { riskScore: number; }; @@ -14,15 +12,15 @@ export const RiskChartBox = ({ riskScore }: Props) => { const gaugeData: GaugeData = getGaugeData(riskScore); return ( -
-

전세 계약 최종 위험도

+
+

전세 계약 최종 위험도

위험 점수 : {riskScore}

-
+
{ cy='50%' startAngle={0} endAngle={180} - innerRadius={65} - outerRadius={100} + innerRadius={130} + outerRadius={180} paddingAngle={4} cornerRadius={6} dataKey='value' @@ -41,12 +39,12 @@ export const RiskChartBox = ({ riskScore }: Props) => { - {/* --- 게이지 바늘 --- */} + {/* 게이지 바늘 */} {/* 위험도 레이블 */}
{gaugeData.label} From d9f0affeafb9184a39fad35d2aaad6df09a78138 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 20:06:30 +0900 Subject: [PATCH 21/41] =?UTF-8?q?style:=20=EC=9C=84=ED=97=98=20=EB=B6=84?= =?UTF-8?q?=EC=84=9D=20=EC=9A=94=EC=95=BD=20=EC=84=B9=EC=85=98=EC=9D=98=20?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20=EB=B0=8F=20=EC=8A=A4?= =?UTF-8?q?=ED=83=80=EC=9D=BC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/main/ui/RiskAnalysisSummarySection.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/features/main/ui/RiskAnalysisSummarySection.tsx b/src/features/main/ui/RiskAnalysisSummarySection.tsx index 7c6bd14..0a134cb 100644 --- a/src/features/main/ui/RiskAnalysisSummarySection.tsx +++ b/src/features/main/ui/RiskAnalysisSummarySection.tsx @@ -6,12 +6,11 @@ export const RiskAnalysisSummarySection = () => { const currentData = DEFAULT_RISK_ANALYSIS_DATA; // 사진과 일치하는 기본 데이터 return ( -
+
{/* 메인 위험도 분석 섹션 */} -
+
{/* 왼쪽: 위험도 게이지 */} - {/* 오른쪽: 핵심 위험 요인 */}
From 6866db0b83b3d3872d6a2531316aab69dd72fc70 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 20:06:55 +0900 Subject: [PATCH 22/41] =?UTF-8?q?fix:=20ChartLineItem=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EC=97=90=EC=84=9C=20Line=20=EC=9A=94?= =?UTF-8?q?=EC=86=8C=EC=97=90=20key=20=EC=86=8D=EC=84=B1=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/main/components/common/ChartLineItem.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/features/main/components/common/ChartLineItem.tsx b/src/features/main/components/common/ChartLineItem.tsx index 53d2696..a082bfb 100644 --- a/src/features/main/components/common/ChartLineItem.tsx +++ b/src/features/main/components/common/ChartLineItem.tsx @@ -11,8 +11,7 @@ type Props = { export const ChartLineItem = ({ line, index }: Props) => { const { getLineConfig } = useChartConfig(); - const config = getLineConfig(line, index); - return ; + return ; }; From 419995e39539eb76dca3a6fce0e118949f305942 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 20:14:23 +0900 Subject: [PATCH 23/41] =?UTF-8?q?fix:=20=EC=9C=84=ED=97=98=20=EC=9A=94?= =?UTF-8?q?=EC=9D=B8=20=EB=B0=95=EC=8A=A4=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=EC=97=90=EC=84=9C=20key=20=EC=86=8D=EC=84=B1=EC=9D=84?= =?UTF-8?q?=20factor.name=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD=ED=95=98?= =?UTF-8?q?=EC=97=AC=20=EA=B3=A0=EC=9C=A0=EC=84=B1=EC=9D=84=20=ED=99=95?= =?UTF-8?q?=EB=B3=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/components/features/chart/RiskFactorsBox.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/main/components/features/chart/RiskFactorsBox.tsx b/src/features/main/components/features/chart/RiskFactorsBox.tsx index d469249..3788324 100644 --- a/src/features/main/components/features/chart/RiskFactorsBox.tsx +++ b/src/features/main/components/features/chart/RiskFactorsBox.tsx @@ -9,8 +9,8 @@ export const RiskFactorsBox = ({ riskFactors }: Props) => {

주요 위험 요인

- {riskFactors.map((factor, index) => ( -
+ {riskFactors.map((factor) => ( +
{factor.name} From 1cdd1aaaf60d4f3b2a68d1779556bbdde8ab8eaa Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 20:14:36 +0900 Subject: [PATCH 24/41] =?UTF-8?q?fix:=20getLineConfig=20=ED=95=A8=EC=88=98?= =?UTF-8?q?=EC=97=90=EC=84=9C=20key=20=EC=86=8D=EC=84=B1=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=ED=95=98=EC=97=AC=20=EC=A4=91=EB=B3=B5=20=EB=B0=A9?= =?UTF-8?q?=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/main/hooks/useChartConfig.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/features/main/hooks/useChartConfig.ts b/src/features/main/hooks/useChartConfig.ts index 96bfb5b..0bcb7ad 100644 --- a/src/features/main/hooks/useChartConfig.ts +++ b/src/features/main/hooks/useChartConfig.ts @@ -59,7 +59,6 @@ export const useChartConfig = () => { // 라인 설정 생성 함수 const getLineConfig = (line: ChartLine, index: number) => ({ - key: line.key, type: 'monotone' as const, dataKey: line.key, stroke: getChartColor(index), From 3ffd9cb7364b78c10cee086521974e931f80edf0 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 20:15:00 +0900 Subject: [PATCH 25/41] =?UTF-8?q?style:=20Needle=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EC=9D=98=20NeedleIcon=20=ED=81=AC=EA=B8=B0?= =?UTF-8?q?=20=EB=B0=8F=20=EC=9C=84=EC=B9=98=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/main/components/common/Needle.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/features/main/components/common/Needle.tsx b/src/features/main/components/common/Needle.tsx index 519b5d4..7edb443 100644 --- a/src/features/main/components/common/Needle.tsx +++ b/src/features/main/components/common/Needle.tsx @@ -14,7 +14,7 @@ export const Needle = ({ gaugeData }: Props) => { >
- +
); }; From 20e10f74fecd812b7be3ed7a9efe0603edeccb86 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 20:15:12 +0900 Subject: [PATCH 26/41] =?UTF-8?q?refactor:=20MainPage=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EC=97=90=EC=84=9C=20=EC=9C=84=ED=97=98=20?= =?UTF-8?q?=EB=B6=84=EC=84=9D=20=EC=9A=94=EC=95=BD=20=EC=84=B9=EC=85=98?= =?UTF-8?q?=EA=B3=BC=20=EC=B0=A8=ED=8A=B8=20=EC=84=B9=EC=85=98=EC=9D=98=20?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20=EA=B0=84=EC=86=8C?= =?UTF-8?q?=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/main/MainPage.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/pages/main/MainPage.tsx b/src/pages/main/MainPage.tsx index 1fd1df3..3be8d11 100644 --- a/src/pages/main/MainPage.tsx +++ b/src/pages/main/MainPage.tsx @@ -7,12 +7,8 @@ export default function MainPage() {
-
- -
-
- -
+ +
); } From 7595056471ff3461d0807c3ce5d7b9ab4a9d4bc3 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 21:02:53 +0900 Subject: [PATCH 27/41] =?UTF-8?q?refactor:=20=EC=9C=84=ED=97=98=EB=8F=84?= =?UTF-8?q?=20=EC=B0=A8=ED=8A=B8=20=EA=B4=80=EB=A0=A8=20=ED=95=A8=EC=88=98?= =?UTF-8?q?=EC=9D=98=20=EC=A3=BC=EC=84=9D=20=EC=88=98=EC=A0=95=20=EB=B0=8F?= =?UTF-8?q?=20GaugeData=20=ED=83=80=EC=9E=85=20=EC=A0=95=EC=9D=98=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/shared/utils/chart/risk-chart.ts | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/shared/utils/chart/risk-chart.ts b/src/shared/utils/chart/risk-chart.ts index c88613d..e8ef61c 100644 --- a/src/shared/utils/chart/risk-chart.ts +++ b/src/shared/utils/chart/risk-chart.ts @@ -9,12 +9,12 @@ import { } from '../../constants'; // 게이지 데이터 타입 정의 -export interface GaugeData { +export type GaugeData = { data: RiskSegment[]; color: string; label: string; angle: number; -} +}; // 상수 정의 const MAX_SCORE = 100; @@ -33,17 +33,19 @@ export const toRiskScore = (score: number): RiskScore => { /** * 위험도 점수에 따른 구간 인덱스를 계산 - * @param score - 위험도 점수 (0-100) - * @returns 구간 인덱스 (0-4) + * 점수가 낮을수록 위험하므로 인덱스를 그대로 계산 + * @param score - 위험도 점수 (0-100, 낮을수록 위험) + * @returns 구간 인덱스 (0-4, 0=위험, 4=안전) */ export const getSegmentIndex = (score: RiskScore): RiskSegmentIndex => { + // 점수가 낮을수록 위험하므로 그대로 계산 const segmentIndex = Math.floor(score / SEGMENT_SIZE); return Math.min(segmentIndex, 4) as RiskSegmentIndex; }; /** * 위험도 점수에 따른 구간 정보를 반환 - * @param score - 위험도 점수 (0-100) + * @param score - 위험도 점수 (0-100, 낮을수록 위험) * @returns 현재 구간 정보 */ export const getRiskSegment = (score: number): RiskSegment => { @@ -54,17 +56,20 @@ export const getRiskSegment = (score: number): RiskSegment => { /** * 위험도 점수에 따른 각도 계산 - * @param score - 위험도 점수 (0-100) + * 점수가 낮을수록 위험하므로 각도를 그대로 계산 + * @param score - 위험도 점수 (0-100, 낮을수록 위험) * @returns 바늘 각도 (0-180도) */ export const getGaugeAngle = (score: number): number => { const normalizedScore = toRiskScore(score); + // 점수가 낮을수록 위험하므로 각도를 그대로 계산 + // 0점(위험) = 0도, 100점(안전) = 180도 return (normalizedScore / MAX_SCORE) * MAX_ANGLE; }; /** * 위험도 점수에 따른 게이지 데이터를 생성 - * @param score - 위험도 점수 (0-100) + * @param score - 위험도 점수 (0-100, 낮을수록 위험) * @returns 게이지 차트에 필요한 데이터 */ export const getGaugeData = (score: number): GaugeData => { From c9d891ae9f09c78d79c8e3ab6a426e441f10f4a8 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 21:03:16 +0900 Subject: [PATCH 28/41] =?UTF-8?q?refactor:=20=EC=B0=A8=ED=8A=B8=20?= =?UTF-8?q?=EA=B4=80=EB=A0=A8=20=EC=97=94=ED=8B=B0=ED=8B=B0=EB=A5=BC=20typ?= =?UTF-8?q?es=20=ED=8F=B4=EB=8D=94=EB=A1=9C=20=EC=9D=B4=EB=8F=99=ED=95=98?= =?UTF-8?q?=EA=B3=A0=20=EB=B6=88=ED=95=84=EC=9A=94=ED=95=9C=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/entities/chart/index.ts | 22 +---------------- src/entities/chart/types/chart.type.ts | 21 ++++++++++++++++ src/entities/chart/types/index.ts | 3 ++- .../types/risk-analysis.type.ts} | 24 +++++++++---------- src/entities/chart/types/risk-chart.type.ts | 4 ---- 5 files changed, 36 insertions(+), 38 deletions(-) create mode 100644 src/entities/chart/types/chart.type.ts rename src/entities/{risk-analysis/index.ts => chart/types/risk-analysis.type.ts} (81%) delete mode 100644 src/entities/chart/types/risk-chart.type.ts diff --git a/src/entities/chart/index.ts b/src/entities/chart/index.ts index 98cee81..fcb073f 100644 --- a/src/entities/chart/index.ts +++ b/src/entities/chart/index.ts @@ -1,21 +1 @@ -/** - * 차트 관련 entities - */ - -// 차트 라인 데이터 -export interface ChartLine { - key: string; - name: string; -} - -// 차트 데이터 포인트 -export interface ChartDataPoint { - [key: string]: string | number; -} - -// 차트 데이터 -export interface ChartData { - title: string; - lines: ChartLine[]; - data: ChartDataPoint[]; -} +export * from './types'; diff --git a/src/entities/chart/types/chart.type.ts b/src/entities/chart/types/chart.type.ts new file mode 100644 index 0000000..98cee81 --- /dev/null +++ b/src/entities/chart/types/chart.type.ts @@ -0,0 +1,21 @@ +/** + * 차트 관련 entities + */ + +// 차트 라인 데이터 +export interface ChartLine { + key: string; + name: string; +} + +// 차트 데이터 포인트 +export interface ChartDataPoint { + [key: string]: string | number; +} + +// 차트 데이터 +export interface ChartData { + title: string; + lines: ChartLine[]; + data: ChartDataPoint[]; +} diff --git a/src/entities/chart/types/index.ts b/src/entities/chart/types/index.ts index b927d53..45df87b 100644 --- a/src/entities/chart/types/index.ts +++ b/src/entities/chart/types/index.ts @@ -1 +1,2 @@ -export * from './risk-chart.type'; +export * from './chart.type'; +export * from './risk-analysis.type'; diff --git a/src/entities/risk-analysis/index.ts b/src/entities/chart/types/risk-analysis.type.ts similarity index 81% rename from src/entities/risk-analysis/index.ts rename to src/entities/chart/types/risk-analysis.type.ts index c2eba79..767c9ed 100644 --- a/src/entities/risk-analysis/index.ts +++ b/src/entities/chart/types/risk-analysis.type.ts @@ -3,20 +3,20 @@ */ // 위험 요인 -export interface RiskFactor { +export type RiskFactor = { name: string; percent: number; -} +}; // 위험도 요약 -export interface RiskSummary { +export type RiskSummary = { score: number; grade: string; factors: RiskFactor[]; -} +}; // 임대인 정보 -export interface Landlord { +export type Landlord = { landlordId: number; name: string; normalizedKey: string; @@ -24,28 +24,28 @@ export interface Landlord { grade: string; createdAt: string; updatedAt: string; -} +}; // 임대인 신뢰도 -export interface LandlordTrust { +export type LandlordTrust = { trustScore: number; subrogationCount: number; arrearsCount: number; litigationCount: number; ownedUnsoldCount: number; grade: string; -} +}; // 임대인 소유 매물 -export interface LandlordPlace { +export type LandlordPlace = { placeId: number; label: string; address: string; addressDetail: string; -} +}; // 위험도 분석 응답 데이터 -export interface RiskAnalysisResponse { +export type RiskAnalysisResponse = { data: { riskSummary: RiskSummary; landlord: Landlord; @@ -56,4 +56,4 @@ export interface RiskAnalysisResponse { serverDateTime: string; errorCode: string | null; errorMessage: string | null; -} +}; diff --git a/src/entities/chart/types/risk-chart.type.ts b/src/entities/chart/types/risk-chart.type.ts deleted file mode 100644 index 62e3a78..0000000 --- a/src/entities/chart/types/risk-chart.type.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type RiskFactor = { - name: string; - percentage: number; -}; From 4190df2774657957e41ee3bb9d89f6ece6270a0d Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 21:03:39 +0900 Subject: [PATCH 29/41] =?UTF-8?q?refactor:=20=EC=9C=84=ED=97=98=EB=8F=84?= =?UTF-8?q?=20=EB=A0=88=EB=B2=A8=20=EB=B0=8F=20=EC=83=89=EC=83=81=20?= =?UTF-8?q?=EC=83=81=EC=88=98=EC=9D=98=20=EB=AA=85=EC=B9=AD=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/shared/constants/risk-chart-segments.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/shared/constants/risk-chart-segments.ts b/src/shared/constants/risk-chart-segments.ts index b03d42a..e020259 100644 --- a/src/shared/constants/risk-chart-segments.ts +++ b/src/shared/constants/risk-chart-segments.ts @@ -3,13 +3,13 @@ */ // 타입 정의 -export type RiskLevel = '위험' | '다소 위험' | '양호' | '다소안전' | '안전'; +export type RiskLevel = '안전' | '다소 안전' | '양호' | '다소 위험' | '위험'; -export interface RiskSegment { +export type RiskSegment = { name: RiskLevel; value: number; fill: string; -} +}; // 색상 상수 const COLORS = { @@ -34,7 +34,7 @@ const NAMES = { RISK: '위험' as const, MODERATE_RISK: '다소 위험' as const, GOOD: '양호' as const, - MODERATE_SAFE: '다소안전' as const, + MODERATE_SAFE: '다소 안전' as const, SAFE: '안전' as const, } as const; From 2e642080574bd7f7b524f1c91fe8c4fee881128d Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 21:03:59 +0900 Subject: [PATCH 30/41] =?UTF-8?q?refactor:=20=EC=9C=84=ED=97=98=20?= =?UTF-8?q?=EB=B6=84=EC=84=9D=20=EB=8D=B0=EC=9D=B4=ED=84=B0=EC=9D=98=20?= =?UTF-8?q?=EC=A0=90=EC=88=98=20=EB=B0=8F=20=EB=93=B1=EA=B8=89=20=EC=88=98?= =?UTF-8?q?=EC=A0=95,=20=EC=9C=84=ED=97=98=20=EC=9A=94=EC=9D=B8=20?= =?UTF-8?q?=EB=B9=84=EC=9C=A8=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/main/mock/risk-analysis.mock.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/features/main/mock/risk-analysis.mock.ts b/src/features/main/mock/risk-analysis.mock.ts index ca66f25..2701a06 100644 --- a/src/features/main/mock/risk-analysis.mock.ts +++ b/src/features/main/mock/risk-analysis.mock.ts @@ -8,14 +8,14 @@ export const TEMP_RISK_ANALYSIS_DATA: RiskAnalysisResponse[] = [ { data: { riskSummary: { - score: 0, - grade: '안전', + score: 20, + grade: '위험', factors: [ - { name: '전세가율', percent: 0 }, - { name: '가격하락', percent: 0 }, - { name: '미분양(재고)', percent: 0 }, - { name: '정책/규제', percent: 0 }, - { name: '법적 리스크', percent: 0 }, + { name: '전세가율', percent: 12 }, + { name: '가격하락', percent: 10 }, + { name: '미분양(재고)', percent: 7 }, + { name: '정책/규제', percent: 18 }, + { name: '법적 리스크', percent: 16 }, ], }, landlord: { @@ -23,7 +23,7 @@ export const TEMP_RISK_ANALYSIS_DATA: RiskAnalysisResponse[] = [ name: '홍길동', normalizedKey: '부산-사업자번호-해시', ownedCount: 1, - grade: 'A', + grade: 'B', createdAt: '2025-08-21T02:26:22.055226', updatedAt: '2025-08-21T02:26:22.055226', }, From 0908db3deda752c778207fc6db39e3a223ad9e25 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 21:04:15 +0900 Subject: [PATCH 31/41] =?UTF-8?q?refactor:=20Needle=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EC=9D=98=20=ED=81=AC=EA=B8=B0=20=EB=B0=8F=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=20=EC=A1=B0=EC=A0=95=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EB=B0=98=EC=9D=91=ED=98=95=20=EB=94=94=EC=9E=90=EC=9D=B8=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/main/components/common/Needle.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/features/main/components/common/Needle.tsx b/src/features/main/components/common/Needle.tsx index 7edb443..576e6b7 100644 --- a/src/features/main/components/common/Needle.tsx +++ b/src/features/main/components/common/Needle.tsx @@ -7,14 +7,14 @@ type Props = { export const Needle = ({ gaugeData }: Props) => { return (
-
+
- +
); }; From 49c39c93012deffe7df8d92d887b1ebdf889453b Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 21:04:37 +0900 Subject: [PATCH 32/41] =?UTF-8?q?refactor:=20=EC=9C=84=ED=97=98=EB=8F=84?= =?UTF-8?q?=20=EC=B0=A8=ED=8A=B8=EC=9D=98=20innerRadius=20=EB=B0=8F=20oute?= =?UTF-8?q?rRadius=20=EA=B0=92=EC=9D=84=20=EB=B9=84=EC=9C=A8=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=ED=95=98=EC=97=AC=20=EB=B0=98=EC=9D=91?= =?UTF-8?q?=ED=98=95=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/main/components/features/chart/RiskChartBox.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/main/components/features/chart/RiskChartBox.tsx b/src/features/main/components/features/chart/RiskChartBox.tsx index b93c3ac..b1581fc 100644 --- a/src/features/main/components/features/chart/RiskChartBox.tsx +++ b/src/features/main/components/features/chart/RiskChartBox.tsx @@ -29,8 +29,8 @@ export const RiskChartBox = ({ riskScore }: Props) => { cy='50%' startAngle={0} endAngle={180} - innerRadius={130} - outerRadius={180} + innerRadius='70%' + outerRadius='95%' paddingAngle={4} cornerRadius={6} dataKey='value' From 3b7de49643a2bbd6bdfe4a06d13ac4c0e2d6c931 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 23:24:36 +0900 Subject: [PATCH 33/41] =?UTF-8?q?fix:=20=EB=B0=94=EB=8A=98=20=EC=9C=84?= =?UTF-8?q?=EC=B9=98=20=EC=88=98=EC=A0=95=20-=20=EA=B8=B0=EC=A1=B4=20?= =?UTF-8?q?=EB=B0=94=EB=8A=98=EC=9D=B4=200=20=EB=8F=84=EA=B0=80=20?= =?UTF-8?q?=EC=95=84=EB=8B=8C=2090=EB=8F=84=20=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=8B=9C=EC=9E=91=ED=96=88=EC=9D=8C=EC=9D=84=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8=20-=20props=EB=A1=9C=20=EB=B0=9B=EC=95=84=EC=98=A8=20?= =?UTF-8?q?=EA=B0=81=EB=8F=84=EC=97=90=20=EC=9E=84=EC=9D=98=EB=A1=9C=2090?= =?UTF-8?q?=EB=A5=BC=20=EB=B9=BC=EC=84=9C=20=EC=98=AC=EB=B0=94=EB=A5=B8=20?= =?UTF-8?q?=EA=B0=92=EC=9D=84=20=EA=B0=80=EB=A6=AC=ED=82=AC=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/main/components/common/Needle.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/features/main/components/common/Needle.tsx b/src/features/main/components/common/Needle.tsx index 576e6b7..9e62e85 100644 --- a/src/features/main/components/common/Needle.tsx +++ b/src/features/main/components/common/Needle.tsx @@ -1,15 +1,17 @@ -import { type GaugeData, NeedleIcon } from '@/shared'; +import { NeedleIcon } from '@/shared'; type Props = { - gaugeData: GaugeData; + needleAngle: number; }; -export const Needle = ({ gaugeData }: Props) => { +export const Needle = ({ needleAngle }: Props) => { + const correctedAngle = needleAngle - 90; + return (
From 93548c25fe8119b58f6a87759969146aedec6556 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 23:28:27 +0900 Subject: [PATCH 34/41] =?UTF-8?q?refactor:=20=EC=9C=84=ED=97=98=20?= =?UTF-8?q?=EB=B6=84=EC=84=9D=20=EB=8D=B0=EC=9D=B4=ED=84=B0=EC=9D=98=20?= =?UTF-8?q?=EC=A0=90=EC=88=98=20=EB=B0=8F=20=EB=93=B1=EA=B8=89=20=EC=88=98?= =?UTF-8?q?=EC=A0=95,=20=EC=9C=84=ED=97=98=EB=8F=84=20=EB=93=B1=EA=B8=89?= =?UTF-8?q?=EB=B3=84=20=EC=83=89=EC=83=81=20=EB=A7=A4=ED=95=91=20=EC=97=85?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/features/main/mock/risk-analysis.mock.ts | 242 +------------------ 1 file changed, 11 insertions(+), 231 deletions(-) diff --git a/src/features/main/mock/risk-analysis.mock.ts b/src/features/main/mock/risk-analysis.mock.ts index 2701a06..b3ff28f 100644 --- a/src/features/main/mock/risk-analysis.mock.ts +++ b/src/features/main/mock/risk-analysis.mock.ts @@ -8,8 +8,7 @@ export const TEMP_RISK_ANALYSIS_DATA: RiskAnalysisResponse[] = [ { data: { riskSummary: { - score: 20, - grade: '위험', + score: 50, factors: [ { name: '전세가율', percent: 12 }, { name: '가격하락', percent: 10 }, @@ -49,226 +48,6 @@ export const TEMP_RISK_ANALYSIS_DATA: RiskAnalysisResponse[] = [ errorCode: null, errorMessage: null, }, - { - data: { - riskSummary: { - score: 15, - grade: '위험', - factors: [ - { name: '전세가율', percent: 20 }, - { name: '가격하락', percent: 33 }, - { name: '미분양(재고)', percent: 13 }, - { name: '정책/규제', percent: 20 }, - { name: '법적 리스크', percent: 13 }, - ], - }, - landlord: { - landlordId: 2, - name: '김철수', - normalizedKey: '서울-사업자번호-해시', - ownedCount: 3, - grade: 'B', - createdAt: '2025-08-21T02:26:22.055226', - updatedAt: '2025-08-21T02:26:22.055226', - }, - landlordTrust: { - trustScore: 75, - subrogationCount: 1, - arrearsCount: 0, - litigationCount: 0, - ownedUnsoldCount: 1, - grade: 'B', - }, - landlordPlaces: [ - { - placeId: 2, - label: '서울특별시 강남구 테헤란로 123', - address: '서울특별시 강남구 테헤란로 123', - addressDetail: '201동 1501호', - }, - ], - }, - status: 'SUCCESS', - serverDateTime: '2025-08-21T02:28:11.398139', - errorCode: null, - errorMessage: null, - }, - { - data: { - riskSummary: { - score: 35, - grade: '다소 위험', - factors: [ - { name: '전세가율', percent: 15 }, - { name: '가격하락', percent: 25 }, - { name: '미분양(재고)', percent: 10 }, - { name: '정책/규제', percent: 15 }, - { name: '법적 리스크', percent: 10 }, - ], - }, - landlord: { - landlordId: 3, - name: '이영희', - normalizedKey: '대구-사업자번호-해시', - ownedCount: 2, - grade: 'B', - createdAt: '2025-08-21T02:26:22.055226', - updatedAt: '2025-08-21T02:26:22.055226', - }, - landlordTrust: { - trustScore: 65, - subrogationCount: 0, - arrearsCount: 1, - litigationCount: 0, - ownedUnsoldCount: 1, - grade: 'C', - }, - landlordPlaces: [ - { - placeId: 3, - label: '대구광역시 수성구 동대구로 456', - address: '대구광역시 수성구 동대구로 456', - addressDetail: '301동 2001호', - }, - ], - }, - status: 'SUCCESS', - serverDateTime: '2025-08-21T02:28:11.398139', - errorCode: null, - errorMessage: null, - }, - { - data: { - riskSummary: { - score: 55, - grade: '양호', - factors: [ - { name: '전세가율', percent: 10 }, - { name: '가격하락', percent: 18 }, - { name: '미분양(재고)', percent: 8 }, - { name: '정책/규제', percent: 12 }, - { name: '법적 리스크', percent: 8 }, - ], - }, - landlord: { - landlordId: 4, - name: '박민수', - normalizedKey: '인천-사업자번호-해시', - ownedCount: 1, - grade: 'A', - createdAt: '2025-08-21T02:26:22.055226', - updatedAt: '2025-08-21T02:26:22.055226', - }, - landlordTrust: { - trustScore: 85, - subrogationCount: 0, - arrearsCount: 0, - litigationCount: 0, - ownedUnsoldCount: 0, - grade: 'A', - }, - landlordPlaces: [ - { - placeId: 4, - label: '인천광역시 연수구 송도동로 789', - address: '인천광역시 연수구 송도동로 789', - addressDetail: '401동 3001호', - }, - ], - }, - status: 'SUCCESS', - serverDateTime: '2025-08-21T02:28:11.398139', - errorCode: null, - errorMessage: null, - }, - { - data: { - riskSummary: { - score: 75, - grade: '다소 안전', - factors: [ - { name: '전세가율', percent: 5 }, - { name: '가격하락', percent: 12 }, - { name: '미분양(재고)', percent: 5 }, - { name: '정책/규제', percent: 8 }, - { name: '법적 리스크', percent: 5 }, - ], - }, - landlord: { - landlordId: 5, - name: '최지영', - normalizedKey: '부산-사업자번호-해시2', - ownedCount: 1, - grade: 'A', - createdAt: '2025-08-21T02:26:22.055226', - updatedAt: '2025-08-21T02:26:22.055226', - }, - landlordTrust: { - trustScore: 90, - subrogationCount: 0, - arrearsCount: 0, - litigationCount: 0, - ownedUnsoldCount: 0, - grade: 'A', - }, - landlordPlaces: [ - { - placeId: 5, - label: '부산광역시 해운대구 센텀동로 67', - address: '부산광역시 해운대구 센텀동로 67', - addressDetail: '501동 4001호', - }, - ], - }, - status: 'SUCCESS', - serverDateTime: '2025-08-21T02:28:11.398139', - errorCode: null, - errorMessage: null, - }, - { - data: { - riskSummary: { - score: 95, - grade: '매우 안전', - factors: [ - { name: '전세가율', percent: 2 }, - { name: '가격하락', percent: 5 }, - { name: '미분양(재고)', percent: 2 }, - { name: '정책/규제', percent: 3 }, - { name: '법적 리스크', percent: 2 }, - ], - }, - landlord: { - landlordId: 6, - name: '정수진', - normalizedKey: '서울-사업자번호-해시2', - ownedCount: 1, - grade: 'A', - createdAt: '2025-08-21T02:26:22.055226', - updatedAt: '2025-08-21T02:26:22.055226', - }, - landlordTrust: { - trustScore: 98, - subrogationCount: 0, - arrearsCount: 0, - litigationCount: 0, - ownedUnsoldCount: 0, - grade: 'A', - }, - landlordPlaces: [ - { - placeId: 6, - label: '서울특별시 강남구 테헤란로 456', - address: '서울특별시 강남구 테헤란로 456', - addressDetail: '601동 5001호', - }, - ], - }, - status: 'SUCCESS', - serverDateTime: '2025-08-21T02:28:11.398139', - errorCode: null, - errorMessage: null, - }, ]; // 기본 위험도 데이터 (사진과 일치) @@ -285,24 +64,25 @@ export const RISK_LEVEL_DESCRIPTIONS = { // 위험도 점수별 등급 매핑 export const getRiskGrade = (score: number): string => { - if (score <= 20) return '매우 안전'; - if (score <= 40) return '안전'; + if (score <= 20) return '매우 위험'; + if (score <= 40) return '위험'; if (score <= 60) return '양호'; - if (score <= 80) return '다소 위험'; - return '위험'; + if (score <= 80) return '다소 안전'; + return '안전'; }; // 위험도 등급별 색상 export const getRiskGradeColor = (grade: string): string => { switch (grade) { - case '매우 안전': - case '안전': - return 'text-green-600'; + case '매우 위험': + case '위험': + return 'text-red-600'; case '양호': - return 'text-blue-600'; + return 'text-green-600'; case '다소 위험': return 'text-yellow-600'; - case '위험': + case '다소 안전': + case '안전': return 'text-red-600'; default: return 'text-gray-600'; From 000b4ec551f07b767a256a163e95967ca482be06 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 23:46:44 +0900 Subject: [PATCH 35/41] =?UTF-8?q?style:=20=EC=9C=84=ED=97=98=EB=8F=84=20?= =?UTF-8?q?=EC=83=89=EC=83=81=20=EB=B3=80=EC=88=98=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/index.css | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/index.css b/src/index.css index 15854c6..e06becf 100644 --- a/src/index.css +++ b/src/index.css @@ -69,6 +69,11 @@ --color-lighthouse-button-secondary: var(--lighthouse-button-secondary); --color-lighthouse-button-secondary-text: var(--lighthouse-button-secondary-text); --color-lighthouse-button-secondary-hover: var(--lighthouse-button-secondary-hover); + --color-risk-very-danger: var(--risk-very-danger); + --color-risk-danger: var(--risk-danger); + --color-risk-good: var(--risk-good); + --color-risk-safe: var(--risk-safe); + --color-risk-very-safe: var(--risk-very-safe); } :root { @@ -122,6 +127,11 @@ --sidebar-accent-foreground: oklch(0.205 0 0); --sidebar-border: oklch(0.922 0 0); --sidebar-ring: oklch(0.708 0 0); + --risk-very-danger: #ff6f6f; + --risk-danger: #ffba6f; + --risk-good: #ffe93f; + --risk-safe: #d9ff41; + --risk-very-safe: #2cdf44; } .dark { From a535aedd1565e668e80a20d6d0a456f442e7bd56 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 23:47:01 +0900 Subject: [PATCH 36/41] =?UTF-8?q?refactor:=20=EC=9C=84=ED=97=98=EB=8F=84?= =?UTF-8?q?=20=EC=A0=90=EC=88=98=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EA=B5=AC?= =?UTF-8?q?=EA=B0=84=20=EC=9D=B8=EB=8D=B1=EC=8A=A4=20=EB=B0=8F=20=EA=B0=81?= =?UTF-8?q?=EB=8F=84=20=EA=B3=84=EC=82=B0=20=EB=A1=9C=EC=A7=81=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/shared/utils/chart/risk-chart.ts | 56 ++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 15 deletions(-) diff --git a/src/shared/utils/chart/risk-chart.ts b/src/shared/utils/chart/risk-chart.ts index e8ef61c..c7ef34f 100644 --- a/src/shared/utils/chart/risk-chart.ts +++ b/src/shared/utils/chart/risk-chart.ts @@ -17,8 +17,6 @@ export type GaugeData = { }; // 상수 정의 -const MAX_SCORE = 100; -const MAX_ANGLE = 180; const SEGMENT_SIZE = 20; // 점수 범위 검증 함수 @@ -33,14 +31,20 @@ export const toRiskScore = (score: number): RiskScore => { /** * 위험도 점수에 따른 구간 인덱스를 계산 - * 점수가 낮을수록 위험하므로 인덱스를 그대로 계산 + * 점수가 낮을수록 위험하므로 인덱스를 반대로 계산 * @param score - 위험도 점수 (0-100, 낮을수록 위험) - * @returns 구간 인덱스 (0-4, 0=위험, 4=안전) + * @returns 구간 인덱스 (0-4, 0=매우 안전, 4=매우 위험) */ export const getSegmentIndex = (score: RiskScore): RiskSegmentIndex => { - // 점수가 낮을수록 위험하므로 그대로 계산 - const segmentIndex = Math.floor(score / SEGMENT_SIZE); - return Math.min(segmentIndex, 4) as RiskSegmentIndex; + // 점수가 낮을수록 위험하므로 인덱스를 반대로 계산 + // 0-20점: 4번 인덱스 (매우 위험) + // 21-40점: 3번 인덱스 (위험) + // 41-60점: 2번 인덱스 (양호) + // 61-80점: 1번 인덱스 (안전) + // 81-100점: 0번 인덱스 (매우 안전) + const segmentIndex = Math.floor((score - 1) / SEGMENT_SIZE); + const reversedIndex = 4 - Math.max(0, Math.min(segmentIndex, 4)); + return reversedIndex as RiskSegmentIndex; }; /** @@ -51,20 +55,40 @@ export const getSegmentIndex = (score: RiskScore): RiskSegmentIndex => { export const getRiskSegment = (score: number): RiskSegment => { const normalizedScore = toRiskScore(score); const segmentIndex = getSegmentIndex(normalizedScore); - return RISK_CHART_SEGMENTS[segmentIndex]; + const result = RISK_CHART_SEGMENTS[segmentIndex]; + return result; }; /** - * 위험도 점수에 따른 각도 계산 - * 점수가 낮을수록 위험하므로 각도를 그대로 계산 + * 위험도 점수를 5구간으로 나눠서 각도로 변환 (이산형) + * 5구간을 36도씩 나누어 표현 (0-36, 36-72, 72-108, 108-144, 144-180) * @param score - 위험도 점수 (0-100, 낮을수록 위험) * @returns 바늘 각도 (0-180도) */ export const getGaugeAngle = (score: number): number => { - const normalizedScore = toRiskScore(score); - // 점수가 낮을수록 위험하므로 각도를 그대로 계산 - // 0점(위험) = 0도, 100점(안전) = 180도 - return (normalizedScore / MAX_SCORE) * MAX_ANGLE; + const normalized = Math.max(0, Math.min(100, score)); // 0~100 보정 + + // 5구간을 36도씩 나누어 계산 + let result: number; + if (normalized <= 20) { + // 0-20점: 매우 위험 (0-36도, 왼쪽) + result = (normalized / 20) * 36; + } else if (normalized <= 40) { + // 21-40점: 위험 (36-72도) + result = 36 + ((normalized - 20) / 20) * 36; + } else if (normalized <= 60) { + // 41-60점: 양호 (72-108도, 중앙) + result = 72 + ((normalized - 40) / 20) * 36; + } else if (normalized <= 80) { + // 61-80점: 안전 (108-144도) + result = 108 + ((normalized - 60) / 20) * 36; + } else { + // 81-100점: 매우 안전 (144-180도, 오른쪽) + result = 144 + ((normalized - 80) / 20) * 36; + } + + console.log(`보정점수 : ${normalized} 바늘 위치: ${score}점 → ${result}도`); + return result; }; /** @@ -77,12 +101,14 @@ export const getGaugeData = (score: number): GaugeData => { const currentSegment = getRiskSegment(normalizedScore); const angle = getGaugeAngle(normalizedScore); - return { + const result = { data: RISK_CHART_SEGMENTS as RiskSegment[], color: currentSegment.fill, label: currentSegment.name, angle, }; + + return result; }; /** From 76dbc46d48214fba2216008f81061698178caa1d Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 23:50:16 +0900 Subject: [PATCH 37/41] =?UTF-8?q?refactor:=20=EC=9C=84=ED=97=98=EB=8F=84?= =?UTF-8?q?=20=EC=B0=A8=ED=8A=B8=EC=97=90=EC=84=9C=20=EC=A0=90=EC=88=98=20?= =?UTF-8?q?=EC=83=89=EC=83=81=20=EB=B0=8F=20=EB=B0=94=EB=8A=98=20=EA=B0=81?= =?UTF-8?q?=EB=8F=84=20=EA=B3=84=EC=82=B0=20=EB=A1=9C=EC=A7=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../features/chart/RiskChartBox.tsx | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/features/main/components/features/chart/RiskChartBox.tsx b/src/features/main/components/features/chart/RiskChartBox.tsx index b1581fc..559e0bf 100644 --- a/src/features/main/components/features/chart/RiskChartBox.tsx +++ b/src/features/main/components/features/chart/RiskChartBox.tsx @@ -1,6 +1,13 @@ import { Pie, PieChart, ResponsiveContainer } from 'recharts'; -import { type GaugeData, getGaugeData } from '@/shared'; +import { + type GaugeData, + getGaugeAngle, + getGaugeData, + getRiskBoxShadowClass, + getRiskColorClass, + getRiskScoreColorClass, +} from '@/shared'; import { Needle } from '../../common'; @@ -11,12 +18,16 @@ type Props = { export const RiskChartBox = ({ riskScore }: Props) => { const gaugeData: GaugeData = getGaugeData(riskScore); + const scoreColorClass = getRiskScoreColorClass(riskScore); + + const needleAngle = getGaugeAngle(riskScore); + return (

전세 계약 최종 위험도

위험 점수 : - {riskScore} + {riskScore}

@@ -40,12 +51,11 @@ export const RiskChartBox = ({ riskScore }: Props) => { {/* 게이지 바늘 */} - + {/* 위험도 레이블 */}
{gaugeData.label}
From 3007cfaac04ebe2b7ef444ad2d7a95ae9528d49e Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Thu, 21 Aug 2025 23:50:30 +0900 Subject: [PATCH 38/41] =?UTF-8?q?refactor:=20=EC=9C=84=ED=97=98=20?= =?UTF-8?q?=EC=9A=94=EC=95=BD=EC=97=90=EC=84=9C=20=EB=93=B1=EA=B8=89=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/entities/chart/types/risk-analysis.type.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/entities/chart/types/risk-analysis.type.ts b/src/entities/chart/types/risk-analysis.type.ts index 767c9ed..f0fd2ce 100644 --- a/src/entities/chart/types/risk-analysis.type.ts +++ b/src/entities/chart/types/risk-analysis.type.ts @@ -11,7 +11,6 @@ export type RiskFactor = { // 위험도 요약 export type RiskSummary = { score: number; - grade: string; factors: RiskFactor[]; }; From 4ad58895f300f4c4cbf23368fdd78ea8b0fefb88 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Fri, 22 Aug 2025 00:07:41 +0900 Subject: [PATCH 39/41] =?UTF-8?q?feat:=20=EC=B0=A8=ED=8A=B8=20=EC=83=89?= =?UTF-8?q?=EC=83=81=20=EC=83=81=EC=88=98=20=EB=B0=8F=20Tailwind=20CSS=20?= =?UTF-8?q?=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/shared/constants/chart-color.ts | 23 +++++++++++++++++++++++ src/shared/constants/index.ts | 1 + 2 files changed, 24 insertions(+) create mode 100644 src/shared/constants/chart-color.ts diff --git a/src/shared/constants/chart-color.ts b/src/shared/constants/chart-color.ts new file mode 100644 index 0000000..d6bf3de --- /dev/null +++ b/src/shared/constants/chart-color.ts @@ -0,0 +1,23 @@ +// CSS 변수 기반 색상 배열 +export const CHART_COLORS = [ + 'var(--chart-orange)', // 첫 번째 (1개일 때) + 'var(--chart-blue)', // 두 번째 + 'var(--chart-green)', // 세 번째 + 'var(--chart-purple)', // 네 번째 + 'var(--chart-red)', // 다섯 번째 + 'var(--chart-yellow)', // 여섯 번째 + 'var(--chart-pink)', // 일곱 번째 + 'var(--chart-indigo)', // 여덟 번째 +] as const; + +// Tailwind CSS 클래스 배열 +export const CHART_COLOR_CLASSES = [ + 'bg-chart-orange', // 첫 번째 (1개일 때) + 'bg-chart-blue', // 두 번째 + 'bg-chart-green', // 세 번째 + 'bg-chart-purple', // 네 번째 + 'bg-chart-red', // 다섯 번째 + 'bg-chart-yellow', // 여섯 번째 + 'bg-chart-pink', // 일곱 번째 + 'bg-chart-indigo', // 여덟 번째 +] as const; diff --git a/src/shared/constants/index.ts b/src/shared/constants/index.ts index 0a8f9ed..0899ce9 100644 --- a/src/shared/constants/index.ts +++ b/src/shared/constants/index.ts @@ -1,2 +1,3 @@ export * from './router-path'; export * from './risk-chart-segments'; +export * from './chart-color'; From a47562bd814d83acaee76c2d88c2bd5a849e8d56 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Fri, 22 Aug 2025 00:07:52 +0900 Subject: [PATCH 40/41] =?UTF-8?q?refactor:=20=EC=9C=84=ED=97=98=EB=8F=84?= =?UTF-8?q?=20=EB=93=B1=EA=B8=89=20=EB=B0=8F=20=EC=83=89=EC=83=81=20?= =?UTF-8?q?=EC=83=81=EC=88=98=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8,=20?= =?UTF-8?q?=EC=9C=84=ED=97=98=EB=8F=84=20=EA=B5=AC=EA=B0=84=20=EC=A0=95?= =?UTF-8?q?=EC=9D=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/shared/constants/risk-chart-segments.ts | 29 ++++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/src/shared/constants/risk-chart-segments.ts b/src/shared/constants/risk-chart-segments.ts index e020259..1d20f7e 100644 --- a/src/shared/constants/risk-chart-segments.ts +++ b/src/shared/constants/risk-chart-segments.ts @@ -3,7 +3,7 @@ */ // 타입 정의 -export type RiskLevel = '안전' | '다소 안전' | '양호' | '다소 위험' | '위험'; +export type RiskLevel = '매우 위험' | '위험' | '양호' | '안전' | '매우 안전'; export type RiskSegment = { name: RiskLevel; @@ -20,31 +20,40 @@ const COLORS = { GREEN: '#2cdf44', } as const; +// 위험도 점수별 색상 클래스 +export const RISK_SCORE_COLORS = { + VERY_DANGER: 'text-risk-very-danger', // 매우 위험 (0-20점) + DANGER: 'text-risk-danger', // 위험 (21-40점) + CAUTION: 'text-risk-good', // 양호 (41-60점) + SAFE: 'text-risk-safe', // 안전 (61-80점) + VERY_SAFE: 'text-risk-very-safe', // 매우 안전 (81-100점) +} as const; + // 값 상수 const VALUES = { + VERY_RISK: 20, RISK: 20, - MODERATE_RISK: 20, GOOD: 20, - MODERATE_SAFE: 20, SAFE: 20, + VERY_SAFE: 20, } as const; // 이름 상수 const NAMES = { + VERY_RISK: '매우 위험' as const, RISK: '위험' as const, - MODERATE_RISK: '다소 위험' as const, GOOD: '양호' as const, - MODERATE_SAFE: '다소 안전' as const, SAFE: '안전' as const, + VERY_SAFE: '매우 안전' as const, } as const; -// 위험도 구간 정의 +// 위험도 구간 정의 (왼쪽이 위험, 오른쪽이 안전) export const RISK_CHART_SEGMENTS: readonly RiskSegment[] = [ - { name: NAMES.RISK, value: VALUES.RISK, fill: COLORS.RED }, - { name: NAMES.MODERATE_RISK, value: VALUES.MODERATE_RISK, fill: COLORS.ORANGE }, + { name: NAMES.VERY_SAFE, value: VALUES.VERY_SAFE, fill: COLORS.GREEN }, + { name: NAMES.SAFE, value: VALUES.SAFE, fill: COLORS.YELLOW_GREEN }, { name: NAMES.GOOD, value: VALUES.GOOD, fill: COLORS.YELLOW }, - { name: NAMES.MODERATE_SAFE, value: VALUES.MODERATE_SAFE, fill: COLORS.YELLOW_GREEN }, - { name: NAMES.SAFE, value: VALUES.SAFE, fill: COLORS.GREEN }, + { name: NAMES.RISK, value: VALUES.RISK, fill: COLORS.ORANGE }, + { name: NAMES.VERY_RISK, value: VALUES.VERY_RISK, fill: COLORS.RED }, ]; // 구간 인덱스 타입 From 5988a87bea393a02f123ad9e365b7292b8577083 Mon Sep 17 00:00:00 2001 From: Dobbymin Date: Fri, 22 Aug 2025 00:08:11 +0900 Subject: [PATCH 41/41] =?UTF-8?q?feat:=20=EC=9C=84=ED=97=98=EB=8F=84=20?= =?UTF-8?q?=EC=A0=90=EC=88=98=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=83=89?= =?UTF-8?q?=EC=83=81=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EB=B0=8F=20=EB=B0=95?= =?UTF-8?q?=EC=8A=A4=20=EA=B7=B8=EB=A6=BC=EC=9E=90=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/shared/utils/chart/chart-colors.ts | 66 ++++++++++++++++---------- 1 file changed, 42 insertions(+), 24 deletions(-) diff --git a/src/shared/utils/chart/chart-colors.ts b/src/shared/utils/chart/chart-colors.ts index dcc5dcd..39e2b65 100644 --- a/src/shared/utils/chart/chart-colors.ts +++ b/src/shared/utils/chart/chart-colors.ts @@ -1,30 +1,7 @@ /** * 차트 색상 관련 유틸리티 */ - -// CSS 변수 기반 색상 배열 -const CHART_COLORS = [ - 'var(--chart-orange)', // 첫 번째 (1개일 때) - 'var(--chart-blue)', // 두 번째 - 'var(--chart-green)', // 세 번째 - 'var(--chart-purple)', // 네 번째 - 'var(--chart-red)', // 다섯 번째 - 'var(--chart-yellow)', // 여섯 번째 - 'var(--chart-pink)', // 일곱 번째 - 'var(--chart-indigo)', // 여덟 번째 -] as const; - -// Tailwind CSS 클래스 배열 -const CHART_COLOR_CLASSES = [ - 'bg-chart-orange', // 첫 번째 (1개일 때) - 'bg-chart-blue', // 두 번째 - 'bg-chart-green', // 세 번째 - 'bg-chart-purple', // 네 번째 - 'bg-chart-red', // 다섯 번째 - 'bg-chart-yellow', // 여섯 번째 - 'bg-chart-pink', // 일곱 번째 - 'bg-chart-indigo', // 여덟 번째 -] as const; +import { CHART_COLORS, CHART_COLOR_CLASSES, RISK_SCORE_COLORS } from '../../constants'; /** * 인덱스에 따른 차트 색상 CSS 변수를 반환 @@ -44,6 +21,47 @@ export const getChartColorClass = (index: number): string => { return CHART_COLOR_CLASSES[index % CHART_COLOR_CLASSES.length]; }; +/** + * 위험도 점수에 따른 색상 클래스를 반환 + * @param score - 위험도 점수 (0-100, 낮을수록 위험) + * @returns Tailwind CSS 색상 클래스명 + */ +export const getRiskScoreColorClass = (score: number): string => { + if (score <= 20) return RISK_SCORE_COLORS.VERY_DANGER; // 매우 위험 + if (score <= 40) return RISK_SCORE_COLORS.DANGER; // 위험 + if (score <= 60) return RISK_SCORE_COLORS.CAUTION; // 양호 + if (score <= 80) return RISK_SCORE_COLORS.SAFE; // 안전 + return RISK_SCORE_COLORS.VERY_SAFE; // 매우 안전 +}; + +/** + * 위험도 색상 HEX 값을 Tailwind CSS 클래스로 변환 + * @param hexColor - HEX 색상 값 + * @returns Tailwind CSS box-shadow 클래스 + */ +export const getRiskColorClass = (hexColor: string): string => { + const colorMap: Record = { + '#ff6f6f': 'bg-risk-very-danger', // 매우 위험 + '#ffba6f': 'bg-risk-danger', // 위험 + '#ffe93f': 'bg-risk-good', // 양호 + '#d9ff41': 'bg-risk-safe', // 안전 + '#2cdf44': 'bg-risk-very-safe', // 매우 안전 + }; + + return colorMap[hexColor] || 'bg-gray-400'; +}; + +export const getRiskBoxShadowClass = (hexColor: string): string => { + const colorMap: Record = { + '#ff6f6f': 'shadow-[0_0_12px_var(--color-risk-very-danger)]', // 매우 위험 + '#ffba6f': 'shadow-[0_0_12px_var(--color-risk-danger)]', // 위험 + '#ffe93f': 'shadow-[0_0_12px_var(--color-risk-good)]', // 양호 + '#d9ff41': 'shadow-[0_0_12px_var(--color-risk-safe)]', // 안전 + '#2cdf44': 'shadow-[0_0_12px_var(--color-risk-very-safe)]', // 매우 안전 + }; + return colorMap[hexColor] || 'shadow-[0_0_12px_rgba(156,163,175,0.5)]'; +}; + /** * 차트 색상 개수 */