Skip to content

feat: 차트 옵션 unit 추가#76

Merged
10hajin15 merged 2 commits into
mainfrom
feat/chart-option-unit
May 20, 2026
Merged

feat: 차트 옵션 unit 추가#76
10hajin15 merged 2 commits into
mainfrom
feat/chart-option-unit

Conversation

@10hajin15

@10hajin15 10hajin15 commented May 20, 2026

Copy link
Copy Markdown
Collaborator

🛠 관련 이슈

🌱 PR 포인트

  • 차트 모달 옵션 패널에 unit 추가합니다.

📸 스크린샷 / 링크

스크린샷
스크린샷 2026-05-20 103851

📎 레퍼런스

❗ 이슈사항 / 기타사항 / 에러슈팅

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 차트 제목과 단위를 설정하고 편집할 수 있는 기능 추가
    • 차트 업데이트 및 삭제 시 알림 메시지 제공
  • 개선사항

    • 차트 저장 기능의 동작 방식 개선
    • 사용자 인터페이스 레이아웃 최적화
    • 제목 렌더링 로직 단순화

Review Change Stack

@10hajin15 10hajin15 requested a review from shubug1015 May 20, 2026 01:50
@10hajin15 10hajin15 self-assigned this May 20, 2026
@10hajin15 10hajin15 added the enhancement New feature or request label May 20, 2026
@10hajin15 10hajin15 linked an issue May 20, 2026 that may be closed by this pull request
1 task
@coderabbitai

coderabbitai Bot commented May 20, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

이 PR은 차트 시스템에 데이터 단위(unit) 필드를 추가합니다. API 타입과 스토어를 확장하고, ChartOptionPanel에서 단위를 입력할 수 있도록 UI를 구성하며, 소켓 핸들러를 통해 실시간 동기화와 사용자 피드백(토스트)을 제공합니다.

Changes

차트 단위 기능 추가

Layer / File(s) Summary
차트 데이터 모델 확장 및 스토어 초기화
src/types/api/chartAPI.ts, src/chart/store/chartOptionStore.ts, src/chart/components/FieldRow.tsx
ChartListItemChart 타입에 unit: string 필드를 추가하고, chartStore의 초기 상태에 빈 문자열로 기본값을 설정하며, FieldRow의 toggle 프로퍼티를 선택사항으로 변경합니다.
ChartOptionPanel UI 및 저장 로직 업데이트
src/chart/components/ChartOptionPanel.tsx
패널 상단에 TITLE(차트 제목)과 UNIT(단위) 입력 섹션을 추가하여 chartState.namechartState.unit을 직접 갱신하고, 저장 시 이 두 필드를 포함한 api.patch 호출로 변경하며, 축 입력 레이블과 버튼 레이아웃을 조정합니다.
ChartModal 타이틀 렌더링 단순화 및 chartTitleRef 추가
src/chart/components/ChartModal.tsx
chartTitleRef를 export하여 외부 참조를 가능하게 하고, 타이틀을 단순한 텍스트로만 렌더링하도록 변경하며, 포퍼버의 저장 처리를 await api.patch(...) 기반으로 전환하여 토스트 처리를 제거합니다.
openChartModal 초기화 함수 확장
src/chart/utils/openChartModal.tsx
dataSettingsStore를 통합하고 initChartOption 헬퍼 함수를 추가하여 데이터 표시 플래그와 축 이름 표시를 활성화하며, 차트 응답에서 unit 필드를 초기화합니다.
소켓 핸들러의 unit 동기화 및 토스트 알림
src/sockets/chartSocketHandler.ts, src/sockets/chartModalSocketHandler.ts
chartSocketHandler에서 CHART_UPDATED 시 unit을 동기화하고 chartTitleRef를 사용해 DOM을 업데이트하며 성공 토스트를 표시하고, chartModalSocketHandler에서 데이터 업데이트 및 삭제 이벤트에 토스트 알림을 추가합니다.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Suggested reviewers

  • shubug1015
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR 제목 '차트 옵션 unit 추가'는 변경사항의 핵심(차트 옵션에 unit 필드 추가)을 명확하고 간결하게 요약합니다.
Linked Issues check ✅ Passed PR의 모든 코드 변경사항이 이슈 #73의 요구사항(차트 모달에 단위 입력창 구현)을 충족합니다.
Out of Scope Changes check ✅ Passed 모든 변경사항이 unit 필드 추가와 관련된 범위 내에서 이루어졌으며, 불필요한 외부 변경은 없습니다.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/chart-option-unit

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/sockets/chartSocketHandler.ts (1)

