diff --git a/CHANGELOG.md b/CHANGELOG.md index d8834a24d..9a632832c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,4 +5,4 @@ - [FE] ๐Ÿ—‘๏ธ **๋ชจ๋“  ๋…ธ๋“œ ์ง€์šฐ๊ธฐ ๊ธฐ๋Šฅ ์ถ”๊ฐ€**: ์บ”๋ฒ„์Šค์˜ ๋ชจ๋“  ํ…Œ์ด๋ธ” ๋…ธ๋“œ์™€ ๊ด€๊ณ„๋ฅผ ํ•œ ๋ฒˆ์— ์ดˆ๊ธฐํ™”ํ•˜๋Š” ๋ฒ„ํŠผ์„ ํˆด๋ฐ”์— ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. - [FE] ๐Ÿ“‹ **ํ…Œ์ด๋ธ” ๋ณต์ œ ๊ธฐ๋Šฅ ์ถ”๊ฐ€**: ํŽธ์ง‘ ๋ชจ๋‹ฌ ๋‚ด์—์„œ ๊ธฐ์กด ํ…Œ์ด๋ธ”์˜ ๊ตฌ์กฐ(์ปฌ๋Ÿผ ์ •๋ณด ํฌํ•จ)๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณต์‚ฌํ•˜์—ฌ ์ƒˆ ํ…Œ์ด๋ธ” ๋…ธ๋“œ๋กœ ์ƒ์„ฑํ•˜๋Š” '๋ณต์ œ' ๋ฒ„ํŠผ์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. - [FE] `autoInfer.ts`์— ๋Œ€ํ•œ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ ๋ฐ UI ์ปดํฌ๋„ŒํŠธ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ 100% ํ…Œ์ŠคํŠธ ์ปค๋ฒ„๋ฆฌ์ง€๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. - +- [FE] โฌ‡๏ธ **DBML Export**: ERD ๋‹ค์ด์–ด๊ทธ๋žจ์„ DBML (Database Markup Language) ํ˜•์‹์œผ๋กœ ๋‚ด๋ณด๋‚ผ ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ–ˆ์Šต๋‹ˆ๋‹ค. ์ƒ๋‹จ์˜ DBML ๋ฒ„ํŠผ์„ ํด๋ฆญํ•˜์—ฌ ๋‹ค์šด๋กœ๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 708bb2248..822e866b6 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -58,6 +58,7 @@ import { } from "./erd/export"; import { exportMermaid } from "./erd/mermaid"; import { inferRelationships } from "./erd/autoInfer"; +import { exportDbml } from "./erd/dbml"; import { GRID_COLUMNS, GRID_X_GAP, GRID_Y_GAP } from "./erd/layoutConstants"; import type { Connection, Project, Snapshot, SnapshotDetail } from "./types"; @@ -69,6 +70,16 @@ const TERMINAL_SNAPSHOT_STATUSES = new Set([ const SUPPORTED_DSN_PROTOCOLS = new Set(["postgres:", "postgresql:", "snowflake:"]); +function sanitizeHtml(str: string | null | undefined): string { + if (!str) return ""; + return String(str) + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); +} + type CurrentUser = { subject: string; display_name: string | null; @@ -626,6 +637,10 @@ export default function App() { downloadText("pg-erd-diagram.mermaid", exportMermaid(nodes, edges), "text/plain"); } + function onDownloadDbml() { + downloadText("pg-erd-diagram.dbml", exportDbml(nodes, edges), "text/plain"); + } + function onRelDelete() { if (!editingEdge) return; if (!window.confirm("์ •๋ง๋กœ ์ด ๊ด€๊ณ„๋ฅผ ์‚ญ์ œํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?")) return; @@ -1244,7 +1259,7 @@ export default function App() { }} >