50-52: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

CHART_UPDATED에서 리스트 상태를 부분 갱신만 하고 있습니다.

현재 name만 갱신되어 unit/xaxis/yaxis가 오래된 값으로 남습니다. 업데이트 payload의 필드를 함께 반영해 주세요.

제안 diff
 chartListState.charts = chartListState.charts.map(chart =>
-  chart.id === updatedChart.id ? { ...chart, name: updatedChart.name } : chart
+  chart.id === updatedChart.id
+    ? {
+        ...chart,
+        name: updatedChart.name,
+        xaxis: updatedChart.xaxis,
+        yaxis: updatedChart.yaxis,
+        unit: updatedChart.unit,
+      }
+    : chart
 );
As per coding guidelines, "Predictability: Names match behavior, no hidden side effects."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/sockets/chartSocketHandler.ts` around lines 50 - 52, The CHART_UPDATED
handler only copies the name into the existing chart, leaving unit/xaxis/yaxis
stale; update the merge logic in the chartListState.charts mapping (the function
referencing chart and updatedChart) to merge all fields from updatedChart into
the existing chart (or replace the chart object with updatedChart) so unit,
xaxis, yaxis and any other fields are updated atomically; locate the map
callback where chart.id === updatedChart.id and change the result from only
updating name to spreading in updatedChart (e.g., produce a merged object) to
ensure no stale fields remain.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/chart/components/ChartModal.tsx`:
- Around line 111-118: The onSave handler in ChartModal (the async IIFE that
calls api.patch with `/canvas/charts/${targetData.chartId}/${targetData.id}`)
lacks failure handling; wrap the await api.patch call in a try/catch, only
apply/commit local state changes after a successful response, and in the catch
block revert any optimistic updates (or refetch the chart data) and show an
error to the user (e.g., via existing toast/modal error handler) so the UI
doesn't remain inconsistent on failure.

In `@src/chart/components/ChartOptionPanel.tsx`:
- Around line 16-24: The onClickedChangeChart handler calls
api.patch(`/canvas/charts/${chartId}`...) inside an async IIFE without error
handling, so failed save requests are swallowed; wrap the await api.patch call
in a try/catch inside onClickedChangeChart (or inside the async IIFE) to catch
errors from api.patch, handle the failure by logging or surfacing it (e.g.,
processLogger/console.error and showing a user-facing error/toast or setting an
error state), and keep references to chartId and chartState when composing the
patch payload so the catch block can include contextual info about the failed
save.

In `@src/sockets/chartModalSocketHandler.ts`:
- Line 14: The socket handlers call openToastMessage with toastLayerRef.current
without guarding for null, which can throw if the ref is empty; update the code
in chartModalSocketHandler.ts to check that toastLayerRef.current is truthy
before calling openToastMessage (for both occurrences where openToastMessage({
dom: toastLayerRef.current, ... }) is used), e.g., wrap the call in an if
(toastLayerRef.current) { ... } or early-return to skip toasting when the DOM
ref is missing so the socket handlers remain safe.

---

Outside diff comments:
In `@src/sockets/chartSocketHandler.ts`:
- Around line 50-52: The CHART_UPDATED handler only copies the name into the
existing chart, leaving unit/xaxis/yaxis stale; update the merge logic in the
chartListState.charts mapping (the function referencing chart and updatedChart)
to merge all fields from updatedChart into the existing chart (or replace the
chart object with updatedChart) so unit, xaxis, yaxis and any other fields are
updated atomically; locate the map callback where chart.id === updatedChart.id
and change the result from only updating name to spreading in updatedChart
(e.g., produce a merged object) to ensure no stale fields remain.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2a711186-13fa-400a-82a9-7e6e464a0605

📥 Commits

Reviewing files that changed from the base of the PR and between 7703ba9 and 8a93532.

📒 Files selected for processing (8)
  • src/chart/components/ChartModal.tsx
  • src/chart/components/ChartOptionPanel.tsx
  • src/chart/components/FieldRow.tsx
  • src/chart/store/chartOptionStore.ts
  • src/chart/utils/openChartModal.tsx
  • src/sockets/chartModalSocketHandler.ts
  • src/sockets/chartSocketHandler.ts
  • src/types/api/chartAPI.ts

Comment on lines 111 to 118
onSave={() => {
void (async () => {
await api
.patch(`/canvas/charts/${targetData.chartId}/${targetData.id}`, {
name: targetData.name,
value: targetData.value,
// opacity: targetData.opacity > 1 ? targetData.opacity / 100 : targetData.opacity,
})
.then(res => {
openToastMessage({ dom: toastLayerRef.current, type: 'success', message: res.message });
});
await api.patch(`/canvas/charts/${targetData.chartId}/${targetData.id}`, {
name: targetData.name,
value: targetData.value,
// opacity: targetData.opacity > 1 ? targetData.opacity / 100 : targetData.opacity,
});
})();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

바 수정 저장 실패 분기가 없습니다.

onSave의 PATCH 실패를 처리하지 않아 실패 시 상태 불일치가 남을 수 있습니다. 실패 분기를 추가해 주세요.

제안 diff
 onSave={() => {
   void (async () => {
-    await api.patch(`/canvas/charts/${targetData.chartId}/${targetData.id}`, {
-      name: targetData.name,
-      value: targetData.value,
-      // opacity: targetData.opacity > 1 ? targetData.opacity / 100 : targetData.opacity,
-    });
+    try {
+      await api.patch(`/canvas/charts/${targetData.chartId}/${targetData.id}`, {
+        name: targetData.name,
+        value: targetData.value,
+        // opacity: targetData.opacity > 1 ? targetData.opacity / 100 : targetData.opacity,
+      });
+    } catch {
+      // TODO: 저장 실패 피드백 처리
+    }
   })();
 }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
onSave={() => {
void (async () => {
await api
.patch(`/canvas/charts/${targetData.chartId}/${targetData.id}`, {
name: targetData.name,
value: targetData.value,
// opacity: targetData.opacity > 1 ? targetData.opacity / 100 : targetData.opacity,
})
.then(res => {
openToastMessage({ dom: toastLayerRef.current, type: 'success', message: res.message });
});
await api.patch(`/canvas/charts/${targetData.chartId}/${targetData.id}`, {
name: targetData.name,
value: targetData.value,
// opacity: targetData.opacity > 1 ? targetData.opacity / 100 : targetData.opacity,
});
})();
onSave={() => {
void (async () => {
try {
await api.patch(`/canvas/charts/${targetData.chartId}/${targetData.id}`, {
name: targetData.name,
value: targetData.value,
// opacity: targetData.opacity > 1 ? targetData.opacity / 100 : targetData.opacity,
});
} catch {
// TODO: 저장 실패 피드백 처리
}
})();
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/chart/components/ChartModal.tsx` around lines 111 - 118, The onSave
handler in ChartModal (the async IIFE that calls api.patch with
`/canvas/charts/${targetData.chartId}/${targetData.id}`) lacks failure handling;
wrap the await api.patch call in a try/catch, only apply/commit local state
changes after a successful response, and in the catch block revert any
optimistic updates (or refetch the chart data) and show an error to the user
(e.g., via existing toast/modal error handler) so the UI doesn't remain
inconsistent on failure.

Comment on lines 16 to 24
const onClickedChangeChart = () => {
void (async () => {
await api
.patch(`/canvas/charts/${chartId}`, {
xAxis: chartState.xAxisName,
yAxis: chartState.yAxisName,
})
.then(res => {
openToastMessage({ dom: toastLayerRef.current, type: 'success', message: res.message });
});
await api.patch(`/canvas/charts/${chartId}`, {
xAxis: chartState.xAxisName,
yAxis: chartState.yAxisName,
name: chartState.name,
unit: chartState.unit,
});
})();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

저장 요청 실패 처리가 빠져 있습니다.

api.patch 실패 시 예외가 처리되지 않아 저장 실패가 조용히 누락됩니다. 최소한 try/catch로 실패 분기를 명시해 주세요.

제안 diff
 const onClickedChangeChart = () => {
   void (async () => {
-    await api.patch(`/canvas/charts/${chartId}`, {
-      xAxis: chartState.xAxisName,
-      yAxis: chartState.yAxisName,
-      name: chartState.name,
-      unit: chartState.unit,
-    });
+    try {
+      await api.patch(`/canvas/charts/${chartId}`, {
+        xAxis: chartState.xAxisName,
+        yAxis: chartState.yAxisName,
+        name: chartState.name,
+        unit: chartState.unit,
+      });
+    } catch {
+      // TODO: 저장 실패 피드백 처리
+    }
   })();
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const onClickedChangeChart = () => {
void (async () => {
await api
.patch(`/canvas/charts/${chartId}`, {
xAxis: chartState.xAxisName,
yAxis: chartState.yAxisName,
})
.then(res => {
openToastMessage({ dom: toastLayerRef.current, type: 'success', message: res.message });
});
await api.patch(`/canvas/charts/${chartId}`, {
xAxis: chartState.xAxisName,
yAxis: chartState.yAxisName,
name: chartState.name,
unit: chartState.unit,
});
})();
const onClickedChangeChart = () => {
void (async () => {
try {
await api.patch(`/canvas/charts/${chartId}`, {
xAxis: chartState.xAxisName,
yAxis: chartState.yAxisName,
name: chartState.name,
unit: chartState.unit,
});
} catch {
// TODO: 저장 실패 피드백 처리
}
})();
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/chart/components/ChartOptionPanel.tsx` around lines 16 - 24, The
onClickedChangeChart handler calls api.patch(`/canvas/charts/${chartId}`...)
inside an async IIFE without error handling, so failed save requests are
swallowed; wrap the await api.patch call in a try/catch inside
onClickedChangeChart (or inside the async IIFE) to catch errors from api.patch,
handle the failure by logging or surfacing it (e.g., processLogger/console.error
and showing a user-facing error/toast or setting an error state), and keep
references to chartId and chartState when composing the patch payload so the
catch block can include contextual info about the failed save.

chartSocket.enterChartSession(chartId, message => {
switch (message.action) {
case 'CHART_BAR_UPDATED': {
openToastMessage({ dom: toastLayerRef.current, type: 'success', message: '차트 데이터 값이 변경되었습니다.' });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

토스트 레이어 null 가드가 없어 런타임 오류 위험이 있습니다.

toastLayerRef.current가 비어있는 시점에 소켓 이벤트가 오면 토스트 호출이 깨질 수 있습니다. 가드 후 호출로 맞춰 주세요.

제안 diff
- openToastMessage({ dom: toastLayerRef.current, type: 'success', message: '차트 데이터 값이 변경되었습니다.' });
+ if (toastLayerRef.current) {
+   openToastMessage({ dom: toastLayerRef.current, type: 'success', message: '차트 데이터 값이 변경되었습니다.' });
+ }

- openToastMessage({ dom: toastLayerRef.current, type: 'success', message: '차트 바가 삭제되었습니다.' });
+ if (toastLayerRef.current) {
+   openToastMessage({ dom: toastLayerRef.current, type: 'success', message: '차트 바가 삭제되었습니다.' });
+ }

Also applies to: 33-33

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/sockets/chartModalSocketHandler.ts` at line 14, The socket handlers call
openToastMessage with toastLayerRef.current without guarding for null, which can
throw if the ref is empty; update the code in chartModalSocketHandler.ts to
check that toastLayerRef.current is truthy before calling openToastMessage (for
both occurrences where openToastMessage({ dom: toastLayerRef.current, ... }) is
used), e.g., wrap the call in an if (toastLayerRef.current) { ... } or
early-return to skip toasting when the DOM ref is missing so the socket handlers
remain safe.

@10hajin15 10hajin15 merged commit cafb828 into main May 20, 2026
9 checks passed
This was referenced May 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE] 차트 모달 옵션 단위 추가

1 participant