Skip to content

Latest commit

ย 

History

History
1346 lines (922 loc) ยท 202 KB

README.md

File metadata and controls

1346 lines (922 loc) ยท 202 KB

devup-mcp

/devup starts devup_project_plan intake for projects, features, bug fixes, Figma pages and refactors. Read sources, settle every question, confirm the ERD and plan, then split supervisor work units; Figma screen planning is one branch.

Rust-native MCP server that reads Figma designs and generates DevupUI artifacts. One binary acts as a local stdio MCP server and a read-only client of Figma Remote MCP.

์ €์žฅ์†Œ๋Š” Cargo workspace์ด๋ฉฐ devup-mcp ์‹คํ–‰ crate, OAuthยทupstreamยทsnapshot์„ ๋‹ด๋‹นํ•˜๋Š” devup-mcp-figma, TSXยทtheme projection์„ ๋‹ด๋‹นํ•˜๋Š” devup-mcp-devup-ui, PNG ๋น„๊ต library/CLI์ธ devup-mcp-visual๋กœ ๊ตฌ์„ฑ๋ฉ๋‹ˆ๋‹ค. ๋ณ„๋„ IR/auth/server crate ์—†์ด MCP ์ œํ’ˆ ์„ค์น˜ ๋‹จ์œ„๋Š” devup-mcp ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.

๋„๊ตฌ

Figma ์ชฝ 5๊ฐœ, ํ”„๋กœ์ ํŠธ ์ชฝ 7๊ฐœ, ์Šคํ‚ฌ 1๊ฐœ, ๋ชจ๋‘ 13๊ฐœ์ž…๋‹ˆ๋‹ค.

  • devup_figma_export: Figma๋ฅผ ํ•œ ๋ฒˆ ์ˆ˜์ง‘ํ•ด ์š”์ฒญํ•œ outputs๋งŒ ํˆฌ์˜ํ•ฉ๋‹ˆ๋‹ค. TSX๊ฐ€ ์‚ฐ์ถœ๋ฌผ์ด๊ณ , componentTsxยทresponsiveTsxยทdevup.jsonยทsource mapยทasset manifestยทreference PNG๋ฅผ ๊ฐ™์€ ์ˆ˜์ง‘์—์„œ ํ•จ๊ป˜ ์–ป๊ฑฐ๋‚˜, cache.artifactId๋กœ ์žฌ์ˆ˜์ง‘ ์—†์ด ์ถ”๊ฐ€ ํˆฌ์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. raw snapshotยทraw payload๋Š” ๊ตฌํ˜„์ด ์•„๋‹ˆ๋ผ ์ง„๋‹จ์— ์“ฐ๋Š” ๊ฒƒ์ด๋ผ debug: true๋กœ๋งŒ ์—ด๋ฆฝ๋‹ˆ๋‹ค.
  • devup_figma_search: page, section, frame, component๋ฅผ ์ด๋ฆ„์œผ๋กœ ํƒ์ƒ‰. URL์— node-id๊ฐ€ ์žˆ์œผ๋ฉด ๊ทธ ๋…ธ๋“œ์™€ ๊ทธ ์•„๋ž˜๋กœ ๋ฒ”์œ„๋ฅผ ์ขํžˆ๊ณ , ์—†์œผ๋ฉด ํŒŒ์ผ ์ „์ฒด๋ฅผ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค. ๋‘˜ ์ค‘ ๋ฌด์—‡์„ ํ–ˆ๋Š”์ง€๋Š” ์‘๋‹ต์˜ scope๊ฐ€ ์•Œ๋ ค์ค๋‹ˆ๋‹ค
  • devup_figma_explore: ๋งํฌ๋œ ์š”๊ตฌ์‚ฌํ•ญ/๋ผ๋ฒจ ์ฃผ๋ณ€์˜ ์‹ค์ œ ํ™”๋ฉด ํ›„๋ณด๋ฅผ ๊ณต๊ฐ„ ์ˆœ์„œ๋กœ ํƒ์ƒ‰
  • devup_figma_auth: Figma์— ๋‹ฟ๋Š” ๋‘ ๊ฒฝ๋กœ(๋ธŒ๋ฆฌ์ง€ ํ”Œ๋Ÿฌ๊ทธ์ธยทdirect OAuth)๋ฅผ ํ•จ๊ป˜ ๋ณด๊ณ (status) โ€” ๋ถ™์–ด ์žˆ๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ํŒŒ์ผยทํ˜„์žฌ ํŽ˜์ด์ง€ยท์„ ํƒ๊นŒ์ง€. ๊ทธ ๋ฐ–์— ๋ธŒ๋ผ์šฐ์ € OAuth ๋กœ๊ทธ์ธ, ๋กœ๊ทธ์•„์›ƒ, ์‚ฌ์ „ ๋“ฑ๋ก ์ž๊ฒฉ์ฆ๋ช… ์ฃผ์ž…(configure), ์ฐธ์กฐ ๋ฐ์ดํ„ฐ๊นŒ์ง€ ๋ถ™์ธ doctor. ๋กœ๊ทธ์ธ์„ ์š”์ฒญํ•˜๊ธฐ ์ „์— status๋ถ€ํ„ฐ ๋ถ€๋ฅด์„ธ์š” โ€” ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋ถ™์–ด ์žˆ์œผ๋ฉด ๋กœ๊ทธ์ธ์€ ํ•„์š” ์—†์Šต๋‹ˆ๋‹ค
  • devup_skills: devup-mcp๊ฐ€ ๋‚ด๋†“๋Š” ์ฝ”๋“œ์— ํ•„์š”ํ•œ ์—์ด์ „ํŠธ ์Šคํ‚ฌ์ด ์ด ์›Œํฌ์ŠคํŽ˜์ด์Šค์— ์žˆ๋Š”์ง€ ๋ณด๊ณ (status)ํ•˜๊ณ , devup-mcp๊ฐ€ ํ’ˆ๊ณ  ์žˆ๋Š” ๊ฒƒ์„ ์„ค์น˜(install). ํ…์ŠคํŠธ๋ฅผ ์‘๋‹ต์— ์‹ค์–ด ๋ณด๋‚ด๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ์Šคํ‚ฌ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์„ค์น˜ํ•ด์„œ ์—์ด์ „ํŠธ ์ž์‹ ์˜ ๋กœ๋”๊ฐ€ ์ฝ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค โ€” ํ•œ ๋ฒˆ ์ฝ์€ ๋ฌธ์„œ๋Š” ํ•œ ๋ฒˆ ์“ฐ์ด์ง€๋งŒ, ์„ค์น˜๋œ ์Šคํ‚ฌ์€ ์ดํ›„ ๋ชจ๋“  ์„ธ์…˜์— ๊ณ„์† ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ์–ด๋А ๋””๋ ‰ํ„ฐ๋ฆฌ์ธ์ง€๋Š” clientInfo๋กœ ํŒ๋ณ„ํ•œ ๋Ÿฐํƒ€์ž„์ด ์ •ํ•˜๊ณ , projectRoot๋กœ ์–ด๋А ํ”„๋กœ์ ํŠธ์ธ์ง€ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค
  • devup_project_context: ํ”„๋กœ์ ํŠธ์˜ ์‹ค์ œ devup.json ํ† ํฐ, openapi.json ์—”๋“œํฌ์ธํŠธ, Vespertide ๋ชจ๋ธ์„ ์ฝ์Œ. ์ค‘์ฒฉ ์ฒดํฌ์•„์›ƒ๊ณผ ๋นŒ๋“œ ์‚ฐ์ถœ๋ฌผ ๋””๋ ‰ํ„ฐ๋ฆฌ๋Š” ์Šค์บ”์—์„œ ์ œ์™ธํ•˜๊ณ  ๋ฌด์—‡์„ ์ œ์™ธํ–ˆ๋Š”์ง€ ๋ณด๊ณ 
  • devup_ui_validate: ์ƒ์„ฑํ•œ TSX๋ฅผ ํ”„๋กœ์ ํŠธ์˜ ์‹ค์ œ devup.json์— ๋Œ€์กฐํ•ด ๊ฒ€์ฆ. ok๋Š” ๊ฐœ์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ผ ์‹ฌ๊ฐ๋„๋กœ ํŒ์ •
  • devup_stack_diff: DB ๋ชจ๋ธ๋ถ€ํ„ฐ ์ƒ์„ฑ๋œ API ํด๋ผ์ด์–ธํŠธ๊นŒ์ง€์˜ ์ธต๊ฐ„ ๋“œ๋ฆฌํ”„ํŠธ ํƒ์ง€. ๋ชจ๋“  ๋ฐœ๊ฒฌ์€ ๋ช…์‹œ์  confidence๋ฅผ ๊ฐ€์ง
  • devup_visual_compare: ์†Œ๋น„์ž๊ฐ€ ๋งŒ๋“  actual.png๋ฅผ reference PNG ํ•˜๋‚˜์™€ ๋น„๊ต. ์Šค์Šค๋กœ ๋ Œ๋”ํ•˜์ง€ ์•Š๊ณ  ๋ธŒ๋ผ์šฐ์ €๋ฅผ ๋„์šฐ์ง€๋„ ๋ช…๋ น์„ ์‹คํ–‰ํ•˜์ง€๋„ ์•Š์Šต๋‹ˆ๋‹ค โ€” ๊ฒฝ๋กœ๋Š” allowlist ์•ˆ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ threshold๋Š” 0.005(0.5%). renderer ํ™˜๊ฒฝ manifest๊ฐ€ ์—†๊ฑฐ๋‚˜ ๋ถˆ์™„์ „ํ•˜๊ฑฐ๋‚˜ ์œ ํšจํ•˜์ง€ ์•Š์œผ๋ฉด visual.passed๊ฐ€ true์—ฌ๋„ ํŒ์ •์€ inconclusive์ž…๋‹ˆ๋‹ค
  • devup_feature_trace: ๋ช…์‹œ์  anchor(routePath, figmaNodeId/artifactId, operationId ๋˜๋Š” apiPath+method, componentPath, tableName)์—์„œ ์ถœ๋ฐœํ•ด ํ™”๋ฉดโ†’APIโ†’๋ผ์šฐํŠธโ†’๋ชจ๋ธโ†’์ปฌ๋Ÿผ ์Šฌ๋ผ์ด์Šค์™€ acceptance matrix๋ฅผ ์ฝ์–ด๋ƒ„. ์š”๊ตฌ์‚ฌํ•ญ ์‚ฐ๋ฌธ๋งŒ ์ฃผ๋ฉด ๊ฑฐ์ ˆํ•ฉ๋‹ˆ๋‹ค. ๊ฐ hop์€ ๊ทผ๊ฑฐ๊ฐ€ ์žˆ๊ฑฐ๋‚˜ UNVERIFIED๋กœ ํ‘œ์‹œ๋˜๊ณ , ์ƒ์„ฑ ์‚ฐ์ถœ๋ฌผ ์†Œ์œ ๊ถŒยทUI ์žฌ์‚ฌ์šฉ ์ˆœ์œ„ยท์ž˜๋ฆผ ์ƒํ•œ์„ ํ•จ๊ป˜ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค
  • devup_runtime_audit: ์ƒ์„ฑํ•œ ํ™”๋ฉด์„ ์—ฌ๋Ÿฌ viewport ํญ์—์„œ ์žฌ ๋ณผ ๋ธŒ๋ผ์šฐ์ € ์‹คํ–‰ ๊ณ„ํš์„ ๊ฒ€์ฆํ•ด ์“ฐ๊ณ (plan), ์†Œ๋น„์ž ํ”„๋กœ์ ํŠธ์˜ ์‹คํ–‰๊ธฐ(harness/runtime-audit)๊ฐ€ ์“ด ๋ณด๊ณ ์„œ๋ฅผ ๊ทธ ๊ณ„ํš์— ๋ฌถ์–ด ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค(ingest). ์„œ๋ฒ„๋Š” ๋ธŒ๋ผ์šฐ์ €๋ฅผ ๋„์šฐ์ง€๋„ ๋ช…๋ น์„ ์‹คํ–‰ํ•˜์ง€๋„ ์•Š์Šต๋‹ˆ๋‹ค โ€” ๊ณ„ํš์€ ์„ ์–ธํ˜• ๋ฐ์ดํ„ฐ๋ฟ์ด๊ณ , ๊ณ„ํš id์™€ ํŒŒ์ผ ํ•ด์‹œ์— ๋ฌถ์ด์ง€ ์•Š์€ ๋ณด๊ณ ์„œ๋Š” ๊ฑฐ์ ˆํ•˜๋ฉฐ ๋ชจ๋“  ๊ฐœ์ˆ˜๋ฅผ ํ•ญ๋ชฉ์—์„œ ๋‹ค์‹œ ์…‰๋‹ˆ๋‹ค
  • devup_screen_plan: ํŽ˜์ด์ง€๋ฅผ ์“ฐ๊ธฐ ์ „์— Figma ํ™”๋ฉด๋งˆ๋‹ค ์–ด๋А URL์ด ๋ ์ง€ ๊ณ„ํšํ•˜๊ณ (draft), ์—ด๋ฆฐ ๊ฒฐ์ •์„ ๊ตฌํ˜„ ์ „์— ์งˆ๋ฌธ ๋ผ์šด๋“œ๋กœ ์—ฐ๋‹ฌ์•„ ๋ชจ๋‘ ๋ฌผ์€ ๋’ค ํ™•์ •ํ•˜๊ณ (confirm), ํ™•์ •๋œ ๊ณ„ํš์„ ์‹ค์ œ ๋ผ์šฐํŠธ์™€ runtime audit ๋ณด๊ณ ์„œ์— ๋Œ€์กฐํ•ด URL ร— ํญ ํ‘œ๋กœ ๋ณด๊ณ (status). ๋ผ์šฐํŠธ๋Š” ๊ทผ๊ฑฐ(๊ทธ ํ”„๋ ˆ์ž„์œผ๋กœ ๋งŒ๋“  ๊ธฐ์กด ํŽ˜์ด์ง€, ๊ธฐ์กด href์— ๋ฌถ์ธ nav ๋ผ๋ฒจ, navigationTargets, ํ”„๋กœ์ ํŠธ ์„ค์ •)๋กœ๋งŒ ์ œ์•ˆํ•˜๊ณ , ๋ ˆ์ด์–ด ์ด๋ฆ„์œผ๋กœ ์ถ”์ •ํ•œ ๊ฒƒ์€ name-match๋กœ ๋“œ๋Ÿฌ๋‚ด ๋ฌป๊ณ , ๋‚˜๋จธ์ง€๋Š” UNVERIFIED ์งˆ๋ฌธ์ด ๋ฉ๋‹ˆ๋‹ค. ํ™•์ • ๋’ค์—๋Š” ๋ฌป์ง€ ์•Š๊ณ  ๊ฐ€์ •(assume)์œผ๋กœ ๊ธฐ๋กํ•˜๋ฉฐ, auditPlan์œผ๋กœ URL๋งˆ๋‹ค ํŽ˜์ด์ง€ยท์„น์…˜ยท์—์…‹ยท์œ„์น˜ยท์ƒํƒœ๋ฅผ ๋ชจ๋‘ ๊ฒ€์ฆํ•˜๋Š” audit ๊ณ„ํš์„ ์“ฐ๊ณ  defer๋กœ ๊ฒฐ์ •์„ ๋ฏธ๋ค„ ๋‘๊ณ  ๋ฉˆ์ถ”์ง€ ์•Š์Šต๋‹ˆ๋‹ค

devup_figma_to_ui์™€ devup_figma_to_json์€ ๊ฐ๊ฐ devup_figma_export์— outputs: ["tsx"], outputs: ["devupJson"]์„ ๋„˜๊ธด ๊ฒƒ๊ณผ ๊ฐ™์•„์„œ ์ œ๊ฑฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๋„๊ตฌ๊ฐ€ ์…‹์ด๋ฉด ๋ชจ๋“  ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„ธ ๊ฐœ์˜ ์Šคํ‚ค๋งˆ๋ฅผ ์ปจํ…์ŠคํŠธ์— ์‹ฃ๊ณ ๋„ ์–ด๋А ๊ฒƒ์„ ๋ถ€๋ฅผ์ง€ ๋งค๋ฒˆ ํŒ๋‹จํ•ด์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

devup-mcp๋Š” Figma Plugin API์˜ readable data property๋ฅผ raw JSON์œผ๋กœ ๋ณด์กดํ•˜๊ณ , ์•Œ๋ ค์ง€์ง€ ์•Š์€ runtime field๋Š” extra, ์‹คํŒจํ•œ getter๋Š” fieldErrors๋กœ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

์‘๋‹ต์— ๋ฌด์—‡์ด ๋“ค์–ด์˜ค๋Š”๊ฐ€

devup_figma_export๋Š” ์š”์ฒญํ•œ outputs๊ฐ€ ๋งŒ๋“œ๋Š” ํ‚ค๋งŒ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. outputs๋ฅผ ์ƒ๋žตํ–ˆ์„ ๋•Œ์˜ ์Šคํ‚ค๋งˆ ๊ธฐ๋ณธ๊ฐ’์€ ["tsx"] ํ•˜๋‚˜๋ฟ์ด๋ฉฐ, ์•„๋ž˜ ํ‘œ๋Š” ๋ฌด์—‡์„ ๋” ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋Š”์ง€์— ๋Œ€ํ•œ ๋ ˆํผ๋Ÿฐ์Šค์ด์ง€ ํ•œ ๋ฒˆ์— ์ „๋ถ€ ์š”์ฒญํ•˜๋ผ๋Š” ๋ชฉ๋ก์ด ์•„๋‹™๋‹ˆ๋‹ค.

devupJson์€ ๊ธฐ๋ณธ๊ฐ’์—์„œ ๋น ์กŒ์Šต๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ์— ์ด๋ฏธ devup.json์ด ์žˆ์œผ๋ฉด ์ฝ”๋“œ๊ฐ€ ๋งž์ถฐ์•ผ ํ•  ๋Œ€์ƒ์€ ๊ทธ ํŒŒ์ผ์ด์ง€ Figma์—์„œ ์ƒˆ๋กœ ๋ฝ‘์€ ์ด๋ฆ„ ์ง‘ํ•ฉ์ด ์•„๋‹ˆ๊ณ , ๊ทธ ํŒŒ์ผ์„ ์ฝ๋Š” ๋„๊ตฌ๋Š” devup_project_context์ž…๋‹ˆ๋‹ค. devupJson์€ ํ”„๋กœ์ ํŠธ์— ์•„์ง devup.json์ด ์—†๊ฑฐ๋‚˜, ๊ทธ ํŒŒ์ผ์ด ์ •์˜ํ•˜์ง€ ์•Š์€ ํ† ํฐ์„ ์ƒˆ๋กœ ๋“ค์ผ ๋•Œ๋งŒ ํ•จ๊ป˜ ์š”์ฒญํ•˜์„ธ์š”.

output ์ถ”๊ฐ€๋˜๋Š” ํ‚ค
tsx tsx
componentTsx componentTsx
responsiveTsx responsiveTsx, responsiveSlots, (ํ‘œํ˜„ ๋ถˆ๊ฐ€ํ•œ ๊ฐ’์ด ์žˆ์œผ๋ฉด) responsiveUnrepresented
devupJson devupJson, themeCounts, themeCompleteness, conflicts, unresolvedVariables
sourceMap / assetManifest ๊ฐ™์€ ์ด๋ฆ„์˜ ํ‚ค
referencePng referencePng, (ํ”Œ๋Ÿฌ๊ทธ์ธ์ด get_screenshot๊ณผ ๊ฐ™์€ ๊ทธ๋ฆผ์„ ๋‹ค ๊ทธ๋ฆฌ์ง€ ๋ชปํ•ด ์บ”๋ฒ„์Šค์˜ ๊ทธ ์ž๋ฆฌ๋ฅผ ๊ทธ๋ ธ์œผ๋ฉด) referencePngNote
rawSnapshot / rawPayload ๊ฐ™์€ ์ด๋ฆ„์˜ ํ‚ค โ€” debug: true ํ•„์š”

๊ทธ ๋ฐ–์— ํ•ญ์ƒ ๋ถ™๋Š” ๊ฒƒ์€ status, quality, completeness, cache, collection, source, targetKind, failures, assetSummary, outputPaths๋ฟ์ž…๋‹ˆ๋‹ค.

์กฐ๊ฑด์ด ๋งž์„ ๋•Œ๋งŒ ๋ถ™๋Š” ํ‚ค๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

ํ‚ค ๋‚˜์˜ค๋Š” ์กฐ๊ฑด
fidelity / completenessReport ๊ฒฐ๊ณผ๊ฐ€ exact/complete๊ฐ€ ์•„๋‹ ๋•Œ, ๋˜๋Š” includeDiagnostics: true์ผ ๋•Œ
deliverable status๊ฐ€ complete์ด๊ณ  TSX๋ฅผ ๋งŒ๋“ค์—ˆ์„ ๋•Œ
warnings ์ˆจ๊น€ ๋…ธ๋“œ์ฒ˜๋Ÿผ ์‹คํŒจ๊ฐ€ ์•„๋‹Œ ์ž์‚ฐ ๋ฌธ์ œ๊ฐ€ ์žˆ์„ ๋•Œ
frames Section์—์„œ ์—ฌ๋Ÿฌ ํ™”๋ฉด์„ ํ•œ ๋ฒˆ์— exportํ–ˆ์„ ๋•Œ
selection / nextAction status๊ฐ€ selection_required์ผ ๋•Œ
widthBehavior tsx(Section ๋ฐฐ์น˜์—์„œ๋Š” componentTsx๋„)๋ฅผ ๋งŒ๋“ค์—ˆ์„ ๋•Œ. ๋‹จ์ผ ํ™”๋ฉด์€ ์ตœ์ƒ์œ„, Section ๋ฐฐ์น˜๋Š” frames[] ๊ฐ ํ•ญ๋ชฉ (๊ทธ๋ฆฐ ํญ์ด ์•„๋‹Œ ํญ)
verificationScope ์ƒ์„ฑ TSX์˜ ํญ(widthBehavior.designWidth, ๋ฐ˜์‘ํ˜•์ด๋ฉด ์ˆ˜์ง‘ํ•œ ํญ๋“ค)์„ ์•Œ ๋•Œ

fidelity์™€ completenessReport๋ฅผ ๊ธฐ๋ณธ์—์„œ ๋บ€ ์ด์œ ๋Š” ๊นจ๋—ํ•œ ๊ฒฐ๊ณผ์—์„œ quality๊ฐ€ ์ด๋ฏธ ํ•œ ๋ง์„ ๋˜ํ’€์ดํ•  ๋ฟ์ด๊ธฐ ๋•Œ๋ฌธ์ด๊ณ , ๊ทธ๋งŒํผ(์ธก์ •๊ฐ’ 797 B) ๋งค ์‘๋‹ต์ด ๊ฐ€๋ฒผ์›Œ์ง‘๋‹ˆ๋‹ค.

assetSummary โ€” ์ž์‚ฐ์ด "์—†์Œ"์ธ์ง€ "๋ชป ๋ฐ›์Œ"์ธ์ง€

์ž์‚ฐ์€ ์—†๋Š” ๊ฒƒ๊ณผ ๋ชป ๋ฐ›์€ ๊ฒƒ์ด ์ „ํ˜€ ๋‹ค๋ฅธ ์ƒํ™ฉ์ธ๋ฐ ์˜ˆ์ „์—๋Š” ๋‘˜ ๋‹ค ๋นˆ ๋ชฉ๋ก์œผ๋กœ ๋ณด์˜€์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ assetSummary๊ฐ€ ๊ตฌ๋ถ„ํ•ด ์ค๋‹ˆ๋‹ค.

  • status: none(์ž์‚ฐ์ด ์—†๋‹ค) ยท unknown(์Šค๋ƒ…์ƒท์ด ๋ถˆ์™„์ „ํ•ด ํŒ๋‹จ ๋ถˆ๊ฐ€) ยท not-collected ยท partial ยท collected
  • discovery: complete | incomplete, discoveryReason: index-only | snapshot-incomplete
  • discoveredCount / collectedCount / manifestIncluded
  • unavailable[]: ํ•ญ๋ชฉ๋งˆ๋‹ค assetId, nodeId, errorCode์™€ reason โ€” hidden-node(์ˆจ๊น€ ๋…ธ๋“œ) ยท export-failed(export ์‹คํŒจ) ยท not-requested(์• ์ดˆ์— ์š”์ฒญํ•˜์ง€ ์•Š์Œ) ยท capture-not-in-artifact(์š”์ฒญํ–ˆ์ง€๋งŒ ์ด artifact์—” ๊ทธ format/scale์ด ์—†์Œ)
  • export-failed์—๋Š” errorDetail ์ด ๋ถ™์Šต๋‹ˆ๋‹ค โ€” ์‹คํŒจํ•œ ๋‹จ๊ณ„, ๊ฑธ๋ฆฐ ์‹œ๊ฐ„, Figma๋‚˜ ์ „์†ก์ด ํ•œ ๋ง ๊ทธ๋Œ€๋กœ(์ตœ๋Œ€ 500์ž). ์˜ˆ์ „์—๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์˜ˆ์™ธ๋ฅผ ๋ฒ„๋ ค์„œ ์ฝ”๋“œ๋งŒ ๋‚จ์•˜๊ณ , ์ธ์Šคํ„ด์Šค ์•ˆ์ชฝ ๋ ˆ์ด์–ด ์—ฌ์„ฏ ๊ฐœ๊ฐ€ ๊ฐ๊ฐ 11์ดˆ์”ฉ ๊ฑธ๋ ค ์‹คํŒจํ•œ ์ด์œ ๋ฅผ ์•„๋ฌด๋„ ์•Œ ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. failures[]์˜ ๊ฐ™์€ ํ•ญ๋ชฉ์—๋„ errorDetail์ด ์‹ค๋ฆฌ๊ณ , Figma ๋Ÿฐํƒ€์ž„์ด ๋ณด๊ณ  ์žˆ๋Š” ํŽ˜์ด์ง€ ๋ฐ–์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค์ง€ ๋ชปํ•œ ๊ฒฝ์šฐ๋ฉด hint๊ฐ€ ํ•  ์ผ์„ ๋งํ•ฉ๋‹ˆ๋‹ค

์ˆจ๊น€ ๋…ธ๋“œ ์‹คํŒจ๋Š” failures[]๊ฐ€ ์•„๋‹ˆ๋ผ warnings[]๋กœ ๊ฐ‘๋‹ˆ๋‹ค. ๊ตฌํ˜„์„ ๋ง‰๋Š” ์‹คํŒจ๊ฐ€ ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๊ทธ ์—ฌ์„ฏ ๊ฐœ์˜ ์›์ธ์€ Figma ๋ฐ์Šคํฌํ†ฑ ์•ฑ์˜ ํƒญ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋„๋Š” ํŒŒ์ผ์˜ ํƒญ์ด ๋‹ค๋ฅธ ํŒŒ์ผ์˜ ํƒญ ๋’ค์— ๋†“์—ฌ ์žˆ๋Š” ๋™์•ˆ, Figma ๋Ÿฐํƒ€์ž„์€ ์•„์ง ๋ถˆ๋Ÿฌ์˜ค์ง€ ์•Š์€ ๋ฐ์ดํ„ฐ๊ฐ€ ํ•„์š”ํ•œ ์ฝ๊ธฐ โ€” ์ธ์Šคํ„ด์Šค ์•ˆ์ชฝ ๋ ˆ์ด์–ด์˜ export, ๋‹ค๋ฅธ ํŽ˜์ด์ง€์˜ ๋ฉ”์ธ ์ปดํฌ๋„ŒํŠธ, id๋กœ ์ฐพ๋Š” ๋ณ€์ˆ˜์™€ ์Šคํƒ€์ผ โ€” ๋งˆ๋‹ค 10์ดˆ๋ฅผ ๊ธฐ๋‹ค๋ฆฐ ๋’ค "Unable to establish connection to Figma after 10 seconds"๋กœ ๋‹ตํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ํƒญ์„ ๋ˆ„๋ฅด์ž ๊ฐ™์€ ์ฝ๊ธฐ๊ฐ€ ๊ณง๋ฐ”๋กœ ๋‹ตํ–ˆ์Šต๋‹ˆ๋‹ค โ€” ๊ทธ ๋ฌธ๊ตฌ๋กœ 10.4 s ๋งŒ์— ์‹คํŒจํ•˜๋˜ ๊ฒ€์ƒ‰์ด 0.2 s์— ๋‹ตํ–ˆ๊ณ , ๊ณต์ง€ ํ™”๋ฉด์ด ์“ฐ๋Š” ๋ณ€์ˆ˜ยท์Šคํƒ€์ผ์ด 0/14ยท0/23์—์„œ 14ยท23์œผ๋กœ ๋Œ์•„์™”์Šต๋‹ˆ๋‹ค. ๋ถ„ ๋‹จ์œ„๋กœ ๋‹ค์‹œ ์žฌ ๋ณด๋‹ˆ ํƒญ์ด ๋’ค๋กœ ๊ฐ„ ์ง€ 1๋ถ„์งธ๋Š” ๋‹ตํ•˜๊ณ  2๋ถ„์งธ๋ถ€ํ„ฐ ๊ฐ™์€ ๋ฌธ๊ตฌ๋กœ ์‹คํŒจํ–ˆ๊ณ , ํƒญ์„ ์•ž์— ๋‘” ์ฑ„ ์ฐฝ๋งŒ ์ตœ์†Œํ™”ํ•ด๋„ 3๋ถ„์งธ๋ถ€ํ„ฐ ๊ฐ™์•˜์Šต๋‹ˆ๋‹ค โ€” ๊ทธ ํŒŒ์ผ์ด ํ™”๋ฉด์— ๋ณด์ด์ง€ ์•Š๋Š” ๊ฒƒ์ด ์›์ธ์ž…๋‹ˆ๋‹ค(์•„๋ž˜ "๊ทธ ํŒŒ์ผ์„ ํ™”๋ฉด์— ๋„์›Œ ๋‘์„ธ์š”"). ์ฒ˜์Œ์—๋Š” ๋ช‡ ์‹œ๊ฐ„ ์—ด์–ด ๋‘” ํ”Œ๋Ÿฌ๊ทธ์ธ ์ฐฝ์„ ์›์ธ์œผ๋กœ ๋ณด์•˜์Šต๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ๊ทธ ํŒŒ์ผ์˜ ํƒญ์—์„œ๋งŒ ์‹คํ–‰๋˜๋ฏ€๋กœ, ๋‹ค์‹œ ์‹คํ–‰ํ•˜๋Š” ์ฒ˜๋ฐฉ์ด ํ†ตํ•œ ๊ฒƒ๋„ ํŒŒ์ผ์ด ์•ž์œผ๋กœ ์™”๊ธฐ ๋•Œ๋ฌธ์œผ๋กœ ๋ณด์ž…๋‹ˆ๋‹ค. ์ด ๋ฌธ๊ตฌ๊ฐ€ ์˜ค๋ฉด hint(์ž์‚ฐ)์™€ nextAction.how(์ฝ๊ธฐ)๊ฐ€ ์ฐฝ์„ ์—ด๊ณ  ๊ทธ ํŒŒ์ผ์˜ ํƒญ์„ ๋ˆ„๋ฅด๋ผ๊ณ  ๋งํ•˜๊ณ , ํ•ด์„ํ•˜์ง€ ๋ชปํ•œ ๋ณ€์ˆ˜ยท์Šคํƒ€์ผ์ด ์žˆ์œผ๋ฉด completenessReport.resources์— Figma๊ฐ€ ํ•œ ๋ง(lookupError)๊ณผ ๊ฐ™์€ hint๊ฐ€ ์‹ค๋ฆฝ๋‹ˆ๋‹ค.

rawSnapshot๊ณผ rawPayload๋Š” ์ˆ˜์ง‘ํ•œ ๋””์ž์ธ์„ raw๋กœ ๋‹ด์€ ๊ฒƒ์ด๋ผ debug: true ์—†์ด๋Š” ๊ฑฐ์ ˆ๋ฉ๋‹ˆ๋‹ค. ํ™”๋ฉด์„ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐ๋Š” ํ•„์š” ์—†์Šต๋‹ˆ๋‹ค โ€” ์‹ค์ œ ์บก์ฒ˜ 10๊ฐœ ํ™”๋ฉด์—์„œ tsx๊ฐ€ nodeยทtextยทtypographyยทassetยทlayout ๊ธฐ๋Œ€์น˜๋ฅผ 100% ๋‹ด๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์“ฐ๋Š” ์ž๋ฆฌ๋Š” ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค: ํ™”๋ฉด์ด ์ด์ƒํ•ด ๋ณด์ผ ๋•Œ ์ƒ์„ฑ๊ธฐ ํƒ“์ธ์ง€ ๋””์ž์ธ์ด ์›๋ž˜ ๊ทธ๋Ÿฐ์ง€ ํŒ์ •ํ•˜๋Š” ๊ฒƒ. ๊ทธ๋•Œ๋Š” ๋””์ž์ธ์„ ์ฝ”๋“œ ์˜†์— ๋†“๊ณ  ์ฝ์–ด์•ผ ํ•˜๊ณ , ๊ทธ๊ฒŒ ์ด ํ”Œ๋ž˜๊ทธ์ž…๋‹ˆ๋‹ค.

์—๋Ÿฌ๋Š” ํ˜ธ์ถœ ์ž์ฒด๊ฐ€ ์ž˜๋ชป๋œ ๊ฒฝ์šฐ(DEVUP_INVALID_INPUT, ์—†๋Š” node/ํŒŒ์ผ, ๋งŒ๋ฃŒยท๋ถ€์ ํ•ฉํ•œ artifactId ๋“ฑ) JSON-RPC -32602 INVALID_PARAMS๋กœ, ๊ทธ ๋ฐ–์˜ ์‹คํŒจ๋Š” -32603 INTERNAL_ERROR๋กœ ์˜ต๋‹ˆ๋‹ค. ์ธ์ž๋ฅผ ๊ณ ์ณ ๋‹ค์‹œ ๋ถ€๋ฅผ ์ผ์ธ์ง€ ๋ฉˆ์ถ”๊ณ  ๋ณด๊ณ ํ•  ์ผ์ธ์ง€๋ฅผ ๋ฉ”์‹œ์ง€๋ฅผ ํŒŒ์‹ฑํ•˜์ง€ ์•Š๊ณ  ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ •ํ™•ํ•œ code์™€ retryable์€ ์˜ˆ์ „์ฒ˜๋Ÿผ data์— ๊ทธ๋Œ€๋กœ ์‹ค๋ฆฝ๋‹ˆ๋‹ค.

์ด ๊ตฌ๋ถ„์€ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋˜์ง„ ์˜ค๋ฅ˜์—๋„ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋˜์ง€๋Š” ์ฝ”๋“œ๋Š” 25์ข…์ธ๋ฐ ๊ทธ์ค‘ JSON payload๋ฅผ ํ•จ๊ป˜ ์‹ฃ๋Š” ๊ฒƒ์€ ๋‘˜๋ฟ์ด๋ผ, ๋‚˜๋จธ์ง€๋Š” pluginCode: null์— ์ผ๋ฐ˜ ์•ˆ๋‚ด๋งŒ ๋‹ฌ๋ ค ๋„์ฐฉํ–ˆ์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ ๋ฉ”์‹œ์ง€์—์„œ ์ฝ”๋“œ๋ฅผ ์ฝ์–ด pluginCode์— ์ฑ„์šฐ๊ณ , ํ˜ธ์ถœ์ž๊ฐ€ ๊ณ ์น  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ๋งŒ ๊ทธ์— ๋งž๋Š” code๋กœ ์˜ฌ๋ฆฝ๋‹ˆ๋‹ค.

ํ”Œ๋Ÿฌ๊ทธ์ธ ์ฝ”๋“œ code JSON-RPC
DEVUP_NODE_NOT_FOUND ยท DEVUP_PAGE_NOT_FOUND DEVUP_FIGMA_NODE_NOT_FOUND -32602
DEVUP_ENVELOPE_TOO_LARGE ยท DEVUP_EXPLORE_PROJECTION_TOO_LARGE DEVUP_FIGMA_RESPONSE_TOO_LARGE -32603
๊ทธ ๋ฐ–(์ˆ˜์ง‘ ๋‚ด๋ถ€ ์กฐ๊ฑด) DEVUP_SNAPSHOT_UNSUPPORTED -32603

์—†๋Š” node๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ๋งํฌ๋Š” ๊ฐ€์žฅ ํ”ํ•œ ์‹ค์ˆ˜์ธ๋ฐ ์˜ˆ์ „์—๋Š” -32603 INTERNAL_ERROR๋กœ ์™€์„œ, ์œ„ ๋ฌธ๋‹จ์ด ์•ฝ์†ํ•œ ๊ตฌ๋ถ„์ด ๋ฐ”๋กœ ๊ทธ ์ง€์ ์—์„œ ๊นจ์กŒ์Šต๋‹ˆ๋‹ค. ๋‚ด๋ถ€ ์ˆ˜์ง‘ ์กฐ๊ฑด๊นŒ์ง€ ํ˜ธ์ถœ์ž ์‹ค์ˆ˜๋กœ ์˜ฌ๋ฆฌ์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค โ€” ๊ณ ์น  ๊ฒƒ์ด ์—†๋Š” ์‚ฌ๋žŒ์—๊ฒŒ ์ธ์ž๋ฅผ ๊ณ ์น˜๋ผ๊ณ  ๋ณด๋‚ด๋Š” ์…ˆ์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

devup-mcp๋Š” Figma Remote MCP์— ์ง์ ‘ ๋ถ™์Šต๋‹ˆ๋‹ค โ€” OAuth discovery, Dynamic Client Registration, PKCE S256, ์ผ์‹œ์ ์ธ 127.0.0.1 callback์„ ๊ตฌํ˜„ํ•ฉ๋‹ˆ๋‹ค. Figma๋Š” MCP Catalog์— ์Šน์ธ๋œ client์˜ registration๋งŒ ํ—ˆ์šฉํ•˜๋ฏ€๋กœ ๋“ฑ๋ก์€ allowlist์— ์žˆ๋Š” client_name์œผ๋กœ ์ด๋ฃจ์–ด์ง‘๋‹ˆ๋‹ค(๊ธฐ๋ณธ๊ฐ’ Codex). Figma PAT๋‚˜ ์‚ฌ์šฉ์ž๊ฐ€ ๋งŒ๋“  OAuth app์€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋นŒ๋“œ์™€ ์„ค์น˜

MCP Bundle (.mcpb) โ€” ํˆด์ฒด์ธ ์—†์ด ํ•œ ๋ฒˆ์— ์„ค์น˜

๋ฆด๋ฆฌ์Šค๋งˆ๋‹ค devup-mcp-<version>.mcpb ํŒŒ์ผ ํ•˜๋‚˜๊ฐ€ ํ•จ๊ป˜ ์˜ฌ๋ผ๊ฐ‘๋‹ˆ๋‹ค. ์ด ํ•˜๋‚˜์— Linux x86_64, Windows x86_64, macOS universal ๋ฐ”์ด๋„ˆ๋ฆฌ๊ฐ€ ๋ชจ๋‘ ๋“ค์–ด ์žˆ๊ณ , manifest.json์˜ server.mcp_config.platform_overrides๊ฐ€ ์‹คํ–‰ ์‹œ์ ์— ํ˜ธ์ŠคํŠธ์˜ ์šด์˜์ฒด์ œ์— ๋งž๋Š” ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ๊ณ ๋ฆ…๋‹ˆ๋‹ค. ์šด์˜์ฒด์ œ๋ณ„๋กœ ์–ด๋–ค ํŒŒ์ผ์„ ๋ฐ›์•„์•ผ ํ•˜๋Š”์ง€ ๊ณ ๋ฅผ ํ•„์š”๊ฐ€ ์—†๊ณ , Rust ํˆด์ฒด์ธ๋„ Node ๋Ÿฐํƒ€์ž„๋„ cargo install๋„ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

  1. Releases์—์„œ devup-mcp-<version>.mcpb๋ฅผ ๋ฐ›์Šต๋‹ˆ๋‹ค.
  2. .mcpb๋ฅผ ์ง€์›ํ•˜๋Š” ํ˜ธ์ŠคํŠธ(์˜ˆ: Claude for macOS/Windows)์—์„œ ํŒŒ์ผ์„ ์—ฝ๋‹ˆ๋‹ค.
  3. ์„ค์น˜ ๋Œ€ํ™”์ƒ์ž์˜ Workspace directory์— ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•  ํ”„๋กœ์ ํŠธ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋””๋ ‰ํ„ฐ๋ฆฌ๊ฐ€ devup-mcp๊ฐ€ ํŒŒ์ผ์„ ์“ธ ์ˆ˜ ์žˆ๋Š” ์œ ์ผํ•œ ์œ„์น˜์ด๋ฉฐ, ..ยท๋‹ค๋ฅธ driveยทsymlink๋กœ ๊ทธ ๋ฐ–์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ๊ฒฝ๋กœ๋Š” ๊ธฐ๋ก ์ „์— ๊ฑฐ์ ˆ๋ฉ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ root๊ฐ€ ํ•„์š”ํ•˜๋ฉด ์•„๋ž˜ stdio ์„ค์ •์œผ๋กœ --allow-write-root๋ฅผ ๋ฐ˜๋ณตํ•ด ๋“ฑ๋กํ•˜์„ธ์š”.

.mcpb๋Š” ๊ทธ๋ƒฅ zip์ด๋ฏ€๋กœ unzip -l๋กœ ๋‚ด์šฉ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๊ณ , ์•ˆ์˜ ๋ฐ”์ด๋„ˆ๋ฆฌ๋Š” ๊ฐ™์€ ๋ฆด๋ฆฌ์Šค์— ๋”ฐ๋กœ ์˜ฌ๋ผ๊ฐ€๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ํŒŒ์ผ์ž…๋‹ˆ๋‹ค. CI๋Š” pack ์งํ›„ ์•„์นด์ด๋ธŒ๋ฅผ ๋‹ค์‹œ ์ฝ์–ด Unix ๋ฐ”์ด๋„ˆ๋ฆฌ์— ์‹คํ–‰ ๋น„ํŠธ๊ฐ€ ๋‚จ์•„ ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ , ์—†์œผ๋ฉด ๋ฆด๋ฆฌ์Šค๋ฅผ ๊ฒŒ์‹œํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿผ์—๋„ macOS์—์„œ ์„ค์น˜ ์งํ›„ ์„œ๋ฒ„๊ฐ€ EACCES๋กœ ์‹คํŒจํ•œ๋‹ค๋ฉด ํ˜ธ์ŠคํŠธ๊ฐ€ ์••์ถ•์„ ํ’€๋ฉด์„œ ๊ถŒํ•œ์„ ์ง€์šด ๊ฒฝ์šฐ์ด๋ฉฐ(mcpb#294), ๊ทธ๋•Œ๋Š” ๊ฐ™์€ ๋ฆด๋ฆฌ์Šค์˜ devup-mcp-macos-universal ๋ฐ”์ด๋„ˆ๋ฆฌ๋ฅผ ์ง์ ‘ ๋ฐ›์•„ ์•„๋ž˜ stdio ์„ค์ •์œผ๋กœ ๋“ฑ๋กํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์†Œ์Šค์—์„œ ๋นŒ๋“œ

Rust 1.98 ์ด์ƒ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. compile-in Figma ํƒ์ƒ‰ ํ–‰๋™ fixture๋ฅผ ์ง์ ‘ ์‹คํ–‰ํ•˜๋ ค๋ฉด CI์™€ ๋™์ผํ•œ Node.js 24๊ฐ€ ํ•„์š”ํ•˜๋ฉฐ ์ œํ’ˆ binary์—๋Š” Node๊ฐ€ ํ•„์š”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

cargo install --git https://github.com/dev-five-git/devup-mcp.git --branch owjs3901/figma-remote-mcp devup-mcp

์„ค์น˜ ๋˜๋Š” binary ๊ต์ฒด ํ›„์—๋Š” ๋จผ์ € ๋กœ์ปฌ ์ง„๋‹จ์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

devup-mcp --version
devup-mcp --self-check

--version์€ package version๊ณผ build ID๋ฅผ, --self-check๋Š” network/OAuth ์—†์ด binary, credential backend ์ดˆ๊ธฐํ™”์™€ server ๊ตฌ์„ฑ์„ ์•ˆ์ „ํ•œ JSON์œผ๋กœ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ๋‘˜ ๋‹ค ์„ฑ๊ณตํ•˜์ง€๋งŒ ๋“ฑ๋ก๋œ connector๊ฐ€ Transport closed๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด MCP host๊ฐ€ ๊ต์ฒด ์ „ process์˜ ์ข…๋ฃŒ๋œ stdio pipe๋ฅผ ๋ณด์œ ํ•œ ์ƒํƒœ์ด๋ฏ€๋กœ host์˜ MCP ์—ฐ๊ฒฐ์„ ์žฌ์‹œ์ž‘ํ•˜๊ฑฐ๋‚˜ ๋‹ค์‹œ ๋“ฑ๋กํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ƒˆ๋กœ ์‹คํ–‰๋œ server๊ฐ€ host๊ฐ€ ๋ณด์œ ํ•œ ์ด์ „ pipe๋ฅผ ์Šค์Šค๋กœ ๋ณต๊ตฌํ•  ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค.

์ตœ์‹  ์œ ์ง€ โ€” ๋‹ค์Œ ์‹œ์ž‘ ๋•Œ ๋ฐ”๋€๋‹ˆ๋‹ค

์ง์ ‘ ์˜ฌ๋ ค ๋‘์ง€ ์•Š์•„๋„ ์ตœ์‹  ๋ฆด๋ฆฌ์Šค๋กœ ์ˆ˜๋ ดํ•ฉ๋‹ˆ๋‹ค. ๋ฆด๋ฆฌ์Šค ํ™•์ธ(24์‹œ๊ฐ„ ์ฃผ๊ธฐ, ์ฒซ ํ™•์ธ์€ initialize 20์ดˆ ๋’ค)์ด ์ƒˆ ๋ฒ„์ „์„ ์ฐพ์œผ๋ฉด ์ด ํ”Œ๋žซํผ์˜ ์—์…‹์„ ๋ฐ›์•„ devup-mcp.exe.staged๋กœ ๋‘๊ณ , ๊ทธ ํŒŒ์ผ์ด ์‹ค์ œ๋กœ ์‹คํ–‰๋ผ --self-check์— ๋‹ตํ•ด์•ผ๋งŒ ๊ทธ ์ž๋ฆฌ์—์„œ ์‹คํ–‰ ํŒŒ์ผ์˜ ์ด๋ฆ„์„ ๋„˜๊ฒจ๋ฐ›์Šต๋‹ˆ๋‹ค.

์ ์šฉ์€ ๋‹ค์Œ ์‹คํ–‰๋ถ€ํ„ฐ์ž…๋‹ˆ๋‹ค. ์žฌ์‹œ์ž‘ ํ•œ ๋ฒˆ์ด๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์‹œ์  ์ผ์–ด๋‚˜๋Š” ์ผ ๊ทธ ์„ธ์…˜์ด ๋„๋Š” build
์„ธ์…˜ N ๊ฐ์ง€ โ†’ ๋‹ค์šด๋กœ๋“œ โ†’ ๊ฒ€์ฆ โ†’ ๊ต์ฒด ์ด์ „ build (์ด๋ฏธ ๋ฉ”๋ชจ๋ฆฌ์— ๋กœ๋“œ๋จ)
์„ธ์…˜ N+1 โ€” ์ƒˆ build

์‹คํ–‰ ์ค‘์ธ ํ”„๋กœ์„ธ์Šค๋Š” ์ ˆ๋Œ€ ๊ต์ฒดํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์œ„ ๋ฌธ๋‹จ์ด ๋งํ•œ ๊ทธ๋Œ€๋กœ, ์ด๋ฏธ host๊ฐ€ ์ฅ” stdio pipe๋Š” ๊ต์ฒด๋œ binary๊ฐ€ ๋ณต๊ตฌํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋ฐ”๊พธ๋Š” ๊ฒƒ์€ ๋””์Šคํฌ์˜ ํŒŒ์ผ๋ฟ์ด๊ณ , ์ด ํ”„๋กœ์„ธ์Šค๊ฐ€ ์‹คํ–‰ ์ค‘์ธ image๋Š” spawn ์‹œ์ ์— ๋กœ๋“œ๋œ ๊ฒƒ์ด๋ผ rename์ด ๋‹ฟ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๊ฒ€์ฆ ์งํ›„ ๋ฐ”๋กœ ๊ต์ฒดํ•ด๋„ ์•ˆ์ „ํ•˜๋ฉฐ, ๊ต์ฒด๊ฐ€ ์ผ์–ด๋‚œ ์„ธ์…˜์€ stderr์— ๊ทธ ์‚ฌ์‹ค์„ ์ ๊ณ  ๊ณ„์† ์ด์ „ build๋กœ ๋•๋‹ˆ๋‹ค.

๊ธฐ๋™ ์‹œ์ ์˜ ์Šน๊ฒฉ์€ ์ด์ œ ๋ณต๊ตฌ ๊ฒฝ๋กœ์ž…๋‹ˆ๋‹ค โ€” ๊ฒ€์ฆ๊นŒ์ง€ ๋๋‚ธ ํŒŒ์ผ์„ ๋‘๊ณ  ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ฃฝ์€ ๊ฒฝ์šฐ์—๋งŒ ์“ฐ์ž…๋‹ˆ๋‹ค.

์ƒํƒœ ๋œป
owned ์ด ํ”„๋กœ์„ธ์Šค๊ฐ€ ์„ค์น˜ ์œ„์น˜์˜ ์ฃผ์ธ์ด๋ผ ์Šคํ…Œ์ด์ง•ํ•ฉ๋‹ˆ๋‹ค
package-managed binary๊ฐ€ ํŒจํ‚ค์ง€ ๋งค๋‹ˆ์ € ๋””๋ ‰ํ„ฐ๋ฆฌ(_npx, node_modules, .cargo ๋“ฑ) ์•ˆ์— ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ์ชฝ์ด ์ž๊ธฐ ์ฃผ๊ธฐ๋กœ ๊ฐฑ์‹ ํ•˜๋ฏ€๋กœ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š์Šต๋‹ˆ๋‹ค
not-writable ์„ค์น˜ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์“ธ ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ์„ค์น˜ํ•œ ์ฃผ์ฒด์˜ ๋ชซ์ž…๋‹ˆ๋‹ค
unsupported-platform ์ด ํ”Œ๋žซํผ/์•„ํ‚คํ…์ฒ˜์šฉ์œผ๋กœ ๋ฐœํ–‰๋œ ์—์…‹์ด ์—†์Šต๋‹ˆ๋‹ค
disabled DEVUP_MCP_NO_AUTO_UPDATE๋กœ ๊ป๊ฑฐ๋‚˜, DEVUP_MCP_NO_UPDATE_CHECK๋กœ ๋ฆด๋ฆฌ์Šค ํ™•์ธ ์ž์ฒด๋ฅผ ๊ป์Šต๋‹ˆ๋‹ค. ํ›„์ž๋ฉด ์ƒˆ ๋ฆด๋ฆฌ์Šค๋ฅผ ๋ฐœ๊ฒฌํ•˜๋Š” ์ผ์ด ์—†์–ด ์Šคํ…Œ์ด์ง•์ด ํ•  ์ผ์ด ์—†์œผ๋ฏ€๋กœ, ์Šคํ…Œ์ด์ง•์„ ๋„์ง€ ์•Š์•˜์–ด๋„ ๊ฒฐ๊ณผ๋Š” ๊บผ์ง„ ๊ฒƒ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. note๊ฐ€ ๋‘˜ ์ค‘ ์–ด๋А ์ชฝ์ธ์ง€ ์•Œ๋ ค์ค๋‹ˆ๋‹ค

server.updateAvailable.autoUpdate๊ฐ€ ์ด ์ƒํƒœ์™€ stagedForNextStart, ์ด ํ”Œ๋žซํผ์˜ asset, ๊ทธ๋ฆฌ๊ณ  **๊ต์ฒดํ•  ํŒŒ์ผ์˜ ์‹ค์ œ ๊ฒฝ๋กœ์ธ installedPath**๋ฅผ ํ•จ๊ป˜ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค. ์ž๋™์ด ๋ฌผ๋Ÿฌ๋‚œ ๊ฒฝ์šฐ ์‚ฌ๋žŒ์ด ๋ฌด์—‡์„ ๊ณ ์ณ์•ผ ํ•˜๋Š”์ง€๊ฐ€ ์‘๋‹ต ์•ˆ์— ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฒ€์ฆ๋˜์ง€ ์•Š์€ bytes๋Š” ์ด๋ฆ„์„ ๋„˜๊ฒจ๋ฐ›์ง€ ๋ชปํ•˜๊ณ  ๊ทธ ์ž๋ฆฌ์—์„œ ์‚ญ์ œ๋ฉ๋‹ˆ๋‹ค โ€” ๋งค ์‹œ์ž‘๋งˆ๋‹ค ๊ฐ™์€ ํŒŒ์ผ์„ ๋‹ค์‹œ ๊ฒ€์‚ฌํ•˜์ง€ ์•Š๊ธฐ ์œ„ํ•ด์„œ์ž…๋‹ˆ๋‹ค. ์Šน๊ฒฉ์€ ์‚ญ์ œ๊ฐ€ ์•„๋‹ˆ๋ผ rename์ด๋ผ ์ง์ „ binary๊ฐ€ devup-mcp.exe.previous๋กœ ๋‚จ๊ณ , ๋˜๋Œ๋ฆฌ๋Š” ์ผ์€ rename ํ•œ ๋ฒˆ์ž…๋‹ˆ๋‹ค.

๋Ÿฐ์ฒ˜๋กœ ์„ค์น˜ํ–ˆ๋‹ค๋ฉด ์ด ๊ธฐ๋Šฅ์€ ํ•„์š” ์—†์Šต๋‹ˆ๋‹ค. npx ... @latest ๊ฐ™์€ ๋Ÿฐ์ฒ˜๋Š” ๋งค ์‹คํ–‰๋งˆ๋‹ค ์ตœ์‹ ์„ ํ•ด๊ฒฐํ•˜๋ฏ€๋กœ ๊ทธ์ชฝ์ด ๋” ๋‚˜์€ ๋ฐฉ๋ฒ•์ด๊ณ , ๋‘ ๊ฐฑ์‹  ์ฃผ์ฒด๊ฐ€ ํ•œ ๊ฒฝ๋กœ๋ฅผ ๋‘๊ณ  ๋‹คํˆฌ๋Š” ๊ฒƒ์ด ๋‘˜ ์ค‘ ํ•˜๋‚˜๋งŒ ์žˆ๋Š” ๊ฒƒ๋ณด๋‹ค ๋‚˜์ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ์„ค์น˜๋Š” ์œ„ ํ‘œ์˜ package-managed๋กœ ์Šค์Šค๋กœ ๋ฌผ๋Ÿฌ๋‚˜๋ฉฐ, ์•„๋‹ˆ๋ผ๋ฉด DEVUP_MCP_NO_AUTO_UPDATE=1๋กœ ๋„์‹ญ์‹œ์˜ค.

์„œ๋ฒ„ ์‹œ์ž‘ ์‹œ OAuth ์ค€๋น„๊ฐ€ ์‹คํŒจํ•˜๋ฉด ํ”„๋กœ์„ธ์Šค๊ฐ€ ์ฃฝ์ง€ ์•Š๊ณ  ์ง„๋‹จ ๊ฐ€๋Šฅํ•œ ์˜ค๋ฅ˜๋กœ ๋‚˜์˜ต๋‹ˆ๋‹ค. HTTP ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™”๊ฐ€ ์‹คํŒจํ•˜๋ฉด DEVUP_FIGMA_DIRECT_UNAVAILABLE๋กœ Cannot start Figma OAuth: HTTP client initialization failed. Check TLS configuration and system certificate initialization, then restart devup-mcp.์™€ ํ•จ๊ป˜ ์›์ธ ์ฒด์ธ์„ ๋ถ™์—ฌ ๋Œ๋ ค์ค๋‹ˆ๋‹ค. endpoint URL์ด ์ž˜๋ชป๋์œผ๋ฉด DEVUP_INVALID_INPUT์ž…๋‹ˆ๋‹ค. ์˜ˆ์ „์—๋Š” ์ด ๊ฒฝ๋กœ๊ฐ€ panic์ด๋ผ ๋ฌด์—‡์ด ์ž˜๋ชป๋๋Š”์ง€ ์•Œ ์ˆ˜ ์—†์—ˆ์Šต๋‹ˆ๋‹ค โ€” TLS๋‚˜ ์‹œ์Šคํ…œ ์ธ์ฆ์„œ ์„ค์ •์„ ๋จผ์ € ํ™•์ธํ•˜์„ธ์š”.

์†Œ์Šค์—์„œ ๊ฒ€์ฆํ•˜๋ ค๋ฉด ๋‹ค์Œ์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

cargo fmt --all -- --check
node --test crates/devup-mcp-figma/tests/explore_script_behavior.mjs plugin/tests/withdraw.test.mjs harness/runtime-audit/test/checks.test.mjs
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace --all-features
cargo test -p devup-mcp --test stdio_smoke
cargo insta test --workspace --all-features --check
cargo build --workspace --release

MCP ์„ค์ •

stdio MCP๋ฅผ ์ง€์›ํ•˜๋Š” ํด๋ผ์ด์–ธํŠธ์— ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค.

{
  "mcpServers": {
    "devup-mcp": {
      "command": "devup-mcp",
      "args": ["--allow-write-root", "/absolute/path/to/workspace"]
    }
  }
}

์‹œ์Šคํ…œ ๋ธŒ๋ผ์šฐ์ €๋Š” devup_figma_auth์˜ login์„ ๋ช…์‹œ์ ์œผ๋กœ ํ˜ธ์ถœํ•  ๋•Œ๋งŒ ์—ด๋ฆฝ๋‹ˆ๋‹ค. ๋ณ€ํ™˜ ๋„๊ตฌ๋Š” ์ž๊ฒฉ์ฆ๋ช…์ด ์—†์œผ๋ฉด ๋ธŒ๋ผ์šฐ์ €๋ฅผ ์—ด์ง€ ์•Š๊ณ  ๋กœ๊ทธ์ธ์ด ํ•„์š”ํ•˜๋‹ค๋Š” ์˜ค๋ฅ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ธ์ฆ ์ •๋ณด๋Š” ์šด์˜์ฒด์ œ credential store์—๋งŒ ์ €์žฅ๋˜๋ฉฐ logout์€ ํ•ด๋‹น ์ •๋ณด๋งŒ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค. DEVUP_FIGMA_CREDENTIAL_STORE=memory๋กœ ์‹œ์ž‘ํ•˜๋ฉด ์šด์˜์ฒด์ œ ์ €์žฅ์†Œ๋ฅผ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๊ณ  ๋น„์–ด ์žˆ๋Š” ๋ฉ”๋ชจ๋ฆฌ ์ €์žฅ์†Œ๋กœ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค โ€” ์ €์žฅ๋œ ๋กœ๊ทธ์ธ์„ ์ง€์šฐ์ง€ ์•Š๊ณ  ๋กœ๊ทธ์ธ ์—†๋Š” ๋™์ž‘์„ ํ™•์ธํ•  ๋•Œ ์”๋‹ˆ๋‹ค.

์—ฐ๊ฒฐ ์ƒํƒœ์™€ ์ธ์ฆ

{ "action": "status" }

action์€ status, login, logout, configure, doctor ์ค‘ ํ•˜๋‚˜์ด๋ฉฐ, ์Šคํ‚ค๋งˆ๊ฐ€ ์ด ๋ชฉ๋ก์„ ๊ทธ๋Œ€๋กœ ๊ฒŒ์‹œํ•ฉ๋‹ˆ๋‹ค. status๋Š” OAuth ์ƒํƒœ๊ฐ€ ์•„๋‹ˆ๋ผ Figma์— ๋‹ฟ๋Š” ๊ฒฝ๋กœ ์ „์ฒด๋ฅผ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค. login/logout๋„ ๋™์ž‘์„ ๋งˆ์นœ ๋’ค ๊ฐ™์€ ๋ณด๊ณ ๋ฅผ ๋Œ๋ ค์ค๋‹ˆ๋‹ค.

{
  "connected": true,
  "status": "connected",
  "activePath": "bridge",
  "preferredPath": "bridge",
  "paths": {
    "bridge": {
      "available": true,
      "role": "relay",
      "listening": false,
      "port": 1993,
      "host": { "pid": 32536, "version": "0.12.0", "buildId": "f1962d5", "thisProcess": false },
      "attachedFiles": [{
        "fileKey": null,
        "fileName": "Landing",
        "currentPage": { "id": "0:1", "name": "Page 1" },
        "selection": [{ "id": "1:2", "name": "Hero", "type": "FRAME" }],
        "selectionCount": 1,
        "fileKeyNote": "This plugin could not report its file key ..."
      }],
      "attachedFilesNote": "The files the attached plugins have open, with the page in view and what is selected on it - as the devup-mcp holding the bridge port sees them ...",
      "reason": "Another devup-mcp on this machine (pid 32536, ...) holds the bridge port 1993 and the plugins attach to it; this process reads through it. ..."
    },
    "direct": {
      "available": false,
      "credentialSource": "none",
      "credentialSourceNote": "Where the OAuth *client registration* credential came from ...",
      "tokenState": "absent",
      "callbackPort": { "port": null, "free": null },
      "registrationClientName": { "value": "Codex", "isDefault": true },
      "reason": "No access token is stored. ... While a bridge plugin is attached this path is needed only for ..."
    }
  },
  "nextAction": {
    "tool": "devup_figma_export",
    "arguments": { "outputs": ["tsx"] },
    "note": "One Devup Bridge plugin is attached, so no url is needed: ..."
  }
}
  • connected/activePath โ€” ์ง€๊ธˆ ์ฝ๊ธฐ๊ฐ€ ์‹ค์ œ๋กœ ์–ด๋А ๊ฒฝ๋กœ๋กœ ๊ฐ€๋Š”์ง€. ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋ถ™์–ด ์žˆ์œผ๋ฉด bridge, ์•„๋‹ˆ๋ฉด ํ† ํฐ์ด ์žˆ์„ ๋•Œ direct, ๋‘˜ ๋‹ค ์—†์œผ๋ฉด null์ž…๋‹ˆ๋‹ค.
  • ์ตœ์ƒ์œ„ status๊ฐ€ disconnected์ธ ๊ฒƒ์€ ๋‘ ๊ฒฝ๋กœ๊ฐ€ ๋ชจ๋‘ ๋ง‰ํ˜”์„ ๋•Œ๋ฟ์ž…๋‹ˆ๋‹ค. ์˜ˆ์ „์—๋Š” direct(OAuth) ํ•˜๋‚˜๋งŒ ๋ณด๊ณ  disconnected๋ผ๊ณ  ๋‹ตํ•ด์„œ, ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋ถ™์–ด ์žˆ๋Š”๋ฐ๋„ ์—์ด์ „ํŠธ๊ฐ€ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋กœ๊ทธ์ธ์„ ์š”์ฒญํ–ˆ์Šต๋‹ˆ๋‹ค. status ํ‚ค๋Š” ๊ทธ๋Œ€๋กœ๋ผ ๊ทธ ๊ฐ’๋งŒ ์ฝ๋Š” ํ˜ธ์ถœ์ž๋„ ๊นจ์ง€์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • paths.bridge.attachedFiles โ€” ๋ถ™์–ด ์žˆ๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ๋งˆ๋‹ค ํŒŒ์ผ ์ด๋ฆ„, ๋ณด๊ณ  ์žˆ๋Š” ํŽ˜์ด์ง€, ์„ ํƒํ•œ ๋…ธ๋“œ(์•ž 20๊ฐœ)์™€ ์ „์ฒด ์ˆ˜. ํŒŒ์ผ ํ‚ค๋ฅผ ๋ณด๊ณ ํ•˜์ง€ ๋ชปํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ(์˜ˆ: manifest์— enablePrivatePluginApi๊ฐ€ ์—†๋˜ ์˜ˆ์ „ ๋นŒ๋“œ)์€ fileKey: null์ด๋ฉฐ, ํŽ˜์ด์ง€ยท์„ ํƒ์„ ๋ณด๊ณ ํ•˜๊ธฐ ์ „์— ๋นŒ๋“œ๋œ ํ”Œ๋Ÿฌ๊ทธ์ธ์€ selection: null์ž…๋‹ˆ๋‹ค. inView๋Š” Figma๊ฐ€ ๊ทธ ํŒŒ์ผ์„ ์ง€๊ธˆ ํ™”๋ฉด์— ๋ณด์—ฌ ์ฃผ๋Š”์ง€์ด๊ณ , false๋ฉด inViewNote๊ฐ€ ํ•  ์ผ์„ ๋งํ•ฉ๋‹ˆ๋‹ค(์•„๋ž˜ "๊ทธ ํŒŒ์ผ์„ ํ™”๋ฉด์— ๋„์›Œ ๋‘์„ธ์š”").
  • nextAction โ€” ์ง€๊ธˆ ์ƒํƒœ์—์„œ ํ•  ๋‹ค์Œ ํ˜ธ์ถœ. ํ”Œ๋Ÿฌ๊ทธ์ธ ํ•˜๋‚˜๋ฉด url ์—†๋Š” devup_figma_export, direct๋งŒ ์—ด๋ ค ์žˆ์œผ๋ฉด frame ๋งํฌ๊ฐ€ ํ•„์š”ํ•œ export, ๋‘˜ ๋‹ค ๋ง‰ํ˜”์œผ๋ฉด options์— ํ”Œ๋Ÿฌ๊ทธ์ธ ์‹คํ–‰(๊ถŒ์žฅ)๊ณผ ๋กœ๊ทธ์ธ์„ ์ฐจ๋ก€๋กœ ๋‹ด์Šต๋‹ˆ๋‹ค.

login์€ ๋ง‰์ง€ ์•Š๊ณ  ๊ฒฝ๊ณ ํ•ฉ๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋ถ™์–ด ์žˆ์„ ๋•Œ ๋ถ€๋ฅด๋ฉด ๋กœ๊ทธ์ธ์€ ๊ทธ๋Œ€๋กœ ์ง„ํ–‰ํ•˜๊ณ , ์‘๋‹ต์— warning์ด ๋ถ™์Šต๋‹ˆ๋‹ค โ€” ๋ธŒ๋ฆฌ์ง€๊ฐ€ ๋ชจ๋“  ์ฝ๊ธฐ๋ฅผ ๋งก์œผ๋ฉฐ, ๋กœ๊ทธ์ธ์€ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋‹ค ๋‹ตํ•˜์ง€ ๋ชปํ•œ ์ฝ๊ธฐ(ํŒŒ์ผ์ด ํ™”๋ฉด์— ๋ณด์ด์ง€ ์•Š๋Š” ๋™์•ˆ Figma๊ฐ€ ๋‚ด์ฃผ์ง€ ์•Š์€ ๋ฐ์ดํ„ฐ, get_screenshot์ฒ˜๋Ÿผ ๊ทธ๋ฆฌ์ง€ ๋ชปํ•œ ์ฐธ์กฐ PNG, ์˜ค๋ž˜๋œ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋ชจ๋ฅด๋Š” ์ฝ๊ธฐ)๋ฅผ ํ•œ๋„๋ฅผ ์“ฐ๋Š” direct ๊ฒฝ๋กœ๋กœ ๋‹ค์‹œ ์ฝ๊ฒŒ ํ•  ๋ฟ์ด๋ผ๋Š” ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.

{ "action": "doctor" }

doctor๋Š” ๊ฐ™์€ ๋ณด๊ณ ์— preferredPathNote์™€ ํด๋ผ์ด์–ธํŠธ ์„ค์ • ์ฐธ์กฐ ๋ฐ์ดํ„ฐ(clientSetup)๋ฅผ ๋”ํ•ฉ๋‹ˆ๋‹ค. status์™€ doctor ๋ชจ๋‘ ๋„คํŠธ์›Œํฌ ํ˜ธ์ถœ์„ ์ „ํ˜€ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์—ฐ๊ฒฐ์„ "์ธ์ฆ"๊ณผ ๋–ผ์–ด ๋†“์œผ๋ ค๊ณ  ๋ณ„๋„ ๋„๊ตฌ(devup_figma_connection ๋“ฑ)๋ฅผ ๋‘๋Š” ๋ฐฉ์•ˆ๋„ ๊ฒ€ํ† ํ–ˆ์ง€๋งŒ ๋‘์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋„๊ตฌ๊ฐ€ ํ•˜๋‚˜ ๋Š˜๋ฉด ๋ชจ๋“  ํด๋ผ์ด์–ธํŠธ๊ฐ€ ๋งค ์„ธ์…˜ ๊ทธ ์Šคํ‚ค๋งˆ๋ฅผ ์‹ฃ๊ณ , ์ด๋ฆ„์„ ๋ฐ”๊พธ๋ฉด ๊ธฐ์กด ํ˜ธ์ถœ์ด ๊นจ์ง‘๋‹ˆ๋‹ค. ๋Œ€์‹  devup_figma_auth์˜ ์„ค๋ช…๊ณผ status์˜ ์‘๋‹ต์ด ๋‘ ๊ฒฝ๋กœ๋ฅผ ๋จผ์ € ๋งํ•ฉ๋‹ˆ๋‹ค.

๊ฒฝ๋กœ๋Š” ๋‘˜์ด๊ณ  ๋Œ€๋“ฑํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. preferredPath๊ฐ€ ์–ธ์ œ๋‚˜ bridge์ธ ์ด์œ ์ž…๋‹ˆ๋‹ค โ€” ๋ธŒ๋ฆฌ์ง€๋Š” ๋กœ๊ทธ์ธ๋„ ํ•„์š” ์—†๊ณ  Figma ํ•œ๋„๋„ ์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํ•œ ๊ธฐ๊ธฐ์— devup-mcp๊ฐ€ ์—ฌ๋Ÿฟ ๋–  ์žˆ์–ด๋„(MCP ํด๋ผ์ด์–ธํŠธยท์„ธ์…˜๋งˆ๋‹ค ํ•˜๋‚˜์”ฉ ๋œจ๋Š” ๊ฒƒ์ด ์ •์ƒ์ž…๋‹ˆ๋‹ค) ๋ชจ๋‘๊ฐ€ ๊ฐ™์€ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์”๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋ถ™๋Š” ํฌํŠธ๋ฅผ ์žก์€ ํ”„๋กœ์„ธ์Šค๊ฐ€ ํ˜ธ์ŠคํŠธ๊ฐ€ ๋˜๊ณ , ๋‚˜๋จธ์ง€๋Š” ํ˜ธ์ŠคํŠธ๋ฅผ ํ†ตํ•ด ์ฝ์Šต๋‹ˆ๋‹ค. paths.bridge.role์ด ์ด ํ”„๋กœ์„ธ์Šค๊ฐ€ ๋ฌด์—‡์„ ํ•˜๋Š”์ง€ ๋งํ•˜๊ณ , ์ƒํƒœ๋งˆ๋‹ค ๊ณ ์น˜๋Š” ๋ฐฉ๋ฒ•์ด ๋‹ค๋ฅด๋‹ˆ ๊ตฌ๋ถ„ํ•ด์„œ ์ฝ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

role ๋œป ํ•  ์ผ
host ์ด ํ”„๋กœ์„ธ์Šค๊ฐ€ ํฌํŠธ๋ฅผ ์ฅ์—ˆ๊ณ (listening: true) ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์—ฌ๊ธฐ์— ๋ถ™์Šต๋‹ˆ๋‹ค attachedFiles๊ฐ€ ๋น„์–ด ์žˆ์œผ๋ฉด ๋Œ€์ƒ ํŒŒ์ผ์—์„œ Devup Bridge ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‹คํ–‰ํ•˜์„ธ์š”
relay ๋‹ค๋ฅธ devup-mcp๊ฐ€ ์ฅ” ํฌํŠธ๋ฅผ ํ†ตํ•ด ์ฝ์Šต๋‹ˆ๋‹ค. host๊ฐ€ ๊ทธ ํ”„๋กœ์„ธ์Šค์˜ pidยท๋ฒ„์ „ยทbuildId์ž…๋‹ˆ๋‹ค ํ˜ธ์ŠคํŠธ์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค. attachedFiles๋Š” ์–ด๋А ํ”„๋กœ์„ธ์Šค์—์„œ ๋ณด๋“  ๊ฐ™์Šต๋‹ˆ๋‹ค
connecting ํฌํŠธ๋ฅผ ์ฅ” ํ”„๋กœ์„ธ์Šค๊ฐ€ ๋ฐฉ๊ธˆ ๋– ๋‚˜ ์ด์–ด๋ฐ›๊ฑฐ๋‚˜ ๋‹ค์‹œ ๋ถ™๋Š” ์ค‘์ž…๋‹ˆ๋‹ค(handoverFrom์ด ๋– ๋‚œ ํ”„๋กœ์„ธ์Šค) ๋ช‡ ์ดˆ ๋’ค status๋ฅผ ๋‹ค์‹œ ๋ถ€๋ฅด์„ธ์š”. ํ”Œ๋Ÿฌ๊ทธ์ธ์€ 2์ดˆ๋งˆ๋‹ค ์Šค์Šค๋กœ ๋‹ค์‹œ ๋ถ™์Šต๋‹ˆ๋‹ค
unavailable ์ด ํ”„๋กœ์„ธ์Šค๋Š” ์ง€๊ธˆ ๋ธŒ๋ฆฌ์ง€๋ฅผ ์“ธ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. issue๊ฐ€ ์ด์œ ์ž…๋‹ˆ๋‹ค reason๊ณผ nextAction์˜ ์ฒซ ํ•ญ๋ชฉ์ด ๊ณ ์น˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค
off DEVUP_FIGMA_BRIDGE_PORT๊ฐ€ off/0์ด๊ฑฐ๋‚˜ ํฌํŠธ ๋ฒˆํ˜ธ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค ๋ธŒ๋ฆฌ์ง€๋ฅผ ์“ฐ๋ ค๋ฉด ๊ทธ ๊ฐ’์„ ์ง€์šฐ์„ธ์š”

issue๋Š” ๋‹ค์Œ ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค โ€” legacy-host(์ด ๊ธฐ๋Šฅ ์ด์ „์˜ devup-mcp๊ฐ€ ํฌํŠธ๋ฅผ ์ฅ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ํด๋ผ์ด์–ธํŠธ๋ฅผ ์žฌ์‹œ์ž‘ยท๊ฐฑ์‹ ํ•˜๋ฉด ์ด ํ”„๋กœ์„ธ์Šค๊ฐ€ ์Šค์Šค๋กœ ์ด์–ด๋ฐ›์Šต๋‹ˆ๋‹ค), foreign-program(devup-mcp๊ฐ€ ์•„๋‹Œ ํ”„๋กœ๊ทธ๋žจ), incompatible-protocol(์ค‘๊ณ„ ๊ทœ์•ฝ์˜ ํŒ์ด ๋‹ค๋ฅธ devup-mcp โ€” ํ‹€๋ฆฐ ๋‹ต์„ ๋‚ด๋А๋‹ˆ ์ž‡์ง€ ์•Š์Šต๋‹ˆ๋‹ค), authentication-failed(๊ฐ™์€ ์‚ฌ์šฉ์ž์˜ devup-mcp์ž„์„ ์ฆ๋ช…ํ•˜์ง€ ๋ชปํ•จ), secret-unavailable, bind-failed. ์–ด๋А ๊ฒฝ์šฐ๋“  status๋Š” ๋ช‡ ์ดˆ ์•ˆ์— ๋‹ตํ•˜๊ณ  ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

legacy-host์™€ foreign-program์€ ํฌํŠธ๋ฅผ ์ฅ” ์ชฝ์ด ์ž์‹ ์„ ๋ฐํžˆ์ง€ ์•Š์œผ๋ฏ€๋กœ, ์šด์˜์ฒด์ œ์— ๊ทธ ํฌํŠธ์—์„œ ๋“ฃ๋Š” ํ”„๋กœ์„ธ์Šค๋ฅผ ๋ฌผ์–ด pidยท์ด๋ฆ„ยท์‹คํ–‰ ํŒŒ์ผ ๊ฒฝ๋กœ๋ฅผ ๋ถ™์ž…๋‹ˆ๋‹ค โ€” ์˜ˆ์ „ devup-mcp๋ฉด host์—(๋ฒ„์ „์€ ์•Œ ์ˆ˜ ์—†์–ด null), ๋‹ค๋ฅธ ํ”„๋กœ๊ทธ๋žจ์ด๋ฉด holder์— ์‹ฃ๊ณ  reason์—๋„ ์ ์Šต๋‹ˆ๋‹ค. ์‹คํ–‰ ํŒŒ์ผ ๊ฒฝ๋กœ๋ฅผ ๋ณด๋ฉด ์–ด๋А ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์„ค์น˜ํ•œ ๊ฒƒ์ธ์ง€ ๋“œ๋Ÿฌ๋‚ฉ๋‹ˆ๋‹ค. ๋ฌป๋Š” ๋ฐ๋Š” ์™ธ๋ถ€ ๋ช…๋ น(Windows netstatยทtasklistยทPowerShell, macOS lsofยทps, Linux /proc)์ด ๋“ค์–ด์„œ, status๋Š” ๊ทธ๊ฒƒ์„ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ๋จผ์ € ๋‹ตํ•œ ๋’ค ์•Œ๊ฒŒ ๋˜๋Š” ๋Œ€๋กœ ๋ง๋ถ™์ž…๋‹ˆ๋‹ค. ํฌํŠธ๋ฅผ ์ฅ” ํ”„๋กœ๊ทธ๋žจ์„ ์ด ์‚ฌ์šฉ์ž ๊ถŒํ•œ์œผ๋กœ ์‹คํ–‰ํ•ด ๋ณด์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.

available: true๋Š” ์ง€๊ธˆ ์‹ค์ œ๋กœ ์ฝ๊ธฐ๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์„ ๋•Œ๋งŒ์ž…๋‹ˆ๋‹ค โ€” ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋ณด์ด๊ณ , ๊ทธ๊ฒƒ์— ๋‹ฟ๋Š” ๊ธธ(์ด ํ”„๋กœ์„ธ์Šค์˜ ํฌํŠธ, ๋˜๋Š” ํฌํŠธ๋ฅผ ์ฅ” ํ”„๋กœ์„ธ์Šค์™€์˜ ์—ฐ๊ฒฐ)์ด ์žˆ์„ ๋•Œ. ์ด ์ƒํƒœ๋ฉด ๋กœ๊ทธ์ธ ์—†์ด ๊ทธ ํŒŒ์ผ์˜ ์ˆ˜์ง‘์ด ๊ทธ๋Œ€๋กœ ๋˜๊ณ , ์ค‘๊ณ„ ํ”„๋กœ์„ธ์Šค์—์„œ๋„ ๋กœ๊ทธ์ธํ•˜๋ผ๋Š” ์•ˆ๋‚ด๋ฅผ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

paths.direct์˜ ์„ธ ํ•„๋“œ๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ๊ฒƒ์„ ๋งํ•˜๋ฏ€๋กœ ํ•จ๊ป˜ ์ฝ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • credentialSource โ€” client ๋“ฑ๋ก ์ž๊ฒฉ์ฆ๋ช…(client_id/client_secret)์ด ์–ด๋””์„œ ์™”๋Š”์ง€. cli-arg, env, credential-store, none ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.
  • tokenState โ€” ์‚ฌ์šฉ์ž์˜ access token ์ƒํƒœ. valid, expired, absent ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.
  • available โ€” ์ง€๊ธˆ direct ๊ฒฝ๋กœ๋ฅผ ์“ธ ์ˆ˜ ์žˆ๋Š”์ง€.

credentialSource: "none"์€ "์‚ฌ์ „ ๋“ฑ๋ก๋œ client๊ฐ€ ์ฃผ์ž…๋˜์ง€ ์•Š์•˜๋‹ค"๋Š” ๋œป์ผ ๋ฟ์ด๋ฉฐ, ๊ทธ ๊ฒฝ์šฐ ๋กœ๊ทธ์ธ์€ registrationClientName์˜ ์ด๋ฆ„์œผ๋กœ ๋™์  ๋“ฑ๋กํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ฒŒ ๋กœ๊ทธ์ธํ•œ ์ •์ƒ ์„ธ์…˜์€ credentialSource: "none"๊ณผ tokenState: "valid"๋ฅผ ๋™์‹œ์— ๋ณด์ž…๋‹ˆ๋‹ค. ์˜ˆ์ „์—๋Š” ์ด ์กฐํ•ฉ์ด ๋ชจ์ˆœ์ฒ˜๋Ÿผ ๋ณด์˜€๋Š”๋ฐ, ์ง€๊ธˆ์€ credentialSourceNote๊ฐ€ ์‘๋‹ต ์•ˆ์—์„œ ์ง์ ‘ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

callbackPort๋Š” --figma-callback-port๋ฅผ ์ง€์ •ํ–ˆ์„ ๋•Œ๋งŒ ์‹ค์ธกํ•œ port/free๋ฅผ ๋‹ด๊ณ , registrationClientName์€ DCR์ด ๋ณด๋‚ผ ์ด๋ฆ„(value)๊ณผ ๊ทธ๊ฒƒ์ด ๊ธฐ๋ณธ๊ฐ’์ธ์ง€(isDefault)๋ฅผ ์•Œ๋ ค์ค๋‹ˆ๋‹ค. ์ž์„ธํ•œ ์ œ์•ฝ์€ ์•„๋ž˜ "Figma ์—ฐ๊ฒฐ ์„ค์ •" ์ ˆ์„ ์ฐธ๊ณ ํ•˜์„ธ์š”.

direct ๊ฒฝ๋กœ์— ์‚ฌ์ „ ๋“ฑ๋ก๋œ client ์ž๊ฒฉ์ฆ๋ช… ์ฃผ์ž…ํ•˜๊ธฐ

Figma MCP Catalog์— ์Šน์ธ๋œ client(์˜ˆ: ์ง์ ‘ waitlist๋กœ ๋“ฑ๋กํ•ด ๋ฐœ๊ธ‰๋ฐ›์€ client)์˜ client_id/client_secret์„ ์ด๋ฏธ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๋ฉด, devup-mcp์— ๋‹ค์Œ ์„ธ ๊ฐ€์ง€ ๋ฐฉ๋ฒ• ์ค‘ ํ•˜๋‚˜๋กœ ์ฃผ์ž…ํ•ด Dynamic Client Registration์„ ์™„์ „ํžˆ ๊ฑด๋„ˆ๋›ธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์šฐ์„ ์ˆœ์œ„๋Š” ์‹œ์ž‘ ์ธ์ž > ํ™˜๊ฒฝ๋ณ€์ˆ˜ > configure๋กœ ์ €์žฅํ•œ ๊ฐ’์ž…๋‹ˆ๋‹ค.

  • ์‹œ์ž‘ ์ธ์ž: devup-mcp --figma-client-id <id> --figma-client-secret <secret>
  • ํ™˜๊ฒฝ๋ณ€์ˆ˜: DEVUP_FIGMA_CLIENT_ID, DEVUP_FIGMA_CLIENT_SECRET
  • ๋„๊ตฌ: devup_figma_auth { "action": "configure", "clientId": "...", "clientSecret": "..." } โ€” OS credential store(์‹œ์ž‘ ์ธ์ž/ํ™˜๊ฒฝ๋ณ€์ˆ˜์™€๋Š” ๋ณ„๋„ ํ•ญ๋ชฉ)์— ์ €์žฅ๋˜์–ด ํ”„๋กœ์„ธ์Šค๋ฅผ ์žฌ์‹œ์ž‘ํ•ด๋„ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.

์ž๊ฒฉ์ฆ๋ช…์ด ํ•ด์„๋˜๋ฉด devup_figma_auth { "action": "login" }์€ registration ์—”๋“œํฌ์ธํŠธ๋ฅผ ์ „ํ˜€ ํ˜ธ์ถœํ•˜์ง€ ์•Š๊ณ  ๋ฐ”๋กœ authorization_code + PKCE ํ๋ฆ„์œผ๋กœ ์ง„์ž…ํ•ฉ๋‹ˆ๋‹ค. ์ž๊ฒฉ์ฆ๋ช…์ด ์—†์œผ๋ฉด DCR์„ ์‹œ๋„ํ•˜๊ณ , 403์ด๋ฉด ๊ทธ๋Œ€๋กœ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค. DCR ์š”์ฒญ์˜ client_name ๊ธฐ๋ณธ๊ฐ’์€ "Codex"์ž…๋‹ˆ๋‹ค(DEFAULT_CLIENT_NAME). allowlist๋Š” ์ด๋ฆ„์„ ์ •ํ™•ํžˆ ์ผ์น˜์‹œ์ผœ ํŒ์ •ํ•˜๊ณ  "devup-mcp"๋Š” ๊ฑฐ๊ธฐ์— ์—†์œผ๋ฏ€๋กœ, ๊ทธ ์ด๋ฆ„์œผ๋กœ ๋ณด๋‚ด๋ฉด ๋“ฑ๋ก์ด 403์œผ๋กœ ๊ฑฐ์ ˆ๋˜์–ด direct ๊ฒฝ๋กœ ์ž์ฒด๊ฐ€ ์„ฑ๋ฆฝํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด ๋“ฑ๋ก์€ Figma์—๊ฒŒ devup-mcp๊ฐ€ ์•„๋‹ˆ๋ผ Codex๋กœ ๊ธฐ๋ก๋ฉ๋‹ˆ๋‹ค. ๋ณธ์ธ client๊ฐ€ ์นดํƒˆ๋กœ๊ทธ์— ์Šน์ธ๋˜๋ฉด --figma-client-name ๋˜๋Š” DEVUP_FIGMA_CLIENT_NAME์œผ๋กœ ๊ทธ ์ด๋ฆ„์„ ๋„˜๊ธฐ์„ธ์š”. client_secret์€ ๋กœ๊ทธ, ์—๋Ÿฌ, MCP ์‘๋‹ต, doctor ์ถœ๋ ฅ ์–ด๋””์—๋„ ๋…ธ์ถœ๋˜์ง€ ์•Š์œผ๋ฉฐ doctor๋Š” credentialSource๋กœ ์กด์žฌ ์—ฌ๋ถ€๋งŒ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค.

์—์ด์ „ํŠธ ์Šคํ‚ฌ โ€” ๋นˆ PC์—์„œ ํ—›์ง“๊ฑฐ๋ฆฌํ•˜์ง€ ์•Š๊ฒŒ

devup-mcp๊ฐ€ ๋Œ๋ ค์ฃผ๋Š” TSX๋Š” devup-ui ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. devup-mcp๋งŒ ๊น”๋ฆฐ ๊ธฐ๊ณ„์˜ ์—์ด์ „ํŠธ๋Š” devup-ui๋ฅผ ๋ณธ ์ ์ด ์—†์Šต๋‹ˆ๋‹ค โ€” @devup-ui/react ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋นŒ๋“œ ํƒ€์ž„ placeholder๋ผ๋Š” ๊ฒƒ๋„, $token์ด devup.json์„ ๊ฐ€๋ฆฌํ‚จ๋‹ค๋Š” ๊ฒƒ๋„, ์Šคํƒ€์ผ prop์ด ๋ฐ˜์‘ํ˜• ๋ฐฐ์—ด์„ ๋ฐ›๋Š”๋‹ค๋Š” ๊ฒƒ๋„ ๋ชจ๋ฆ…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ง€์–ด๋ƒ…๋‹ˆ๋‹ค. ์ด ์„œ๋ฒ„๋Š” ๊ทธ ์ถ”์ธก์„ ๋ณผ ์ˆ˜๋„ ๊ณ ์น  ์ˆ˜๋„ ์—†์Šต๋‹ˆ๋‹ค.

์‘๋‹ต์— ๊ทœ์น™์„ ๋ถ™์—ฌ ๋ณด๋‚ด๋Š” ๊ฒƒ์œผ๋กœ๋Š” ๋ถ€์กฑํ•ฉ๋‹ˆ๋‹ค. ์—์ด์ „ํŠธ ๋Ÿฐํƒ€์ž„์—๋Š” ์ด๋ฏธ SKILL.md๋ฅผ ์ฝ์–ด ์ž๊ธฐ ํŠธ๋ฆฌ๊ฑฐ๋กœ ๊บผ๋‚ด ์ฃผ๋Š” ๋กœ๋”๊ฐ€ ์žˆ๊ณ , ํ•œ ๋ฒˆ ๋˜์ ธ์ค€ ๋ฌธ์„œ๋Š” ํ•œ ๋ฒˆ ์ฝํžˆ์ง€๋งŒ ์„ค์น˜๋œ ์Šคํ‚ฌ์€ ๊ทธ ๋’ค ๋ชจ๋“  ์„ธ์…˜์— ๊ณ„์† ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ devup_skills๋Š” ํ…์ŠคํŠธ๋ฅผ ๋˜์ง€์ง€ ์•Š๊ณ  ์„ค์น˜ ์—ฌ๋ถ€๋ฅผ ๋ณด๊ณ ํ•˜๊ณ  ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

{ "action": "status" }

์Šคํ‚ฌ๋งˆ๋‹ค installed์™€, ์•„๋‹ˆ๋ผ๋ฉด ๊ทธ๊ฒƒ์„ ๋ฉ”์šฐ๋Š” ํ•œ ๊ฐ€์ง€ ๋™์ž‘์„ ๋Œ๋ ค์ค๋‹ˆ๋‹ค. ์ถœ์ฒ˜์— ๋”ฐ๋ผ ๋™์ž‘์ด ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

์Šคํ‚ฌ ์ถœ์ฒ˜ devup-mcp๊ฐ€ ํ•˜๋Š” ์ผ
devup-ui ยท vespera ยท vespertide dev-five-git (์šฐ๋ฆฌ ๊ฒƒ) ๋ฐ”์ด๋„ˆ๋ฆฌ์— ๋‚ด์žฅ. {"action":"install"}์ด upstream ์ตœ์‹  ๋ฌธ์„œ๋ฅผ ์šฐ์„  ๊ฐ€์ ธ์˜ค๊ณ , ์‹คํŒจํ•˜๋ฉด ๋‚ด์žฅ๋ณธ์„ ์Šคํ‚ฌ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์”๋‹ˆ๋‹ค
vercel-react-best-practices ยท vercel-react-view-transitions vercel-labs/agent-skills ๋‚ด์žฅํ•˜์ง€ ์•Š์Œ. ์„ค์น˜ ๋ช…๋ น npx skills add vercel-labs/agent-skills๋ฅผ ๋„˜๊ธธ ๋ฟ, ์‹คํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค

vercel ๊ฒƒ์„ ๋‚ด์žฅํ•˜์ง€ ์•Š๋Š” ์ด์œ ๋Š” ๋‘ ๊ฐ€์ง€์ž…๋‹ˆ๋‹ค. vercel-labs/agent-skills์—๋Š” LICENSE ํŒŒ์ผ์ด ์—†์–ด ์žฌ๋ฐฐํฌํ•  ๊ถŒ๋ฆฌ๊ฐ€ ์—†๊ณ , ๊ทธ ์Šคํ‚ฌ๋“ค์€ ๋‹จ์ผ ํŒŒ์ผ์ด ์•„๋‹ˆ๋ผ SKILL.md + AGENTS.md + ๊ทœ์น™ ํŒŒ์ผ ์ˆ˜์‹ญ ๊ฐœ(ํ•ฉ์ณ ~350 KB)๋ผ์„œ ์• ์ดˆ์— ๋˜์ ธ์ค„ ๋ฌผ๊ฑด์ด ์•„๋‹ˆ๋ผ ์„ค์น˜ํ•  ๋ฌผ๊ฑด์ž…๋‹ˆ๋‹ค.

devup-mcp๋Š” ๊ทธ ๋ช…๋ น์„ ๋Œ€์‹  ์‹คํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋””์ž์ธโ†’์ฝ”๋“œ ์„œ๋ฒ„๊ฐ€ ํŒจํ‚ค์ง€ ์„ค์น˜๊ธฐ๋ฅผ ์‹คํ–‰ํ•˜๋ฉด, ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ ํ•ญ๋ชฉ ํ•˜๋‚˜๊ฐ€ ์˜ค์—ผ๋์„ ๋•Œ ํ™”๋ฉด์„ exportํ•œ ๋ชจ๋“  ๊ธฐ๊ณ„์—์„œ ์ž„์˜ ์‹คํ–‰์ด ๋ฉ๋‹ˆ๋‹ค.

์„ค์น˜ ์œ„์น˜๋Š” ํ”„๋กœ์ ํŠธ ์•ˆ์ž…๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ๋Š” devup-mcp๊ฐ€ ์“ธ ์ˆ˜ ์žˆ๋Š” ์œ ์ผํ•œ ๊ณณ์ด๋ผ ์ƒˆ ๊ถŒํ•œ์ด ํ•„์š” ์—†๊ณ , ์Šคํ‚ฌ์ด ์ €์žฅ์†Œ๋ฅผ ๋”ฐ๋ผ๋‹ค๋‹™๋‹ˆ๋‹ค. ์ด๋ฏธ ๊น”๋ ค ์žˆ์œผ๋ฉด ๋‹ค์‹œ ์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์–ด๋А ๋””๋ ‰ํ„ฐ๋ฆฌ์ธ์ง€๋Š” ๋ถ™์–ด ์žˆ๋Š” ๋Ÿฐํƒ€์ž„์ด ์ •ํ•ฉ๋‹ˆ๋‹ค. ๋Ÿฐํƒ€์ž„๋งˆ๋‹ค ์ฝ๋Š” ๊ณณ์ด ๋‹ค๋ฅด๊ณ , ์ฝ์ง€ ์•Š๋Š” ๊ณณ์— ์„ค์น˜ํ•˜๋Š” ๊ฒƒ์€ ์กฐ์šฉํ•œ ์‹คํŒจ์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค โ€” ํ˜ธ์ถœ์€ ์„ฑ๊ณต์„ ๋ณด๊ณ ํ•˜๊ณ , ํŒŒ์ผ์€ ๋””์Šคํฌ์— ์žˆ๊ณ , ์Šคํ‚ฌ์€ ๋๋‚ด ๋กœ๋“œ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. Codex์—์„œ ์˜ฌ๋ผ์˜จ ๋ณด๊ณ ๊ฐ€ ์ •ํ™•ํžˆ ์ด ๋ชจ์–‘์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์ƒˆ ํ”„๋กœ์ ํŠธ์—๋Š” ์Šคํ‚ฌ ๋ฃจํŠธ๊ฐ€ ์•„์˜ˆ ์—†์œผ๋‹ˆ ์„ ํƒ์ด ๋ชฉ๋ก์˜ ์ฒซ ํ•ญ๋ชฉ์ธ .claude/skills๋กœ ๋–จ์–ด์กŒ๊ณ , Codex๋Š” ๊ทธ๊ณณ์„ ์ฝ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋ˆ„๊ตฌ์ธ์ง€๋Š” MCP initialize๊ฐ€ ๋ณด๋‚ด๋Š” clientInfo ์ด๋ฆ„์œผ๋กœ ์••๋‹ˆ๋‹ค.

๋Ÿฐํƒ€์ž„ ์„ค์น˜ ์œ„์น˜ ๋จธ์‹  ์ „์ฒด(์ฝ๊ธฐ๋งŒ)
Claude Code .claude/skills ~/.claude/skills
Codex .agents/skills ~/.agents/skills, ~/.codex/skills
opencode .opencode/skill ~/.config/opencode/skill
Cursor .cursor/skills ~/.cursor/skills
Zed .agents/skills ~/.agents/skills
Windsurf .windsurf/skills ~/.codeium/windsurf/skills
VS Code / Copilot .github/skills ~/.copilot/skills
Gemini CLI .gemini/skills ~/.gemini/skills
Cline .cline/skills ~/.cline/skills, ~/.agents/skills
Continue .continue/skills ~/.continue/skills
Amp .agents/skills ~/.config/agents/skills
Goose .agents/skills ~/.agents/skills
์ด๋ฆ„์„ ์•ˆ ๋ฐํžŒ ํด๋ผ์ด์–ธํŠธ ์„ธ ๊ณณ ๋ชจ๋‘ (.claude/skillsยท.opencode/skillยท.agents/skills) ์ „๋ถ€ ์ฝ์Šต๋‹ˆ๋‹ค

๊ฐ ๋Ÿฐํƒ€์ž„์€ ํ‘œ์˜ ํ•œ ๊ณณ๋งŒ์ด ์•„๋‹ˆ๋ผ ์ž๊ธฐ๊ฐ€ ์ฝ๋Š” ๊ณณ ์ „๋ถ€๋ฅผ ๊ฒ€์‚ฌํ•ฉ๋‹ˆ๋‹ค(๋Œ€๋ถ€๋ถ„ .agents/skills๋ฅผ ํ˜ธํ™˜์œผ๋กœ ์ฝ์Šต๋‹ˆ๋‹ค). ํ‘œ์— ์ ์€ ๊ฒƒ์€ ์ƒˆ๋กœ ์„ค์น˜ํ•  ๋•Œ ๊ณ ๋ฅด๋Š” ์ž๋ฆฌ์ž…๋‹ˆ๋‹ค.

Cline์ด ์ด ํ‘œ๊ฐ€ ํ•„์š”ํ•œ ์ด์œ ์ž…๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๋Œ€๋ถ€๋ถ„์€ .agents/skills๋ฅผ ์ฝ์–ด์„œ ๊ณต์šฉ ๊ทœ์•ฝ์ด ํ†ตํ•˜๋Š”๋ฐ, Cline์€ ๊ทธ๊ฑธ ํ™ˆ์—์„œ๋งŒ ์ฝ์Šต๋‹ˆ๋‹ค. ํ”„๋กœ์ ํŠธ์˜ .agents/skills๋Š” Cline์ด ์—ด์ง€ ์•Š๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ์ž…๋‹ˆ๋‹ค.

JetBrains AI Assistant๋Š” ์ผ๋ถ€๋Ÿฌ ๋„ฃ์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์ž๋™ ํƒ์ƒ‰ ๋””๋ ‰ํ„ฐ๋ฆฌ๊ฐ€ ์—†๊ณ  ์‚ฌ๋žŒ์ด ์„ค์ •์—์„œ ์ง์ ‘ ๋“ฑ๋กํ•ด์•ผ ํ•ด์„œ, ์•„๋ฌด ๊ฒฝ๋กœ๋‚˜ ๋„ฃ๋Š” ๊ฒƒ์€ ์•ˆ์ „ํ•œ fallback๋ณด๋‹ค ๋‚˜์ฉ๋‹ˆ๋‹ค.

ํ‘œ์˜ ์ ˆ๋ฐ˜์€ ๊ด€์ธก๊ฐ’์ž…๋‹ˆ๋‹ค. CursorยทWindsurfยทVS CodeยทAmp๋Š” ๋‹ซํžŒ ์†Œ์Šค๋ผ clientInfo.name์„ ์†Œ์Šค๋กœ ํ™•์ธํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๊ทธ ์ถ”์ธก์ด ํ‹€๋ฆฌ๋ฉด ๋งค์นญ์ด ์•ˆ ๋˜๊ณ  unknown์œผ๋กœ ๋–จ์–ด์ง€๋Š”๋ฐ โ€” ์•ˆ์ „ํ•˜์ง€๋งŒ ์™„์ „ํžˆ ์กฐ์šฉํ•ฉ๋‹ˆ๋‹ค. devup-mcp๊ฐ€ ์ •๋ง ๋ชจ๋ฅด๋Š” ํด๋ผ์ด์–ธํŠธ์™€ ๊ตฌ๋ณ„์ด ์•ˆ ๋ฉ๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ์‘๋‹ต์˜ runtime.clientName์ด ๋ฐ›์€ ์ด๋ฆ„์„ ๊ทธ๋Œ€๋กœ ๋Œ๋ ค์ค๋‹ˆ๋‹ค. detected๊ฐ€ unknown์ด๋ฉด ifDetectedIsUnknown์ด ๊ทธ๊ฒŒ ํ‹€๋ฆฐ ๊ฒŒ ์•„๋‹ˆ๋ผ ์•ˆ์ „ํ•œ ์ƒํƒœ๋ผ๋Š” ๊ฒƒ๊ณผ, ์ž๊ธฐ ์Šคํ‚ฌ ๋””๋ ‰ํ„ฐ๋ฆฌ๊ฐ€ ์žˆ๋Š” ํด๋ผ์ด์–ธํŠธ๋ผ๋ฉด ๊ทธ ์ด๋ฆ„์„ ์•Œ๋ ค๋‹ฌ๋ผ๋Š” ๊ฒƒ์„ ํ•จ๊ป˜ ๋‹ต๋‹ˆ๋‹ค. ํ‘œ๊ฐ€ ๊ณ ์ณ์ง€๋Š” ์œ ์ผํ•œ ๊ฒฝ๋กœ์ž…๋‹ˆ๋‹ค.

์ด๋ฆ„์ด ์—†๊ณ  ๊ธฐ์กด ๋ฃจํŠธ๋„ ์—†์œผ๋ฉด ๊ณ ๋ฅผ ๊ทผ๊ฑฐ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ์…‹ ์ค‘ ํ•˜๋‚˜๋ฅผ ์ฐ๋Š” ๊ฒƒ์€ ์•„๋ฌด๋„ ์•ˆ ๋ณด๋Š” ๊ณณ์— ์“ธ ํ™•๋ฅ ์ด 3๋ถ„์˜ 2์ด๊ณ , ์‚ฌ๋ณธ ํ•˜๋‚˜๋Š” ํ”„๋กœ์ ํŠธ๊ฐ€ ์ด๋ฏธ ์†Œ์œ ํ•œ ๋””๋ ‰ํ„ฐ๋ฆฌ ์•ˆ์—์„œ ๋ช‡ KB์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ „๋ถ€ ์”๋‹ˆ๋‹ค. ๋‹ค๋งŒ ์ฝ์„ ๋•Œ๋Š” ์•„๋Š” ๊ทœ์•ฝ ์ „๋ถ€๋ฅผ ๋ด…๋‹ˆ๋‹ค โ€” ๋ˆ„๊ฐ€ ์ด๋ฏธ ๊น”์•„๋‘” ๊ฒƒ์„ ๋ชป ์ฐพ๊ณ  ๊ทธ ์˜†์— ๋‘ ๋ฒˆ์งธ ์‚ฌ๋ณธ์„ ์“ฐ๋Š” ๊ฒƒ์ด ๋” ๋‚˜์˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

installed๋Š” ๋” ์ด์ƒ "ํŒŒ์ผ์ด ์žˆ๋‹ค"๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค

์˜ˆ์ „์—๋Š” is_file() ํ•˜๋‚˜๋กœ ํŒ์ •ํ•ด์„œ, ๋ฐ˜๋…„ ์ „ devup-mcp๊ฐ€ ์“ด SKILL.md๋„, ๋ˆ„๊ฐ€ ํ•œ ์ค„ ์ ์–ด๋‘” ์ž๋ฆฌํ‘œ์‹œ์ž๋„ ํ˜„์žฌ ๋ฌธ์„œ์™€ ๊ตฌ๋ณ„๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋” ๋‚˜๋นด๋˜ ๊ฑด install์ด ์ด๋ฏธ ์žˆ์œผ๋ฉด ๊ฑด๋„ˆ๋›ฐ์–ด์„œ ๊ฐฑ์‹ ํ•  ๋ฐฉ๋ฒ•์ด ์•„์˜ˆ ์—†์—ˆ๋‹ค๋Š” ์ ์ž…๋‹ˆ๋‹ค โ€” ๊ธฐ๊ณ„๊ฐ€ ์ฒ˜์Œ ํ•œ ์„ค์น˜๊ฐ€ ๋งˆ์ง€๋ง‰ ์„ค์น˜์˜€์Šต๋‹ˆ๋‹ค.

์ง€๊ธˆ์€ ๋””์Šคํฌ์˜ ๋ฐ”์ดํŠธ๋ฅผ ์ด ๋นŒ๋“œ๊ฐ€ ์“ธ ๋ฐ”์ดํŠธ์™€ ๋Œ€์กฐํ•ด installState.revision์œผ๋กœ ๋‹ตํ•ฉ๋‹ˆ๋‹ค.

revision ๋œป install์ด ํ•˜๋Š” ์ผ
current ์ง€๊ธˆ ์„ค์น˜ํ•˜๋ฉด ๋‚˜์˜ฌ ๊ฒƒ๊ณผ ๊ฐ™์Œ. ๋˜๋Š” upstream์—์„œ ๋ฐ›์•„์˜จ ๊ฒƒ(๋‚ด์žฅ๋ณธ๋ณด๋‹ค ์ตœ์†Œํ•œ ์ƒˆ๊ฒƒ์ด๋ผ ๋„คํŠธ์›Œํฌ ์—†์ด ๋” ๋”ฐ์งˆ ์ˆ˜ ์—†์Œ) ์•„๋ฌด๊ฒƒ๋„ ์•ˆ ํ•จ
older devup-mcp์˜ provenance ์ฃผ์„์€ ์žˆ๋Š”๋ฐ ์ด ๋นŒ๋“œ์˜ ๊ฒƒ์ด ์•„๋‹˜ ์ œ์ž๋ฆฌ์—์„œ ๋‹ค์‹œ ์”๋‹ˆ๋‹ค
incomplete SKILL.md๋Š” ์ด ๋นŒ๋“œ ๊ฒƒ์ธ๋ฐ ๋งํฌํ•œ ๋ฌธ์„œ๊ฐ€ ์—†๊ฑฐ๋‚˜ ๋‹ค๋ฆ„ โ€” ์„ค์น˜๋œ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๊ณ  ๋งํฌ๋Š” ๋Š๊ธด ์ƒํƒœ ๋‹ค์‹œ ์”๋‹ˆ๋‹ค
foreign devup-mcp provenance ์ฃผ์„์ด ์—†์Œ. ๋ˆ„๊ฐ€ ์ง์ ‘ ์“ด ๊ฒƒ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๊ณ  ๋ณด๊ณ ๋งŒ ํ•ฉ๋‹ˆ๋‹ค

foreign์„ ๋ฎ์–ด์“ฐ์ง€ ์•Š๋Š” ์ด์œ ๋Š” ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ์ด ์„œ๋ฒ„๊ฐ€ ํ•˜์ง€ ์•Š์€ ์ž‘์—…์„ ์ง€์šฐ๋Š” ์ผ์ด๊ณ , ์„ค์น˜๋Š” ๊ทธ๊ฒŒ ์‹ค์ˆ˜์˜€๋Š”์ง€ ํŒ๋‹จํ•  ์ž๋ฆฌ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. devup-mcp์˜ ์‚ฌ๋ณธ์„ ๋ฐ›๊ณ  ์‹ถ์œผ๋ฉด ๋จผ์ € ์ง€์šฐ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

report์˜ outdated๊ฐ€ ์ด ์ƒํƒœ์ธ ์Šคํ‚ฌ ์ด๋ฆ„์„ ๋ชจ์•„ ์ค๋‹ˆ๋‹ค. missing๊ณผ ๋”ฐ๋กœ ์„ธ๋Š” ์ด์œ ๋Š” ๋‘˜์ด ๋‹ค๋ฅธ ๋ง์„ ํ•„์š”๋กœ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค โ€” ํ•˜๋‚˜๋Š” ์—์ด์ „ํŠธ๊ฐ€ ๋ณธ ์  ์—†๋Š” ์Šคํ‚ฌ์ด๊ณ , ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” ์ง€๊ธˆ ์ฝ๊ณ  ์žˆ์œผ๋ฉด์„œ ๋‚ก์€ ๊ทœ์น™์„ ๋ฐฐ์šฐ๊ณ  ์žˆ๋Š” ์Šคํ‚ฌ์ž…๋‹ˆ๋‹ค.

๋จธ์‹  ์ „์ฒด ๋ฃจํŠธ๋Š” ์ฝ๊ธฐ๋งŒ ํ•ฉ๋‹ˆ๋‹ค. devup-mcp๋Š” ํ—ˆ์šฉ๋œ write root ์•ˆ์—๋งŒ ์“ฐ๊ณ  ํ™ˆ ๋””๋ ‰ํ„ฐ๋ฆฌ๋Š” ๊ฑฐ๊ธฐ์— ์—†์Šต๋‹ˆ๋‹ค โ€” ๊ทธ ๊ฒฝ๊ณ„๋Š” ๊ทธ๋Œ€๋กœ์ž…๋‹ˆ๋‹ค. ๋‹ค๋งŒ ~/.codex/skills์— ์žˆ๋Š” ์Šคํ‚ฌ์€ ์‹ค์ œ๋กœ ๋กœ๋“œ๋˜๋ฏ€๋กœ ์—†๋‹ค๊ณ  ๋ณด๊ณ ํ•˜๋ฉด ์•ˆ ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ์ „์—๋Š” devup-ui๋ฅผ ~/.agents/skillsยท~/.claude/skillsยท~/.codex/skillsยท~/.config/opencode/skill ๋„ค ๊ณณ์— (์ „๋ถ€ ๊ฐ์ž์˜ ๋Ÿฐํƒ€์ž„์ด ์ฝ๋Š” ์ƒํƒœ๋กœ) ๊ฐ–๊ณ  ์žˆ๋Š” ๊ธฐ๊ณ„๊ฐ€ installedCount: 0์„ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค.

๋ฐ˜๋Œ€ ์‹ค์ˆ˜๋„ ๋˜‘๊ฐ™์ด ์กฐ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด ๋Ÿฐํƒ€์ž„์ด ์—ด์ง€ ์•Š๋Š” ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์žˆ๋Š” ํŒŒ์ผ์€ ๋กœ๋“œ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ installed๋กœ ์„ธ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

projectRoot๋ฅผ ๋„˜๊ธฐ์„ธ์š”. ์ƒ๋žตํ•˜๋ฉด ์„œ๋ฒ„ ์ž์‹ ์˜ write root๋กœ ๋–จ์–ด์ง€๋Š”๋ฐ, ๋‘˜์„ ๊ฐ™๊ฒŒ ์„ค์ •ํ•œ ๊ฒฝ์šฐ๊ฐ€ ์•„๋‹ˆ๋ฉด ๊ทธ๊ฑด ํ”„๋กœ์ ํŠธ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ๊ณต์šฉ ๋ถ€๋ชจ ํ•˜๋‚˜๋ฅผ write root๋กœ ์ค€ ํ˜ธ์ŠคํŠธ(Orca worktree ํ’€, ๋ชจ๋…ธ๋ ˆํฌ ์ฒดํฌ์•„์›ƒ)์—์„œ๋Š” ๋ชจ๋“  ํ˜ธ์ถœ์ด ๊ทธ ๋ถ€๋ชจ๋ฅผ ๋ณด๊ณ ํ–ˆ๊ณ , ๊ฑฐ๊ธฐ์—” ์–ด๋–ค ๋Ÿฐํƒ€์ž„๋„ ์Šคํ‚ฌ์„ ์ฐพ์œผ๋Ÿฌ ๊ฐ€์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ธฐ๊ณ„์— ํ•œ ๋ฒˆ๋งŒ ๊น”๊ณ  ์‹ถ๋‹ค๋ฉด

ํ”„๋กœ์ ํŠธ๋งˆ๋‹ค ํ•œ ๋ฒˆ์”ฉ ์„ค์น˜ํ•˜๋Š” ๊ฒƒ์ด ๊ธฐ๋ณธ์ด๊ณ , ๋งค export๊ฐ€ ๋น ์ง„ ๊ฒƒ์„ ๋‹ค์‹œ ์•Œ๋ ค์ฃผ๋ฏ€๋กœ ์Šค์Šค๋กœ ๋ณต๊ตฌ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜๋„ ๊ธฐ๊ณ„ ํ•˜๋‚˜์— ํ•œ ๋ฒˆ์ด ๋‚ซ๋‹ค๋ฉด โ€” devup-mcp๋Š” ๊ทธ ๊ฒฝ๋กœ๋ฅผ ์•Œ๋ ค์ฃผ๊ธฐ๋งŒ ํ•ฉ๋‹ˆ๋‹ค. ์‘๋‹ต์˜ machineWide๊ฐ€ ์ด ๋Ÿฐํƒ€์ž„์˜ ํ™ˆ ๋””๋ ‰ํ„ฐ๋ฆฌ์™€ ํ•จ๊ป˜ action: "run-this-yourself"๋ฅผ ๋Œ๋ ค์ค๋‹ˆ๋‹ค.

ํ™ˆ์€ ํ—ˆ์šฉ๋œ write root ๋ฐ–์ด๊ณ , ๋””์ž์ธโ†’์ฝ”๋“œ ์„œ๋ฒ„๊ฐ€ $HOME์— ์“ธ ์ˆ˜ ์žˆ๋„๋ก ๊ทธ ๊ฒฝ๊ณ„๋ฅผ ๋„“ํžˆ๋Š” ๊ฒƒ์€ ํŽธ์˜์™€ ๋ฐ”๊ฟ€ ๋งŒํ•œ ๊ฑฐ๋ž˜๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๊ฒฝ๋กœ๋Š” ๋„˜๊ธฐ๊ณ  ์‹คํ–‰์€ ์‚ฌ๋žŒ ๋ชซ์ž…๋‹ˆ๋‹ค. ๊ทธ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ๋“ค์–ด๊ฐ„ ์Šคํ‚ฌ์€ ์ดํ›„ ๋ชจ๋“  ํ”„๋กœ์ ํŠธ์—์„œ ์„ค์น˜๋จ์œผ๋กœ ๋ณด๊ณ ๋ฉ๋‹ˆ๋‹ค.

์„ค์น˜ ์‘๋‹ต์˜ installed[].source๋Š” fetched ๋˜๋Š” embedded์ด๋ฉฐ, ๋‚ด์žฅ๋ณธ์„ ์ผ๋‹ค๋ฉด reason๋„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ€์ ธ์˜ค๊ธฐ๋Š” manifest์˜ ์ €์žฅ์†Œ์™€ ๋ฌธ์„œ ๊ฒฝ๋กœ์—์„œ ๋งŒ๋“  https://raw.githubusercontent.com/REPO/HEAD/PATH๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ, ํ˜ธ์ถœ ์ „์ฒด์˜ ๋„คํŠธ์›Œํฌ ๋Œ€๊ธฐ๋Š” ์ตœ๋Œ€ 4์ดˆ์ž…๋‹ˆ๋‹ค. ๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜, HTTP ์˜ค๋ฅ˜, ETag ๋ˆ„๋ฝ์€ ๋‚ด์žฅ๋ณธ์œผ๋กœ ๋Œ์•„๊ฐ€๊ณ , 404๋Š” upstream ๊ฒฝ๋กœ๊ฐ€ ๋ฐ”๋€Œ์—ˆ์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ warnings์—๋„ ์•Œ๋ฆฝ๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ๋ฌธ์„œ ์ค‘ ํ•˜๋‚˜๋ผ๋„ ์‹คํŒจํ•˜๋ฉด ๊ทธ ์Šคํ‚ฌ ์ „์ฒด๋ฅผ ๋‚ด์žฅ๋ณธ์œผ๋กœ ์„ค์น˜ํ•ฉ๋‹ˆ๋‹ค.

DEVUP_MCP_SKILLS_OFFLINE=1๋กœ ์„œ๋ฒ„๋ฅผ ์‹œ์ž‘ํ•˜๋ฉด ๊ฐ€์ ธ์˜ค๊ธฐ๋ฅผ ์ƒ๋žตํ•ฉ๋‹ˆ๋‹ค. ์ด ์„ค์ •์€ ํ”„๋กœ์„ธ์Šค์—์„œ ํ•œ ๋ฒˆ ์ฝ์œผ๋ฉฐ 0, false, ๋นˆ ๊ฐ’์€ ๊ฐ€์ ธ์˜ค๊ธฐ๋ฅผ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค. own ์Šคํ‚ฌ์€ ํ•ญ์ƒ ๋ฐ”์ด๋„ˆ๋ฆฌ์—์„œ, external ์Šคํ‚ฌ์€ ์—ฌ์ „ํžˆ ์„ค์น˜ ๋ช…๋ น ์•ˆ๋‚ด๋งŒ ํ•ฉ๋‹ˆ๋‹ค. --self-check์™€ ์Šคํ‚ฌ resource ์ฝ๊ธฐ, status๋Š” ๊ฐ€์ ธ์˜ค๊ธฐ๋ฅผ ์‹คํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ณ„๋„ CLI์ธ --install-skills์˜ devup-ui/HOME ์„ค์น˜ ๊ณ„์•ฝ์€ ๊ทธ๋Œ€๋กœ์ž…๋‹ˆ๋‹ค.

๊ฐ€์ ธ์˜จ ๋ฌธ์„œ์˜ ์ฃผ์„์€ ๊ณ ์ • ์ปค๋ฐ‹์„ ์ฃผ์žฅํ•˜์ง€ ์•Š๊ณ  URL, ๊ฐ€์ ธ์˜จ ์‹œ๊ฐ(Unix seconds), ETag, ์ฃผ์„ ์ถ”๊ฐ€ ์ „ SHA-256์„ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค. ์‘๋‹ต์˜ installed[].documents[].provenance๋„ ๊ฐ™์€ ์ •๋ณด๋ฅผ ๋‹ด์Šต๋‹ˆ๋‹ค. ๋‚ด์žฅ๋ณธ์„ ์„ค์น˜ํ•˜๊ฑฐ๋‚˜ resource๋กœ ์ฝ์„ ๋•Œ๋Š” ๊ธฐ์กด ์ปค๋ฐ‹ ์ •๋ณด๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

๋‚ด์žฅ๋ณธ์€ ๊ฐ ๋ ˆํฌ์˜ SKILL.md๋ฅผ ๊ทธ๋Œ€๋กœ ๋ณต์‚ฌํ•œ ๊ฒƒ์ด๊ณ , ์‘๋‹ต๊ณผ ์„ค์น˜๋œ ํŒŒ์ผ ๋ชจ๋‘ ์–ด๋А ์ปค๋ฐ‹์ธ์ง€์™€ ์ตœ์‹ ๋ณธ URL์„ ํ•จ๊ป˜ ๋‹ต๋‹ˆ๋‹ค. ์‚ฌ๋ณธ์€ ๋‚ก์Šต๋‹ˆ๋‹ค โ€” ๊ทธ๊ฒŒ ๋‚ด์žฅ์˜ ์ •์งํ•œ ๋น„์šฉ์ด๊ณ , node scripts/refresh-skills.mjs๊ฐ€ ๊ทธ๊ฑธ ๊ฐฑ์‹ ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค(--check๋Š” ์“ฐ์ง€ ์•Š๊ณ  ๋“œ๋ฆฌํ”„ํŠธ๋งŒ ๋ณด๊ณ ). ์ฃผ์„์€ YAML frontmatter ๋’ค์— ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค. ---๋Š” 0๋ฒˆ์งธ ๋ฐ”์ดํŠธ์— ์žˆ์–ด์•ผ ๋กœ๋”๊ฐ€ ์ฝ์Šต๋‹ˆ๋‹ค.

์„ค์น˜ํ•˜์ง€ ์•Š๊ณ  ์ฝ๊ธฐ๋งŒ ํ•˜๋ ค๋ฉด devup://skill/devup-ui ๋ฆฌ์†Œ์Šค๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ ๊ทธ๊ฑด fallback์ž…๋‹ˆ๋‹ค โ€” ์„ค์น˜ํ•ด์•ผ ๋กœ๋”๊ฐ€ ์•Œ์•„์„œ ๊บผ๋‚ด ์ค๋‹ˆ๋‹ค.

์œ ๋„๋Š” ๋„ค ๊ณณ์—์„œ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฏธ ๊น”๋ ค ์žˆ๊ณ  ์ตœ์‹ ์ธ ์‚ฌ๋žŒ์—๊ฒŒ ๊น”๋ผ๊ณ  ํ•˜๋Š” ๊ฒƒ์€ ๊ทธ ํ•„๋“œ๋ฅผ ๋ฌด์‹œํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ์†Œ์Œ์ด๋ฏ€๋กœ, ๋„ท ๋‹ค ์ด ๋Ÿฐํƒ€์ž„์ด ์‹ค์ œ๋กœ ๋กœ๋“œํ•˜์ง€ ๋ชปํ•˜๊ฑฐ๋‚˜ ๋‚ก์€ ๊ฒƒ์„ ๋กœ๋“œํ•  ๋•Œ๋งŒ ๋œน๋‹ˆ๋‹ค.

  1. devup_figma_export์˜ tool description. ๊ฐ€์žฅ ์ค‘์š”ํ•œ ์ž๋ฆฌ์ž…๋‹ˆ๋‹ค โ€” ๋ชจ๋“  ์—์ด์ „ํŠธ์—๊ฒŒ ๋„๋‹ฌํ•˜๋Š” ์œ ์ผํ•œ ์ฑ„๋„์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. instructions๋Š” ๊ทธ๋ ‡์ง€ ์•Š์Šต๋‹ˆ๋‹ค: task()๋กœ ๋„์šด ์„œ๋ธŒ์—์ด์ „ํŠธ๋Š” tool schema๋งŒ ๋ฐ›๊ณ  ์„œ๋ฒ„ instructions๋Š” ๋ฐ›์ง€ ๋ชปํ•˜๋ฉฐ, Codex์—์„œ instructions๋Š” ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ ํ…์ŠคํŠธ๊ฐ€ ์•„๋‹ˆ๋ผ ๋„ค์ž„์ŠคํŽ˜์ด์Šค ์„ค๋ช… ํ•œ ์ค„์ด ๋ฉ๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ์ฝ”๋“œ๋ฅผ ์“ฐ๋Š” ์ฃผ์ฒด๊ฐ€ ๊ทธ ๋‘ ๊ฒฝ์šฐ์ธ๋ฐ, ์˜ˆ์ „์—๋Š” ๊ฐ€์žฅ ๊ฐ•ํ•œ ๋ฌธ์žฅ์ด ๋ถ€๋ฅผ ์ด์œ ๊ฐ€ ์—†๋Š” ๋„๊ตฌ์ธ devup_skills ์ž์‹ ์˜ description์—๋งŒ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

    ๊ฐ™์€ ์ด์œ ๋กœ ๊ฐ€์ด๋“œ์˜ ๊ทœ์น™ 3ยท4ยท6๋„ ์ด description์œผ๋กœ ์˜ฎ๊ฒผ์Šต๋‹ˆ๋‹ค. ์Šคํฌ๋ฆฐ์ƒท์€ ๊ฒ€์ฆ์ด์ง€ ์ €์ž‘์ด ์•„๋‹ˆ๋ผ๋Š” ๊ฒƒ, ๋…ธ๋“œ ํŠธ๋ฆฌ๋ฅผ ์ง์ ‘ ์ฝ์–ด ์†์œผ๋กœ ์“ฐ์ง€ ๋ง๋ผ๋Š” ๊ฒƒ, ๊ฐ’์„ ์ ˆ๋Œ€ ์ง€์–ด๋‚ด์ง€ ๋ง๋ผ๋Š” ๊ฒƒ โ€” ์…‹์€ ์–ด๊ธฐ๋ฉด ์‘๋‹ต์ด ๋‚˜๋น ์ง€๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ์ž˜๋ชป๋œ ์ฝ”๋“œ๊ฐ€ ๋‚˜์˜ค๋Š” ๊ทœ์น™์ธ๋ฐ, Codex๊ฐ€ blurb ํ•œ ์ค„๋กœ ์ ‘์–ด ๋ฒ„๋ฆฌ๋Š” ๋ฆฌ์†Œ์Šค์—๋งŒ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

  2. devup_figma_export ์‘๋‹ต์˜ skillGap. TSX๋ฅผ ๋Œ๋ ค์ค€ ์‘๋‹ต์— ๋ถ™์Šต๋‹ˆ๋‹ค. projectRoot๋ฅผ ์•ˆ ๋„˜๊ฒจ๋„ ๋ถ™์Šต๋‹ˆ๋‹ค โ€” devup-ui๋ฅผ ๋ชจ๋ฅด๋Š” ํ˜ธ์ถœ์ž๊ฐ€ ๋ฐ”๋กœ ๊ทธ ์ธ์ž๋ฅผ ์•ˆ ๋ณด๋‚ด๋Š” ์‚ฌ๋žŒ์ด๋ผ, ๊ฑฐ๊ธฐ์— ๊ฑธ์–ด๋‘๋ฉด ํ•„์š”ํ•œ ์‚ฌ๋žŒ์—๊ฒŒ๋งŒ ์ •ํ™•ํžˆ ์•ˆ ๋ณด์ž…๋‹ˆ๋‹ค. missing๊ณผ outdated๋ฅผ ๋‚˜๋ˆ  ๋‹ด์Šต๋‹ˆ๋‹ค.

  3. devup_project_context ์‘๋‹ต์˜ skillGap. 2๋ฒˆ์ด devup-mcp๊ฐ€ ์“ฐ๋Š” ์ฝ”๋“œ๋ฅผ ๋งก๋Š”๋‹ค๋ฉด ์ด์ชฝ์€ ์ฝ๋Š” ํŒŒ์ผ์„ ๋งก์Šต๋‹ˆ๋‹ค โ€” devup.json์€ devup-ui์˜ ํ…Œ๋งˆ ํ˜•์‹์ด๊ณ , scope api์˜ openapi.json์€ vespera ๋ผ์šฐํŠธ๊ฐ€ ๋งŒ๋“ค๋ฉฐ, scope db์˜ models/*.json์€ vespertide ์Šคํ‚ค๋งˆ์ž…๋‹ˆ๋‹ค. ๊ทธ scope๊ฐ€ ์‹ค์ œ๋กœ ํŒŒ์ผ์„ ์ฐพ์•˜์„ ๋•Œ๋งŒ ๋œน๋‹ˆ๋‹ค. openapi.json์ด ์—†๋Š” ํ”„๋กœ์ ํŠธ์— vespera๋ฅผ ๊ถŒํ•˜๋Š” ๊ฑด ์†Œ์Œ์ž…๋‹ˆ๋‹ค.

  4. devup_ui_validate์˜ skillGap. ์œ„๋ฐ˜์ด ์žˆ์„ ๋•Œ. ์ด๊ฑด ๋ถ€๊ฒ€์ž…๋‹ˆ๋‹ค: devup-ui๋ฅผ ๋ชจ๋ฅด๋Š” ์—์ด์ „ํŠธ๋Š” validator๋„ ๋ถ€๋ฅด์ง€ ์•Š์œผ๋ฏ€๋กœ ์ด๊ฒƒ๋งŒ์œผ๋กœ๋Š” ๋‹ฟ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. 1~3๋ฒˆ์ด ์ฝ”๋“œ๋ฅผ ์“ฐ๊ธฐ ์ „์„ ๋งก๊ณ , 4๋ฒˆ์€ ์ด๋ฏธ ์“ด ์ฝ”๋“œ๊ฐ€ ๊ฑฐ์ ˆ๋‹นํ•œ ์ˆœ๊ฐ„์„ ๋งก์Šต๋‹ˆ๋‹ค.

Figma ์—ฐ๊ฒฐ ์„ค์ •

devup-mcp๊ฐ€ Figma์— ๋ถ™๋Š” ๊ฒฝ๋กœ๋Š” ๋‘˜์ด๊ณ , ๋Œ€๋“ฑํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๊ฒฝ๋กœ ๋กœ๊ทธ์ธ Figma ํ•œ๋„ ์–ธ์ œ ์“ฐ๋‚˜
๋ธŒ๋ฆฌ์ง€ (bridge) ํ•„์š” ์—†์Œ ์“ฐ์ง€ ์•Š์Œ ๊ธฐ๋ณธ. ๋ฐ์Šคํฌํ†ฑ ์•ฑ์—์„œ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋„์›Œ ๋‘๋ฉด ๊ทธ์ชฝ์œผ๋กœ ์ฝ์Šต๋‹ˆ๋‹ค
์ง์ ‘ (direct) devup_figma_auth { action: "login" } ์”๋‹ˆ๋‹ค ๋ธŒ๋ฆฌ์ง€๊ฐ€ ๋ชป ํ•˜๋Š” ์ฝ๊ธฐ์™€, ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋„์šธ ์ˆ˜ ์—†๋Š” ํ™˜๊ฒฝ(CI ๋“ฑ)

๋ธŒ๋ฆฌ์ง€๋ฅผ ๋จผ์ € ์“ฐ์‹ญ์‹œ์˜ค. direct๋Š” Figma๊ฐ€ ์‚ฌ์šฉ๋Ÿ‰์„ ์„ธ๋Š” ๊ฒฝ๋กœ์ด๊ณ , ํ™”๋ฉด ํ•˜๋‚˜๊ฐ€ ์—ฌ๋Ÿฌ ๋ฒˆ์˜ ์ฝ๊ธฐ๋ฅผ ์“ฐ๋ฏ€๋กœ ํ•œ๋„๊ฐ€ ๊ธˆ๋ฐฉ ๋ฐ”๋‹ฅ๋‚ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ direct๋กœ ๊ฐ€๋Š” ์ฝ๊ธฐ๋Š” ํ•œ๋„์— ๋งž์ถ˜ ์†๋„(DEVUP_FIGMA_CALLS_PER_MINUTE, ๊ธฐ๋ณธ ๋ถ„๋‹น 8ํšŒ)๋กœ ๋Šฆ์ถฐ ๋ณด๋‚ด์ง€๋งŒ, ๋ธŒ๋ฆฌ์ง€๋กœ ๊ฐ€๋Š” ์ฝ๊ธฐ๋Š” ๋Šฆ์ถ”์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์˜ˆ์ „์—๋Š” ๋ธŒ๋ฆฌ์ง€ ์ฝ๊ธฐ๊นŒ์ง€ ๊ฐ™์€ ์†๋„๋กœ ๋ฌถ์—ฌ, ๊ฐ™์€ ์„œ๋ฒ„์˜ ๋‘ ๋ฒˆ์งธ export๊ฐ€ ์“ฐ์ง€๋„ ์•Š๋Š” ํ•œ๋„๋ฅผ 1๋ถ„ ๊ฐ€๊นŒ์ด ๊ธฐ๋‹ค๋ ธ์Šต๋‹ˆ๋‹ค.

ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์ด ํŒŒ์ผ์„ ๋งก๊ณ  ์žˆ์œผ๋ฉด ๋กœ๊ทธ์ธ์„ ์š”๊ตฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์˜ˆ์ „์—๋Š” ์ˆ˜์ง‘์„ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์— ํ† ํฐ๋ถ€ํ„ฐ ํ™•์ธํ•ด์„œ, ํ•œ๋„๋ฅผ ์•„๋ผ๋ ค๊ณ  ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋„์šด ์‚ฌ๋žŒ์—๊ฒŒ "๋จผ์ € ํ•œ๋„ ์“ฐ๋Š” ๊ฒฝ๋กœ๋ฅผ ์—ฌ์„ธ์š”"๋ผ๊ณ  ๊ฑฐ์ ˆํ–ˆ์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ ๋ธŒ๋ฆฌ์ง€๋ฅผ ๋จผ์ € ๋ณด๊ณ , ์ด ํŒŒ์ผ์„ ๋งก์€ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์—†์„ ๋•Œ๋งŒ ๋กœ๊ทธ์ธ์„ ์š”๊ตฌํ•ฉ๋‹ˆ๋‹ค. ์ˆ˜์ง‘ ๋„์ค‘ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋‹ค ๋‹ตํ•˜์ง€ ๋ชปํ•œ ์ฝ๊ธฐ๋Š” ๋กœ๊ทธ์ธ์ด ๋˜์–ด ์žˆ์œผ๋ฉด direct ๊ฒฝ๋กœ๊ฐ€ ๋‹ค์‹œ ์ฝ๊ณ , ์•„๋‹ˆ๋ฉด ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ๋‹ต์ด ๊ทธ ์ด์œ ์™€ ํ•จ๊ป˜ ๊ทธ๋Œ€๋กœ ์˜ต๋‹ˆ๋‹ค(์•„๋ž˜).

์ง€๊ธˆ ์–ด๋А ๊ฒฝ๋กœ๊ฐ€ ์‚ด์•„ ์žˆ๋Š”์ง€๋Š” devup_figma_auth { action: "status" }์˜ activePath์™€ paths.bridge/paths.direct๋กœ ํ™•์ธํ•˜์„ธ์š”. ์—ฐ๊ฒฐ ์ˆœ์„œ๋Š” bridge โ†’ direct์ž…๋‹ˆ๋‹ค โ€” ๋กœ๊ทธ์ธ์„ ์š”์ฒญํ•˜๊ธฐ ์ „์— ์ด๊ฒƒ๋ถ€ํ„ฐ ๋ถ€๋ฅด์„ธ์š”.

๋ธŒ๋ฆฌ์ง€๋Š” ์ˆ˜์ง‘์ด ํ•˜๋Š” ๋ชจ๋“  ์ฝ๊ธฐ๋ฅผ ๋งก์Šต๋‹ˆ๋‹ค. scope: "file"์˜ ๋…ธ๋“œ ์—†๋Š” metadata ์ฝ๊ธฐ(์ตœ์ƒ์œ„ ํŽ˜์ด์ง€ ๋ชฉ๋ก)๋„ ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ํŽ˜์ด์ง€ ๋ชฉ๋ก์„ ๊ณต์‹ get_metadata์™€ ๊ฐ™์€ ๊ธ€๋กœ ์˜ฎ๊ฒจ ๋‹ตํ•˜๋ฏ€๋กœ, ์ˆ˜์ง‘๊ธฐ๋Š” ๋‘ ๊ฒฝ๋กœ์˜ ๋‹ต์„ ๊ฐ™์€ ํŒŒ์„œ๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค. direct ๊ฒฝ๋กœ๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋‹ค ๋‹ตํ•˜์ง€ ๋ชปํ•œ ์ฝ๊ธฐ๋งŒ ๋‹ค์‹œ ์ฝ์Šต๋‹ˆ๋‹ค โ€” ๋กœ๊ทธ์ธ์ด ๋˜์–ด ์žˆ์„ ๋•Œ๋งŒ, ํ•œ๋„์˜ ์†๋„์— ๋งž์ถฐ์„œ์ž…๋‹ˆ๋‹ค. ๋‹ค์‹œ ์ฝ์€ ์ด์œ ๋Š” ์‘๋‹ต์˜ source.directFallbackReasons์— ์‹ค๋ฆฝ๋‹ˆ๋‹ค. ๋‘ ๊ฒฝ๋กœ๋Š” ๊ฐ™์€ ๋ฌธ์„œ๋ฅผ ์ฝ์Šต๋‹ˆ๋‹ค. Figma์˜ MCP๋Š” ์ธ์Šคํ„ด์Šค ์•ˆ์˜ ์ˆจ๊ธด ๋ ˆ์ด์–ด๋ฅผ childrenยทfindAllยทgetNodeByIdAsync์—์„œ ๋นผ๊ณ  ํŽ˜์ด์ง€๋ฅผ ๋ถˆ๋Ÿฌ ๋‘์ง€ ์•Š์€ ์ฑ„ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋Œ๋ฆฌ๋Š”๋ฐ, ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ๋‘˜ ๋‹ค ๋ด…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋…ธ๋“œ๋ฅผ ์ฝ๋Š” ์Šคํฌ๋ฆฝํŠธ๋Š” figma.skipInvisibleInstanceChildren = false๋กœ ์‹œ์ž‘ํ•˜๊ณ , ํŒŒ์ผ ์ „์ฒด ํ…Œ๋งˆ๋Š” ๋ชจ๋“  ํŽ˜์ด์ง€๋ฅผ ๋ถˆ๋Ÿฌ์˜จ ๋’ค ํ›‘์Šต๋‹ˆ๋‹ค.

์ด์œ  ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋‹ตํ•œ ๊ฒƒ ๋กœ๊ทธ์ธ์ด ์—†์œผ๋ฉด
file-out-of-sight ํŒŒ์ผ์ด ํ™”๋ฉด์— ๋ณด์ด์ง€ ์•Š์•„ Figma๊ฐ€ ๋ถˆ๋Ÿฌ์˜ค์ง€ ์•Š์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋‚ด์ฃผ์ง€ ์•Š์Œ("Unable to establish connection to Figma", ์•„๋ž˜ "๊ทธ ํŒŒ์ผ์„ ํ™”๋ฉด์— ๋„์›Œ ๋‘์„ธ์š”") ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ๋‹ต๊ณผ ํ•  ์ผ(hintยทnextAction.how)
reference-png-approximate get_screenshot๊ณผ ๊ฐ™์€ ๊ทธ๋ฆผ์„ ๋‹ค ๊ทธ๋ฆฌ์ง€ ๋ชปํ•œ ์ฐธ์กฐ PNG(์•„๋ž˜ "referencePng๋„ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๊ทธ๋ฆฝ๋‹ˆ๋‹ค") ์บ”๋ฒ„์Šค๊ฐ€ ๋ณด์—ฌ ์ฃผ๋Š” ๊ทธ๋ฆผ๊ณผ referencePngNote
plugin-older-than-server ์ด devup-mcp๋ณด๋‹ค ๋จผ์ € ๋นŒ๋“œ๋œ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋ชจ๋ฅด๋Š” ์ฝ๊ธฐ ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ๋‹ต๊ณผ ํ•  ์ผ(ํ”Œ๋Ÿฌ๊ทธ์ธ ๊ฐฑ์‹ )
plugin-answer-too-large ์กฐ๊ฐ์œผ๋กœ ๋‚ด์ฃผ๊ธฐ ์ „ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋‹ต ํ•˜๋‚˜์— ๋‹ด์ง€ ๋ชปํ•œ ์ฐธ์กฐ PNG ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ๋‹ต
value-from-direct-path direct๊ฐ€ ์ฝ์€ ํฐ ๊ฐ’์˜ ์ด์–ด ์ฝ๊ธฐ โ€” ๊ทธ ๋ฐ”์ดํŠธ๋Š” direct์˜ ๋ Œ๋”๋Ÿฌ์—์„œ๋งŒ ๋‚˜์˜ด ์ƒ๊ธฐ์ง€ ์•Š์Œ

๋‹ค์‹œ ์ฝ๋Š” ์ผ โ€” ๋กœ๊ทธ์ธ ํ™•์ธ, ํ•œ๋„ ์ž๋ฆฌ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ฒƒ, direct์˜ ๋‹ต๊นŒ์ง€ โ€” ์€ 40์ดˆ ์•ˆ์—, ๊ทธ๋ฆฌ๊ณ  ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ์ฝ๊ธฐ์™€ ํ•ฉ์ณ export ์ž‘์—…์ด ์ฝ๊ธฐ ํ•˜๋‚˜์— ์ฃผ๋Š” 90์ดˆ๋ณด๋‹ค 5์ดˆ ๋จผ์ € ๋๋‚˜์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ์ฝ๊ธฐ๊ฐ€ ์˜ค๋ž˜ ๊ฑธ๋ ธ์œผ๋ฉด ๊ทธ๋งŒํผ ๋œ ๊ธฐ๋‹ค๋ฆฌ๊ณ , ๋‚จ์€ ์‹œ๊ฐ„์ด ์—†์œผ๋ฉด ๋‹ค์‹œ ์ฝ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

  • ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ๋‹ต์„ ๊ทธ๋Œ€๋กœ ์“ฐ๋Š” ๊ฒฝ์šฐ: ๊ทธ ์‹œ๊ฐ„์„ ๋„˜๊ธฐ๊ฑฐ๋‚˜, direct์˜ ๋‹ต์ด ์˜ค๋ฅ˜์ด๊ฑฐ๋‚˜, ํ•œ๋„์— ๊ฑธ๋ ธ๋‹ค๋Š” ๋ฌธ์žฅ ํ•˜๋‚˜์ด๊ฑฐ๋‚˜(isError ์—†์ด ์˜ค๊ธฐ๋„ ํ•ฉ๋‹ˆ๋‹ค), ๊ณต์‹ MCP์˜ ํ…์ŠคํŠธ ์ƒํ•œ(20 KB)์—์„œ ์ž˜๋ ธ์„ ๋•Œ์ž…๋‹ˆ๋‹ค. ์“ธ ์ˆ˜ ์žˆ๋Š” ๋‹ต์„ ๋ฒ„๋ฆฌ๊ณ  ์‹คํŒจ๋ฅผ ์˜ฌ๋ฆฌ์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.
  • ํ•œ๋„๊ฐ€ ์ฐฌ ๊ฒƒ์œผ๋กœ ์•Œ๋ฆฌ๋Š” ๊ฒฝ์šฐ: ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์•„์˜ˆ ๋‹ตํ•˜์ง€ ๋ชปํ•œ ์ฝ๊ธฐ(์˜ค๋ž˜๋œ ํ”Œ๋Ÿฌ๊ทธ์ธ ๋“ฑ)๊ฐ€ ์‹œ๊ฐ„ ์•ˆ์— ์ž๋ฆฌ๋ฅผ ์–ป์ง€ ๋ชปํ•˜๋ฉด, ํ•œ๋„๊ฐ€ ์ฐฌ ๊ฒƒ์œผ๋กœ ์•Œ๋ ค(DEVUP_FIGMA_RATE_LIMITED, ๋‹ค์‹œ ํ•ด ๋ณผ ์ˆ˜ ์žˆ์Œ) ์ˆ˜์ง‘์ด ํ•œ๋„๊ฐ€ ๋น„๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ ธ๋‹ค ๋‹ค์‹œ ํ•ฉ๋‹ˆ๋‹ค.
  • file-out-of-sight๋กœ ๋ณด๋Š” ๊ฒฝ์šฐ: "Unable to establish connection to Figma"๊ฐ€ ํ”Œ๋Ÿฌ๊ทธ์ธ ๋‹ต์˜ ์˜ค๋ฅ˜ ์ž๋ฆฌ์— ์žˆ์„ ๋•Œ๋งŒ์ž…๋‹ˆ๋‹ค โ€” ๊ฐ’์„ ์ฝ์ง€ ๋ชปํ•œ $error, fieldErrors, ๋ฏธํ•ด์„ ๋ฆฌ์†Œ์Šค์˜ error, ์ž์‚ฐ์˜ errorDetail, ๋˜๋Š” ์‹คํŒจ๋กœ ๋๋‚œ ๋‹ต์ž…๋‹ˆ๋‹ค. ๋””์ž์ธ์˜ ๊ธ€์ด๋‚˜ ์ด๋ฆ„์— ๊ฐ™์€ ๋ฌธ์žฅ์ด ์žˆ์–ด๋„ ๋‹ค์‹œ ์ฝ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋งํฌ ์—†์ด ์“ฐ๊ธฐ โ€” ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์—ฐ ํŒŒ์ผ๊ณผ Figma ์„ ํƒ

ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์ •ํ™•ํžˆ ํ•˜๋‚˜ ๋ถ™์–ด ์žˆ์œผ๋ฉด url์„ ์ฃผ์ง€ ์•Š์•„๋„ ๋ฉ๋‹ˆ๋‹ค.

๋„๊ตฌ url ์—†์ด ๋ถ€๋ฅด๋ฉด
devup_figma_export ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์—ฐ ํŒŒ์ผ์˜, Figma์—์„œ ์„ ํƒํ•œ ๋…ธ๋“œ. frameIds๋ฅผ ํ•˜๋‚˜ ์ฃผ๋ฉด ๊ทธ ํ”„๋ ˆ์ž„์ด๊ณ , ์—ฌ๋Ÿฟ์ด๋ฉด ์„ ํƒํ•œ Section ์•ˆ์˜ ํ™”๋ฉด๋“ค์ž…๋‹ˆ๋‹ค
devup_figma_search ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์—ฐ ํŒŒ์ผ ์ „์ฒด
devup_figma_explore Figma์—์„œ ์„ ํƒํ•œ ๋…ธ๋“œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ
{ "outputs": ["tsx"] }

๋งํฌ ์ž๋ฆฌ๊ฐ€ ๊ผญ ํ•„์š”ํ•˜๋ฉด ๊ณต์‹ ์ž๋ฆฌํ‘œ์‹œ์ž **figma-bridge://current**๋ฅผ ์“ฐ์„ธ์š”. ?node-id=1-2๋กœ ๋…ธ๋“œ๋ฅผ ์ง€์ •ํ•  ์ˆ˜ ์žˆ๊ณ , ํŒŒ์ผ ํ‚ค๋ฅผ ๋ชจ๋ฅผ ๋•Œ ์‘๋‹ต์ด ๋Œ๋ ค์ฃผ๋Š” ๋งํฌ(canonicalUrl, nextAction)๋„ ์ด ํ˜•ํƒœ๋กœ ์˜ต๋‹ˆ๋‹ค. Figma ํŒŒ์ผ ํ‚ค๋ฅผ ์ง€์–ด๋‚ด์ง€ ๋งˆ์„ธ์š”. ํ‚ค๋ฅผ ๋ณด๊ณ ํ•˜์ง€ ๋ชปํ•œ ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ํ˜ผ์ž ๋ถ™์–ด ์žˆ์„ ๋•Œ ์•„๋ฌด ํ‚ค๋‚˜ ๋ฐ›์•„ ์ฃผ๋ฏ€๋กœ ์ง€์–ด๋‚ธ ํ‚ค๋กœ๋„ ์ฝ๊ธฐ๋Š” ์„ฑ๊ณตํ•˜๊ณ , ๊ทธ ํ‚ค๊ฐ€ ํŒŒ์ผ์˜ ํ‚ค๋กœ ์˜ค์ธ๋ฉ๋‹ˆ๋‹ค. ์‹ค์ œ ์„ธ์…˜์—์„œ ์—์ด์ „ํŠธ๊ฐ€ /design/bridge/bridge๋ฅผ ์ง€์–ด๋‚ธ ๊ฒฝ์œ„๊ฐ€ ์ด๊ฒƒ์ž…๋‹ˆ๋‹ค.

ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์—†๊ฑฐ๋‚˜ ์—ฌ๋Ÿฟ์ด๋ฉด url ์—†๋Š” ํ˜ธ์ถœ์€ ๊ฑฐ์ ˆ๋˜๊ณ , ๊ฑฐ์ ˆ์˜ details.nextAction์ด ๋‹ค์Œ ๊ฑธ์Œ์„ ๋‹ด์Šต๋‹ˆ๋‹ค. ์—†์œผ๋ฉด options์— ํ”Œ๋Ÿฌ๊ทธ์ธ ์‹คํ–‰(๊ถŒ์žฅ, ๋กœ๊ทธ์ธ ๋ถˆํ•„์š”)๊ณผ direct ๊ฒฝ๋กœ(ํ† ํฐ์ด ์—†์œผ๋ฉด ๋กœ๊ทธ์ธ ํ›„ ๋งํฌ)๋ฅผ, ์—ฌ๋Ÿฟ์ด๋ฉด ๋ถ™์–ด ์žˆ๋Š” ํŒŒ์ผ๋งˆ๋‹ค ๊ทธ ํŒŒ์ผ์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ์˜ˆ์‹œ ์ธ์ž๋ฅผ ์ค๋‹ˆ๋‹ค. ์„ ํƒ์ด ๋น„์—ˆ๊ฑฐ๋‚˜ ์—ฌ๋Ÿฟ์ด๊ฑฐ๋‚˜ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์„ ํƒ์„ ๋ณด๊ณ ํ•˜์ง€ ์•Š์œผ๋ฉด, ๋…ธ๋“œ๋ฅผ ์ง€์ •ํ•˜๋Š” ์˜ˆ์‹œ(figma-bridge://current?node-id=...)๋ฅผ ์ค๋‹ˆ๋‹ค.

์–ด๋А ๊ฒฝ๋กœ๊ฐ€ ๋‹ตํ–ˆ๋Š”์ง€ โ€” source

๋ธŒ๋ฆฌ์ง€๊ฐ€ ๋‹ตํ•œ ์‘๋‹ต์€ ๊ทธ๋ ‡๋‹ค๊ณ  ๋งํ•ฉ๋‹ˆ๋‹ค.

"source": {
  "kind": "bridge",
  "bridgePort": 1993,
  "fileKey": null,
  "fileKeyReason": "The Devup Bridge plugin that served this could not report its file key ...",
  "fileName": "Landing",
  "pageName": "Page 1",
  "nodeId": "1:2",
  "bridgeReads": 3
}
  • fileKey๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋ณด๊ณ ํ•œ ํ‚ค๋ฟ์ž…๋‹ˆ๋‹ค. ๋ณด๊ณ ํ•˜์ง€ ๋ชปํ–ˆ์œผ๋ฉด null๊ณผ fileKeyReason์ด ์˜ค๊ณ , ์š”์ฒญ์— ์‹ค๋ ค ์˜จ ํ‚ค๊ฐ€ ์žˆ์—ˆ๋‹ค๋ฉด requestedFileKey๋กœ ๋”ฐ๋กœ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค โ€” ๊ทธ ํ‚ค๋ฅผ ํŒŒ์ผ์˜ ํ‚ค๋กœ ๋‚ด์„ธ์šฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. sourceMap.source.fileKey๋„ ๊ฐ™์€ ๊ทœ์น™์„ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค.
  • bridgeReads๋Š” ์ด ์ˆ˜์ง‘์—์„œ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋‹ตํ•œ ์ฝ๊ธฐ ์ˆ˜์ž…๋‹ˆ๋‹ค. collection.figmaToolCalls๋ณด๋‹ค ์ž‘์œผ๋ฉด ๋‚˜๋จธ์ง€๋Š” direct ๊ฒฝ๋กœ๊ฐ€ ๋‹ตํ–ˆ์Šต๋‹ˆ๋‹ค.
  • directFallbacksยทdirectFallbackReasonsยทdirectFallbackHint๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋‹ตํ–ˆ์ง€๋งŒ direct ๊ฒฝ๋กœ๊ฐ€ ๋‹ค์‹œ ์ฝ์–ด ๊ทธ ๋‹ต์„ ์“ด ์ฝ๊ธฐ์˜ ์ˆ˜, ๊ทธ ์ด์œ (์œ„ ํ‘œ), ๊ทธ๋ฆฌ๊ณ  ์‚ฌ๋žŒ์ด ํ•  ์ผ์ž…๋‹ˆ๋‹ค. ๋‹ค์‹œ ์ฝ์€ ๊ฒƒ์ด ์—†์œผ๋ฉด ๋‚˜์˜ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด ๊ฐ’์€ ์‹œ๋„ํ•œ ๋„คํŠธ์›Œํฌ ํ˜ธ์ถœ์˜ ์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ผ ๋‹ต์˜ ์ถœ์ฒ˜์ž…๋‹ˆ๋‹ค. ๋‹ค์‹œ ์ฝ์—ˆ์ง€๋งŒ ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ๋‹ต์„ ์“ด ์ฝ๊ธฐ๋Š” ์„ธ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋˜ DEVUP_FIGMA_CALL_CACHE๋กœ ์Œ“์•„ ๋‘” ํ˜ธ์ถœ์„ ๋‹ค์‹œ ์“ฐ๋ฉด, bridgeReads์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๊ทธ ์ถœ์ฒ˜๊ฐ€ ๊ทธ๋Œ€๋กœ ๋‹ค์‹œ ์‹ค๋ฆฝ๋‹ˆ๋‹ค.
  • ์บ์‹œ๋œ ์ˆ˜์ง‘์„ ๋‹ค์‹œ ํˆฌ์˜ํ•œ ์‘๋‹ต์€ kind: "artifact"๋ฅผ ์œ ์ง€ํ•˜๊ณ  origin: "bridge"์™€ ๊ฐ™์€ ์ถœ์ฒ˜ ์ •๋ณด๋ฅผ ๋‹ต๋‹ˆ๋‹ค.

๋ธŒ๋ฆฌ์ง€ ํ”Œ๋Ÿฌ๊ทธ์ธ โ€” ํ•œ๋„๋ฅผ ์“ฐ์ง€ ์•Š๊ณ  ์ฝ๊ธฐ

๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๊ฐ€ ์ง์ ‘ ๋งŒ๋“  Figma ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ํ†ตํ•ด ๊ฐ™์€ ์ฝ๊ธฐ๋ฅผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝ๋กœ๋Š” ํ•œ๋„๋ฅผ ์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋ถ™์–ด ์žˆ์œผ๋ฉด ์Šคํฌ๋ฆฝํŠธ ์ฝ๊ธฐ๊ฐ€ ๊ทธ์ชฝ์œผ๋กœ ๊ฐ€๊ณ , ์•ˆ ๋ถ™์–ด ์žˆ์œผ๋ฉด ์•„๋ฌด ์ผ๋„ ์ผ์–ด๋‚˜์ง€ ์•Š๊ณ  ๊ทธ๋Œ€๋กœ ์›๊ฒฉ ๊ฒฝ๋กœ๋กœ ๊ฐ‘๋‹ˆ๋‹ค. ์„ค์น˜ํ•˜์ง€ ์•Š์€ ์‚ฌ๋žŒ์˜ ๋™์ž‘์€ ๋ฐ”๋€Œ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์„ค์น˜ โ€” ๋นŒ๋“œํ•  ํ•„์š” ์—†์Šต๋‹ˆ๋‹ค

๋นŒ๋“œ๋œ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์ €์žฅ์†Œ์— ๋“ค์–ด ์žˆ์Šต๋‹ˆ๋‹ค(plugin/dist/). npm๋„ Node๋„ ํ•„์š” ์—†๊ณ , ์ €์žฅ์†Œ๋ฅผ ๋ฐ›์•„ ๋‘์—ˆ๋‹ค๋ฉด ๋ฐ”๋กœ ๋‹ค์Œ ๋‹จ๊ณ„๋กœ ๊ฐ‘๋‹ˆ๋‹ค.

1. Figma ๋ฐ์Šคํฌํ†ฑ ์•ฑ์—์„œ ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค

Figma์˜ ํ”Œ๋Ÿฌ๊ทธ์ธ ๋“ฑ๋ก์€ ๋ฐ์Šคํฌํ†ฑ ์•ฑ์˜ ๋ฉ”๋‰ด ๋™์ž‘์ด๋ผ ์Šคํฌ๋ฆฝํŠธ๋กœ ๋Œ€์‹ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด ์„ธ ๋‹จ๊ณ„๋งŒ ์ง์ ‘ ํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

  1. Figma ๋ฐ์Šคํฌํ†ฑ ์•ฑ์„ ์—ฝ๋‹ˆ๋‹ค (๋ธŒ๋ผ์šฐ์ €ํŒ์€ ๋กœ์ปฌ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋ชป ๋ถˆ๋Ÿฌ์˜ต๋‹ˆ๋‹ค)
  2. ๋ฉ”๋‰ด์—์„œ Plugins โ†’ Development โ†’ Import plugin from manifest...
  3. ์ด ์ €์žฅ์†Œ์˜ plugin/manifest.json ์„ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค

ํ•œ ๋ฒˆ๋งŒ ํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์ดํ›„์—๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ ๋ชฉ๋ก์— Devup Bridge๋กœ ๋‚จ์Šต๋‹ˆ๋‹ค.

์ปค๋ฐ‹๋œ ๋ฒˆ๋“ค์ด ์†Œ์Šค์™€ ์–ด๊ธ‹๋‚˜์ง€ ์•Š๋„๋ก CI๊ฐ€ ๋งค๋ฒˆ ๋‹ค์‹œ ๋นŒ๋“œํ•ด byte ๋‹จ์œ„๋กœ ๋Œ€์กฐํ•ฉ๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ ์†Œ์Šค๋ฅผ ๊ณ ์ณค๋‹ค๋ฉด cd plugin && npm install && npm run build ํ›„ dist/๋ฅผ ํ•จ๊ป˜ ์ปค๋ฐ‹ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

2. ์ฝ์œผ๋ ค๋Š” ํŒŒ์ผ์—์„œ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค

Figma์—์„œ ๋Œ€์ƒ ํŒŒ์ผ์„ ์—ด๊ณ  Devup Bridge๋ฅผ ์‹คํ–‰ํ•˜๋ฉด ์ฐฝ์ด ํ•˜๋‚˜ ๋œน๋‹ˆ๋‹ค. ๊ทธ ์ฐฝ์ด ์—ด๋ ค ์žˆ๋Š” ๋™์•ˆ๋งŒ ๋ธŒ๋ฆฌ์ง€๊ฐ€ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค. ๋‹ซ์œผ๋ฉด ๋‹ค์Œ ์ฝ๊ธฐ๋ถ€ํ„ฐ ์›๊ฒฉ ๊ฒฝ๋กœ๋กœ ๋Œ์•„๊ฐ‘๋‹ˆ๋‹ค.

์ž˜ ๋˜๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธ

ํ”Œ๋Ÿฌ๊ทธ์ธ ์ฐฝ์˜ ํ‘œ์‹œ๋“ฑ์œผ๋กœ ํŒ๋‹จํ•ฉ๋‹ˆ๋‹ค.

ํ‘œ์‹œ ๋œป
์ดˆ๋ก devup-mcp์— ์—ฐ๊ฒฐ๋จ. ์ด ํŒŒ์ผ์˜ ์ฝ๊ธฐ๋Š” ํ•œ๋„๋ฅผ ์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค
ํšŒ์ƒ‰ devup-mcp๋ฅผ ์ฐพ์ง€ ๋ชปํ•จ. 2์ดˆ๋งˆ๋‹ค ๋‹ค์‹œ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค

ํšŒ์ƒ‰์—์„œ ์•ˆ ๋ฐ”๋€Œ๋ฉด devup-mcp๊ฐ€ ์‹คํ–‰ ์ค‘์ธ์ง€, ๊ทธ๋ฆฌ๊ณ  DEVUP_FIGMA_BRIDGE_PORT=off๋กœ ๊บผ ๋‘์ง€ ์•Š์•˜๋Š”์ง€ ํ™•์ธํ•˜์„ธ์š”.

์•Œ์•„ ๋‘์–ด์•ผ ํ•  ๊ฒƒ

  • ๊ธฐ๋ณธ์œผ๋กœ ์ผœ์ ธ ์žˆ์Šต๋‹ˆ๋‹ค. devup-mcp๋Š” ์‹œ์ž‘ํ•  ๋•Œ 127.0.0.1:1993์— ๋Œ€๊ธฐํ•ฉ๋‹ˆ๋‹ค. ๊ทธ ํฌํŠธ๋ฅผ ๋‹ค๋ฅธ devup-mcp๊ฐ€ ์ด๋ฏธ ์ฅ๊ณ  ์žˆ์œผ๋ฉด ๊ทธ ํ”„๋กœ์„ธ์Šค๋ฅผ ํ†ตํ•ด ๊ฐ™์€ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ฝ๊ณ , ๊ทธ ํ”„๋กœ์„ธ์Šค๊ฐ€ ๋๋‚˜๋ฉด ๋‚จ์€ devup-mcp ๊ฐ€์šด๋ฐ ํ•˜๋‚˜๊ฐ€ ํฌํŠธ๋ฅผ ์ด์–ด๋ฐ›์Šต๋‹ˆ๋‹ค(ํ”Œ๋Ÿฌ๊ทธ์ธ์€ 2์ดˆ ์•ˆ์— ์ƒˆ ํ˜ธ์ŠคํŠธ์— ๋‹ค์‹œ ๋ถ™์Šต๋‹ˆ๋‹ค). ๋„๋ ค๋ฉด DEVUP_FIGMA_BRIDGE_PORT=off.

  • ํฌํŠธ๊ฐ€ ๋„˜์–ด๊ฐ€๋„ ์ˆ˜์ง‘์€ ์ด์–ด์ง‘๋‹ˆ๋‹ค. ๊ทธ๋•Œ ์ง„ํ–‰ ์ค‘์ด๋˜ ์ฝ๊ธฐ๋Š” ์‹คํŒจ๋กœ ๋๋‚˜์ง€ ์•Š๊ณ , ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์ƒˆ ํ˜ธ์ŠคํŠธ์— ๋‹ค์‹œ ๋ถ™๋Š” ๋Œ€๋กœ ๋‹ค์‹œ ๋ณด๋ƒ…๋‹ˆ๋‹ค(์ฝ๊ธฐ๋Š” ๋ฌธ์„œ๋ฅผ ๋ฐ”๊พธ์ง€ ์•Š์•„ ๋‘ ๋ฒˆ ๋Œ์•„๋„ ํ•ด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค). ํ”Œ๋Ÿฌ๊ทธ์ธ์ด 10์ดˆ ์•ˆ์— ๋Œ์•„์˜ค์ง€ ์•Š์œผ๋ฉด ๊ทธ๋•Œ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. ์š”์ฒญํ•œ ์ชฝ์ด ๋– ๋‚˜๊ฑฐ๋‚˜ ์‹œ๊ฐ„์ด ๋‹ค ๋œ ์ฝ๊ธฐ๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์— ์ทจ์†Œ๋ฅผ ๋ณด๋‚ด, ์ฐจ๋ก€๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋˜ ์ž‘์—…์€ ๋Œ๋ฆฌ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

  • ์—ฌ๋Ÿฌ devup-mcp๊ฐ€ ๋‚˜๋ˆ  ์“ฐ๋Š” ๊ฒƒ์€ ๊ฐ™์€ ์‚ฌ์šฉ์ž์˜ ๊ฒƒ๋ผ๋ฆฌ์ž…๋‹ˆ๋‹ค. ์ค‘๊ณ„๋Š” ๊ทธ ์‚ฌ์šฉ์ž๋งŒ ์ฝ์„ ์ˆ˜ ์žˆ๋Š” ๋น„๋ฐ€๊ฐ’์œผ๋กœ ์„œ๋กœ๋ฅผ ์ฆ๋ช…ํ•ด์•ผ ์ด์–ด์ง€๊ณ , ๋น„๋ฐ€๊ฐ’ ์ž์ฒด๋Š” ์—ฐ๊ฒฐ๋กœ ์˜ค๊ฐ€์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋น„๋ฐ€๊ฐ’์€ Windows %USERPROFILE%\AppData\Local\devup-mcp\bridge-relay.key, macOSยทLinux /tmp/devup-mcp-<uid>/bridge-relay.key์— ์žˆ์Šต๋‹ˆ๋‹ค. Unix์—์„œ ํ™˜๊ฒฝ ๋ณ€์ˆ˜(HOME)๊ฐ€ ์•„๋‹ˆ๋ผ ์‚ฌ์šฉ์ž ๋ฒˆํ˜ธ๋กœ ์ž๋ฆฌ๋ฅผ ์ •ํ•˜๋Š” ๊ฒƒ์€ MCP ํด๋ผ์ด์–ธํŠธ๋งˆ๋‹ค ์„œ๋ฒ„์— ๋„˜๊ธฐ๋Š” HOME์ด ๋‹ฌ๋ผ(์ƒŒ๋“œ๋ฐ•์Šค๋กœ ๋ฐ”๊ฟ” ๋„์šฐ๋Š” ํด๋ผ์ด์–ธํŠธ๋„ ์žˆ์Šต๋‹ˆ๋‹ค) ๊ฐ™์€ ์‚ฌ์šฉ์ž์˜ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์„œ๋กœ๋ฅผ ์•Œ์•„๋ณด์ง€ ๋ชปํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. /tmp๋Š” ๋ˆ„๊ตฌ๋‚˜ ์“ฐ๋Š” ๊ณณ์ด๋ผ, ๊ทธ ๋””๋ ‰ํ„ฐ๋ฆฌ๊ฐ€ ์ด ์‚ฌ์šฉ์ž์˜ ๊ฒƒ์ด๊ณ  ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž๊ฐ€ ๋“ค์–ด์˜ฌ ์ˆ˜ ์—†๋Š”์ง€ ์“ธ ๋•Œ๋งˆ๋‹ค ํ™•์ธํ•ฉ๋‹ˆ๋‹ค โ€” ์—ด๋ ค ์žˆ์—ˆ์œผ๋ฉด ๋‹ซ๊ณ  ๋น„๋ฐ€๊ฐ’์„ ์ƒˆ๋กœ ๋งŒ๋“ค๋ฉฐ, ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž์˜ ๊ฒƒ์ด๊ฑฐ๋‚˜ ๋งํฌ๋ฉด ์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค(๊ทธ๋•Œ๋Š” secret-unavailable). ์žฌ๋ถ€ํŒ…ํ•˜๋ฉด ์ง€์›Œ์ง€์ง€๋งŒ ๊ทธ๋•Œ๋Š” ๊ทธ ๋น„๋ฐ€๊ฐ’์„ ์•Œ๋˜ ํ”„๋กœ์„ธ์Šค๋„ ๋ชจ๋‘ ๋๋‚œ ๋’ค์ž…๋‹ˆ๋‹ค.

  • ๋ธŒ๋ผ์šฐ์ € ํŽ˜์ด์ง€๋Š” ๋ถ™์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. ์ค‘๊ณ„ ๋ฌธ์€ Origin์ด ๋ถ™์€ ์š”์ฒญ์„ ๋ชจ๋‘ ๊ฑฐ์ ˆํ•˜๊ณ , ํ”Œ๋Ÿฌ๊ทธ์ธ ๋ฌธ(/plugin)์€ Figma ํ”Œ๋Ÿฌ๊ทธ์ธ ์ฐฝ(Origin: null)๊ณผ figma.com๋งŒ ๋ฐ›์Šต๋‹ˆ๋‹ค. ํ”ํ•œ ์›น ํŽ˜์ด์ง€๊ฐ€ ํ”Œ๋Ÿฌ๊ทธ์ธ์ธ ์ฒ™ ์ฝ๊ธฐ๋ฅผ ๊ฐ€๋กœ์ฑ„์ง€ ๋ชปํ•˜๊ฒŒ ํ•˜๋ ค๋Š” ๊ฒƒ์ธ๋ฐ, ์ƒŒ๋“œ๋ฐ•์Šค iframe์ด๋‚˜ file:// ํŽ˜์ด์ง€๋„ null์„ ์‹ค์œผ๋ฏ€๋กœ ๊ทธ๋Ÿฐ ํŽ˜์ด์ง€๊นŒ์ง€ ๋ง‰์ง€๋Š” ๋ชปํ•ฉ๋‹ˆ๋‹ค.

  • ์ฝ๊ธฐ ์ „์šฉ์ž…๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์‹คํ–‰ํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ๋Š” ๋นŒ๋“œ ์‹œ์ ์— ํ”Œ๋Ÿฌ๊ทธ์ธ ์•ˆ์— ๋ฐ•ํ˜€ ์žˆ๊ณ , devup-mcp๋Š” ๊ทธ์ค‘ ์–ด๋А ๊ฒƒ์„ ์‹คํ–‰ํ• ์ง€ ์ด๋ฆ„๋งŒ ๋ณด๋ƒ…๋‹ˆ๋‹ค. ์†Œ์ผ“์œผ๋กœ ์ฝ”๋“œ๊ฐ€ ์˜ค๊ฐ€์ง€ ์•Š์œผ๋ฉฐ Figma ๋ฌธ์„œ๋ฅผ ๋ฐ”๊พธ๋Š” ํ˜ธ์ถœ์€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

  • ์ด ๊ธฐ๊ธฐ์—์„œ๋งŒ ๋ฉ๋‹ˆ๋‹ค. ๋Œ€๊ธฐ ์ฃผ์†Œ๋Š” 127.0.0.1์ด๋ผ ๋‹ค๋ฅธ ๊ธฐ๊ธฐ์—์„œ๋Š” ๋ถ™์„ ์ˆ˜ ์—†๊ณ , ์›๊ฒฉ CI์—์„œ๋Š” ๋ธŒ๋ฆฌ์ง€๊ฐ€ ์—†์œผ๋‹ˆ ์ž๋™์œผ๋กœ ์›๊ฒฉ ๊ฒฝ๋กœ๋ฅผ ์”๋‹ˆ๋‹ค.

  • ํŒŒ์ผ์€ ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ์€ figma.fileKey๋กœ ์ž๊ธฐ ํŒŒ์ผ์„ ์•Œ๋ฆฌ๋Š”๋ฐ, Figma๋Š” ์ด ๊ฐ’์„ private plugin์—๋งŒ ์ค๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ manifest์— enablePrivatePluginApi๋ฅผ ์ ์–ด ๋‘์—ˆ๊ณ , ์ด ์ค„์ด ์—†๋˜ ์˜ˆ์ „ ๋นŒ๋“œ๋Š” ํ‚ค๋ฅผ ๋ณด๊ณ ํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค(fileKey: null). ํ‚ค ์—†๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ํ˜ผ์ž ๋ถ™์–ด ์žˆ์„ ๋•Œ๋งŒ ์ฝ๊ธฐ๋ฅผ ๋ฐ›์Šต๋‹ˆ๋‹ค. ๋‘ ๊ฐœ ์ด์ƒ์ด๋ฉด ์–ด๋А ํŒŒ์ผ์ธ์ง€ ์•Œ ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ์›๊ฒฉ ๊ฒฝ๋กœ๋กœ ๋„˜์–ด๊ฐ‘๋‹ˆ๋‹ค.

  • ํฌํŠธ๋ฅผ ๋ฐ”๊พธ๋ ค๋ฉด ์„ธ ๊ณณ์„ ๊ฐ™์ด ๊ณ ์ณ์•ผ ํ•ฉ๋‹ˆ๋‹ค. Figma๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์ ‘์†ํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผ์†Œ๋ฅผ manifest์— ๋ฏธ๋ฆฌ ์ ์–ด ๋‘๊ฒŒ ํ•˜๊ณ  ์‹คํ–‰ ์ค‘์—๋Š” ๋ฐ”๊ฟ€ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. manifest.json์˜ allowedDomains, src/code.ts์˜ PORT, ๊ทธ๋ฆฌ๊ณ  ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ๋ชจ๋‘ ๊ณ ์นœ ๋’ค ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋‹ค์‹œ ๋นŒ๋“œยท์„ค์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ•˜๋‚˜๋งŒ ๋ฐ”๊พธ๋ฉด ์•„๋ฌด ๋ง ์—†์ด ์›๊ฒฉ ๊ฒฝ๋กœ๋กœ ๋Œ์•„๊ฐ€ ํ•œ๋„๋ฅผ ๊ทธ๋Œ€๋กœ ์”๋‹ˆ๋‹ค.

  • ws://127.0.0.1์€ ์“ธ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. Figma์˜ allowedDomains๊ฐ€ ๊ทธ ์ฃผ์†Œ๋ฅผ ๊ฑฐ๋ถ€ํ•ด manifest ์ „์ฒด๊ฐ€ ๋ฌดํšจ๊ฐ€ ๋˜๊ณ  ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์•„์˜ˆ ๋กœ๋“œ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. localhost์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ํ•œ ํ™”๋ฉด์€ ํ•œ ๋ฒˆ์˜ ์ฝ๊ธฐ๋กœ ์˜ต๋‹ˆ๋‹ค. ๋ธŒ๋ฆฌ์ง€๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์—๊ฒŒ ํ™”๋ฉด ํ•˜๋‚˜๋ฅผ ๋ด‰ํˆฌ ํ•˜๋‚˜(์ตœ๋Œ€ 8 MiB)์— ๋‹ด์•„ ๋‹ฌ๋ผ๊ณ  ํ•˜๊ณ , ๋””์ฝ”๋”๋„ ๊ทธ ํฌ๊ธฐ๊นŒ์ง€ ๋ฐ›์Šต๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ํŒŒ์ผ ํ‚ค๋ฅผ ๋ณด๊ณ ํ•˜์ง€ ๋ชปํ•˜๋ฉด ๋ด‰ํˆฌ ์•ˆ์˜ ํ‚ค ์ž๋ฆฌ(sourceยทsnapshot)๋ฅผ ์š”์ฒญํ•œ ํŒŒ์ผ๋กœ ์ฑ„์›๋‹ˆ๋‹ค. ์˜ˆ์ „์—๋Š” ๋‘˜ ๋‹ค ๋งž์ง€ ์•Š์•„ 64 KiB๊ฐ€ ๋„˜๋Š” ํ™”๋ฉด์ด๋‚˜ ํ‚ค ์—†๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ๋ด‰ํˆฌ๊ฐ€ ๋ชจ๋‘ ๋ฒ„๋ ค์ง€๊ณ , ๋…ธ๋“œ๋งˆ๋‹ค ์ฝ๋Š” ์˜ˆ์ „ ๊ฒฝ๋กœ๋กœ ๋–จ์–ด์กŒ์Šต๋‹ˆ๋‹ค. ๊ทธ ๊ฒฝ๋กœ๋Š” breakpoint ํ˜•์ œ ํญ์„ ๋ชจ์œผ์ง€ ์•Š์•„ responsiveTsx๋„ ๋‚˜์˜ค์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์‹ค์ œ ๊ณต์ง€ ํ™”๋ฉด์€ 4๋ฒˆ ์ฝ๋˜ ๊ฒƒ์ด 1๋ฒˆ์ด ๋˜๊ณ , ์„ธ ํญ์ด ํ•จ๊ป˜ ์ˆ˜์ง‘๋ฉ๋‹ˆ๋‹ค.

  • Figma ์ฐฝ์„ ๋’ค๋กœ ๋ณด๋‚ด๋ฉด ๋А๋ ค์ง‘๋‹ˆ๋‹ค. ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋ถ„๋‹น ํ•œ ๋ฒˆ๊ผด๋กœ ๊นจ์šฐ๊ธฐ ๋•Œ๋ฌธ์—, ์•ž์—์„œ 160ms์ธ snapshot์ด ๋’ค์—์„œ๋Š” 11์ดˆ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ์ˆ˜์ง‘ ์ค‘์—๋Š” Figma๋ฅผ ์•ž์— ๋‘์„ธ์š”.

  • ๊ทธ ํŒŒ์ผ์„ ํ™”๋ฉด์— ๋„์›Œ ๋‘์„ธ์š”. ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋„๋Š” ํŒŒ์ผ์ด ํ™”๋ฉด์—์„œ 23๋ถ„ ์‚ฌ๋ผ์ง€๋ฉด โ€” ํƒญ์ด ๋‹ค๋ฅธ ํŒŒ์ผ์˜ ํƒญ ๋’ค๋กœ ๊ฐ€๊ฑฐ๋‚˜, ํƒญ์€ ์•ž์— ๋‘” ์ฑ„ Figma ์ฐฝ์„ ์ตœ์†Œํ™”ํ•˜๊ฑฐ๋‚˜, ์ฐฝ์ด ๋‹ค๋ฅธ ์ฐฝ์— ๊ฐ€๋ฆฌ๊ธฐ๋งŒ ํ•ด๋„ โ€” Figma ๋Ÿฐํƒ€์ž„์€ ์•„์ง ๋ถˆ๋Ÿฌ์˜ค์ง€ ์•Š์€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์„ ๋•Œ๋งˆ๋‹ค 10์ดˆ ๋’ค "Unable to establish connection to Figma"๋กœ ๋‹ตํ•ฉ๋‹ˆ๋‹ค(์‹ค์ธก: ํƒญ์ด ๋’ค์— ๊ฐ„ ๋’ค 1๋ถ„์งธ๋Š” ์ •์ƒ, 2๋ถ„์งธ๋ถ€ํ„ฐ ์‹คํŒจ. ์ฐฝ๋งŒ ์ตœ์†Œํ™”ํ•˜๋ฉด 2๋ถ„์งธ ์ •์ƒ, 3๋ถ„์งธ๋ถ€ํ„ฐ ์‹คํŒจ). ์ด๋ฏธ ๋ถˆ๋Ÿฌ์˜จ ๋ณ€์ˆ˜ยท์Šคํƒ€์ผ์€ ํ•œ๋™์•ˆ ๋‹ตํ•˜์ง€๋งŒ(10๋ถ„์งธ๋„ ํ•ด์„) ๊ทธ๊ฒƒ๋งˆ์ € ๋น ์ง€๋ฉด ํ™”๋ฉด์€ ๋ฐ›์•„์ง€๋Š”๋ฐ ํ† ํฐ ๋Œ€์‹  ๊ฐ’์ด ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค(๊ณต์ง€ ํ™”๋ฉด: 37๊ฐœ ์ค‘ 0๊ฐœ ํ•ด์„). ์ฐฝ์„ ์•ž์œผ๋กœ ๊ฐ€์ ธ์™€ ๊ทธ ํŒŒ์ผ์˜ ํƒญ์„ ๋ˆ„๋ฅด๋ฉด ๊ณง๋ฐ”๋กœ ํ’€๋ฆฌ๋ฉฐ, ์‘๋‹ต์˜ hintยทnextAction.how๊ฐ€ ๊ทธ๋ ‡๊ฒŒ ๋งํ•ฉ๋‹ˆ๋‹ค. ๋กœ๊ทธ์ธ์ด ๋˜์–ด ์žˆ์œผ๋ฉด ๊ทธ ์ฝ๊ธฐ๋Š” direct ๊ฒฝ๋กœ๊ฐ€ ๋‹ค์‹œ ์ฝ์–ด(ํ•œ๋„๋ฅผ ์”๋‹ˆ๋‹ค) ๊ฒฐ๊ณผ๊ฐ€ ๋‹ฌ๋ผ์ง€์ง€ ์•Š๊ณ , source.directFallbackReasons์˜ file-out-of-sight์™€ directFallbackHint๊ฐ€ ๊ทธ ์‚ฌ์‹ค๊ณผ ํ•  ์ผ์„ ๋งํ•ฉ๋‹ˆ๋‹ค. ๋ฏธ๋ฆฌ ๋ถˆ๋Ÿฌ ๋‘๊ธฐ(์•„๋ž˜) ์ „์˜ ์‹ค์ธก: Figma ์ฐฝ์„ ๋‹ค๋ฅธ ์ฐฝ์— ๊ฐ€๋ฆฐ ์ง€ 57๋ถ„ ๋’ค ํŒŒ์ผ ์ „์ฒด ๊ฒ€์ƒ‰ ๋‘ ๋ฒˆ์—์„œ ๊ฐ๊ฐ ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ์ฝ๊ธฐ ํ•˜๋‚˜๊ฐ€ ๊ทธ๋ ‡๊ฒŒ ๋Œ์•„์™€ direct๊ฐ€ ๋‹ค์‹œ ์ฝ์—ˆ๊ณ , ๊ทธ์ค‘ Figma๋ฅผ ์•ž์— ๋‘๊ณ  ๋‹ค์‹œ ํ•œ ๊ฒ€์ƒ‰์€ ๊ฒฐ๊ณผ(100๊ฑด)๊ฐ€ ๊ฐ™์•˜์Šต๋‹ˆ๋‹ค(15 s, ์•ž์— ๋‘๋ฉด 0.4 s). ์ง€๊ธˆ์€ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์‹œ์ž‘ํ•  ๋•Œ์™€ Figma๊ฐ€ ๊ทธ ํŒŒ์ผ์„ ๋‹ค์‹œ ๋ณด์—ฌ ์ค„ ๋•Œ๋งˆ๋‹ค ๋ชจ๋“  ํŽ˜์ด์ง€์™€ ๋กœ์ปฌ ๋ณ€์ˆ˜ยท์Šคํƒ€์ผ์„ ๋ฏธ๋ฆฌ ๋ถˆ๋Ÿฌ ๋‘ก๋‹ˆ๋‹ค. ๋ถˆ๋Ÿฌ ๋‘” ๋ฐ์ดํ„ฐ๋Š” ๊ฐ€๋ ค์ง„ ๋™์•ˆ์—๋„ ๋‹ตํ•˜๋ฏ€๋กœ, direct๋ฅผ ๋‹ฟ์„ ์ˆ˜ ์—†๊ฒŒ ํ•œ ์ฑ„ Figma๋ฅผ ๋‹ค๋ฅธ ์ฐฝ์œผ๋กœ 5๋ถ„ยท8๋ถ„ ๊ฐ€๋ฆฌ๊ณ  ํ•œ ํŒŒ์ผ ์ „์ฒด ๊ฒ€์ƒ‰ยท๊ณต์ง€ ๋ชจ๋ฐ”์ผ TSXยท์ฐธ์กฐ PNG ๋‘ ๊ฐœ๊ฐ€ ์•ž์— ๋‘” ๊ฒƒ๊ณผ ๊ฐ™์€ ๋‹ต(๊ฒ€์ƒ‰ 100๊ฑด๊ณผ ๊ทธ id, TSXยทPNG ํ•ด์‹œ)์ด์—ˆ๊ณ  "Unable to establish connection"์€ ํ•œ ๋ฒˆ๋„ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ ๋ณด์ด๋Š”์ง€๋Š” attachedFiles[].inView๊ฐ€ ์•Œ๋ฆฝ๋‹ˆ๋‹ค(์ด ๊ธฐ๋Šฅ ์ „์˜ devup-mcp๊ฐ€ ํฌํŠธ๋ฅผ ์ฅ๊ณ  ์žˆ์œผ๋ฉด ๊ทธ ํ•„๋“œ๊ฐ€ ๋น ์ง‘๋‹ˆ๋‹ค).

  • ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋‹ตํ•œ ์‹คํŒจ๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ๋‹ต์œผ๋กœ ์˜ต๋‹ˆ๋‹ค. ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ๋˜์ง„ ์ฝ”๋“œ๋‚˜ Figma ๋Ÿฐํƒ€์ž„์˜ ๋ง์€ ์›๊ฒฉ use_figma์™€ ๊ฐ™์€ ๋ชจ์–‘์œผ๋กœ ์˜ฌ๋ผ๊ฐ€, ๋‘ ๊ฒฝ๋กœ๊ฐ€ ๊ฐ™์€ ์˜ค๋ฅ˜๋กœ ๋ถ„๋ฅ˜๋ฉ๋‹ˆ๋‹ค โ€” ์—†๋Š” ๋…ธ๋“œ๋Š” DEVUP_FIGMA_NODE_NOT_FOUND(-32602), ๊ทธ ๋ฐ–์€ stage: "plugin-execution". ์˜ˆ์ „์—๋Š” ๋ธŒ๋ฆฌ์ง€๋ฅผ ๊ฑฐ์นœ ๋ชจ๋“  ์‹คํŒจ๊ฐ€ DEVUP_FIGMA_DIRECT_UNAVAILABLE์ด์–ด์„œ, Figma ๋Ÿฐํƒ€์ž„์ด ๋ณด๊ณ  ์žˆ๋Š” ํŽ˜์ด์ง€ ๋ฐ–์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค์ง€ ๋ชปํ–ˆ๋‹ค๋Š” "Unable to establish connection to Figma after 10 seconds"๊ฐ€ direct ๊ฒฝ๋กœ์˜ ์žฅ์• ๋กœ ์ฝํ˜”์Šต๋‹ˆ๋‹ค โ€” ๊ทธ ์ฝ๊ธฐ๋Š” direct ๊ฒฝ๋กœ์— ๊ฐ„ ์ ์ด ์—†์Šต๋‹ˆ๋‹ค. ์ด ๋ฌธ๊ตฌ์—๋Š” nextAction.how๊ฐ€ ํ•  ์ผ(๊ทธ ํŒŒ์ผ์˜ ํƒญ ๋ˆ„๋ฅด๊ธฐ)์„ ๋‹ต๋‹ˆ๋‹ค. ํฌํŠธ๋ฅผ ์ฅ” ํ˜ธ์ŠคํŠธ๋ฅผ ๊ฑฐ์นœ ์‹คํŒจ๋„ ๊ฐ™์Šต๋‹ˆ๋‹ค. ํ˜ธ์ŠคํŠธ๊ฐ€ scriptError๋กœ ์•Œ๋ฆฌ๊ณ , ๊ทธ ํ‘œ์‹œ๋ฅผ ๋ณด๋‚ด์ง€ ์•Š๋Š” ์˜ˆ์ „ ํ˜ธ์ŠคํŠธ๋ฉด ํ˜ธ์ŠคํŠธ๊ฐ€ ์Šค์Šค๋กœ ์“ฐ๋Š” ๋ฌธ๊ตฌ์ธ์ง€๋กœ ๊ฐ€๋ฆฝ๋‹ˆ๋‹ค.

  • referencePng๋„ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๊ทธ๋ฆฝ๋‹ˆ๋‹ค โ€” ๊ฑฐ์ ˆํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. get_screenshot์€ ๋…ธ๋“œ๋ฅผ ์กฐ์ƒ๋“ค์˜ ๋งฅ๋ฝ โ€” ์ฑ„์›€(๊ทธ๋ผ๋””์–ธํŠธยท์‚ฌ์ง„ ํฌํ•จ), ์ž˜๋ผ ๋‚ด๊ธฐ, ๋ถˆํˆฌ๋ช…๋„, ๋ธ”๋ Œ๋“œ ๋ชจ๋“œ, ๋ ˆ์ด์–ด ๋ธ”๋Ÿฌ, ๋“œ๋กญ ์„€๋„ โ€” ์•ˆ์—์„œ ๊ทธ๋ฆฌ๋˜ ๋‹ค๋ฅธ ๋ ˆ์ด์–ด๋Š” ๋ชจ๋‘ ๋นผ๊ณ , ๋…ธ๋“œ์˜ ๋ Œ๋” ์˜์—ญ(๊ทธ๋ฆผ์žยท๋ฐ–์œผ๋กœ ๋„˜์นœ ์ž์‹ ํฌํ•จ, ์ž˜๋ผ ๋‚ด๋Š” ์กฐ์ƒ์ด ์ž๋ฅธ ๋งŒํผ)์œผ๋กœ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ๊ทธ ์˜์—ญ์„ ๋Š˜ ๊ฐ™์€ ์„ค์ •(useAbsoluteBounds: false, scale 1์ด๋˜ ๊ธด ๋ณ€์€ direct์˜ maxDimension๊ณผ ๊ฐ™์€ 8192px๊นŒ์ง€, sRGB)์œผ๋กœ exportํ•ฉ๋‹ˆ๋‹ค.

    • ๊ทธ ์˜์—ญ์— ๋‹ค๋ฅธ ๋ ˆ์ด์–ด๊ฐ€ ๋‹ฟ์ง€ ์•Š์œผ๋ฉด ์บ”๋ฒ„์Šค์˜ ๊ทธ ์ž๋ฆฌ(contentsOnly: false)๊ฐ€ ๊ณง ๊ทธ ๊ทธ๋ฆผ์ž…๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ๊ทธ๊ฒƒ๊ณผ export์— ๋น ์ง€๋Š” ํŽ˜์ด์ง€ ๋ฐฐ๊ฒฝ์„ ๋ณด๋‚ด๊ณ , ๋ธŒ๋ฆฌ์ง€๊ฐ€ ๊ทธ ์œ„๋กœ ํ‰ํ‰ํ•˜๊ฒŒ ๋ฎ์Šต๋‹ˆ๋‹ค.
    • ๋‹ฟ์œผ๋ฉด ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ๋…ธ๋“œ๋งŒ(contentsOnly: true) exportํ•˜๊ณ  ์กฐ์ƒ๋“ค์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค โ€” ๋ณ€ํ™˜ยทํฌ๊ธฐยท๋ชจ์„œ๋ฆฌ, ์ฑ„์›€, ๋ถˆํˆฌ๋ช…๋„ยท๋ธ”๋ Œ๋“œ, ์ž˜๋ผ ๋‚ด๊ธฐ, ํšจ๊ณผ, ๋งˆ์Šคํฌ, ์ฑ„์›€์— ์“ด ์‚ฌ์ง„์˜ ์›๋ณธ. ๋‚˜๋จธ์ง€๋Š” devup-mcp๊ฐ€ ๊ทธ๋ฆฝ๋‹ˆ๋‹ค: ๋‹จ์ƒ‰ยท๊ทธ๋ผ๋””์–ธํŠธ(์„ ํ˜•ยท๋ฐฉ์‚ฌยท๊ฐยท๋งˆ๋ฆ„๋ชจ)ยท์‚ฌ์ง„(PNGยทJPEGยทGIF, FILLยทFITยทTILEยทCROP) ์ฑ„์›€, ๋‘ฅ๊ทผ ๋ชจ์„œ๋ฆฌ๋กœ ์ž˜๋ผ ๋‚ด๊ธฐ, ๋ธ”๋ Œ๋“œ ๋ชจ๋“œ 18๊ฐ€์ง€, ์•ŒํŒŒยท๋ฐ๊ธฐ ๋งˆ์Šคํฌ, ๋ ˆ์ด์–ด ๋ธ”๋Ÿฌ, ๋“œ๋กญ ์„€๋„(spread ํฌํ•จ, ๋ ˆ์ด์–ด ๋’ค๋กœ ๋ณด์ด์ง€ ์•Š๊ฒŒ ํ•œ ๊ทธ๋ฆผ์ž๋Š” ๋ ˆ์ด์–ด ์•„๋ž˜๋ฅผ ์ž˜๋ผ ๋ƒ„)์™€ ์•ˆ์ชฝ ๊ทธ๋ฆผ์ž, ์‚ฌ๊ฐํ˜• ์กฐ์ƒ์˜ ํ…Œ๋‘๋ฆฌ(๋‹จ์ƒ‰ยท๊ทธ๋ผ๋””์–ธํŠธ, ์ ์„  ์—†์Œ, ๋ชจ์„œ๋ฆฌ๊ฐ€ ๊ฐ์ง„ ๊ฒƒ), ์กฐ์ƒ๊ณผ ๋…ธ๋“œ ์ž์‹ ์˜ ๋ฐฐ๊ฒฝ ๋ธ”๋Ÿฌ. ๋…ธ๋“œ์˜ ๋ฐฐ๊ฒฝ ๋ธ”๋Ÿฌ๊ฐ€ ์•„๋ž˜ ๋ ˆ์ด์–ด๋ฅผ ๋จธ๊ธˆ์œผ๋ฉด ๋…ธ๋“œ๋ฅผ ์ œ ์ฑ„์›€๊ณผ ์ž์‹๋“ค์˜ export๋กœ ๊ทธ๋ฆฌ๊ณ , ๋…ธ๋“œ์˜ ๋งˆ์Šคํฌ๋Š” ๊ทธ ์ „๋ถ€(์ฑ„์›€ยท๋ฐฐ๊ฒฝ ๋ธ”๋Ÿฌยท์ž์‹)๋ฅผ ์ž๋ฆ…๋‹ˆ๋‹ค. ๋งˆ์Šคํฌ๋Š” Figma๊ฐ€ ๋ Œ๋” ์˜์—ญ์„ ์ฃผ์ง€ ์•Š์œผ๋ฏ€๋กœ ์ œ ์ƒ์ž๋กœ exportํ•ด ๋†“๊ณ , ๋งˆ์Šคํฌ๋ฅผ ์ด๋ฃจ๋Š” ๋ ˆ์ด์–ด ์•ˆ์˜ ๋…ธ๋“œ๋Š” get_screenshot์ฒ˜๋Ÿผ ๊ทธ๋ฆฌ์ง€ ์•Š์Šต๋‹ˆ๋‹ค(๊ทธ ์•„๋ž˜๋งŒ ๋ณด์ž…๋‹ˆ๋‹ค). devup-mcp๊ฐ€ ๋””์ฝ”๋”ฉํ•˜์ง€ ์•Š๋Š” ์‚ฌ์ง„ โ€” PNGยทJPEGยทGIF๊ฐ€ ์•„๋‹Œ ๊ฒƒ(WebP ๋“ฑ), ๊ธด ๋ณ€์ด 8192px๋ฅผ ๋„˜๋Š” ๊ฒƒ, ๋Œ€๋ถ€๋ถ„์ด ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์ธ ๊ฒƒ โ€” ์€ ํ”Œ๋Ÿฌ๊ทธ์ธ ์ฐฝ(๋ธŒ๋ผ์šฐ์ €)์ด ๋””์ฝ”๋”ฉํ•ด ๊ธด ๋ณ€ 8192px ์ดํ•˜์˜ PNG๋กœ ๋Œ๋ ค์ฃผ๊ณ , ํ•ฉ์„ฑ๊ธฐ๋Š” ์›๋ณธ ํฌ๊ธฐ์— ๋งž์ถฐ ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค. ์ฐฝ์€ ํ•œ ๋ฒˆ์— ํ•˜๋‚˜๋งŒ ๋””์ฝ”๋”ฉํ•˜๊ณ , ์žฅ๋ฉด ํ•˜๋‚˜์˜ ์‚ฌ์ง„๋“ค์ด ๋ณ€ํ™˜ ์‹œ๊ฐ„ 20์ดˆ๋ฅผ ๋‚˜๋ˆ  ์”๋‹ˆ๋‹ค. ๋ฐ”๊พธ๋Š” ๋ฐ 512 MiB๊ฐ€ ๋„˜๊ฒŒ ๋“ค ์‚ฌ์ง„(๋ณ€ํ™˜์ด ๋งŒ๋“œ๋Š” ๋ฒ„ํผ๋ฅผ ๋จผ์ € ํ’€๋ฆฌ๋Š” ๊ฒƒ ์—†์ด ๋ชจ๋‘ ๋”ํ•จ: ์‚ฌ์ง„ ๋ฐ”์ดํŠธ์˜ ์‚ฌ๋ณธ ๋‹ค์„ฏ โ€” ์ด์ชฝ์˜ ์›๋ณธ, ์ฐฝ์œผ๋กœ ๊ฐ€๋Š” ๋ฉ”์‹œ์ง€, ์ฐฝ์ด ๋ฐ›์€ ์‚ฌ๋ณธ, Blob, ๋””์ฝ”๋”๊ฐ€ ์ฝ๋Š” ๊ฒƒ โ€”, ๋””์ฝ”๋”ฉํ•œ ๊ทธ๋ฆผ๊ณผ ์ค„์ผ ๋•Œ์˜ ์‚ฌ๋ณธ, ์บ”๋ฒ„์Šค, ๊ทธ ์บ”๋ฒ„์Šค๋กœ ๋งŒ๋“  PNG์™€ ๊ทธ ์‚ฌ๋ณธ ์…‹)๊ณผ ์‹œ๊ฐ„์ด ๋‹ค ๋œ ๋’ค์˜ ์‚ฌ์ง„์€ ์›๋ž˜ ๋ฐ”์ดํŠธ๋กœ ๋ณด๋ƒ…๋‹ˆ๋‹ค.
    • ๊ทธ๋ฆฌ์ง€ ๋ชปํ•˜๋Š” ๊ฒƒ์ด ์žˆ์œผ๋ฉด ์บ”๋ฒ„์Šค์˜ ๊ทธ ์ž๋ฆฌ๋ฅผ ๊ทธ๋ฆฌ๊ณ , ์‘๋‹ต์˜ referencePngNote๊ฐ€ ์ด์œ ์™€ ๋‹ฟ์€ ๋ ˆ์ด์–ด๋ฅผ ๋Œ‘๋‹ˆ๋‹ค. ๋‹ค์Œ์ด ์—ฌ๊ธฐ์— ๋“ญ๋‹ˆ๋‹ค. ๋กœ๊ทธ์ธ์ด ๋˜์–ด ์žˆ์œผ๋ฉด direct์˜ get_screenshot์ด ๋Œ€์‹  ๋‹ตํ•ฉ๋‹ˆ๋‹ค(reference-png-approximate).
      • ์‚ฌ๊ฐํ˜•์ด ์•„๋‹Œ ์กฐ์ƒ์˜ ํ…Œ๋‘๋ฆฌ, ์ ์„  ํ…Œ๋‘๋ฆฌ, ๋ชจ์„œ๋ฆฌ ์ด์Œ์ด ๋‘ฅ๊ธ€๊ฑฐ๋‚˜ ๊นŽ์ธ ๋ฐ”๊นฅยท๊ฐ€์šด๋ฐ ํ…Œ๋‘๋ฆฌ, ๋‹จ์ƒ‰ยท๊ทธ๋ผ๋””์–ธํŠธ๊ฐ€ ์•„๋‹Œ ํ…Œ๋‘๋ฆฌ
      • ์•„๋ž˜ ๋ ˆ์ด์–ด๋ฅผ ๋จธ๊ธˆ๋Š” ๋ฐฐ๊ฒฝ ๋ธ”๋Ÿฌ๊ฐ€ ๋…ธ๋“œ ์•ˆ์˜ ๋ ˆ์ด์–ด์— ์žˆ๊ฑฐ๋‚˜, ์ž์‹์„ ๋‘˜ ์ˆ˜ ์—†๋Š” ๋…ธ๋“œ(์‚ฌ๊ฐํ˜•ยท๋ฒกํ„ฐ ๋“ฑ)๋‚˜ ํ…Œ๋‘๋ฆฌ๊ฐ€ ์žˆ๋Š” ๋…ธ๋“œ์— ์žˆ๋Š” ๊ฒฝ์šฐ
      • ํšŒ์ „ยทํ•„ํ„ฐ๊ฐ€ ์žˆ๋Š” ์‚ฌ์ง„ ์ฑ„์›€, ๊ทธ ๋ฐ–์˜ ์น , ๋…ธ์ด์ฆˆยทํ…์Šค์ฒ˜์ฒ˜๋Ÿผ devup-mcp๊ฐ€ ๋ชจ๋ฅด๋Š” ํšจ๊ณผ
      • 8M ํ”ฝ์…€์ด ๋„˜๋Š” ์˜์—ญ, ํ•ฉ์ณ 64 MiB๊ฐ€ ๋„˜๋Š” ์‚ฌ์ง„
      • ์˜คํ”„์…‹๊ณผ spread๋ฅผ ํ•ฉ์ณ ์˜์—ญ ๋‘˜๋ ˆ์— ๊ทธ๋ ค ๋‘” ์—ฌ๋ฐฑ(์ตœ๋Œ€ 512px)๋ณด๋‹ค ๋จผ ๊ณณ์—์„œ ๋“œ๋ฆฌ์›Œ์ง€๋Š” ๊ทธ๋ฆผ์ž
      • ๊ทธ๋ฆฌ๋Š” ๋ฐ 1.5 GiB๊ฐ€ ๋„˜๋Š” ๋ฉ”๋ชจ๋ฆฌ๋‚˜ 40์–ต ๋ฒˆ์ด ๋„˜๋Š” ๋ธ”๋Ÿฌยท๊ทธ๋ฆผ์ž spread ๊ณ„์‚ฐ์ด ๋“œ๋Š” ๊ฒฝ์šฐ โ€” ๊ทธ๋ฆฌ๊ธฐ ์ „์—, ๋ ˆ์ด์–ด๋งˆ๋‹ค ๋‚จ๋Š” ๋งˆ์Šคํฌ์™€ ๊ทธ๋ฆผยท์‚ฌ์ง„์˜ ๋””์ฝ”๋”ฉ๊นŒ์ง€ ์…‰๋‹ˆ๋‹ค. ๋””์ฝ”๋”ฉ์€ ๋””์ฝ”๋”๋ฅผ ๋งŒ๋“ค๊ธฐ ์ „์— ํ—ค๋”๋งŒ ์ฝ๊ณ  ์…‰๋‹ˆ๋‹ค. PNGยทGIF ๋””์ฝ”๋”์—๋Š” ๊ทธ ํ—ˆ์šฉ๋Ÿ‰์„ ๊ฑธ๊ณ , PNG ๋””์ฝ”๋”๊ฐ€ ๊ทธ ๋ฐ–์—์„œ ์žก๋Š” ๊ฒƒ(ํ’€์–ด๋‚ด๋Š” ํ–‰, ํ…์ŠคํŠธ ์ฒญํฌ๋งˆ๋‹ค์˜ ํ•ญ๋ชฉ)๋„ ์…‰๋‹ˆ๋‹ค. JPEG ๋””์ฝ”๋”๋Š” ์ƒํ•œ์„ ๋ฐ›์ง€ ์•Š์œผ๋ฏ€๋กœ, ํ”„๋ ˆ์ž„ ํ—ค๋”์˜ ์ƒ˜ํ”Œ๋ง์œผ๋กœ ์„ผ ๊ณ„์ˆ˜ ๋ฒ„ํผ์™€ ๋ฐ”์ดํŠธ ์‚ฌ๋ณธ๊นŒ์ง€ ์…‰๋‹ˆ๋‹ค. ํ”„๋ ˆ์ž„ ํ—ค๋” ์•ž์— ๋‹จ๋… ๋งˆ์ปค(SOIยทRSTยท์ฑ„์›€ 0)๊ฐ€ ์žˆ๋Š” JPEG๋Š” ๋””์ฝ”๋”ฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค
      • ํ”Œ๋Ÿฌ๊ทธ์ธ ์ฐฝ์ด PNG๋กœ ๋ฐ”๊ฟ” ์ฃผ์ง€ ๋ชปํ•œ ์‚ฌ์ง„(์ฐฝ๋„ ๋””์ฝ”๋”ฉํ•˜์ง€ ๋ชปํ–ˆ๊ฑฐ๋‚˜, ๋ฐ”๊พธ๋Š” ๋ฐ 512 MiB๊ฐ€ ๋„˜๊ฒŒ ๋“ค๊ฑฐ๋‚˜, ๋ณ€ํ™˜ ์‹œ๊ฐ„์ด ๋‹ค ๋๊ฑฐ๋‚˜, ์ด ๋””์ฝ”๋”ฉ ์ „์— ๋นŒ๋“œ๋œ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด๊ฑฐ๋‚˜, ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ํ˜•์‹ยทํฌ๊ธฐยท๋ฐ”์ดํŠธ ์ˆ˜๋กœ ์•Œ์•„๋ณด์ง€ ๋ชปํ•œ ๊ฒฝ์šฐ) ๊ฐ€์šด๋ฐ PNGยทJPEGยทGIF๊ฐ€ ์•„๋‹Œ ๊ฒƒ(WebP โ€” ๊ทธ ๋””์ฝ”๋”๋Š” ์ƒํ•œ์„ ๋ฐ›์ง€ ์•Š๊ณ , ํ—ค๋”๋กœ ์…€ ์ˆ˜ ์—†๋Š” ๋ฒ„ํผ๋ฅผ ์žก์Šต๋‹ˆ๋‹ค), 8192px๋ณด๋‹ค ๋„“์€ PNG(๋„“์€ ์ธํ„ฐ๋ ˆ์ด์Šค 16๋น„ํŠธ PNG๋Š” ๋””์ฝ”๋”๊ฐ€ ํ’€์–ด๋‚ด๋Š” ํ–‰์ด ํ—ˆ์šฉ๋Ÿ‰์„ ๋„˜์Šต๋‹ˆ๋‹ค), ํ—ˆ์šฉ๋Ÿ‰ ์•ˆ์—์„œ ๋””์ฝ”๋”ฉ๋˜์ง€ ์•Š๋Š” PNGยทGIF(ํฐ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง„ PNG ๋“ฑ)
      • ํ—ค๋”๋ฅผ ์ฝ์„ ์ˆ˜ ์—†๋Š” ๊ทธ๋ฆผยท์‚ฌ์ง„
      • ํ”Œ๋Ÿฌ๊ทธ์ธ์ด exportํ•˜์ง€ ๋ชปํ•œ ๋ถ€๋ถ„, ๊ทธ๋ฆฌ๋‹ค ๋ฉˆ์ถ˜ ๊ฒฝ์šฐ
    • ๋‹ต ํ•˜๋‚˜(6 MiB)์— ๋“ค์–ด๊ฐ€์ง€ ์•Š๋Š” ๋‹ต์€ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋ถ™๋“ค์–ด ๋‘๊ณ  4 MiB ์กฐ๊ฐ์œผ๋กœ ๋‚ด์ฃผ๋ฉฐ, ๋ธŒ๋ฆฌ์ง€๊ฐ€ ์ด์–ด ๋ฐ›์Šต๋‹ˆ๋‹ค. ์กฐ๊ฐ์˜ ์œ„์น˜๊ฐ€ ์–ด๊ธ‹๋‚˜์ง€ ์•Š๋„๋ก ๋ถ™๋“ค์–ด ๋‘” ๊ธ€์€ ASCII๋กœ ์”๋‹ˆ๋‹ค. ๋ ˆ์ด์–ด ์ด๋ฆ„์€ ํ”ํžˆ ํ•œ๊ธ€์ž…๋‹ˆ๋‹ค.
      • ๋ถ™๋“  ๋‹ต: ๋‹ค ์ฝํžˆ๊ฑฐ๋‚˜ 2๋ถ„์ด ์ง€๋‚  ๋•Œ๊นŒ์ง€ ๋‚จ๊ณ , ๋‹ค๋ฅธ ์š”์ฒญ ๋•Œ๋ฌธ์— ๋ฐ€๋ ค๋‚˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
      • ๋ฐ”์  ๋•Œ: ๋ถ™๋“  ๋‹ต์ด 192 MiB ์ด์ƒ ์Œ“์—ฌ ์žˆ์œผ๋ฉด ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ์ƒˆ ๊ทธ๋ฆผ์„ ๊ทธ๋ฆฌ์ง€ ์•Š๊ณ  ๋ฐ”์˜๋‹ค๊ณ  ๋‹ตํ•˜๋ฉฐ(DEVUP_REFERENCE_PNG_BUSY), ๋ธŒ๋ฆฌ์ง€๋Š” 45์ดˆ๊นŒ์ง€ ๋‹ค์‹œ ๋ฌป์Šต๋‹ˆ๋‹ค. ๊ทธ ์‚ฌ์ด์—๋„ ๋‹ค๋ฅธ ์š”์ฒญ์˜ ์กฐ๊ฐ ์ฝ๊ธฐ๋Š” ์ฒ˜๋ฆฌ๋ฉ๋‹ˆ๋‹ค. 192 MiB๋Š” ์ƒˆ ๊ทธ๋ฆผ์„ ๋ฐ›์•„๋“ค์ด๋Š” ๋ฌธํ„ฑ์ด์ง€ ์ƒํ•œ์ด ์•„๋‹™๋‹ˆ๋‹ค โ€” ๊ทธ ์•„๋ž˜์—์„œ ๋ฐ›์•„๋“ค์ธ ๋‹ต ํ•˜๋‚˜(์ตœ๋Œ€ 90 MiB)๋งŒํผ ๋„˜์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
      • ์‚ฌ๋ผ์ง„ ๋‹ต: ์ฝ๊ธฐ ์ „์— ์‚ฌ๋ผ์ง„ ๋‹ต(ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋‹ค์‹œ ์‹คํ–‰ํ•œ ๊ฒฝ์šฐ ๋“ฑ)์€ ํ•œ ๋ฒˆ ๋‹ค์‹œ ๋ฐ›์Šต๋‹ˆ๋‹ค.
      • ๋„ˆ๋ฌด ํฐ ๋‹ต: ์กฐํ•ฉ์— ๋“œ๋Š” ๊ฒƒ๊นŒ์ง€ ์‹ค์€ ๋‹ต์ด 90 MiB(๋ธŒ๋ฆฌ์ง€๊ฐ€ ๋ฐ›๋Š” 96 MiB ์•„๋ž˜)๋ฅผ ๋„˜์œผ๋ฉด ์กฐํ•ฉ์„ ๋ฒ„๋ฆฌ๊ณ  ์บ”๋ฒ„์Šค ๊ทธ๋ฆผ๋งŒ ์ด์œ ์™€ ํ•จ๊ป˜ ๋ณด๋ƒ…๋‹ˆ๋‹ค.

    ๋ธŒ๋ฆฌ์ง€๋Š” get_screenshot๊ณผ ๊ฐ™์€ ์ด๋ฏธ์ง€ ๋ธ”๋ก ํ•˜๋‚˜๋กœ ๋Œ๋ ค์ฃผ๊ณ , ์ˆ˜์ง‘๊ธฐ๋Š” ๋‘ ๊ฒฝ๋กœ์˜ ๋‹ต์„ ๊ฐ™์€ ๊ฒ€์ฆ์œผ๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค.

  • devup-mcp๋ณด๋‹ค ์˜ค๋ž˜๋œ ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ์ƒˆ ์ฝ๊ธฐ๋ฅผ ๋ชจ๋ฅธ๋‹ค๊ณ  ๋‹ตํ•ฉ๋‹ˆ๋‹ค(DEVUP_BRIDGE_UNKNOWN_SCRIPT). ์‹œ๋„์กฐ์ฐจ ๋˜์ง€ ์•Š์€ ์ฝ๊ธฐ๋ผ, ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ํŒŒ์ผ ํ‚ค๋ฅผ ๋ณด๊ณ ํ–ˆ๊ณ  ๋กœ๊ทธ์ธ์ด ๋˜์–ด ์žˆ์œผ๋ฉด ๊ทธ ์ฝ๊ธฐ๋งŒ direct ๊ฒฝ๋กœ๊ฐ€ ๋Œ€์‹  ์ฝ์Šต๋‹ˆ๋‹ค(ํ•œ๋„๋ฅผ ์”๋‹ˆ๋‹ค). ๋กœ๊ทธ์ธ์ด ์—†์œผ๋ฉด ๋กœ๊ทธ์ธ ์š”๊ตฌ๊ฐ€ ์•„๋‹ˆ๋ผ ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ๋‹ต๊ณผ ํ•  ์ผ โ€” ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋ถˆ๋Ÿฌ์˜จ ์ €์žฅ์†Œ๋ฅผ ๊ฐฑ์‹ ํ•˜๊ณ  ๋‹ค์‹œ ์‹คํ–‰ โ€” ์ด ์˜ต๋‹ˆ๋‹ค. ์กฐ๊ฐ์œผ๋กœ ๋‚ด์ฃผ๊ธฐ ์ „์˜ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์ฐธ์กฐ PNG๋ฅผ ๋ธŒ๋ฆฌ์ง€ ๋‹ต ํ•˜๋‚˜(8 MiB)์— ๋‹ด์ง€ ๋ชปํ•  ๋•Œ(DEVUP_REFERENCE_PNG_TOO_LARGE, direct๋Š” 16 MiB๊นŒ์ง€ ๋ฐ›์Šต๋‹ˆ๋‹ค)๋„ ๊ฐ™์€ ๊ธธ์„ ๊ฐ‘๋‹ˆ๋‹ค.

์ž์„ธํ•œ ๋‚ด๋ถ€ ๊ตฌ์กฐ์™€ ์Šคํฌ๋ฆฝํŠธ ์ƒ์„ฑ ๋ฐฉ์‹์€ plugin/README.md์— ์žˆ์Šต๋‹ˆ๋‹ค.

๋กœ์ปฌ Dev Mode MCP๋ฅผ ์“ฐ์ง€ ์•Š๋Š” ์ด์œ 

Figma ๋ฐ์Šคํฌํ†ฑ ์•ฑ์˜ ๋กœ์ปฌ Dev Mode MCP(http://127.0.0.1:3845/mcp)๋Š” ์„ธ ๋ฒˆ์งธ ๊ฒฝ๋กœ๋กœ ์•ˆ๋‚ดํ–ˆ์œผ๋‚˜ ์ œ๊ฑฐํ–ˆ์Šต๋‹ˆ๋‹ค. ์ฝ๊ธฐ ๋„๊ตฌ 6๊ฐœ(get_design_context, get_variable_defs, get_screenshot, get_motion_context, get_metadata, get_figjam)๋งŒ ์ œ๊ณตํ•˜๊ณ  ๊ทธ์ค‘์— use_figma๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. devup-mcp์˜ ์ˆ˜์ง‘์€ snapshotยทexploreยทsection indexยทtheme ๋ชจ๋‘ use_figma๋กœ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋ฏ€๋กœ ๋กœ์ปฌ์—์„œ๋Š” ์‹คํ–‰ํ•  ๋„๊ตฌ ์ž์ฒด๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๋„๊ตฌ๋“ค์ด fileKey๋ฅผ ๋ฐ›์ง€ ์•Š๊ณ  ๋ฐ์Šคํฌํ†ฑ ์•ฑ์— ์—ด๋ ค ์žˆ๋Š” ํŒŒ์ผ๋งŒ ๊ฐ€๋ฆฌํ‚ค๋Š” ๊ฒƒ๋„ ๊ฐ™์€ ์ด์œ ๋กœ ๋งž์ง€ ์•Š์Šต๋‹ˆ๋‹ค. "OAuth ์—†์ด ๋ฐ”๋กœ ์“ธ ์ˆ˜ ์žˆ๋‹ค"๋Š” ์•ˆ๋‚ด๋Š” ํ™•์‹ ์— ์ฐจ์„œ ํ‹€๋ฆฐ ์•ˆ๋‚ด์˜€๊ณ , ๋ฏฟ์€ ์ชฝ์ด ํ•œ ํ„ด์„ ๋ฒ„๋ฆฐ ๋’ค์—์•ผ ์•Œ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์›๊ฒฉ OAuth ๋“ฑ๋ก ์ œ์•ฝ (์‹ค์ธก)

Figma Remote MCP ๋“ฑ๋ก ์—”๋“œํฌ์ธํŠธ๋Š” POST https://api.figma.com/v1/oauth/mcp/register์ž…๋‹ˆ๋‹ค. ์š”์ฒญ ๋ณธ๋ฌธ์˜ client_name์€ ์ •ํ™•ํžˆ ์ผ์น˜ํ•˜๋Š” allowlist๋กœ๋งŒ ์Šน์ธ๋ฉ๋‹ˆ๋‹ค.

client_name ๊ฒฐ๊ณผ
Codex 200 (client_id + client_secret ๋ฐœ๊ธ‰)
Claude Code 403 (2026-09-06 ์‹ค์ธก; ์ด์ „ ํ‘œ์—๋Š” 200์œผ๋กœ ์ ํ˜€ ์žˆ์—ˆ์Œ)
OpenCode 403
opencode 403
Cursor 403
VS Code 403

403 ์‘๋‹ต ๋ณธ๋ฌธ์€ JSON์ด ์•„๋‹ˆ๋ผ ํ‰๋ฌธ Forbidden์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋งŽ์€ ํด๋ผ์ด์–ธํŠธ๊ฐ€ Invalid OAuth error response ... Raw body: Forbidden์œผ๋กœ ํŒŒ์‹ฑ๊นŒ์ง€ ๊นจ์ง‘๋‹ˆ๋‹ค. X-Figma-Plugin-Bundle ํ—ค๋”๋‚˜ User-Agent๋ฅผ ๋ฐ”๊ฟ”๋„ ๊ฒฐ๊ณผ๋Š” ๋ฐ”๋€Œ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์‹ ๊ทœ client ๋“ฑ๋ก์€ waitlist๋ฅผ ํ†ตํ•ด์„œ๋งŒ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค: https://www.figma.com/mcp-catalog/.

redirect_uri๋„ ํ˜•ํƒœ๊ฐ€ ๊ณ ์ •๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

redirect_uri ๊ฒฐ๊ณผ
http://127.0.0.1:<port>/callback 200
http://127.0.0.1:<port>/mcp/oauth/callback 400
http://localhost:<port>/mcp/oauth/callback 400

๊ฒฝ๋กœ๋Š” ์ •ํ™•ํžˆ /callback์ด์–ด์•ผ ํ•˜๊ณ  ํ˜ธ์ŠคํŠธ๋Š” 127.0.0.1์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค (localhost ๋ถˆ๊ฐ€). Figma PAT(figd_...)๋Š” Authorization: Bearer, X-Figma-Token ์–ด๋А ๋ฐฉ์‹์œผ๋กœ๋„ ์›๊ฒฉ MCP์—์„œ ์ง€์›๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ˆจ์€ ํ•จ์ • โ€” ์ฝœ๋ฐฑ ํฌํŠธ ์ ์œ 

๋กœ์ปฌ OAuth ์ฝœ๋ฐฑ์ด ์“ฐ๋Š” ํฌํŠธ๋ฅผ OS๋‚˜ ๋ณด์•ˆ ์†Œํ”„ํŠธ์›จ์–ด(์˜ˆ: ์‚ฌ๋‚ด ๋ณด์•ˆ ์—์ด์ „ํŠธ)๊ฐ€ ์ด๋ฏธ ์ ์œ ํ•˜๊ณ  ์žˆ์œผ๋ฉด, ๋ธŒ๋ผ์šฐ์ €๋Š” ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ์— "์„ฑ๊ณต"ํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ ๊ทธ ์š”์ฒญ์€ ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค๋กœ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ๋Š” ์•„๋ฌด ์—๋Ÿฌ ์—†์ด Waiting for authorization... ์ƒํƒœ๋กœ ์˜์›ํžˆ ๋‚จ์Šต๋‹ˆ๋‹ค. ๋กœ๊ทธ์ธ์ด ๋ฉˆ์ถ˜ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ด๋ฉด ๊ฐ€์žฅ ๋จผ์ € ์ฝœ๋ฐฑ ํฌํŠธ๋ฅผ ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์“ฐ๊ณ  ์žˆ์ง€ ์•Š์€์ง€ ํ™•์ธํ•˜์„ธ์š”.

๊ธฐ๋ณธ๊ฐ’์€ OS๊ฐ€ ๋งค๋ฒˆ ๋นˆ ์ž„์‹œ ํฌํŠธ๋ฅผ ๊ณจ๋ผ์ฃผ๋ฏ€๋กœ(0) ์ด ์ถฉ๋Œ์„ ํ”ผํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์ „ ๋“ฑ๋กํ•œ client์˜ redirect_uri๊ฐ€ ๊ณ ์ • ํฌํŠธ๋กœ ๋“ฑ๋ก๋˜์–ด ์žˆ์–ด ํŠน์ • ํฌํŠธ๋ฅผ ๊ณ ์ •ํ•ด์•ผ ํ•œ๋‹ค๋ฉด devup-mcp --figma-callback-port <port>๋ฅผ ์ง€์ •ํ•˜์„ธ์š”. ์ด ๊ฒฝ์šฐ devup-mcp๋Š” ๊ทธ ํฌํŠธ๊ฐ€ ์ด๋ฏธ ์‚ฌ์šฉ ์ค‘์ด๋ฉด ์—ฐ๊ฒฐ์„ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  DEVUP_FIGMA_CALLBACK_PORT_IN_USE ์˜ค๋ฅ˜๋ฅผ ์ฆ‰์‹œ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. devup_figma_auth { "action": "doctor" }์˜ paths.direct.callbackPort.free์—์„œ๋„ ์ง€์ •ํ•œ ํฌํŠธ๊ฐ€ ์‹ค์ œ๋กœ ๋น„์–ด ์žˆ๋Š”์ง€ ์‹ค์ธกํ•œ ๊ฐ’์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

opencode์—์„œ direct ๊ฒฝ๋กœ ๋ฏธ๋ฆฌ ์„ค์ •ํ•˜๊ธฐ

Dynamic Client Registration์„ ๊ฑด๋„ˆ๋›ฐ๋ ค๋ฉด mcp.<name>.oauth์— ์ด๋ฏธ ๋ฐœ๊ธ‰๋ฐ›์€ clientId/clientSecret์„ ์ง์ ‘ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

{
  "mcp": {
    "figma": {
      "type": "remote",
      "url": "https://mcp.figma.com/mcp",
      "oauth": {
        "clientId": "<allowlist๋œ client_name์œผ๋กœ ๋“ฑ๋กํ•ด ๋ฐœ๊ธ‰๋ฐ›์€ client_id>",
        "clientSecret": "<allowlist๋œ client_name์œผ๋กœ ๋“ฑ๋กํ•ด ๋ฐœ๊ธ‰๋ฐ›์€ client_secret>",
        "scope": "mcp:connect",
        "callbackPort": 19876,
        "redirectUri": "http://127.0.0.1:19876/callback"
      }
    }
  }
}

Codex๋Š” allowlist์— ์žˆ์–ด ๋ณ„๋„ ์„ค์ • ์—†์ด ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Claude Code๋Š” ํ•œ๋•Œ 200์ด์—ˆ์œผ๋‚˜ 2026-09-06 ์‹ค์ธก์—์„œ 403์œผ๋กœ ๊ฑฐ์ ˆ๋์Šต๋‹ˆ๋‹ค โ€” allowlist๋Š” Figma๊ฐ€ ๋ฐ”๊ฟ€ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์œ„ ํ‘œ๋Š” ์ธก์ • ์‹œ์ ์˜ ๊ธฐ๋ก์ž…๋‹ˆ๋‹ค.

devup-mcp๋Š” ์ง์ ‘ ๊ฒฝ๋กœ๋งŒ ์”๋‹ˆ๋‹ค. ํ˜ธ์ŠคํŠธ(Codex)์— ๋“ฑ๋ก๋œ ๊ณต์‹ Figma MCP๋ฅผ ๋นŒ๋ฆฌ๋Š” ์šฐํšŒ ๊ฒฝ๋กœ๋Š” ๋งŒ๋“ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค โ€” devup-mcp๊ฐ€ ์Šค์Šค๋กœ Codex๋กœ ๋“ฑ๋กํ•ด Figma ์›๊ฒฉ MCP์— ๋ถ™๊ณ , ์ˆ˜์ง‘์— ํ•„์š”ํ•œ use_figma๋ฅผ ๊ทธ ์—ฐ๊ฒฐ๋กœ ์ง์ ‘ ๋ถ€๋ฆ…๋‹ˆ๋‹ค. ํ˜ธ์ŠคํŠธ์— Figma MCP๋ฅผ ๋”ฐ๋กœ ์„ค์ •ํ•  ํ•„์š”๊ฐ€ ์—†๊ณ , ์„ค์ •๋ผ ์žˆ์–ด๋„ devup-mcp๋Š” ๊ทธ๊ฒƒ์„ ์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

claude mcp add --transport http figma https://mcp.figma.com/mcp
codex mcp add figma --url https://mcp.figma.com/mcp

Figma โ†’ DevupUI

{
  "url": "https://www.figma.com/design/<file-key>/<name>?node-id=1-2",
  "outputs": ["tsx"],
  "componentName": "OptionalComponentName",
  "rootLayout": "standalone",
  "scope": "node",
  "outputPaths": { "tsx": "optional/path/Component.tsx" }
}

๊ฒฐ๊ณผ์—๋Š” tsx์™€ ํ•จ๊ป˜ status, quality, cache, collection, source๊ฐ€ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. import ๋ชฉ๋ก๊ณผ ์‚ฌ์šฉ token์€ ๋ณ„๋„ ํ‚ค๋กœ ๋ณด๋‚ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค โ€” ๊ฐ๊ฐ TSX์˜ ์ฒซ ์ค„๊ณผ ๋ณธ๋ฌธ์˜ $token์ด ์ด๋ฏธ ๊ฐ™์€ ๋‚ด์šฉ์„ ๋‹ด๊ณ  ์žˆ์–ด, ์‘๋‹ต์— ๋‘ ๋ฒˆ ์‹ฃ๋Š” ๋งŒํผ์ด ๊ทธ๋Œ€๋กœ ๋‚ญ๋น„์˜€์Šต๋‹ˆ๋‹ค. Auto Layout์€ Flex, ์ผ๋ฐ˜ container๋Š” Box, text๋Š” Text๋กœ ๋ณ€ํ™˜ํ•˜๊ณ  theme binding์ด ์žˆ์œผ๋ฉด JSX prop์—์„œ $token์„ ์šฐ์„  ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋ณ€์ˆ˜ token์€ ๋น„์–ด ์žˆ์ง€ ์•Š์€ Figma codeSyntax.WEB์„ ์šฐ์„ ํ•˜๊ณ , ์—†์œผ๋ฉด ๋ณ€์ˆ˜ ๊ฒฝ๋กœ์˜ ๋งˆ์ง€๋ง‰ ์ด๋ฆ„์„ ์ •๊ทœํ™”ํ•ฉ๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ TSX์˜ $token, devup.json key์™€ source map์ด ๊ฐ™์€ ์ด๋ฆ„์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. rootLayout ๊ธฐ๋ณธ๊ฐ’์ธ standalone์€ ์„ ํƒํ•œ root์˜ ํฌ๊ธฐยท์œ„์น˜ ์ œ์•ฝ๊นŒ์ง€ ํฌํ•จํ•˜๊ณ  Figma instance์˜ ์‹ค์ œ ์ž์‹ ์ƒํƒœ๋ฅผ ํŽผ์ณ ์ •์˜๋˜์ง€ ์•Š์€ component ์ฐธ์กฐ๋ฅผ ๋งŒ๋“ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋ฏธ ๋ ˆ์ด์•„์›ƒ์„ ์†Œ์œ ํ•œ React ๋ถ€๋ชจ ์•ˆ์— ์‚ฝ์ž…ํ•  ๋•Œ๋Š” rootLayout: "embedded"๋กœ root์˜ ์™ธ๋ถ€ ํฌ๊ธฐยท์œ„์น˜ ์ œ์•ฝ๋งŒ ์ƒ๋žตํ•ฉ๋‹ˆ๋‹ค.

๋Œ€ํ˜• ํ™”๋ฉด์˜ ์ง„๋‹จ details์™€ verdict/layout ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋Š” metadataDelivery.uri๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” projection-details.json resource๋กœ ์ „๋‹ฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. inline์—๋Š” ์ด์Šˆ ๊ฐœ์ˆ˜ยทIDยทseverity์™€ ๊ฒ€ํ†  ์•ˆ๋‚ด๋ฅผ ์œ ์ง€ํ•˜๊ณ , ๊ฐ fullDetailsResource.jsonPointer ๋˜๋Š” metadataPreviewFields์˜ pointer๋กœ ์›๋ณธ์„ ์ฐพ์Šต๋‹ˆ๋‹ค. preview์˜ ์ƒ๋žต ๊ฐœ์ˆ˜ยท์ƒํ•œ์€ ๋ช…์‹œํ•˜๋ฉฐ, ํ•„์ˆ˜ ์ฒดํฌ๋ฆฌ์ŠคํŠธ์— ๋”ฐ๋ผ resource๋ฅผ ์ฝ๊ธฐ ์ „์—๋Š” ๊ฒ€์ฆ์„ ์™„๋ฃŒ๋กœ ๋ณด๊ณ ํ•˜์ง€ ๋งˆ์‹ญ์‹œ์˜ค. ์ฝ”๋“œยทthemeยท์—์…‹ ํŒŒ์ผ๊ณผ resource byte/hash ๊ณ„์•ฝ์€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค.

Figma โ†’ devup.json

outputs: ["tsx", "devupJson"]์™€ devupJsonMode: "used-only"๋ฅผ ํ•จ๊ป˜ ์ง€์ •ํ•˜๋ฉด ์ƒ์„ฑ ์ฝ”๋“œ๊ฐ€ ์‹ค์ œ ์ฐธ์กฐํ•˜๋Š” ํ† ํฐ๊ณผ typography ์˜์กด์„ฑ๋งŒ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. projectRoot์— ๊ธฐ์กด theme๊ฐ€ ์žˆ์œผ๋ฉด themeDiff๊ฐ€ ์—†๋Š” ์ •์˜์™€ ์ด๋ฆ„ ์ถฉ๋Œ์„ ๋ณด๊ณ ํ•˜๊ณ , devupJsonMode: "missing-only"๋Š” ๊ธฐ์กด ๊ฐ’์„ ๋ณด์กดํ•œ ์ถ”๊ฐ€๋ถ„๋งŒ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ full์ž…๋‹ˆ๋‹ค. ์ฝ์„ ์ˆ˜ ์—†๋Š” ์Šคํƒ€์ผ์€ skippedStyles์™€ ์ด๋ฆ„ยท๋…ธ๋“œ ID๊ฐ€ ์žˆ๋Š” ๊ฒฝ๊ณ ๋กœ ๋ถ„๋ฆฌํ•˜๋ฉฐ, strict ๋ชจ๋“œ๋Š” ๊ฑฐ์ ˆํ•ฉ๋‹ˆ๋‹ค. ๋ณ€์ˆ˜/๋ฏธ์ง€์ • fontSize์˜ ๋ฐฑ๋ถ„์œจ lineHeight๋Š” ๋ฌธ์ž์—ด๋กœ ๋ณด์กดํ•ฉ๋‹ˆ๋‹ค.

์ฝ”๋“œ ์ถœ๋ ฅ์€ ์ •์ ์ž…๋‹ˆ๋‹ค. interactiveCandidates, ํ”„๋กœํ† ํƒ€์ž…/์ƒํƒœ TSX ์ฃผ์„, ambiguityQuestions, ํ•„์ˆ˜ reviewChecklist๋ฅผ ์‹ค์ œ ๋ผ์šฐํŠธ์™€ ๋™์ž‘์— ์—ฐ๊ฒฐํ•˜๊ณ  devup_ui_validate๋กœ ๊ฒ€์ฆํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. projectRoot๋Š” ๊ธฐ์กด UI/๋ผ์šฐํŠธ์™€ ํ™˜๊ฒฝ ํ‚ค ์ด๋ฆ„๋งŒ ๋ณ‘ํ•ฉํ•ฉ๋‹ˆ๋‹ค. devup_project_context์˜ routes scope์™€ validator์˜ renderedHtml href ์ง‘๊ณ„๋Š” ์ •์  ์ฆ๊ฑฐ์ด๋ฉฐ ํด๋ฆญ ์„ฑ๊ณต์„ ์ฆ๋ช…ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ณ ์ • navigation ๋ฐ‘์ค„์€ ํ™œ์„ฑ ์ƒํƒœ๋ฅผ ๋‹จ์ •ํ•˜์ง€ ์•Š๊ณ  info ๊ฒ€ํ†  ํ›„๋ณด๋กœ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค. responsiveGroups์™€ breakpointCompletion์€ exportํ•œ ํญ๊ณผ ๋‚จ์€ ํญ์„ ๊ตฌ๋ถ„ํ•˜๋ฉฐ, ๊ตฌํ˜„/์‹œ๊ฐ ๊ฒ€์ฆ์€ ๋ณ„๋„์ž…๋‹ˆ๋‹ค.

์ค‘์ฒฉ I44:8533;10:973 ๋…ธ๋“œ ID๋„ URL/assetRequests๋กœ ์ง€์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. SVG native export ์‹คํŒจ ์‹œ ์ง€์›๋˜๋Š” ์‹ค์ œ subtree geometry๋งŒ read-only๋กœ ๋ณต๊ตฌํ•˜๋ฉฐ, ์ง€์›๋˜์ง€ ์•Š๋Š” mask/paint/effect๋Š” ์‹คํŒจ ์›์ธ์„ ๋ณด์กดํ•ฉ๋‹ˆ๋‹ค. ์‹คํŒจํ•œ ์—์…‹์—๋Š” ์ƒ์„ฑ ๊ฒฝ๋กœ๋ฅผ ํ‘œ์‹œํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. DEVUP_ASSET_TIMEOUT_MS๋Š” 1000โ€“600000 ms ๋ฒ”์œ„์ด๋ฉฐ ๊ธฐ๋ณธ๊ฐ’์€ 90000 ms์ž…๋‹ˆ๋‹ค. ์ง„๋‹จ์— ๋‹จ๊ณ„๋ณ„ ์‹œ๊ฐ„์ด ํฌํ•จ๋˜๋ฉฐ Figma ๋‚ด๋ถ€ renderer timeout ์ž์ฒด๋ฅผ ๋ฐ”๊พธ์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. ์ƒˆ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์ ์šฉํ•˜๋ ค๋ฉด ๊ฐฑ์‹ ๋œ bridge host์™€ ์žฌ๋นŒ๋“œํ•œ plugin์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.

{
  "url": "https://www.figma.com/design/<file-key>/<name>?node-id=1-2",
  "outputs": ["devupJson"],
  "scope": "file",
  "outputPaths": { "devupJson": "optional/path/devup.json" }
}

๊ฒฐ๊ณผ๋Š” theme.colors, theme.typography, theme.length, theme.shadow๋ฅผ ํฌํ•จํ•˜๋Š” ๊ฒฐ์ •์  JSON ๋ฌธ์ž์—ด๊ณผ counts, completeness๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

ํ…์ŠคํŠธ ์Šคํƒ€์ผ์˜ ๊ธ€์ž ํฌ๊ธฐ๊ฐ€ ๋ณ€์ˆ˜์— ๋ฌถ์—ฌ ์žˆ๊ณ  ๊ทธ ๋ณ€์ˆ˜์˜ ๋ชจ๋“œ๊ฐ€ ๋ธŒ๋ ˆ์ดํฌํฌ์ธํŠธ ์ด๋ฆ„(MobileยทTabletยทDesktopยทPC, ๋˜๋Š” ์ˆซ์ž)์ด๋ฉด, ๊ทธ ์Šคํƒ€์ผ์€ ๋ชจ๋“œ๋งˆ๋‹ค ํ•œ ์นธ์ธ ๋ฐ˜์‘ํ˜• typography ๋ฐฐ์—ด์ด ๋ฉ๋‹ˆ๋‹ค โ€” Mobile์€ ์ฒซ ์นธ, Tablet์€ ์…‹์งธ ์นธ(768px), DesktopยทPC๋Š” ๋‹ค์„ฏ์งธ ์นธ(1280px)์ด๊ณ , ์นธ๋งˆ๋‹ค ๊ทธ ๋ชจ๋“œ์—์„œ ํ’€๋ฆฐ ํฌ๊ธฐ๋ฅผ ์”๋‹ˆ๋‹ค. ํผ์„ผํŠธ ํ–‰๊ฐ„์€ ํฌ๊ธฐ๊ฐ€ ๊ณ ์ •์ด๋ฉด Figma๊ฐ€ ๊ทธ๋ฆฌ๋Š” ์ •์ˆ˜ px ํ–‰๊ฐ„์œผ๋กœ, ํฌ๊ธฐ๊ฐ€ ๋ณ€์ˆ˜์— ๋ฌถ์˜€๊ฑฐ๋‚˜ ์—†์œผ๋ฉด 150%์ฒ˜๋Ÿผ ํผ์„ผํŠธ ๊ทธ๋Œ€๋กœ ์”๋‹ˆ๋‹ค(CSS๊ฐ€ ์ ์šฉ๋˜๋Š” ๊ธ€์ž ํฌ๊ธฐ์— ๋งž์ถฐ ๋Š˜์ž…๋‹ˆ๋‹ค). ์˜ˆ์ „์—๋Š” ๊ทธ ๊ฒฝ์šฐ ํ…Œ๋งˆ ์ „์ฒด๋ฅผ DEVUP_THEME_CONFLICT๋กœ ๊ฑฐ์ ˆํ–ˆ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“œ๊ฐ€ ๋ธŒ๋ ˆ์ดํฌํฌ์ธํŠธ ์ด๋ฆ„์ด ์•„๋‹ˆ๊ฑฐ๋‚˜(LightยทDark ๋“ฑ), ์Šคํƒ€์ผ ์ด๋ฆ„์— ์ด๋ฏธ ๋ธŒ๋ ˆ์ดํฌํฌ์ธํŠธ๊ฐ€ ๋“ค์–ด ์žˆ๊ฑฐ๋‚˜, ํฌ๊ธฐ๊ฐ€ ๋ชจ๋“  ๋ชจ๋“œ์—์„œ ๊ฐ™๊ฑฐ๋‚˜, ๋‘ ๋ชจ๋“œ๊ฐ€ ํ•œ ์นธ์— ๋†“์ด๊ฑฐ๋‚˜, ํ’€๋ฆฌ์ง€ ์•Š๋Š” ๋ชจ๋“œ๊ฐ€ ์žˆ์œผ๋ฉด ํฌ๊ธฐ๋Š” ์˜ˆ์ „์ฒ˜๋Ÿผ ๋ณ€์ˆ˜์˜ ํ† ํฐ($textTest)์ž…๋‹ˆ๋‹ค. ์ˆซ์ž ๋ชจ๋“œ ์ด๋ฆ„์€ 0โ€“5๋ฒˆ ์นธ์ž…๋‹ˆ๋‹ค. ๋ชจ๋“œ๊ฐ€ ๋ชจ๋‘ ๋ธŒ๋ ˆ์ดํฌํฌ์ธํŠธ ์ด๋ฆ„์ธ ์ปฌ๋ ‰์…˜์˜ ๊ธธ์ด ๋ณ€์ˆ˜๋Š” length ํ† ํฐ ํ•˜๋‚˜๋ฅผ ๋ฐ˜์‘ํ˜• ๋ฐฐ์—ด๋กœ ์จ์„œ ๋ชจ๋“  ํ…Œ๋งˆ์— ๋„ฃ์Šต๋‹ˆ๋‹ค(containerX: ["20px", null, "40px"], ๋ชจ๋“œ๋งˆ๋‹ค ๊ฐ’์ด ๊ฐ™์œผ๋ฉด ๊ฐ’ ํ•˜๋‚˜). ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ๋ชจ๋“œ ์ด๋ฆ„์„ ํ…Œ๋งˆ ํ‚ค(theme.length.mobile)๋กœ ์“ฐ๋Š”๋ฐ, devup-ui์—์„œ ๊ทธ๊ฒƒ์€ ๊ทธ data-theme์ผ ๋•Œ๋งŒ ๊ฐ’์ด ์žˆ๋Š” ํ† ํฐ์ž…๋‹ˆ๋‹ค. ํ…Œ๋งˆ์— ๊ฐ™์€ ์ด๋ฆ„์˜ ํ† ํฐ์ด ์ด๋ฏธ ์žˆ์œผ๋ฉด ๊ทธ ํ…Œ๋งˆ์˜ ๊ฐ’์„ ๋‘๊ณ  conflicts์— ๋‚จ๊น๋‹ˆ๋‹ค. ๋ธŒ๋ ˆ์ดํฌํฌ์ธํŠธ ๊ธธ์ด ๋‘˜์ด ๊ฐ™์€ ์ด๋ฆ„์— ๋‹ค๋ฅธ ๊ฐ’์„ ๊ฐ€์ง€๋ฉด ์ •ํ•ด์ง„ ์ˆœ์„œ๋กœ ํ•˜๋‚˜๋ฅผ ๊ณ ๋ฅด๊ณ , ๊ทธ ์ถฉ๋Œ์€ conflicts์— mode @breakpoints๋กœ ๋‚จ์Šต๋‹ˆ๋‹ค. ์ƒ‰์€ devup-ui๊ฐ€ ๋ฐฐ์—ด์„ ๋ฐ›์ง€ ์•Š์•„ ์˜ˆ์ „์ฒ˜๋Ÿผ ๋ชจ๋“œ ์ด๋ฆ„์„ ํ…Œ๋งˆ ํ‚ค๋กœ ์”๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์ปฌ๋ ‰์…˜์˜ ๋ณ€์ˆ˜๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” alias๋Š” ๊ทธ ์ปฌ๋ ‰์…˜์— ๋ชจ๋“œ๊ฐ€ ํ•˜๋‚˜๋ฟ์ด๋ฉด(๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ primitive ๋“ฑ) ๊ทธ ํ•˜๋‚˜์˜ ๊ฐ’์œผ๋กœ ํ’‰๋‹ˆ๋‹ค. ํ…Œ๋งˆ๋Š” ํŒŒ์ผ ์ „์ฒด๋“  ๋…ธ๋“œ ๋ฒ”์œ„๋“ , ๋…ธ๋“œ๊ฐ€ ๋ฌถ์ง€ ์•Š์•˜๋”๋ผ๋„ ๋ณ€์ˆ˜๊ฐ€ alias๋กœ ๊ฐ€๋ฆฌํ‚ค๋Š” ๋ณ€์ˆ˜๋ฅผ ์ฝ์–ด(๊ทธ ๋ณ€์ˆ˜๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ๋ณ€์ˆ˜๊นŒ์ง€) ํ•จ๊ป˜ ๋„ฃ์Šต๋‹ˆ๋‹ค. ๋ชจ๋“œ๊ฐ€ ์—ฌ๋Ÿฟ์ด๋ฉด ์–ด๋А ๋ชจ๋“œ๋ฅผ ์“ธ์ง€๋Š” ์†Œ๋น„์ž๊ฐ€ ์ •ํ•˜๋ฏ€๋กœ unresolvedVariables์— ๋‚จ๊น๋‹ˆ๋‹ค.

์ƒ์„ฑ TSX๋Š” ๋ณ€์ˆ˜์— ๋ฌถ์ธ ๊ธธ์ด(padding, gap, ํ•œ ๊ฐ’์ธ ๋ชจ์„œ๋ฆฌ ๋ฐ˜๊ฒฝ, ๊ทธ๋ฆผ์ž์˜ ์˜คํ”„์…‹ยท๋ธ”๋Ÿฌยทspread, ๊ธ€์ž ํฌ๊ธฐ)๋ฅผ ๊ทธ ๋ณ€์ˆ˜์˜ $ํ† ํฐ์œผ๋กœ ์”๋‹ˆ๋‹ค(px="$containerX", boxShadow="0 $shadowMdY 20px 0 #00000026"). ์กฐ๊ฑด์€ ๊ฐ™์€ ๋ณ€์ˆ˜๋กœ ๋งŒ๋“  ํ…Œ๋งˆ์—์„œ ๊ทธ ํ† ํฐ์ด, ํŽ˜์ด์ง€๊ฐ€ data-theme ์—†์ด ๋ณด์—ฌ ์ฃผ๋Š” ํ…Œ๋งˆ์—์„œ ํ™”๋ฉด์ด ๊ทธ๋ ค์ง„ ํญ(๋…ธ๋“œ๊ฐ€ ๋†“์ธ ํ™”๋ฉด ํ”„๋ ˆ์ž„์˜ ํญ)์ผ ๋•Œ ์ฝ”๋“œ์— ์“ฐ๋Š” ๊ฐ’๊ณผ ์ •ํ™•ํžˆ ๊ฐ™์€ ๊ฒƒ์ž…๋‹ˆ๋‹ค(๋ฐ˜์˜ฌ๋ฆผํ•ด์„œ ๊ฐ™์€ ๊ฒƒ์€ ๊ฐ™์€ ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค: 20.004px ํ† ํฐ์€ 20px ์ž๋ฆฌ์— ๋“ค์–ด๊ฐ€์ง€ ์•Š์Šต๋‹ˆ๋‹ค). ๋ฐ˜์‘ํ˜• ํ† ํฐ์ด๋ฉด ๊ทธ ๋’ค๋กœ ํ™”๋ฉด ํญ์— ๋”ฐ๋ผ ๋””์ž์ธ์˜ ๋ชจ๋“œ ๊ฐ’์„ ๋”ฐ๋ผ๊ฐ€๊ณ , ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์˜ˆ์ „์ฒ˜๋Ÿผ px๋ฅผ ์”๋‹ˆ๋‹ค(Mobile ๊ฐ’์˜ ํ™”๋ฉด์ด 800px ํญ์ด๋ฉด ํ† ํฐ์ด ๊ฑฐ๊ธฐ์„œ 40px์ด๋ฏ€๋กœ 20px). ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ๊ธธ์ด๋ฅผ ํ† ํฐ์œผ๋กœ ์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํญยท๋†’์ด๋Š” ์น˜์ˆ˜ ์ฆ๋ช…์ด px ๊ฐ’์— ๊ธฐ๋Œ€๋ฏ€๋กœ ์˜ˆ์ „์ฒ˜๋Ÿผ px์ž…๋‹ˆ๋‹ค.

outputPath๋ฅผ ์ƒ๋žตํ•˜๋ฉด ๊ฒฐ๊ณผ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์™€ MCP ์‘๋‹ต์—๋งŒ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. ๋ช…์‹œํ•˜๋ฉด ์ƒ์„ฑ๋œ TSX ๋˜๋Š” devup.json๋งŒ ํ•ด๋‹น ๊ฒฝ๋กœ์— ๊ธฐ๋กํ•˜๊ณ  ์‹ค์ œ ์ ˆ๋Œ€ ๊ฒฝ๋กœ๋ฅผ ์‘๋‹ตํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ํ—ˆ์šฉ write root๋Š” devup-mcp process ์‹œ์ž‘ ๋‹น์‹œ์˜ current directory ํ•˜๋‚˜์ด๋ฉฐ, ๊ทธ ๋ฐ–์˜ workspace๋Š” ๋ฐ˜๋ณต ๊ฐ€๋Šฅํ•œ --allow-write-root <directory> ์‹œ์ž‘ ์ธ์ž๋กœ๋งŒ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Tool ์ž…๋ ฅ์œผ๋กœ root ์ž์ฒด๋ฅผ ๋„“ํž ์ˆ˜ ์—†๊ณ  .., ๋‹ค๋ฅธ drive/UNC, alternate data stream, symlink/junction์„ ํ†ตํ•œ root ํƒˆ์ถœ์€ ๊ธฐ๋ก ์ „์— ๊ฑฐ์ ˆ๋ฉ๋‹ˆ๋‹ค.

์—ฌ๋Ÿฌ text/asset output์€ ๋ชจ๋‘ ๊ฒ€์ฆํ•œ ๋’ค ๊ฐ™์€ directory์˜ exclusive ์ž„์‹œ ํŒŒ์ผ์— stagingํ•˜๊ณ  ํ•œ transaction์œผ๋กœ ๊ต์ฒดํ•ฉ๋‹ˆ๋‹ค. ์ •์ƒ runtime ์˜ค๋ฅ˜์—์„œ๋Š” ์ด๋ฏธ ๊ต์ฒดํ•œ ํŒŒ์ผ์„ ์—ญ์ˆœ์œผ๋กœ ๋˜๋Œ๋ฆฌ๊ณ  ๊ธฐ์กด ํŒŒ์ผ์„ ๋ณต๊ตฌํ•ฉ๋‹ˆ๋‹ค. ๊ฐœ๋ณ„ rename์€ atomicํ•˜์ง€๋งŒ ์—ฌ๋Ÿฌ directory์™€ process crash๋ฅผ ๊ฐ€๋กœ์ง€๋ฅด๋Š” ์™„์ „ํ•œ ์›์ž์„ฑ์€ ์ผ๋ฐ˜ filesystem ํŠน์„ฑ์ƒ ๋ณด์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํ†ตํ•ฉ ์ˆ˜์ง‘๊ณผ ๋‹ค์ค‘ ์ถœ๋ ฅ

{
  "url": "https://www.figma.com/design/<file-key>/<name>?node-id=1-2",
  "outputs": ["tsx"],
  "scope": "node",
  "strict": true,
  "refresh": false,
  "delivery": "auto"
}

์œ„ outputs๋Š” ์Šคํ‚ค๋งˆ ๊ธฐ๋ณธ๊ฐ’๊ณผ ๊ฐ™์€ ๊ถŒ์žฅ ํ˜ธ์ถœ์ž…๋‹ˆ๋‹ค. ์ฝ์„ output๋งŒ ์š”์ฒญํ•˜์„ธ์š” โ€” ์œ„์˜ ์‘๋‹ต์— ๋ฌด์—‡์ด ๋“ค์–ด์˜ค๋Š”๊ฐ€ ํ‘œ๊ฐ€ ์ „์ฒด ๋ชฉ๋ก ๋ ˆํผ๋Ÿฐ์Šค์ด๊ณ , ๊ทธ ํ‘œ๋ฅผ ๊ทธ๋Œ€๋กœ ํ•œ ๋ฐฐ์—ด์— ์˜ฎ๊ฒจ ์ ์œผ๋ผ๋Š” ๋œป์ด ์•„๋‹™๋‹ˆ๋‹ค. ํŠนํžˆ rawSnapshot/rawPayload๋ฅผ debug: true ์—†์ด outputs์— ๋„ฃ์€ ํ˜ธ์ถœ์€ ํˆฌ์˜ ์ „์— -32602๋กœ ๊ฑฐ์ ˆ๋ฉ๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ ์ด ์ ˆ์˜ ์˜ˆ์‹œ๊ฐ€ ํ•œ๋•Œ ์—ฌ๋Ÿ output์„ ์ „๋ถ€ ๋‚˜์—ดํ•˜๋ฉด์„œ debug๋Š” ๋น ๋œจ๋ฆฌ๊ณ  ์žˆ์—ˆ๊ณ , ๊ทธ๋Œ€๋กœ ๋ณต์‚ฌํ•˜๋ฉด ์‹คํ–‰๋˜์ง€ ์•Š๋Š” ํ˜ธ์ถœ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

ํ•œ ๋ฒˆ์— ์–ผ๋งˆ๋‚˜ ์š”์ฒญํ•  ๊ฒƒ์ธ๊ฐ€

devup_figma_export๋Š” ์ž‘์€ ์„ ํƒ์„ ์œ„ํ•œ ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

  • ํ•œ ํ˜ธ์ถœ์— ํ”„๋ ˆ์ž„ 1~3๊ฐœ๋ฅผ ๊ถŒ์žฅํ•˜๊ณ , ํ”„๋ ˆ์ž„ 6๊ฐœยท(ํ”„๋ ˆ์ž„ ร— output) 12๋‹จ์œ„๊นŒ์ง€๋งŒ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ์ƒํ•œ์„ ๋„˜๋Š” ์„ ํƒ์€ ํ™”๋ฉด ์ˆ˜์ง‘์„ ์‹œ์ž‘ํ•˜๊ธฐ ์ „์— ๊ฑฐ์ ˆํ•˜๋ฏ€๋กœ, frameIds๋ฅผ ๋ฐฐ์น˜๋กœ ๋‚˜๋ˆ  ๋ถ€๋ฅด์„ธ์š”.
  • ๊ณ„ํš์šฉ ์–ด๋ฆผ๊ฐ’์€ ํ”„๋ ˆ์ž„๋‹น 15~60์ดˆ์ž…๋‹ˆ๋‹ค. ๋ณด์žฅ์ด ์•„๋‹ˆ๋ผ ์–ด๋ฆผ์ด๋ฉฐ, ํŽ˜์ด์ง€๋„ค์ด์…˜ยท๋ณต์žก๋„ยท์Šค๋กœํ‹€๋ง์ด ๊ฒน์น˜๋ฉด ๋„˜์–ด๊ฐ‘๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ๋Š” ๋ณดํ†ต 300์ดˆ์—์„œ ํƒ€์ž„์•„์›ƒํ•ฉ๋‹ˆ๋‹ค.
  • ํ”„๋ ˆ์ž„๋ณ„ ํˆฌ์˜์ด ์ผ๋ถ€ ์‹คํŒจํ•ด๋„ ์„ฑ๊ณตํ•œ ํ”„๋ ˆ์ž„์˜ ์‚ฐ์ถœ๋ฌผ์€ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.

์ด๋ฏธ ๋…ธ๋“œ ID๋ฅผ ์•Œ๊ณ  ์žˆ๋‹ค๋ฉด(๋ธŒ๋ฆฌํ”„์— ์ ํ˜€ ์žˆ๊ฑฐ๋‚˜ ์•ž์„  ํ˜ธ์ถœ์ด ์•Œ๋ ค์คฌ๋‹ค๋ฉด) devup_figma_explore๋ฅผ ๊ฑด๋„ˆ๋›ฐ๊ณ  frameIds์— ๋ฐ”๋กœ ๋„˜๊ธฐ์„ธ์š”. ๊ฐ–๊ณ  ์žˆ๋Š” ID๋ฅผ ๋‹ค์‹œ ์ฐพ์œผ๋ ค๊ณ  ํƒ์ƒ‰ํ•˜๋Š” ๊ฒƒ์€ Figma ํ˜ธ์ถœ ํ•˜๋‚˜๋ฅผ ๊ทธ๋ƒฅ ์“ฐ๋Š” ์ผ์ž…๋‹ˆ๋‹ค. ํƒ์ƒ‰์€ Section ๋งํฌ๋ฐ–์— ์—†์„ ๋•Œ ์“ฐ๋Š” ๋„๊ตฌ์ž…๋‹ˆ๋‹ค.

devup_figma_export๋Š” ๋™์ผํ•œ node/resource acquisition์—์„œ ์—ฌ๋Ÿฌ projection์„ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ์‘๋‹ต์˜ cache.artifactId๋ฅผ ๋‹ค์Œ ์š”์ฒญ์˜ artifactId๋กœ ๋„˜๊ธฐ๋ฉด Figma๋ฅผ ๋‹ค์‹œ ํ˜ธ์ถœํ•˜์ง€ ์•Š๊ณ  ๋‹ค๋ฅธ output์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. artifactId๋Š” urlยทrefresh์™€ ๋™์‹œ์— ์“ธ ์ˆ˜ ์—†๊ณ , ํ•จ๊ป˜ ๋ณด๋‚ด๋ฉด DEVUP_FIGMA_HANDOFF_INVALID๋กœ ๊ฑฐ์ ˆ๋ฉ๋‹ˆ๋‹ค. URL ์š”์ฒญ์€ ๊ฐ™์€ process ์•ˆ์—์„œ 10๋ถ„ TTL, ์ตœ๋Œ€ 8๊ฐœยท์ „์ฒด 128 MiB์ธ memory-only LRU cache๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๋ฉฐ, refresh: true๋Š” ์™„๋ฃŒ cache๋ฟ ์•„๋‹ˆ๋ผ ์ง„ํ–‰ ์ค‘ ์š”์ฒญ ๊ณต์œ ๋„ ์šฐํšŒํ•ด URL์„ ์ƒˆ๋กœ ์ˆ˜์ง‘ํ•ฉ๋‹ˆ๋‹ค. ์ˆ˜์ง‘ ํ•˜๋‚˜๊ฐ€ ๊ทธ resource ์ถœ๋ ฅ๊ณผ ํ•จ๊ป˜ 128 MiB๋ฅผ ๋‹ค ์“ธ ์ˆ˜ ์žˆ๊ณ , ๊ทธ๋งŒํผ ์˜ค๋ž˜๋œ ํ•ญ๋ชฉ์„ ๋ฐ€์–ด๋ƒ…๋‹ˆ๋‹ค. ์˜ˆ์ „์˜ ํ•ญ๋ชฉ๋‹น 32 MiB๋Š” ํ…Œ์ŠคํŠธ ํŒŒ์ผ ํ•˜๋‚˜์˜ scope: "file" ์ˆ˜์ง‘(63.6 MB)์„ ๊ฑฐ์ ˆํ•ด export๋ฅผ ์‹คํŒจ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค. ๋™์ผ acquisition์˜ ์„ ํ–‰ ์ž‘์—…์ด ์ทจ์†Œ๋˜๋”๋ผ๋„ ๋‹ซํžŒ in-flight ํ‘œ์‹์„ ๋‹ค์Œ ์š”์ฒญ์ด ์›์ž์ ์œผ๋กœ ์ œ๊ฑฐํ•˜๊ณ  ๋‹ค์‹œ ์ˆ˜์ง‘ํ•˜๋ฏ€๋กœ ๊ฐ™์€ key๊ฐ€ process ์ˆ˜๋ช… ๋™์•ˆ ์˜ค์—ผ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. cache์—๋Š” reuseKind, ageSeconds, remainingTtlSeconds, avoidedFigmaToolCalls, ์› ์ˆ˜์ง‘์˜ originCollection์ด ํฌํ•จ๋˜๊ณ , ์‘๋‹ต ์ตœ์ƒ์œ„ collection์€ ํ˜„์žฌ ์š”์ฒญ์ด ์‹ค์ œ๋กœ ์‹คํ–‰ํ•œ ํ˜ธ์ถœ๋งŒ ์ง‘๊ณ„ํ•ฉ๋‹ˆ๋‹ค. cache.capabilities๋Š” artifact์˜ kind(design, theme-only, search, explore, section-index), collectionScope, resourceScope, referencePng ๋ณด์œ  ์—ฌ๋ถ€, redacted assetCaptureCount์™€ sectionSelection๋งŒ ๊ณต๊ฐœํ•ฉ๋‹ˆ๋‹ค. sectionSelection์ด ๋งˆ์ง€๋ง‰์œผ๋กœ ๊ฒ€์ฆ๋œ Section ์„ ํƒ(frameIds ๋˜๋Š” allScreens)์„ ๋ณด์กดํ•˜๋ฏ€๋กœ, artifact๋ฅผ ์žฌ์‚ฌ์šฉํ•  ๋•Œ frameIds๋ฅผ ๋‹ค์‹œ ๋ณด๋‚ด์ง€ ์•Š์•„๋„ ์„ ํƒ์ด ์œ ์ง€๋ฉ๋‹ˆ๋‹ค. ์˜ˆ์ „์—๋Š” artifactId๋งŒ ๋„˜๊ธฐ๋ฉด ์„ ํƒ์ด ์‚ฌ๋ผ์ ธ ํ›„๋ณด ๋ชฉ๋ก ์ „์ฒด๊ฐ€ ๋‹ค์‹œ ๋Œ์•„์™”์Šต๋‹ˆ๋‹ค. ๋‚ด๋ถ€ artifact๋Š” asset IDยทformatยทscale ์ „์ฒด๋ฅผ ๋ณด์กดํ•˜๊ณ  ์„ธ ๊ฐ’์ด ์ •ํ™•ํžˆ ๊ฐ™์€ capture๋งŒ ์ถ”๊ฐ€ Figma ํ˜ธ์ถœ ์—†์ด ์žฌ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์žฌ์‚ฌ์šฉ ์š”์ฒญ์ด ์ด ๋ฒ”์œ„๋ฅผ ๋„˜์œผ๋ฉด DEVUP_FIGMA_HANDOFF_INVALID๋กœ ํˆฌ์˜๊ณผ ํŒŒ์ผ ๊ธฐ๋ก ์ „์— ๊ฑฐ์ ˆํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด node/used-resource artifact๋กœ file ์ „์ฒด devupJson์„ ๋งŒ๋“ค๊ฑฐ๋‚˜ screenshot์„ ์ˆ˜์ง‘ํ•˜์ง€ ์•Š์€ artifact๋กœ referencePng๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. credential, screenshot๊ณผ asset binary๋Š” cache key๋‚˜ ํ†ต๊ณ„์— ํฌํ•จํ•˜์ง€ ์•Š๊ณ , process๊ฐ€ ๋๋‚˜๋ฉด cache๋„ ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค.

delivery๋Š” auto | inline | resource์ž…๋‹ˆ๋‹ค. auto๋Š” JSON escape, base64์™€ structured/text ์ด์ค‘ ํ‘œํ˜„์„ ํฌํ•จํ•œ ์‹ค์ œ MCP wire ํฌ๊ธฐ๋ฅผ ๊ณ„์‚ฐํ•ด ๊ฐœ๋ณ„ 256 KiBยทํ•ฉ๊ณ„ 1 MiB ์ดํ•˜๋งŒ inline์œผ๋กœ ๋ฐ˜ํ™˜ํ•˜๊ณ , ๊ทธ๋ณด๋‹ค ํฐ ๊ฒฐ๊ณผ๋Š” native MCP ResourceLink์™€ devup://artifact/... URI๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค. ๋งํฌ URI๋Š” JSON manifest๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋ฏ€๋กœ link MIME์€ application/json์ด๊ณ  payload MIMEยท๊ธธ์ดยทSHA-256์€ payload* metadata๋กœ ๋ถ„๋ฆฌํ•ฉ๋‹ˆ๋‹ค. manifest๋Š” chunkUris๋กœ ๋‚ด์šฉ ์ฒญํฌ๋ฅผ ์ง์ ‘ ๊ฐ€๋ฆฌํ‚ต๋‹ˆ๋‹ค โ€” ๋งํฌ๋งŒ ๋”ฐ๋ผ๊ฐ€๋ฉด manifest์—์„œ ๋ง‰ํžˆ๊ณ  ์ฒญํฌ URI ํ˜•์‹์„ ๋”ฐ๋กœ ์•Œ์•„๋‚ด ์†์œผ๋กœ ์กฐ๋ฆฝํ•ด์•ผ ํ–ˆ๋˜ ๋ฌธ์ œ๊ฐ€ ์—†์–ด์กŒ์Šต๋‹ˆ๋‹ค. ๋‘ URI ํ˜•์‹์€ resources/templates/list์—๋„ ๊ทธ๋Œ€๋กœ ๊ฒŒ์‹œ๋ฉ๋‹ˆ๋‹ค.

devup://artifact/{artifactId}/outputs/{outputId}/manifest
devup://artifact/{artifactId}/outputs/{outputId}/chunks/{index}
``` `resource`๋Š” ํฌ๊ธฐ์™€ ๋ฌด๊ด€ํ•˜๊ฒŒ TSX/JSON/PNG๋ฅผ bounded chunk resource๋กœ ์ œ๊ณตํ•˜๋ฉฐ, binary chunk๋Š” base64 MCP blob์ž…๋‹ˆ๋‹ค. asset manifest๋Š” binary๋ฅผ ๋‚ด์žฅํ•˜์ง€ ์•Š๊ณ  ๊ฐ asset์˜ ๋…๋ฆฝ resource URIยทMIMEยท๊ธธ์ดยทSHA-256์„ ์ฐธ์กฐํ•˜๋ฏ€๋กœ `resources/read`๋กœ ์›๋ณธ bytes๋ฅผ ์ •ํ™•ํžˆ ์žฌ๊ตฌ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ™์€ artifact์™€ ์ •๊ทœํ™”ํ•œ projection์€ content hash๊ฐ€ ๊ฐ™์€ resource๋ฅผ ์žฌ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ ์ถœ๋ ฅ๊ณผ ์ƒˆ resource publication์„ ํ•จ๊ป˜ ์š”์ฒญํ•˜๋ฉด resource ์กฐํšŒ๋ฅผ reservation ๋™์•ˆ ์ฐจ๋‹จํ•œ ํ•˜๋‚˜์˜ transaction์œผ๋กœ ๋‹ค๋ฃจ๋ฉฐ, ํŒŒ์ผ commit์ด ์ „๋ถ€ ์„ฑ๊ณตํ•œ ๋’ค์—๋งŒ resource์™€ LRU ๋ณ€๊ฒฝ์„ ๊ณต๊ฐœํ•ฉ๋‹ˆ๋‹ค. ์‹คํŒจํ•˜๋ฉด ์›๋ž˜ ํŒŒ์ผ์„ fingerprint๋กœ ๊ฒ€์ฆํ•ด ๋ณต์›ํ•˜๊ณ  ๋ณต์› ๋ถˆ๋Šฅ backup ๊ฒฝ๋กœ๋ฅผ ๊ตฌ์กฐํ™”ํ•ด ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ transaction์ด ๋งŒ๋“  temp๋Š” ์ •์ƒ ์ข…๋ฃŒยทrollback์—์„œ ์ง์ ‘ ์ œ๊ฑฐํ•˜์ง€๋งŒ, ์†Œ์œ ๊ถŒ์„ ์ฆ๋ช…ํ•  ์ˆ˜ ์—†๋Š” pre-existing temp๋‚˜ crashยทrollback recovery backup์€ ์ž๋™ ์‚ญ์ œํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

`referencePng`๋Š” ์„ ํƒํ–ˆ์„ ๋•Œ๋งŒ ์ฝ๊ธฐ๋ฅผ ์ •ํ™•ํžˆ ํ•œ ๋ฒˆ ๋” ํ•ฉ๋‹ˆ๋‹ค. ๋ธŒ๋ฆฌ์ง€๊ฐ€ ๋งก์€ ํŒŒ์ผ์ด๋ฉด ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋…ธ๋“œ๋ฅผ PNG๋กœ exportํ•˜๊ณ (์œ„ "๋ธŒ๋ฆฌ์ง€ ํ”Œ๋Ÿฌ๊ทธ์ธ"), ์•„๋‹ˆ๋ฉด ๊ณต์‹ read-only `get_screenshot`์„ ๋ถ€๋ฆ…๋‹ˆ๋‹ค. ๋‘ ๊ฒฝ๋กœ์˜ PNG๋Š” ํฌ๊ธฐ๋Š” ๊ฐ™์•„๋„ ํ”ฝ์…€ ๊ฐ’์ด ์กฐ๊ธˆ์”ฉ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ๋ฐ์Šคํฌํ†ฑ ์•ฑ๊ณผ Figma ์„œ๋ฒ„์˜ ๋ Œ๋”๋Ÿฌ๊ฐ€ ๋‹ฌ๋ผ์„œ์ž…๋‹ˆ๋‹ค. ๊ฐ™์€ ํŒŒ์ผ์—์„œ `get_screenshot`๊ณผ ๋น„๊ตํ•ด ์žฐ ๊ฐ’์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

- **๋‹ค๋ฅธ ๋ ˆ์ด์–ด๊ฐ€ ๋‹ฟ์•„ devup-mcp๊ฐ€ ๊ทธ๋ฆฐ ๋…ธ๋“œ 6๊ฐœ:** ํ”ฝ์…€์˜ 99.65~99.97%๊ฐ€ 2/255 ์•ˆ์ด๊ณ  ์ตœ๋Œ€ 15/255์ž…๋‹ˆ๋‹ค. 6๊ฐœ๋Š” ๊ทธ๋ผ๋””์–ธํŠธ ์œ„, ๋ธ”๋Ÿฌยท๊ทธ๋ฆผ์žยท๋ถˆํˆฌ๋ช…๋„๊ฐ€ ๊ฑธ๋ฆฐ ์กฐ์ƒ ์•„๋ž˜, ๋ฐฐ๊ฒฝ ๋ธ”๋Ÿฌ๋ฅผ ๊ฐ€์ง„ ๋…ธ๋“œ, ํšŒ์ „ํ•œ ๋…ธ๋“œ์˜€์Šต๋‹ˆ๋‹ค. ์˜ˆ์™ธ๋Š” ๋ธ”๋Ÿฌ๊ฐ€ ๊ฑธ๋ฆฐ ์กฐ์ƒ์„ ์„น์…˜์ด ์ž๋ฅด๋Š” ๊ฐ€์žฅ์ž๋ฆฌ ํ•œ ์ค„๋กœ, 84/255์ด๊ณ  ๊ทธ ๋…ธ๋“œ ํ”ฝ์…€์˜ 0.19%์ž…๋‹ˆ๋‹ค.
- **ํ•˜๋„ค์Šค ํ™”๋ฉด 11๊ฐœ(360~1920 ํญ, ๋†’์ด 7240๊นŒ์ง€):** 99.98% ์ด์ƒ์ด 2/255 ์•ˆ์ด๊ณ  ์ตœ๋Œ€ 21/255์ž…๋‹ˆ๋‹ค. ์‚ฌ์ง„ ๊ทธ๋ฆฌ๋“œ๋งŒ 90.5%, ์ตœ๋Œ€ 37/255(16/255 ์ดˆ๊ณผ 0.07%)์ž…๋‹ˆ๋‹ค.
- **์บ”๋ฒ„์Šค ๊ทธ๋Œ€๋กœ์ธ ๋…ธ๋“œ 12๊ฐœ:** 99.09% ์ด์ƒ์ด 2/255 ์•ˆ์ž…๋‹ˆ๋‹ค. ์ž‘์€ ๋ธ”๋Ÿฌ ๊ธ€์ž๊ฐ€ ์žˆ๋Š” ํ•œ ๋…ธ๋“œ๋งŒ 94.45%, ์ตœ๋Œ€ 67/255์ธ๋ฐ, ์ด๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ export ์ž์ฒด๊ฐ€ ๊ทธ๋ ‡์Šต๋‹ˆ๋‹ค.

๋ Œ๋” ํ•˜๋„ค์Šค(`--channel-tolerance 24`)์˜ ์ ์ˆ˜๋Š” reference๋ฅผ ์–ด๋А ๊ฒฝ๋กœ์—์„œ ๋ฐ›์•„๋„ ํ™”๋ฉด๋งˆ๋‹ค 0.02%p ์•ˆ์—์„œ ๊ฐ™์•˜์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜๋„ ํ•œ ๊ธฐ์ค€์„ ์˜ reference๋Š” ํ•œ ๊ฒฝ๋กœ์—์„œ ๋ฐ›์œผ์„ธ์š” โ€” `source.bridgeReads`๊ฐ€ `collection.figmaToolCalls`์™€ ๊ฐ™์œผ๋ฉด PNG๋„ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๊ทธ๋ฆฐ ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ณต์‹ ๋„๊ตฌ๋Š” ๊ธฐ๋ณธ์œผ๋กœ PNG์˜ URL๊ณผ curl ์•ˆ๋‚ด๋ฅผ text๋กœ๋งŒ ๋Œ๋ ค์ฃผ๊ณ  ๊ธด ๋ณ€์„ 1024px๋กœ ์ค„์ด๋ฏ€๋กœ, `enableBase64Response: true`์™€ `maxDimension: 8192`๋กœ ํ˜ธ์ถœํ•ด node ์›๋ž˜ ํฌ๊ธฐ์˜ PNG๋ฅผ inline์œผ๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ์˜ image block์€ ์ •ํ™•ํžˆ ํ•˜๋‚˜์—ฌ์•ผ ํ•˜๋ฉฐ(๊ณ์˜ text block์€ ์ฝ์ง€ ์•Š์Œ), JSON/text์— ์ˆจ๊ธด image๋‚˜ ๋‹ค์ค‘ image๋Š” ๊ฑฐ์ ˆํ•ฉ๋‹ˆ๋‹ค. 16 MiB compressed, 8192px, 64 MiB decoded ์ƒํ•œ ์•ˆ์—์„œ PNG ์ „์ฒด๋ฅผ ์‹ค์ œ decodeํ•œ ๋’ค byte length์™€ SHA-256์„ ํ™•์ธํ•ด artifact์— ๋ณด์กดํ•˜๋ฉฐ, ๋‹จ์ผ ๋งํฌ node์—๋งŒ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ `referencePng`๋ฅผ `frameIds` ๋˜๋Š” `allScreens: true`์™€ ํ•จ๊ป˜ ์š”์ฒญํ•˜๋ฉด ์ˆ˜์ง‘ ์ „์— ๊ฑฐ์ ˆ๋˜๊ณ , Section์˜ ์—ฌ๋Ÿฌ Frame์€ ๋จผ์ € ๋ฐ˜ํ™˜๋œ canonical URL๋ณ„๋กœ ํ•˜๋‚˜์”ฉ ์ˆ˜์ง‘ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. PNG bytes๋Š” logยทํ†ต๊ณ„ยทcache key์— ํฌํ•จ๋˜์ง€ ์•Š์œผ๋ฉฐ `outputPaths.referencePng`๋ฅผ ๋ช…์‹œํ•˜์ง€ ์•Š์œผ๋ฉด ๋””์Šคํฌ์— ๊ธฐ๋กํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋ชจ๋“  ์™„๋ฃŒ ์‘๋‹ต์—๋Š” ๋‹ค์Œ์ฒ˜๋Ÿผ ์š”์ฒญํ•œ ์‚ฐ์ถœ๋ฌผ๋ณ„ `quality`๊ฐ€ ํฌํ•จ๋ฉ๋‹ˆ๋‹ค.

```json
{
  "status": "complete",
  "quality": {
    "acquisition": "complete",
    "projection": "exact",
    "theme": "not-requested",
    "assets": "not-requested"
  }
}

acquisition์€ complete | expected-projection | partial | failed, projection์€ exact | approximated | lossy | failed | not-requested, theme์€ complete | conflicted | unresolved | not-requested, assets๋Š” complete | partial | failed | not-requested์ž…๋‹ˆ๋‹ค. ๊ฒ€์ƒ‰ยทํƒ์ƒ‰์˜ ์˜๋„์ ์ธ ์–•์€ graph๋Š” expected-projection์œผ๋กœ ์ •์ƒ ์™„๋ฃŒํ•˜์ง€๋งŒ, ํฌํ•จ๋œ field์˜ ์‹คํŒจ๋‚˜ truncation์€ partial์ž…๋‹ˆ๋‹ค. mask/effect fallback์€ lossy, absolute layout fallback์€ approximated์ด๋ฉฐ includeDiagnostics: false์—ฌ๋„ ํ’ˆ์งˆ ํŒ์ •์—๋Š” ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค. ์ตœ์ƒ์œ„ status๋Š” complete | partial | failed | selection_required ์ค‘ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ์š”์ฒญํ•œ ๋ชจ๋“  ์ถ•์ด ์ •ํ™•ยท์™„์ „ํ•˜๊ณ  failures[]๊ฐ€ ๋น„์–ด ์žˆ์„ ๋•Œ๋งŒ complete์ž…๋‹ˆ๋‹ค. ์ถ• ํ•˜๋‚˜๋ผ๋„ ์–ด๊ธ‹๋‚˜๊ฑฐ๋‚˜ ์‹คํŒจ๊ฐ€ ํ•˜๋‚˜๋ผ๋„ ์ง‘๊ณ„๋˜๋ฉด partial์ด๊ณ , ์ˆ˜์ง‘์ด๋‚˜ ํˆฌ์˜ ์ž์ฒด๊ฐ€ ์ฃฝ์œผ๋ฉด failed, Section ๋งํฌ์—์„œ ์•„์ง ํ™”๋ฉด์„ ๊ณ ๋ฅด์ง€ ์•Š์•˜์œผ๋ฉด selection_required์ž…๋‹ˆ๋‹ค.

failures[]์—๋Š” ๋…ธ๋“œ ๋‹จ์œ„ ํˆฌ์˜ ์‹คํŒจ, ์ž์‚ฐ export ์‹คํŒจ, Section์˜ ํ”„๋ ˆ์ž„ ๋‹จ์œ„ ์‹คํŒจ๊ฐ€ ๋ชจ๋‘ ํ•จ๊ป˜ ์ง‘๊ณ„๋ฉ๋‹ˆ๋‹ค. ์˜ˆ์ „์ฒ˜๋Ÿผ ์ตœ์ƒ์œ„๊ฐ€ complete์ธ๋ฐ ์•ˆ์—์„œ ์ž์‚ฐ์ด ์‹คํŒจํ•ด ์žˆ๋Š” ์ƒํƒœ๋Š” ๋‚˜์˜ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

strict: true๋Š” ๋ชจ๋“  ์š”์ฒญ ์ถ•์ด exact/complete๊ฐ€ ์•„๋‹ˆ๋ฉด quality์™€ completenessReport๋ฅผ ๋‹ด์€ ์˜ค๋ฅ˜๋กœ ๊ฑฐ์ ˆํ•ฉ๋‹ˆ๋‹ค.

๋ชจ๋“  ๊ณต๊ฐœ TSX generator๋Š” ๋ฐ˜ํ™˜ ์ „์— Rust์˜ ๊ณ ์ •๋œ TypeScript+JSX parser๋ฅผ ํ†ต๊ณผํ•ฉ๋‹ˆ๋‹ค. parser ์˜ค๋ฅ˜๋Š” ๋””์ž์ธ ์›๋ฌธ์„ ๋…ธ์ถœํ•˜์ง€ ์•Š๊ณ  byte range์™€ ์˜ค๋ฅ˜ category๋งŒ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์‘๋‹ต์˜ fidelity๋Š” ์ƒ์„ฑ๋œ mapping ์ˆ˜๊ฐ€ ์•„๋‹ˆ๋ผ ์ˆ˜์ง‘ํ•œ source snapshot์—์„œ ๋…๋ฆฝ์ ์œผ๋กœ ๊ณ„์‚ฐํ•œ node/text segment/variable/style/asset/layout ๊ธฐ๋Œ€ ์ง‘ํ•ฉ์„ ๋ถ„๋ชจ๋กœ ์‚ฌ์šฉํ•˜๊ณ , ๋‚ด๋ถ€ validator๊ฐ€ ๊ฐ ํ•ญ๋ชฉ์„ emitted | flattened | ignored ์ค‘ ์ •ํ™•ํžˆ ํ•˜๋‚˜๋กœ ๊ฒ€์ฆํ–ˆ๋Š”์ง€์™€ ์ถ•๋ณ„ coverageยทtyped impact count๋ฅผ ๋‹ด์Šต๋‹ˆ๋‹ค. component set, non-default variant selector์™€ inline instance๋„ ์ตœ์ข… ๋ณ€ํ™˜ ํ›„ source identity๋ณ„ provenance๋ฅผ ๋‹ค์‹œ ๋งŒ๋“ค๋ฉฐ, ๋ฐ˜๋ณต๋œ ๋™์ผ text segment๋Š” ํ•˜๋‚˜์˜ mapping์„ ์žฌ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  occurrence๋ณ„๋กœ ์†Œ๋น„ํ•˜๊ณ  multilineยท์ค‘์ฒฉ text์™€ asset identity๋ฅผ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค. ์•Œ ์ˆ˜ ์—†๋Š” codegen warning/error๋„ ๊ฐ๊ฐ ์ตœ์†Œ approximated/failed๋กœ ๋ณด์ˆ˜์ ์œผ๋กœ ํŒ์ •ํ•˜๋ฉฐ, strict๋Š” syntax, source-derived trace coverage, lossy/failed impact๋ฅผ ํ•จ๊ป˜ ๊ฒ€์‚ฌํ•ฉ๋‹ˆ๋‹ค.

๋ธŒ๋ผ์šฐ์ € ์‹œ๊ฐ ํšŒ๊ท€๋Š” MCP ์„œ๋ฒ„๊ฐ€ ์ž„์˜ ๋ช…๋ น์„ ์‹คํ–‰ํ•˜์ง€ ์•Š๊ณ  ์†Œ๋น„์ž repository๊ฐ€ ์‹ค์ œ font/asset/DevupUI ํ™˜๊ฒฝ์œผ๋กœ actual.png๋ฅผ ๋งŒ๋“  ๋’ค ์ˆœ์ˆ˜ Rust devup-mcp-visual๋กœ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค.

๋ Œ๋”๋ง ํ•˜๋„ค์Šค โ€” ์ƒ์„ฑ ์ฝ”๋“œ๋ฅผ Figma๊ฐ€ ๊ทธ๋ฆฐ PNG์™€ ๋น„๊ต

์ƒ์„ฑ ์ฝ”๋“œ๋ฅผ ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ๋‹ต์•ˆ๊ณผ ์ค„ ๋‹จ์œ„๋กœ ๋งž์ถฐ๋ณด๋ฉด ๋‘˜์ด ์ผ์น˜ํ•œ๋‹ค๋Š” ๊ฒƒ๊นŒ์ง€๋Š” ์•Œ ์ˆ˜ ์žˆ์ง€๋งŒ, ๋‘˜ ์ค‘ ์–ด๋А ์ชฝ๋„ Figma๊ฐ€ ๊ทธ๋ฆฌ๋Š” ๊ทธ๋ฆผ๊ณผ ๊ฐ™์€์ง€๋Š” ๋งํ•ด์ฃผ์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค. harness/render๋Š” ๊ทธ ์งˆ๋ฌธ์— ๋‹ตํ•ฉ๋‹ˆ๋‹ค โ€” ๊ฐ ํ™”๋ฉด์„ devup-ui๋กœ ๋นŒ๋“œํ•ด ํ”„๋ ˆ์ž„ ํฌ๊ธฐ ๊ทธ๋Œ€๋กœ ์—ด๊ณ , Figma๊ฐ€ ๊ฐ™์€ ํ”„๋ ˆ์ž„์„ ๋ Œ๋”ํ•œ PNG์™€ ํ”ฝ์…€ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค.

cd harness/render && npm install
python scripts/acquire.py            # ๋ชจ๋“ˆยทํ…Œ๋งˆยท์—์…‹ยท๊ธฐ์ค€ PNG๋ฅผ ์‹คํ–‰ ์ค‘์ธ devup-mcp์—์„œ ๊ฐ€์ ธ์˜ด
node scripts/render.mjs              # ๋นŒ๋“œยท์บก์ฒ˜ยท๋น„๊ต, ํ™”๋ฉด๋ณ„ ์ž„๊ณ„๊ฐ’ ์ดˆ๊ณผ ์‹œ exit 1

acquire.py๋Š” Figma ํ˜ธ์ถœ์„ fixtures/local-call-bank์— ์ ๋ฆฝํ•˜๋ฏ€๋กœ ์žฌ์‹คํ–‰์€ ์ด๋ฏธ ์ง€๋ถˆํ•œ ๋งŒํผ ๋ฌด๋ฃŒ์ž…๋‹ˆ๋‹ค. ํ™”๋ฉด๋งˆ๋‹ค ์ž๊ธฐ ํ…Œ๋งˆ๋ฅผ node scope๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค โ€” ํŒŒ์ผ ํ•˜๋‚˜์— ์—ฌ๋Ÿฌ ๋ธŒ๋žœ๋“œ ์ปฌ๋ ‰์…˜์ด ์„ž์ด๋ฉด primary ๊ฐ™์€ ํ† ํฐ์ด ์„œ๋กœ ๋ฎ์–ด์จ์„œ, ๊ณต์ง€ ํ™”๋ฉด์ด Figma๊ฐ€ ํŒŒ๋ž‘์œผ๋กœ ๊ทธ๋ฆฌ๋Š” ์ž๋ฆฌ๋ฅผ ๋ณด๋ผ์ƒ‰์œผ๋กœ ๊ทธ๋ ธ์Šต๋‹ˆ๋‹ค. devup-ui๋Š” ํ…Œ๋งˆ๋ฅผ ๋นŒ๋“œ ์‹œ์ ์— ๊ตฝ๊ธฐ ๋•Œ๋ฌธ์— ํ™”๋ฉด๋“ค์€ ํ•„์š”ํ•œ ํ…Œ๋งˆ๋ณ„๋กœ ๋ฌถ์—ฌ ๊ทธ๋ฃน๋งˆ๋‹ค ํ•œ ๋ฒˆ์”ฉ ๋นŒ๋“œ๋ฉ๋‹ˆ๋‹ค. ๋ฆฌ์…‹์€ ์ƒ์„ฑ ์ฝ”๋“œ๊ฐ€ ์ „์ œํ•˜๋Š” @devup-ui/reset-css ๊ทธ๋Œ€๋กœ์ž…๋‹ˆ๋‹ค.

thresholds.json์ด ํ™”๋ฉด๋ณ„๋กœ Figma์™€ ๋ฒŒ์–ด์ ธ๋„ ๋˜๋Š” ์ตœ๋Œ€์น˜๋ฅผ ๋“ค๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ดˆ๊ณผํ•˜๋ฉด ์‹คํŒจํ•˜๊ณ , ๋ฐ‘๋Œ๋ฉด ๊ทธ๋ ‡๋‹ค๊ณ  ์•Œ๋ ค์ค๋‹ˆ๋‹ค(= ์ˆ˜์น˜๋ฅผ ์กฐ์ผ ์ฐจ๋ก€). ์ธก์ •๊ฐ’:

ํ™”๋ฉด 1920 992 / 768 360 / 390
popup 0.83% 2.19% 3.59%
popup (ํ”Œ๋Ÿฌ๊ทธ์ธ ๋‹ต์•ˆ) 21.06% 7.14% 12.02%
notice 2.33% 4.19% 8.29%
about 4.54% 6.94% 11.26%
report 1.87% ยท grid 2.96% ยท keyframes 6.71%

์ฐจ์ด๊ฐ€ ์–ด๋”” ์žˆ๋Š”์ง€๋Š” ๋ณด์กฐ ๋„๊ตฌ๊ฐ€ ๋‹ตํ•ฉ๋‹ˆ๋‹ค โ€” bands.mjs(๊ฐ€์žฅ ๋งŽ์ด ์–ด๊ธ‹๋‚œ ๊ตฌ๊ฐ„), drift.mjs(๋‹จ์ˆœ ์ด๋™์ธ์ง€ ์‹ค์ œ ์ฐจ์ด์ธ์ง€), crop.mjs(๊ตฌ๊ฐ„์„ ๊ธฐ์ค€/์บก์ฒ˜ ๋‚˜๋ž€ํžˆ), boxes.mjs(DOM ์ƒ์ž๋ฅผ Figma ์ขŒํ‘œ์™€ ๋Œ€์กฐ), elements.mjs(๊ทธ๋ฆผ์ด ์‹ค์ œ๋กœ ๋ช‡ ํ”ฝ์…€๋กœ ๋‚˜์™”๋Š”์ง€). ๊ธด ํ™”๋ฉด์„ ํ†ต์งธ๋กœ ์ค„์ธ ์Šคํฌ๋ฆฐ์ƒท์€ ์•„๋ฌด๊ฒƒ๋„ ๋ณด์—ฌ์ฃผ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

text-check.mjs๋Š” ํ”ฝ์…€์ด ์•„๋‹ˆ๋ผ ๊ธ€์ž๋ฅผ ๋ด…๋‹ˆ๋‹ค. ์ƒ์„ฑ๊ธฐ๋Š” ํ…์ŠคํŠธ ๋…ธ๋“œ์˜ characters๋ฅผ JSX์— ์“ฐ๋Š”๋ฐ, JSX๋Š” ๊ณต๋ฐฑ์— ์ž๊ธฐ ๊ทœ์น™์ด ์žˆ์Šต๋‹ˆ๋‹ค โ€” ํ•œ ๋ฌธ์žฅ์ด ์†Œ์Šค ๋‘ ์ค„๋กœ ๋‚˜๋‰˜๋ฉด ์‚ฌ์ด์— ๊ณต๋ฐฑ ํ•˜๋‚˜๊ฐ€ ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค. ๋””์ž์ธ์— ๊ทธ ๊ณต๋ฐฑ์ด ์—†์œผ๋ฉด ํ™”๋ฉด์€ ๋””์ž์ธ์— ์—†๋Š” ๋‹จ์–ด๋ฅผ ์ฐ๊ณ , ๋ฌธ๋‹จ์€ Figma๊ฐ€ ๋Š์ง€ ์•Š๋Š” ์ž๋ฆฌ์—์„œ ๊ฐ๊น๋‹ˆ๋‹ค. JSX๋ฅผ ์ฝ์–ด ๋ฌด์—‡์ด ๊ทธ๋ ค์งˆ์ง€ ์ถ”๋ก ํ•˜๋Š” ๊ฑด ๊ทธ ๊ทœ์น™์„ ๋‹ค์‹œ ๊ตฌํ˜„ํ•˜๋Š” ์ผ์ด๊ณ , ๊ทธ๋ ‡๊ฒŒ ๋„˜๊ฒจ์งš์œผ๋ฉด ์—†๋Š” ๊ฒฐํ•จ์„ ๋งŒ๋“ค์–ด๋ƒ…๋‹ˆ๋‹ค โ€” ๊ทธ๋ž˜์„œ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์‹ค์ œ๋กœ ์ฐ์€ ๊ธ€์ž๋ฅผ characters์™€ ๋Œ€์กฐํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ 245๊ฐœ ํ…์ŠคํŠธ ์ค‘ 3๊ฐœ(๊ฐ™์€ ๋ฌธ๋‹จ์˜ ์„ธ ํญ)๊ฐ€ ๋””์ž์ธ๋Œ€๋กœ ์ฐํžˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์บก์ฒ˜ยทํ…Œ๋งˆยท์—์…‹ยท๋นŒ๋“œ ์‚ฐ์ถœ๋ฌผ์€ ์ปค๋ฐ‹ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค(harness/render/.gitignore). ์ด ํ•˜๋„ค์Šค๊ฐ€ ์ฐพ์•„๋‚ธ ๊ฒฐํ•จ์€ ํ…Œ๋งˆ ์Šค์ฝ”ํ”„, ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์น ํ•˜๋Š” ๊ทธ๋ฆผ์˜ ๋งค๋‹ˆํŽ˜์ŠคํŠธ ๋ˆ„๋ฝ, ์ž˜๋ฆฐ fill์˜ crop ํ–‰๋ ฌ, ํŒŒ์ผ์‹œ์Šคํ…œ์ด ๋ชป ๋ฐ›๋Š” ๋ ˆ์ด์–ด ์ด๋ฆ„, ํญ๋งˆ๋‹ค ํฌ๊ธฐ๊ฐ€ ๋‹ค๋ฅธ ์‚ฌ์ง„์˜ ํŒŒ์ผ ๊ณต์œ , ํˆฌ๋ช…๋„ 0 ๋…ธ๋“œ์˜ export ๊ฑฐ๋ถ€, ๊ทธ๋ฆฌ๊ณ  positioned child ๋„ˆ๋จธ๋กœ CSS๊ฐ€ ๋ชป ๋ฏธ์น˜๋Š” ๋†’์ด์ž…๋‹ˆ๋‹ค.

Section ๋งํฌ์—์„œ TSX๋ฅผ ์š”์ฒญํ•˜๋ฉด ๋จผ์ € ๋‚ด๋ถ€ screen frame ํ›„๋ณด์™€ canonical URL์„ selection_required๋กœ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. frameIds๋กœ ๊ฒ€ํ† ํ•œ frame๋งŒ ๊ณ ๋ฅด๊ฑฐ๋‚˜ allScreens: true๋กœ ๋ชจ๋“  ํ™”๋ฉด์„ ์‹œ๊ฐ ์ˆœ์„œ๋Œ€๋กœ batch exportํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๋‘ ์˜ต์…˜์€ ๋™์‹œ์— ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. sourceMap์€ nodeIdยท์›๋ณธ propertyยทgeneratedPropertyยทresolution์œผ๋กœ ํ•„๋“œโ†’์ƒ์„ฑ ์†์„ฑ์„ ์„ค๋ช…ํ•˜๋Š” sidecar์ž…๋‹ˆ๋‹ค. TSX ๋ฌธ์ž/๋ฐ”์ดํŠธ ์˜คํ”„์…‹์€ ์ œ๊ณตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ƒ์„ฑ ์ฝ”๋“œ ๋ฐœ์ทŒ๋Š” ์ง„๋‹จ์˜ generatedSource๋ฅผ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. devup.json์€ JSON pointer๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. assetManifest๋Š” image hash/vector/export provenance๋ฅผ ํ•ญ์ƒ ์—ด๊ฑฐํ•˜๊ณ , assetRequests๋กœ ๋ช…์‹œํ•œ ํ•ญ๋ชฉ๋งŒ ๊ถŒ์žฅ 1โ€“3๊ฐœ, ์ตœ๋Œ€ 6๊ฐœยทscale 1~4 ๋ฒ”์œ„์—์„œ read-only SVG/PNG exportํ•ฉ๋‹ˆ๋‹ค. assetRequests๋ฅผ ์“ฐ๋Š” ํ˜ธ์ถœ์€ outputs์— assetManifest๊ฐ€ ํ•จ๊ป˜ ์žˆ์–ด์•ผ ํ•˜๋ฉฐ, ๋น ๋œจ๋ฆฌ๋ฉด ๊ฑฐ์ ˆ๋ฉ๋‹ˆ๋‹ค. outputPath๋ฅผ ์ง€์ •ํ•˜๋ฉด binary๋ฅผ ํ•ด๋‹น ํŒŒ์ผ๋กœ ๋””์ฝ”๋”ฉํ•˜๊ณ  ์‘๋‹ต์˜ base64๋ฅผ ์ œ๊ฑฐํ•˜๋ฉฐ, ์ƒ๋žตํ•˜๋ฉด ํ›„์† ์†Œ๋น„๋ฅผ ์œ„ํ•ด base64๊ฐ€ memory-only artifact์™€ ํ•ด๋‹น MCP ์‘๋‹ต์— ๋‚จ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ž์‚ฐ 6๊ฐœ ์ดˆ๊ณผ ์š”์ฒญ์€ ์ธ์ฆยท์ˆ˜์ง‘ยทํŒŒ์ผ ์“ฐ๊ธฐ ์ „์— ๊ฑฐ์ ˆํ•˜๋ฉฐ recommendedBatchSize, maxAssetCount, recommendedAssetRequests, remainingAssetRequests๋กœ ๋ถ„ํ• ์„ ์•ˆ๋‚ดํ•ฉ๋‹ˆ๋‹ค. ๋А๋ฆฐ ์ž์‚ฐ ํ˜ธ์ถœ์€ 1์ดˆ ๋Œ€๊ธฐ ํ›„ assetJob.jobId, ๋‹จ๊ณ„๋ณ„ ํ˜ธ์ถœ ๊ธฐ๋ก, ์ž์‚ฐ๋ณ„ ์ˆ˜์ง‘/ํŒŒ์ผ ์ƒํƒœ๋ฅผ ๋จผ์ € ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. {"jobId":"..."}๋กœ ์กฐํšŒํ•˜๊ณ , paused์ด๋ฉด {"jobId":"...","jobAction":"resume"}๋กœ ๋ฏธ์™„๋ฃŒ ํ˜ธ์ถœ๋งŒ ์žฌ๊ฐœํ•ฉ๋‹ˆ๋‹ค. ๋™์ผ ์ธ์ž๋ฅผ ๋‹ค์‹œ ๋ณด๋‚ด๋ฉด ์œ ์‹ค๋œ ์ตœ์ดˆ ์‘๋‹ต์˜ ์ž‘์—…์„ ์ฐพ์Šต๋‹ˆ๋‹ค. ํด๋ผ์ด์–ธํŠธ ํƒ€์ž„์•„์›ƒ์€ ์ž‘์—…์„ ์ทจ์†Œํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ฒดํฌํฌ์ธํŠธ๋Š” ๊ฐ™์€ ์„œ๋ฒ„ ํ”„๋กœ์„ธ์Šค์—์„œ ์ตœ๋Œ€ 30๋ถ„, ์™„๋ฃŒ ๊ฒฐ๊ณผ๋Š” 5๋ถ„ ๋ณด์กดํ•˜๋ฉฐ ์„œ๋ฒ„ ์žฌ์‹œ์ž‘ ํ›„์—๋Š” ๋ณต๊ตฌ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ฐœ๋ณ„ upstream ํ˜ธ์ถœ์€ 90์ดˆ์— ์ผ์‹œ์ •์ง€ํ•ฉ๋‹ˆ๋‹ค. ์ž์‚ฐ exported๋Š” ๊ฒ€์ฆ๋œ ๋ฐ”์ดํŠธ ์ˆ˜์ง‘์ด๋ฉฐ, fileState=written์€ ์‹ค์ œ ํŒŒ์ผ ๋ฐ”์ดํŠธ๊นŒ์ง€ ํ™•์ธํ•œ ๊ฒฐ๊ณผ์ž…๋‹ˆ๋‹ค.

assetManifest๋Š” ๋ชฉ๋ก์ผ ๋ฟ ํŒŒ์ผ์„ ์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํŒŒ์ผ์„ ์–ป์œผ๋ ค๋ฉด assetRequests๋กœ ๋‹ค์‹œ ๋ถ€๋ฅด๋˜, ๊ทธ ํ˜ธ์ถœ์€ artifactId๊ฐ€ ์•„๋‹ˆ๋ผ ์›๋ž˜ url๋กœ ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ž์‚ฐ ์บก์ฒ˜ ์—†์ด ์ˆ˜์ง‘๋œ artifact๋Š” ์ž์‚ฐ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๊ทธ ์กฐํ•ฉ์œผ๋กœ ๋ถ€๋ฅด๋ฉด ๋ฌด์—‡์ด ์—†๋Š”์ง€์™€ ์–ด๋–ป๊ฒŒ ๋ณต๊ตฌํ•˜๋Š”์ง€๋ฅผ ๋‹ด์•„ ๊ฑฐ์ ˆํ•ฉ๋‹ˆ๋‹ค:

This artifact was collected without the requested asset captures. Remove artifactId and call again with the original url and assetRequests.

๊ธฐ๋ณธ๊ฐ’์—์„œ๋Š” manifest์˜ path์™€ TSX์˜ ์ž์‚ฐ ์ฐธ์กฐ๊ฐ€ ์ž๋ฆฌํ‘œ์‹œ์ž๋กœ ๋‚จ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ๊ธฐ๋ณธ ๋™์ž‘์ด๊ณ , assetPublicRoot๋Š” ์˜ตํŠธ์ธ์ž…๋‹ˆ๋‹ค.

์‹ค์ œ ๊ฒฝ๋กœ๋กœ ๋ฐ”๊พธ๋ ค๋ฉด ๊ฐ™์€ ํ˜ธ์ถœ์—์„œ assetPublicRoot์™€ assetRequests.outputPath๋ฅผ ํ•จ๊ป˜ ์ฃผ์„ธ์š”. ๊ทœ์น™์€ ์ด๋ ‡์Šต๋‹ˆ๋‹ค.

  • assetPublicRoot๋Š” URL /๋กœ ์„œ๋น™๋˜๋Š” ์ด๋ฏธ ์กด์žฌํ•˜๋Š” ์ ˆ๋Œ€ ๋กœ์ปฌ ๋””๋ ‰ํ„ฐ๋ฆฌ์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ƒ๋Œ€ ๊ฒฝ๋กœ์ด๊ฑฐ๋‚˜ ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด ํ˜ธ์ถœ์ž ์˜ค๋ฅ˜์ž…๋‹ˆ๋‹ค.
  • ๊ฐ™์€ ํ˜ธ์ถœ์— assetRequests.outputPath๊ฐ€ ์ตœ์†Œ ํ•œ ๊ฐœ ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ €์žฅ๋œ output ๋งคํ•‘์€ artifact์— ๋ณด์กด๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ artifactId๋กœ ์žฌ์‚ฌ์šฉํ•ด๋„ ์ด์–ด์ง€์ง€ ์•Š๊ณ , ์ด ์ง€์ •์€ ํ˜ธ์ถœ๋งˆ๋‹ค ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • outputPath๊ฐ€ assetPublicRoot ๋ฐ–์ด๊ฑฐ๋‚˜ ํ—ˆ์šฉ write root๋ฅผ ๋ฒ—์–ด๋‚˜๋ฉด ๊ฑฐ์ ˆ๋ฉ๋‹ˆ๋‹ค.

์กฐ๊ฑด์ด ๋งž์œผ๋ฉด ๊ทธ ๋ฃจํŠธ ์•„๋ž˜์— ์“ฐ์ธ ํŒŒ์ผ์ด manifest์™€ ๋ชจ๋“  TSX output์—์„œ percent-encoding๋œ ์ƒ๋Œ€ URL๋กœ ๋ฐ”๋€๋‹ˆ๋‹ค.

๋‚ด๋ณด๋‚ธ ๋ฐ”์ดํŠธ๊ฐ€ ๋™์ผํ•˜๋ฉด ํ•˜๋‚˜์˜ canonical path/outputPath๋ฅผ ๊ณต์œ ํ•˜๊ณ , ๊ทธ canonical ๊ฒฝ๋กœ๋Š” ์‘๋‹ต์— ์‹ค๋ ค ๋Œ์•„์˜ค๋ฏ€๋กœ ๋ฐ˜ํ™˜๋œ ๊ฒฝ๋กœ๋ฅผ ๊ทธ๋Œ€๋กœ ํ™•์ธํ•ด ์“ฐ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

์ค‘๋ณต ์ œ๊ฑฐ์˜ ํŒ๋‹จ ๊ธฐ์ค€์€ ์ข์Šต๋‹ˆ๋‹ค.

  • ๋””์ฝ”๋“œํ•œ export ๋ฐ”์ดํŠธ์˜ SHA-256์œผ๋กœ๋งŒ ํŒ๋‹จํ•ฉ๋‹ˆ๋‹ค. ๋ ˆ์ด์–ด ์ด๋ฆ„์ด๋‚˜ ๊ฒ€์ฆ๋˜์ง€ ์•Š์€ imageHash๋กœ๋Š” ํ•ฉ์น˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ๋จผ์ € ๋ฌด๊ฒฐ์„ฑ์„ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค. ์ž์‚ฐ์ด ์‹ ๊ณ ํ•œ byteLengthยทsha256์ด ์‹ค์ œ ๋””์ฝ”๋“œ ๊ฒฐ๊ณผ์™€ ๋‹ค๋ฅด๋ฉด ํ•ฉ์น˜๊ธฐ ์ „์— ๊ฑฐ์ ˆํ•ฉ๋‹ˆ๋‹ค.
  • ๋‚ด์šฉ๊ณผ ํฌ๋งท์ด ๋ชจ๋‘ ๊ฐ™์„ ๋•Œ๋งŒ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค. ํŒ์ • ํ‚ค๋Š” (๋‚ด์šฉ ํ•ด์‹œ, ํ™•์žฅ์ž, MIME)์ด๋ผ ๊ฐ™์€ ๊ทธ๋ฆผ์˜ SVG์™€ PNG๋Š” ๊ฐ๊ฐ ๋‚จ์Šต๋‹ˆ๋‹ค.
  • manifest์˜ ๋…ธ๋“œ๋ณ„ ํ•ญ๋ชฉ์€ ๊ทธ๋Œ€๋กœ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค. ํ•ฉ์ณ์ง€๋Š” ๊ฒƒ์€ ํŒŒ์ผ ๊ฒฝ๋กœ์ด์ง€ ๋ชฉ๋ก์˜ ์ค„์ด ์•„๋‹ˆ๋ฏ€๋กœ, ์–ด๋А ๋…ธ๋“œ๊ฐ€ ์–ด๋А ์ž์‚ฐ์„ ์“ฐ๋Š”์ง€๋Š” ๊ณ„์† ๋ณด์ž…๋‹ˆ๋‹ค.

assetNamesPerNode ๊ธฐ๋ณธ๊ฐ’์€ ์—ฌ์ „ํžˆ true์ž…๋‹ˆ๋‹ค.

asset์˜ ํŒŒ์ผ ์ด๋ฆ„์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋ ˆ์ด์–ด ์ด๋ฆ„์ž…๋‹ˆ๋‹ค โ€” ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๊ทธ๋ ‡๊ฒŒ ์ง“๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋””์ž์ด๋„ˆ๊ฐ€ ๊ฐ™์€ ์ด๋ฆ„์„ ์ค€ ๋…ธ๋“œ๋“ค์€ ํŒŒ์ผ ํ•˜๋‚˜๋ฅผ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ™์€ ๊ทธ๋ฆผ์ด๋ฉด ๋งž์ง€๋งŒ ์•„๋‹ˆ๋ฉด ์†์‹ค์ž…๋‹ˆ๋‹ค. ํ•œ ํ™”๋ฉด์—์„œ ์—ฌ๋Ÿ ๋…ธ๋“œ๊ฐ€ Logo.svg ํ•˜๋‚˜๋ฅผ ์ฃผ์žฅํ•˜๋Š”๋ฐ ์‹ค์ œ๋กœ๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ๊ทธ๋ฆผ ๋‹ค์„ฏ ๊ฐœ์˜€๊ณ , ํญ๋งˆ๋‹ค ๊ทธ๋ ค์ง„ ์‚ฌ์ง„์€ ๋งˆ์ง€๋ง‰์œผ๋กœ export๋œ ํญ์˜ ํŒŒ์ผ๋งŒ ๋‚จ์•„ ๋‹ค๋ฅธ ํญ์—์„œ๋Š” ์ƒ์ž์™€ ํฌ๊ธฐ๊ฐ€ ์–ด๊ธ‹๋‚œ ์ฑ„ ๋Š˜์–ด๋‚ฉ๋‹ˆ๋‹ค(ํŒŒ์ผ์ด ์ƒ์ž์™€ ๊ฐ™์€ ํฌ๊ธฐ์ด๋ฉด object-fit์ด ๋ฌด์—‡์ด๋“  ๊ฒฐ๊ณผ๊ฐ€ ๊ฐ™์œผ๋ฏ€๋กœ, ํ”Œ๋Ÿฌ๊ทธ์ธ์—์„œ๋Š” ์ด ๋ฌธ์ œ๊ฐ€ ๋“œ๋Ÿฌ๋‚˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค).

assetNamesPerNode๋Š” ๊ฐ asset์„ ๊ทธ ๋…ธ๋“œ์˜ ์ด๋ฆ„์œผ๋กœ ์ง€์–ด(Logo-422-6921.svg, Frame 269-422-3392.png) ๋‘˜์„ ํ•จ๊ป˜ ์—†์•ฑ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ true์ž…๋‹ˆ๋‹ค โ€” ๋ Œ๋”๋งํ•ด ๋ณด๋ฉด ์ด์ชฝ์ด Figma๊ฐ€ ๊ทธ๋ฆฌ๋Š” ๊ทธ๋ฆผ์— ๊ฐ€๊น๊ณ (๊ณต์ง€ ํ™”๋ฉด 992ํญ 6.48% โ†’ 4.19%, about 992ํญ 10.79% โ†’ 6.94%), ํ”Œ๋Ÿฌ๊ทธ์ธ golden 268๊ฐœ๋Š” ๊ทธ๋Œ€๋กœ ํ†ต๊ณผํ•ฉ๋‹ˆ๋‹ค. golden์€ CodegenOptions๋ฅผ ์ง์ ‘ ์“ฐ๊ณ  ๊ทธ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๊ธฐ๋ณธ๊ฐ’์€ ์—ฌ์ „ํžˆ ํ”Œ๋Ÿฌ๊ทธ์ธ๊ณผ ๋™์ผํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ๊ณผ byte ๋‹จ์œ„๋กœ ๊ฐ™์€ ์ด๋ฆ„์ด ํ•„์š”ํ•˜๋ฉด assetNamesPerNode: false๋กœ ๋„์‹ญ์‹œ์˜ค.

๋ˆ ์ƒํƒœ์—์„œ ์„œ๋กœ ๋‹ค๋ฅธ ๊ทธ๋ฆผ์ด ํ•œ ํŒŒ์ผ์„ ๊ณ„์† ์ฃผ์žฅํ•˜๋ฉด ์ฒซ ๋ฒˆ์งธ๋งŒ ๊ธฐ๋กํ•˜๊ณ  ๋‚˜๋จธ์ง€๋Š” DEVUP_ASSET_NAME_SHARED diagnostic์œผ๋กœ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค โ€” ์กฐ์šฉํžˆ ๋ฎ์–ด์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋ ˆ์ด์–ด ์ด๋ฆ„์ด ํŒŒ์ผ์‹œ์Šคํ…œ์ด ๋ฐ›์ง€ ๋ชปํ•˜๋Š” ์ด๋ฆ„์ผ ๋•Œ(ic:round-arrow-left์ฒ˜๋Ÿผ ์ฝœ๋ก ์ด ๋“  ์ด๋ฆ„์€ Windows๊ฐ€ ๋งŒ๋“ค์ง€ ๋ชปํ•ฉ๋‹ˆ๋‹ค) ์ „๋‹ฌ ์‹œ์ ์— ์ƒ์„ฑ ์ฝ”๋“œ์™€ manifest๋ฅผ ํ•จ๊ป˜ ๊ฐœ๋ช…ํ•ด ๋‘˜์ด ์–ด๊ธ‹๋‚˜์ง€ ์•Š๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ์ƒ์„ฑ๊ธฐ ์ž์ฒด๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ ์ด๋ฆ„์„ ๊ทธ๋Œ€๋กœ ์“ฐ๋ฏ€๋กœ golden parity๋Š” ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.

๊ฐœ๋ช… ๊ทœ์น™์€ ์ด๋ ‡์Šต๋‹ˆ๋‹ค. ์˜์ˆซ์žยท-ยท_๊ฐ€ ์•„๋‹Œ ๋ฌธ์ž๋ฅผ -๋กœ ๋ฐ”๊พธ๊ณ  ์•ž๋’ค -๋ฅผ ๋–ผ๋ฉฐ, Windows ์˜ˆ์•ฝ์–ด(CON, NUL, COM1โ€ฆ)์ด๊ฑฐ๋‚˜ ๋น„ASCII๊ฐ€ ์žˆ์—ˆ๊ฑฐ๋‚˜ ์ด๋ฆ„์ด ๋ฐ”๋€Œ์—ˆ๊ฑฐ๋‚˜ 120์ž๋ฅผ ๋„˜์œผ๋ฉด ์›๋ž˜ ์ด๋ฆ„์˜ SHA-256 ์•ž 12์ž๋ฆฌ๋ฅผ ์ ‘๋ฏธ์‚ฌ๋กœ ๋ถ™์—ฌ ์„œ๋กœ ๋‹ค๋ฅธ ์ด๋ฆ„์ด ํ•œ ํŒŒ์ผ๋กœ ํ•ฉ์ณ์ง€์ง€ ์•Š๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ํ™•์žฅ์ž๋Š” ASCII ์˜์ˆซ์ž 10์ž๊นŒ์ง€๋งŒ ๋‚จ๊น๋‹ˆ๋‹ค.

/icons/ic:round-arrow-left.svg  ->  /icons/ic-round-arrow-left-b041d24e668d.svg

๊ทธ๋ž˜์„œ ๊ณต๋ฐฑ์ด๋‚˜ ํ•œ๊ธ€์ด ๋“  ๋ ˆ์ด์–ด ์ด๋ฆ„๋„ ์ฝ”๋“œ์™€ manifest ์–‘์ชฝ์—์„œ ๊ฐ™์€ ASCII ๊ฒฝ๋กœ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค.

Section ๋งํฌ๋Š” ์ „์ฒด subtree๋ฅผ ์ง์ ‘ ๋ณ€ํ™˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. selection_required.nextAction์— ๋”ฐ๋ผ ํ›„๋ณด๋ฅผ ํ™•์ธํ•œ ๋’ค frameIds ๋˜๋Š” allScreens: true๋กœ ํ™”๋ฉด๋ณ„ export๋ฅผ ๊ณ„์†ํ•˜๋ฉฐ, ์ผ๋ถ€ ํ™”๋ฉด ์ˆ˜์ง‘์ด ์‹คํŒจํ•˜๋ฉด ์„ฑ๊ณตํ•œ ํ™”๋ฉด์€ ์œ ์ง€ํ•˜๊ณ  ์‹คํŒจํ•œ node๋Š” failures์— ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค. nextAction์€ whyยทhowยทdoNot๊ณผ ํ•จ๊ป˜ ์ฒซ ํ›„๋ณด๋ฅผ ๊ณ ๋ฅด๋Š” ๊ทธ๋Œ€๋กœ ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ example ํ˜ธ์ถœ์„ ๋‹ด์Šต๋‹ˆ๋‹ค.

์—ฌ๋Ÿฌ ํ™”๋ฉด์„ ํ•œ ๋ฒˆ์— ๋ฐ›์œผ๋ฉด frames[]์˜ ๊ฐ ํ•ญ๋ชฉ์ด ์„œ๋กœ ๊ฐ™์€ ๊ตฌ์กฐ์ธ์ง€ ์•Œ๋ ค์ค๋‹ˆ๋‹ค. ์ƒ์„ฑ ๊ตฌ์กฐ๊ฐ€ ์•ž์„  ํ”„๋ ˆ์ž„๊ณผ ๊ฐ™์œผ๋ฉด ๊ทธ ํ”„๋ ˆ์ž„์— structurallyIdenticalTo(๊ฐ™์€ ๊ตฌ์กฐ์ธ ์ฒซ ํ”„๋ ˆ์ž„์˜ node ID), differsOnly(์˜ˆ: ["assetReferences"]), comparedOutputs๊ฐ€ ๋ถ™์Šต๋‹ˆ๋‹ค. ๋‘ ํ™”๋ฉด์ด ์ •๋ง ๋‹ค๋ฅธ์ง€ ํ™•์ธํ•˜๋ ค๊ณ  TSX๋ฅผ ์ง์ ‘ ๋Œ€์กฐํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.

ํ™”๋ฉด ํ›„๋ณด ํŒ์ •์€ explore, export์˜ selection, allScreens๊ฐ€ ๊ฐ™์€ ๊ทœ์น™ ํ•˜๋‚˜๋ฅผ ์”๋‹ˆ๋‹ค. ์…‹์ด ์„œ๋กœ ๋‹ค๋ฅธ "ํ™”๋ฉด" ์ •์˜๋ฅผ ๊ฐ–๋˜ ๋ฌธ์ œ๊ฐ€ ์‚ฌ๋ผ์ ธ, allScreens๊ฐ€ ๋ฐฐ๋„ˆยท์ฃผ์„ ํ”„๋ ˆ์ž„์„ ํ™”๋ฉด์œผ๋กœ ๋งŒ๋“ค์–ด๋‚ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

SECTION์€ 2๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค. 1๋‹จ๊ณ„๋กœ status: "selection_required"์™€ ์„ ํƒ ๋ชฉ๋ก์„ ๋ฐ›๊ณ , 2๋‹จ๊ณ„๋กœ nextAction.example์„ ์‹คํ–‰ํ•ด ๊ณ ๋ฅธ ํ™”๋ฉด์„ exportํ•ฉ๋‹ˆ๋‹ค. ํ•œ ๋ฒˆ์— Section ์ „์ฒด๋ฅผ ์ˆ˜์ง‘ํ•˜๋ ค ํ•˜์ง€ ๋งˆ์„ธ์š”.

SECTION ๊ธฐ๋ณธ ์‘๋‹ต์€ ํ™”๋ฉด ์•„ํ‹ฐํŒฉํŠธ๊ฐ€ ์•„๋‹Œ ์„ ํƒ ๋ชฉ๋ก์ž…๋‹ˆ๋‹ค. selection.status์™€ selection.count๋กœ ๋ชฉ๋ก ์กฐํšŒ ์ƒํƒœ์™€ ํ›„๋ณด ์ˆ˜๋ฅผ ์•Œ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ตœ์ƒ์œ„ status: "selection_required"๋Š” ์•„์ง ํ™”๋ฉด์„ ์„ ํƒํ•ด์•ผ ํ•œ๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค.

๋ชฉ๋ก์€ ๋‘ ๊ฐˆ๋ž˜๋กœ ๋‚˜๋‰ฉ๋‹ˆ๋‹ค.

  • selection.candidates[] โ€” ์ž๋™์œผ๋กœ ํ™”๋ฉด์ด๋ผ๊ณ  ํŒ์ •ํ•œ ํ›„๋ณด์ž…๋‹ˆ๋‹ค. node.name, node.nodeType, node.textPreview, canonicalUrl๋กœ ์„œ๋กœ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. allScreens: true๋Š” ๋ฉ”๋‰ด์˜ ๋ชจ๋“  ํ•ญ๋ชฉ์ด ์•„๋‹ˆ๋ผ ๋ฐ”๋กœ ์ด ์ž๋™ ํ™”๋ฉด ํ›„๋ณด๋ฅผ ํ•œ๊บผ๋ฒˆ์— ๊ณ ๋ฅด๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
  • selection.explicitCandidates[] โ€” ์ž๋™ ํŒ์ •์ด ํ™”๋ฉด์œผ๋กœ ๋ณด์ง€ ์•Š์€ ๊ฒƒ๋“ค์ž…๋‹ˆ๋‹ค. ์ž‘์€ ์‚ฌ๋ก€, ์ฃผ์„ยทํ…์ŠคํŠธ, ๊ทธ๋ฆฌ๊ณ  ํ™”๋ฉด ์น˜์ˆ˜๋กœ ์ธก์ •๋˜์ง€ ์•Š๋Š” ๊ธด ํŽ˜์ด์ง€ ๊ฐ™์€ ๊ฒƒ์ด ์—ฌ๊ธฐ ๋“ค์–ด์˜ต๋‹ˆ๋‹ค. allScreens๋Š” ์ด๋“ค์„ ๊ณ ๋ฅด์ง€ ์•Š์œผ๋ฏ€๋กœ, ํ•„์š”ํ•˜๋ฉด frameIds๋‚˜ ๊ทธ canonicalUrl๋กœ ๋ช…์‹œ์ ์œผ๋กœ ์„ ํƒํ•˜์„ธ์š”.

๊ธด ํŽ˜์ด์ง€๋Š” ๋†’์ดยท๋น„์œจ ์–ด๋А ์ชฝ์œผ๋กœ๋„ ํ™”๋ฉด์œผ๋กœ ์ธก์ •๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๋‹ค๊ณ  ๊ทธ ์•ˆ์„ ๋œฏ์–ด ์กฐ๊ฐ๋“ค๋งŒ ํ›„๋ณด๋กœ ๋‚ด๋†“์œผ๋ฉด ์ •์ž‘ ํŽ˜์ด์ง€ ์ž์ฒด๋Š” ๋ชฉ๋ก์— ์—†๊ฒŒ ๋˜๋ฏ€๋กœ, ํŽ˜์ด์ง€๋Š” explicitCandidates์— ํ†ต์งธ๋กœ ๋‚จ๊ธฐ๊ณ  ๊ทธ ์•ˆ์— ์žˆ๋Š” ์ž๋™ ํ™”๋ฉด ํ›„๋ณด๋„ ํ•จ๊ป˜ ๋ณด์กดํ•ฉ๋‹ˆ๋‹ค. ๋‘˜ ์ค‘ ๋ฌด์—‡์„ ๊ณ ๋ฅผ์ง€๋Š” ํ˜ธ์ถœ์ž๊ฐ€ ์ •ํ•ฉ๋‹ˆ๋‹ค. ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋Š” ๋ณด์ด๋Š” ํ…์ŠคํŠธ๋งŒ ๋ชจ์•„ ์ตœ๋Œ€ 120์ž, ์ „์ฒด ์ตœ๋Œ€ 2KB๋กœ ์ œํ•œํ•˜๋ฏ€๋กœ ๋น„์–ด ์žˆ๊ฑฐ๋‚˜ ์งง์•„๋„ ์‹ค์ œ ํ™”๋ฉด ๋‚ด์šฉ์ด ์—†๋‹ค๋Š” ๋œป์€ ์•„๋‹™๋‹ˆ๋‹ค. nextAction.example์—๋Š” ์ฒซ ํ›„๋ณด๋ฅผ ์„ ํƒํ•˜๋Š” devup_figma_export ํ˜ธ์ถœ ์˜ˆ์‹œ๊ฐ€ ๋“ค์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ์‹œ์˜ frameIds๋ฅผ ๊ฒ€ํ† ํ•œ ํ›„๋ณด ID๋กœ ๋ฐ”๊ฟ” ํ˜ธ์ถœํ•˜๋ฉด ์„ ํƒํ•œ ํ™”๋ฉด๋งŒ ์ˆ˜์ง‘ํ•ฉ๋‹ˆ๋‹ค.

๋ฉ”๋‰ด๊ฐ€ ์ „์†ก ํ•œ๊ณ„๋ฅผ ๋„˜์„ ๋•Œ

ํ”Œ๋Ÿฌ๊ทธ์ธ ์‘๋‹ต์€ 19 KiB(์ธก์ •๋œ ์ƒํ•œ 20,480๋ฐ”์ดํŠธ ์•„๋ž˜)๋ฅผ ๋„˜์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ํ›„๋ณด๊ฐ€ ๋งŽ์€ Section์€ ์ด ํ•œ๊ณ„๋ฅผ ๋„˜๋Š”๋ฐ, ์˜ˆ์ „์—๋Š” ๊ฑฐ์ ˆํ–ˆ์Šต๋‹ˆ๋‹ค โ€” ํ˜ธ์ถœ์ž๋Š” ์•„๋ฌด๊ฒƒ๋„ ๋ฐ›์ง€ ๋ชปํ–ˆ๊ณ , ํ•œ๊ณ„๊ฐ€ Section ํฌ๊ธฐ์˜ ์„ฑ์งˆ์ด๋ผ ๊ฐ™์€ ์š”์ฒญ์„ ๋‹ค์‹œ ๋ณด๋‚ด๋„ ๊ฒฐ๊ณผ๊ฐ€ ๊ฐ™์•˜์Šต๋‹ˆ๋‹ค.

์ง€๊ธˆ์€ ์ˆœ์„œ๋Œ€๋กœ ์–‘๋ณดํ•ฉ๋‹ˆ๋‹ค.

  1. ํ…์ŠคํŠธ ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ฅผ ๋จผ์ € ๋ฒ„๋ฆฝ๋‹ˆ๋‹ค. ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋Š” ํžŒํŠธ์ผ ๋ฟ์ด๋ผ ์„ ํƒ ๊ฐ€๋Šฅํ•œ ํ™”๋ฉด๊ณผ ๋ฐ”๊ฟ€ ๊ฐ’์–ด์น˜๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค(textPreviewState: "budget-exhausted").
  2. ๊ทธ๋ž˜๋„ ๋„˜์œผ๋ฉด ๋’ค์—์„œ๋ถ€ํ„ฐ ํ›„๋ณด๋ฅผ ์ž˜๋ผ๋‚ด๊ณ  projectionTruncated๋ฅผ ์„ธ์›๋‹ˆ๋‹ค. ์ด๋ฏธ traversal ์ƒํ•œ๊ณผ ํ›„๋ณด 100๊ฐœ ์ƒํ•œ์ด ์“ฐ๋˜ ๊ทธ ํ”Œ๋ž˜๊ทธ์ž…๋‹ˆ๋‹ค.
  3. ํ›„๋ณด ํ•˜๋‚˜๋„ ๋“ค์–ด๊ฐ€์ง€ ์•Š์œผ๋ฉด ๊ทธ๋•Œ ๊ฑฐ์ ˆํ•˜๋ฉฐ, discoveredCandidates์™€ retainedCandidates๋ฅผ ํ•จ๊ป˜ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค.

์ž˜๋ ธ๋‹ค๊ณ  ์กฐ์šฉํžˆ ๋„˜์–ด๊ฐ€์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ž˜๋ฆฐ ์ธ๋ฑ์Šค์—์„œ๋Š” allScreens๊ฐ€ ๊ฑฐ์ ˆ๋˜๊ณ (DEVUP_FIGMA_RESPONSE_TOO_LARGE), ๋ชฉ๋ก์— ์—†๋Š” ID๋Š” not-found-in-truncated-index๋กœ ๋ณด๊ณ ํ•˜๋ฉฐ "์—†๋‹ค"๊ณ  ๋‹จ์ •ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์งง์•„๋„ ์ž๊ธฐ๊ฐ€ ์งง๋‹ค๊ณ  ๋งํ•˜๋Š” ๋ฉ”๋‰ด๊ฐ€ ๋นˆ ์‘๋‹ต๋ณด๋‹ค ๋‚ซ๊ณ , ์• ์ดˆ์— ๊ธˆ์ง€๋œ ๊ฒƒ์€ ์ž˜๋ฆผ์ด ์•„๋‹ˆ๋ผ ๋ง ์—†๋Š” ์ž˜๋ฆผ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

ํ•œ ํ™”๋ฉด์˜ ์—ฌ๋Ÿฌ ํญ โ€” ๋ฐ˜์‘ํ˜• ๋ชจ๋“ˆ

Section ์•ˆ์˜ frame์ด mobile / tablet / desktop์ฒ˜๋Ÿผ breakpoint ์ด๋ฆ„์„ ๊ฐ€์ง€๋ฉด, ๊ทธ frame ํ•˜๋‚˜๋ฅผ ์š”์ฒญํ•ด๋„ ๊ฐ™์€ ์ด๋ฆ„ ๊ทœ์น™์˜ ํ˜•์ œ frame์ด ํ•จ๊ป˜ ์ˆ˜์ง‘๋ฉ๋‹ˆ๋‹ค(Section ์ž์ฒด๋Š” ์ˆ˜์ง‘ ๋ฒ”์œ„ ๋ฐ–์ด๋ฉฐ, ๊ทธ ์ด๋ฆ„์€ ๊ฐ frame์˜ parentName์œผ๋กœ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค). ์ด๋•Œ tsx๋‚˜ responsiveTsx๋ฅผ ์š”์ฒญํ•˜๋ฉด ๊ฒฐ๊ณผ์— responsiveTsx๊ฐ€ ์ถ”๊ฐ€๋ฉ๋‹ˆ๋‹ค โ€” ์„ธ ํญ์„ ํ•˜๋‚˜์˜ ํŠธ๋ฆฌ๋กœ ์ ‘๊ณ  ํญ๋งˆ๋‹ค ๋‹ค๋ฅธ ๊ฐ’์„ devup-ui ๋ฐ˜์‘ํ˜• ๋ฐฐ์—ด [mobile, sm, tablet, lg, pc]๋กœ ์“ด ๋ชจ๋“ˆ์ž…๋‹ˆ๋‹ค. ๊ฐ ํญ์ด ๋†“์ด๋Š” slot์€ frame ์ด๋ฆ„์ด ์•„๋‹ˆ๋ผ ํญ์œผ๋กœ ์ •ํ•ด์ง‘๋‹ˆ๋‹ค(โ‰ค480 / โ‰ค768 / โ‰ค992 / โ‰ค1280 / ๊ทธ ์ด์ƒ). ์ปดํฌ๋„ŒํŠธ ์ด๋ฆ„์€ componentName์ด ์šฐ์„ ์ด๊ณ , ์—†์œผ๋ฉด Section ์ด๋ฆ„์˜ PascalCase์— Page๋ฅผ ๋ถ™์ž…๋‹ˆ๋‹ค(about โ†’ AboutPage).

ํ•œ ํญ์—๋งŒ ์žˆ๋Š” ๋…ธ๋“œ๋Š” ๋‹ค๋ฅธ ํญ์—์„œ display: none์œผ๋กœ ์ˆจ๊ธด ๋ณต์‚ฌ๋ณธ๊ณผ ๋ณ‘ํ•ฉ๋˜๋ฉฐ, ์ด๋•Œ ๋ณต์‚ฌ๋ณธ์€ Section ๋ ˆ์ด์–ด ์ˆœ์„œ์ƒ ์ฒซ ํญ์˜ ๊ฐ’์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค โ€” ๊ทธ๋ž˜์„œ ๋ฐฐ์—ด์˜ ์ฒซ slot์— desktop ๊ฐ’์ด ๋†“์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ๋…ธ๋“œ๋ฅผ ๊ฐ€์ง„ ๋ชจ๋“  ํญ์—์„œ ์ˆจ๊ธด ๋ ˆ์ด์–ด๋Š” ์–ด๋А ํญ์—์„œ๋„ ๊ทธ๋ ค์ง€์ง€ ์•Š์œผ๋ฏ€๋กœ tsx์ฒ˜๋Ÿผ ๋ชจ๋“ˆ์—์„œ ๋น ์ง‘๋‹ˆ๋‹ค. ํญ๋งˆ๋‹ค ์ค„๋ฐ”๊ฟˆ ์œ„์น˜๋งŒ ๋‹ค๋ฅธ ํ…์ŠคํŠธ๋Š” <Box as="br" display={[...]} />๋กœ ์“ฐ๊ณ , ์ปดํฌ๋„ŒํŠธ ์ธ์Šคํ„ด์Šค์˜ variant prop์ด ํญ๋งˆ๋‹ค ๋‹ค๋ฅด๋ฉด ๋ฐฐ์—ด๋กœ ์“ธ ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ๊ฐ€์žฅ ๋„“์€ ํญ์˜ ๊ฐ’์„ ์“ฐ๊ณ  responsiveUnrepresented์— ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค. ํ•จ๊ป˜ ๋ฐ˜ํ™˜๋˜๋Š” responsiveSlots, responsiveImports, responsiveComponents๊ฐ€ slot๊ณผ import ๋ชฉ๋ก์ž…๋‹ˆ๋‹ค.

rawPayload๋Š” rawSnapshot์ด node ํŠธ๋ฆฌ๋งŒ ์“ฐ๋Š” ๊ฒƒ๊ณผ ๋‹ฌ๋ฆฌ ์ˆ˜์ง‘ ์ „์ฒด(variables, styles, stats, assets ํฌํ•จ, referencePng ์ œ์™ธ)๋ฅผ ์”๋‹ˆ๋‹ค. ์บก์ฒ˜๋ฅผ fixture๋กœ ๋ณด๊ด€ํ•ด ์˜คํ”„๋ผ์ธ์—์„œ ์„œ๋ฒ„์™€ ๊ฐ™์€ ํ† ํฐ ์ด๋ฆ„($gray200, typography="h4")์œผ๋กœ ๋ณ€ํ™˜ํ•˜๋ ค๋ฉด ์ด๊ฒƒ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ฆฐ ํญ์ด ์•„๋‹Œ ํญ โ€” widthBehavior์™€ widthPolicy

quality.projection์˜ exact๋Š” ๊ทธ๋ฆฐ ํญ์—์„œ ๋งคํ•‘์ด ์ •ํ™•ํ•˜๋‹ค๋Š” ๋œป์ด์ง€, ๋‹ค๋ฅธ viewport์—์„œ๋„ ๊ฐ™๋‹ค๋Š” ๋œป์ด ์•„๋‹™๋‹ˆ๋‹ค. absolute ์ž์‹์ด ์žˆ๋Š” ๋ฃจํŠธ๋Š” ๊ทธ๋ฆฐ ํญ์„ px๋กœ ์œ ์ง€ํ•˜๋ฏ€๋กœ ๊ธฐ๋ณธ ์ถœ๋ ฅ์€ ์–ด๋–ค viewport์—์„œ๋„ ๊ทธ ํญ์œผ๋กœ ๊ทธ๋ ค์ง‘๋‹ˆ๋‹ค. ๋ฃจํŠธ๋ฅผ w="100%"๋กœ ๋„“ํžˆ๋ฉด ๊ฐ ๋…ธ๋“œ๋Š” ์ž๊ธฐ constraint๊ฐ€ ๋ถ™๋“œ๋Š” ๊ฐ€์žฅ์ž๋ฆฌ์— ๋‚จ์Šต๋‹ˆ๋‹ค โ€” Figma ๊ธฐ๋ณธ๊ฐ’ MIN์ด๋ฉด ์™ผ์ชฝ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์˜ค๋ฅธ์ชฝ์— ๊ทธ๋ฆฐ ๋ฐฐ์ง€์˜ ๋ฐฐ๊ฒฝ๊ณผ ๊ธ€์ž๊ฐ€ ๋„“์€ ํ™”๋ฉด์—์„œ ์„œ๋กœ ๋‹ค๋ฅธ ์ž๋ฆฌ์— ๋‚จ๋Š” ์‹์˜ ์ผ์ด ์ƒ๊น๋‹ˆ๋‹ค.

widthBehavior ๋Š” tsx๋ฅผ ๋งŒ๋“  ์‘๋‹ต๋งˆ๋‹ค(Section ๋ฐฐ์น˜๋Š” componentTsx๋ฅผ ๋งŒ๋“  frames[] ๊ฐ ํ•ญ๋ชฉ์—๋„) ๋ถ™์Šต๋‹ˆ๋‹ค. ์ •์  ํŒ์ •์ž…๋‹ˆ๋‹ค โ€” constraint, auto-layout sizing, ๊ทธ๋ฆฐ ์ขŒํ‘œ๋ฅผ ์ƒ์„ฑ๋œ prop๊ณผ ๋Œ€์กฐํ•  ๋ฟ ๋ Œ๋”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค(verification.runtime: "not-performed"). ๊ธฐ๋ณธ ์ถœ๋ ฅ์˜ TSX๋Š” ๋ฐ”๋€Œ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

  • designWidth, root.generatedWidth / root.fluid โ€” ๋ฃจํŠธ๊ฐ€ px ํญ์„ ์œ ์ง€ํ•˜๋Š”์ง€
  • root.componentSized โ€” ๋ฃจํŠธ๋ฅผ ์–ด๋–ค ํ™”๋ฉด๋ณด๋‹ค ์ข๊ฒŒ(320px ๋ฏธ๋งŒ) ๊ทธ๋ ธ๋Š”์ง€. ๊ทธ๋Ÿฌ๋ฉด ํ™”๋ฉด์ด ์•„๋‹ˆ๋ผ ํ˜ธ์ŠคํŠธ๊ฐ€ ๋†“๊ณ  ํฌ๊ธฐ๋ฅผ ์ •ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ(64px ๋กœ๋”ฉ ํ‘œ์‹œ, ์•„์ด์ฝ˜)๋ผ์„œ, "๋„“์€ ํ™”๋ฉด์—์„œ ์ด ๋ถ€๋ถ„์€ ์–ด๋””๋กœ ๊ฐ€๋‚˜"๋ฅผ ๋ฌป๋Š” consumer-policy-required ๋ฐœ๊ฒฌ์€ ๋‚ด์ง€ ์•Š๊ณ  summary.componentRootSuppressed๋กœ ์…‰๋‹ˆ๋‹ค. ์‹ค์ œ 64px ๋กœ๋”ฉ ํ‘œ์‹œ์— ๋ถ™๋˜ ๋ฐœ๊ฒฌ 5๊ฑด์ด ๊ทธ๋Ÿฐ ์†Œ์Œ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์ƒ์„ฑ๊ธฐ๊ฐ€ ํ‘œํ˜„ํ•˜์ง€ ๋ชปํ•œ ๊ฒƒ(generator-approximation)๊ณผ ๋””์ž์ธ์ด ์ •ํ•œ ๊ฒƒ์€ ๊ทธ๋Œ€๋กœ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค
  • findings[](์ตœ๋Œ€ 40๊ฐœ) โ€” ๋…ธ๋“œ๋งˆ๋‹ค kind, status, confidence, evidence(constraint, ์–‘์ชฝ ์—ฌ๋ฐฑ, ์ƒ์„ฑ๋œ prop), ํ•ด๋‹นํ•˜๋ฉด candidates, drawnInside, carries
  • summary, ๊ทธ๋ฆฌ๊ณ  shapeSizeOmitted โ€” ์œ„์น˜ ์ง€์ •๋œ ์ž์‹ ์—†๋Š” ๋„ํ˜•์„ ํ”Œ๋Ÿฌ๊ทธ์ธ์ฒ˜๋Ÿผ h="100%"์— ํญ ์—†์ด ์“ด ๋…ธ๋“œ๋“ค. ๊ธฐ๋ณธ ์ถœ๋ ฅ์€ ๊ทธ๋Œ€๋กœ ๋‘๊ณ  ์—ฌ๊ธฐ์™€ projectionIssues์—์„œ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค
status ๋ˆ„๊ฐ€ ๋‹ตํ•˜๋‚˜
generator-approximation ๋””์ž์ธ์ด ์ •ํ•œ ๊ฒƒ(STRETCHยทSCALEยทCENTER constraint, ์ง์„ )์„ ๊ธฐ๋ณธ ์ถœ๋ ฅ์ด ํ‘œํ˜„ํ•˜์ง€ ๋ชปํ•จ. widthPolicy๊ฐ€ ๊ทธ๋Œ€๋กœ ์”๋‹ˆ๋‹ค
consumer-policy-required ๋””์ž์ธ์ด ๋‹ตํ•˜์ง€ ์•Š๋Š” ์งˆ๋ฌธ โ€” ์˜ค๋ฅธ์ชฝ์— ๊ทธ๋ ธ์ง€๋งŒ ์™ผ์ชฝ์— ๊ณ ์ •๋œ ๋ฐฐ์ง€๊ฐ€ ๋„“์€ ํ™”๋ฉด์—์„œ ์–ด๋”” ์žˆ์–ด์•ผ ํ•˜๋Š”์ง€. candidates์—์„œ ๊ณ ๋ฆ…๋‹ˆ๋‹ค
design-defined ๋””์ž์ธ์ด FIXED ํญ์œผ๋กœ ์ •ํ•œ ๊ฒƒ. ์•Œ๋ฆผ์ผ ๋ฟ์ž…๋‹ˆ๋‹ค
kind ์ƒํ™ฉ
right-aligned-but-held-left ์˜ค๋ฅธ์ชฝ ๊ฐ€์žฅ์ž๋ฆฌ ๊ฐ€๊นŒ์ด ๊ทธ๋ ธ์ง€๋งŒ constraint๊ฐ€ ์™ผ์ชฝ์— ๊ณ ์ •. carries๋Š” ๊ทธ ์•ˆ์— ๊ทธ๋ ค์ ธ ํ•จ๊ป˜ ์˜ฎ๊ฒจ์งˆ ๋…ธ๋“œ
centered-but-held-left ์–‘์ชฝ ์—ฌ๋ฐฑ์ด ๊ฐ™๊ฒŒ ๊ทธ๋ ธ์ง€๋งŒ ์™ผ์ชฝ ๊ณ ์ •. ํ›„๋ณด๋Š” leftยทcenterยทstretch
group-anchor-mismatch ๊ทธ๋ ค์ง„ ์ปจํ…Œ์ด๋„ˆ์™€ ๋‹ค๋ฅธ ๊ฐ€์žฅ์ž๋ฆฌ์— ๊ณ ์ •๋ผ, ํญ์ด ๋ฐ”๋€Œ๋ฉด ๋‘˜์ด ๊ฐˆ๋ผ์ง
constraint-not-expressed STRETCHยทSCALE constraint๋ฅผ ๊ธฐ๋ณธ ์ถœ๋ ฅ์ด px ์œ„์น˜ยทํญ์œผ๋กœ ์”€
center-offset-dropped CENTER constraint์ธ๋ฐ ์ค‘์‹ฌ์—์„œ ๋ฒ—์–ด๋‚˜ ๊ทธ๋ ธ๊ณ , ๊ธฐ๋ณธ ์ถœ๋ ฅ์€ ์ •ํ™•ํžˆ ๊ฐ€์šด๋ฐ์— ๋‘  โ€” ๊ทธ๋ฆฐ ํญ์—์„œ๋„ ์˜ฎ๊ฒจ์ง
fixed-length-line-mask ์ง์„ ์„ mask๋กœ ๊ทธ๋ ค, ์ƒ์ž๊ฐ€ ๋Š˜์–ด๋‚˜๋„ ์„ ์€ ๊ทธ๋ฆฐ ๊ธธ์ด์— ๋จธ๋ฌพ
fixed-width-in-fluid-parent ๋””์ž์ธ์ด FIXED๋กœ ์ •ํ•œ ํญ

widthPolicy ๋Š” ์˜ตํŠธ์ธ์ž…๋‹ˆ๋‹ค. ์ฃผ์ง€ ์•Š์œผ๋ฉด ์ถœ๋ ฅ์€ ์˜ˆ์ „๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

{ "url": "...", "widthPolicy": { "root": "fluid", "anchors": { "2:4": "stretch", "2:6": "right", "2:12": "center" } } }
  • ๋ฃจํŠธ๋Š” ์ปจํ…Œ์ด๋„ˆ์˜ ํญ์„ ๋ฐ›์Šต๋‹ˆ๋‹ค(w="100%", ๋†’์ด๋Š” ์œ ์ง€. rootLayout: "embedded"๋ฉด ์›๋ž˜๋Œ€๋กœ ์ƒ๋žต).
  • ์œ„์น˜ ์ง€์ •๋œ ๋…ธ๋“œ์˜ ๊ฐ€์žฅ์ž๋ฆฌ๋Š” ์ด ์ˆœ์„œ๋กœ ์ •ํ•ฉ๋‹ˆ๋‹ค: anchors์— ์ ์€ ๊ฒƒ โ†’ ๊ทธ ๋…ธ๋“œ๊ฐ€ ๊ทธ๋ ค์ง„ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ํ˜ธ์ถœ์ž๊ฐ€ rightยทcenter๋กœ ๊ณ ์ •ํ–ˆ๋‹ค๋ฉด ๊ทธ๊ฒƒ โ†’ ๋…ธ๋“œ ์ž์‹ ์˜ Figma constraint. ๊ทธ๋ž˜์„œ ๋ฐฐ์ง€ ๋ฐฐ๊ฒฝ๋งŒ right๋กœ ์ฃผ๋ฉด ๊ทธ ์•ˆ์˜ ๊ธ€์ž๋„ ๋”ฐ๋ผ๊ฐ‘๋‹ˆ๋‹ค.
  • right โ†’ right, stretch โ†’ left+right(ํญ ์—†์Œ), scale โ†’ % ํญ, center โ†’ left="50%" ๋˜๋Š” calc(50% ยฑ Npx)์™€ translateX(-50%). ๋Š˜์–ด๋‚˜๋Š” ์ง์„ ์˜ mask๋Š” maskSize="100% 100%", ์ž์‹ ์—†๋Š” ๋„ํ˜•์€ ์ž๊ธฐ px ํฌ๊ธฐ๋กœ ์”๋‹ˆ๋‹ค.
  • ์ด prop๋“ค์ด ๊ทธ๋ฆฐ ํญ์—์„œ ์›๋ž˜ ์œ„์น˜ยทํญ์„ ์ •ํ™•ํžˆ ์žฌํ˜„ํ•œ๋‹ค๋Š” ๊ฒƒ์€ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค(ABSOLUTE ์ง„๋‹จ์˜ components.horizontalยทwidth์™€ anchorSource). ๋‹ค๋ฅธ ํญ์—์„œ ์–ด๋”” ๋†“์ด๋Š”์ง€๋Š” ํ˜ธ์ถœ์ž์˜ ์ •์ฑ…์ด์ง€ ๋””์ž์ธ์— ๋Œ€ํ•œ ์ฃผ์žฅ์ด ์•„๋‹™๋‹ˆ๋‹ค.
  • ์บก์ฒ˜์— ์—†๋Š” node id๋Š” ๊ฑฐ์ ˆํ•ฉ๋‹ˆ๋‹ค(DEVUP_INVALID_INPUT, details.unknownNodeIds) โ€” ์˜คํƒ€๊ฐ€ ์กฐ์šฉํžˆ ์ ์šฉ๋˜์ง€ ์•Š๋Š” ์•ต์ปค๊ฐ€ ๋˜์ง€ ์•Š๋„๋ก. jobId ํด๋ง์—๋Š” ๋ณด๋‚ผ ์ˆ˜ ์—†๊ณ  ์›๋ž˜ ํ˜ธ์ถœ์— ๋„ฃ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ํ•œ ํญ์งœ๋ฆฌ ํ™”๋ฉด์šฉ์ž…๋‹ˆ๋‹ค. ์—ฌ๋Ÿฌ ํญ์„ ์ˆ˜์ง‘ํ•œ responsiveTsx์—๋Š” ์ ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

verificationScope๋Š” ์ด ์‘๋‹ต์ด ๋ฌด์—‡์„ ์–ด๋–ค ํญ์—์„œ ํ™•์ธํ–ˆ๋Š”์ง€ ๋”ฐ๋กœ ๋งํ•ฉ๋‹ˆ๋‹ค โ€” collectedWidths, ์ •์ ์œผ๋กœ ํ™•์ธํ•œ ๊ฒƒ(staticallyChecked), ๊ทธ๋ฆฌ๊ณ  rendered.state: "not-performed". ๋‹ค๋ฅธ ํญ์—์„œ ์‹ค์ œ๋กœ ์–ด๋–ป๊ฒŒ ๊ทธ๋ ค์ง€๋Š”์ง€๋Š” devup_runtime_audit๋กœ ์†Œ๋น„์ž ํ”„๋กœ์ ํŠธ์—์„œ ์žฝ๋‹ˆ๋‹ค.

๋น„์šฉ(WQUW-151 ์‹ค์ œ ํ™”๋ฉด 10๊ฐœ): widthBehavior๋Š” 0.82.8 KB, ๊ณ„์‚ฐ 0.020.3 ms(๊ฐ™์€ ํ™”๋ฉด์˜ codegen 3~33 ms). ๊ธฐ๋ณธ ์ถœ๋ ฅ์˜ TSX๋Š” ๋ณ€๊ฒฝ ์ „๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค โ€” ํ”Œ๋Ÿฌ๊ทธ์ธ golden 268์Œ์ด ํ†ต๊ณผํ•˜๊ณ , ์‹ค์ œ ํ™”๋ฉด 10๊ฐœ์™€ ํ•ฉ์„ฑ ํ™”๋ฉด 1๊ฐœ์˜ TSX SHA-256์ด ๋ณ€๊ฒฝ ์ „ ๋นŒ๋“œ์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค.

codegen์ด ํ™”๋ฉด ํฌ๊ธฐ์˜ ์ œ๊ณฑ์œผ๋กœ ๋А๋ ค์ง€๋˜ ๊ฒƒ์„ ๊ณ ์ณค์Šต๋‹ˆ๋‹ค. ์ƒ์„ฑ๋œ ์†์„ฑ๋งˆ๋‹ค ๊ทธ ์†์„ฑ์ด sourceMap์— ๋งคํ•‘๋๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๊ฐ์‚ฌ๊ฐ€, ํ•ญ๋ชฉ ํ•˜๋‚˜๋ฅผ ๋ณผ ๋•Œ๋งˆ๋‹ค ๊ทธ ์†์„ฑ์˜ ์ฃผ์ธ ๋…ธ๋“œ๋ฅผ sourceMap ์ „์ฒด์—์„œ ๋‹ค์‹œ ์ฐพ์•˜์Šต๋‹ˆ๋‹ค(์†์„ฑ ์ˆ˜ ร— ํ•ญ๋ชฉ ์ˆ˜ยฒ). ์ฃผ์ธ์„ ์†์„ฑ๋งˆ๋‹ค ํ•œ ๋ฒˆ๋งŒ ์ฐพ๊ฒŒ ํ–ˆ๊ณ , ์ถœ๋ ฅ์€ ๊ฐ™์Šต๋‹ˆ๋‹ค โ€” about ํ™”๋ฉด ์„ธ ํญ์˜ CodegenOutput ์ „์ฒด(TSX, sourceMap, ์ง„๋‹จ, fidelity)๊ฐ€ ๋ณ€๊ฒฝ ์ „๊ณผ ๊ฐ™์€ ํ•ด์‹œ์ž…๋‹ˆ๋‹ค. ์‹ค์ œ ํ™”๋ฉด์—์„œ ํญ ํ•˜๋‚˜์˜ codegen์ด ์•ฝ 1 s์—์„œ 70~100 ms๋กœ, ๋ธŒ๋ฆฌ์ง€๋กœ ๋ฐ›์€ about(790 node, ์„ธ ํญ) export์˜ ํˆฌ์˜์ด 15.3 s์—์„œ 0.58 s๋กœ ์ค„์—ˆ์Šต๋‹ˆ๋‹ค.

์‹œ๊ฐ„ ํŠธ๋ฆฌ๊ฑฐ Smart Animate โ€” CSS keyframes

frame์— After delay ํŠธ๋ฆฌ๊ฑฐ๋กœ ๋‹ค๋ฅธ frame์— Smart animateํ•˜๋Š” reaction์ด ์žˆ๊ณ , ๊ทธ frame์ด ๋‹ค์‹œ ๋‹ค์Œ frame์œผ๋กœ ์ด์–ด์ง€๋ฉด ํ•˜๋‚˜์˜ ์ฒด์ธ์ž…๋‹ˆ๋‹ค(์ฒ˜์Œ frame์œผ๋กœ ๋Œ์•„์˜ค๋ฉด ๋ฃจํ”„). ์ฒด์ธ์˜ frame๋“ค์€ ์š”์ฒญํ•œ node์˜ subtree ๋ฐ–์— ์žˆ๋Š” ํ˜•์ œ frame์ด๋ฏ€๋กœ, ์š”์ฒญ ๋ฃจํŠธ๊ฐ€ ํ•˜๋‚˜์ผ ๋•Œ snapshot ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์ฒด์ธ์„ ๋”ฐ๋ผ๊ฐ€๋ฉฐ ์ถ”๊ฐ€ ๋ฃจํŠธ๋กœ ํ•จ๊ป˜ ์ˆ˜์ง‘ํ•ฉ๋‹ˆ๋‹ค(๋‹ค์ค‘ ๋ฃจํŠธ ์š”์ฒญ์€ ๋ฃจํŠธ ๋ชฉ๋ก์„ ๊ทธ๋Œ€๋กœ ๋‘ก๋‹ˆ๋‹ค).

๋ณ€ํ™˜๊ธฐ๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ getReactionProps ๊ทœ์น™๋Œ€๋กœ frame ์‚ฌ์ด์—์„œ ๋ฐ”๋€Œ๋Š” ๊ฒƒ โ€” ์œ„์น˜, ํฌ๊ธฐ, opacity, ์ฒซ fill, ํšŒ์ „(๋ˆ„์  delta) โ€” ์„ ์ด๋ฆ„์ด ๊ฐ™์€ ์ž์‹์—์„œ ๋จผ์ € ์ฐพ์•„ ์ž์‹๋งˆ๋‹ค animationName={keyframes({...})} / animationDuration / animationTimingFunction / animationFillMode / animationIterationCount(๋ฃจํ”„๋ฉด infinite)๋กœ ์“ฐ๊ณ , ๋ฐ”๋€Œ๋Š” ์ž์‹์ด ์—†์„ ๋•Œ๋งŒ frame ์ž์ฒด์— ์”๋‹ˆ๋‹ค. 0%๋Š” ์‹œ์ž‘ frame, ๊ฐ ๋‹จ๊ณ„๋Š” ๋„์ฐฉ ์‹œ์ ์˜ ํผ์„ผํŠธ์— ์ง์ „ keyframe๊ณผ ๋‹ค๋ฅธ ์†์„ฑ๋งŒ, ๋ฃจํ”„๋Š” 100%์—์„œ ์‹œ์ž‘ ๊ฐ’์œผ๋กœ ๋‹ซํžˆ๋ฉฐ duration์€ ๋˜๋Œ์•„๊ฐ€๋Š” ๊ตฌ๊ฐ„๊นŒ์ง€ ์…‰๋‹ˆ๋‹ค. 10ms ๋ฏธ๋งŒ timeout์€ delay๋กœ ์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. keyframes๊ฐ€ ์“ฐ์ด๋ฉด @devup-ui/react์—์„œ import๋ฉ๋‹ˆ๋‹ค.

snapshot์— ์—†๋Š” ๋ชฉ์ ์ง€(legacy ๊ฒฝ๋กœ, ๋‹ค์ค‘ ๋ฃจํŠธ ์š”์ฒญ)๋Š” ์กฐ์šฉํžˆ ๋ฒ„๋ฆฌ์ง€ ์•Š๊ณ  DEVUP_CODEGEN_ANIMATION_UNREACHABLE diagnostic์œผ๋กœ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค.

Figma ์ด๋ฆ„ ๊ฒ€์ƒ‰

{
  "url": "https://www.figma.com/design/<file-key>/<name>",
  "query": "A : STORY-F-PROOFREAD",
  "nodeTypes": ["PAGE", "SECTION", "FRAME", "COMPONENT_SET"],
  "match": "normalized",
  "limit": 20
}

๊ฒ€์ƒ‰ ๋ฒ”์œ„๋Š” URL์ด ์ •ํ•ฉ๋‹ˆ๋‹ค. node-id๊ฐ€ ์žˆ์œผ๋ฉด ๊ทธ ๋…ธ๋“œ์™€ ๊ทธ subtree๋งŒ, ์—†์œผ๋ฉด ํŒŒ์ผ ์ „์ฒด๋ฅผ ๋ด…๋‹ˆ๋‹ค. ์‘๋‹ต์˜ scope๊ฐ€ ์‹ค์ œ๋กœ ์–ด๋А ์ชฝ์„ ๊ฒ€์ƒ‰ํ–ˆ๋Š”์ง€ ๋ณด๊ณ ํ•˜๋ฏ€๋กœ, ๊ฒฐ๊ณผ๊ฐ€ ๊ธฐ๋Œ€์™€ ๋‹ค๋ฅผ ๋•Œ ๋ฒ”์œ„ ๋•Œ๋ฌธ์ธ์ง€ ์ด๋ฆ„ ๋•Œ๋ฌธ์ธ์ง€ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. limit์€ ๋ฐ˜ํ™˜ํ•  ์ผ์น˜ ๊ฐœ์ˆ˜์ž…๋‹ˆ๋‹ค.

ํŒŒ์ผ ์ „์ฒด ๊ฒ€์ƒ‰์€ ๋จผ์ € read-only Plugin API๋กœ ์‹ค์ œ figma.root.children page catalog๋ฅผ ์–ป๊ณ , page๋งˆ๋‹ค ํ•œ ๋ฒˆ์”ฉ ์ „ํ™˜ํ•˜๋Š” ์ž‘์€ query projection์„ ๋ณ‘๋ ฌ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ „์ฒด page snapshot์„ ์‘๋‹ตํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ ํฐ ํŒŒ์ผ์—์„œ๋„ ๊ณต์‹ MCP text ์ƒํ•œ์„ ํ”ผํ•ฉ๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ๋Š” ์›๋ฌธ exact, Unicode NFCยท๊ณต๋ฐฑยท๋Œ€์†Œ๋ฌธ์ž๋ฅผ ์ •๊ทœํ™”ํ•œ exact, prefix, contains ์ˆœ์œผ๋กœ ์ •๋ ฌํ•˜๊ณ  match: "fuzzy"์ผ ๋•Œ๋งŒ ์˜คํƒ€ ํ—ˆ์šฉ ๊ฒ€์ƒ‰์„ ์ถ”๊ฐ€ํ•˜๋ฉฐ, node ID, type, page, ์ „์ฒด breadcrumb์™€ ํ›„์† devup_figma_export์— ๊ทธ๋Œ€๋กœ ์ „๋‹ฌํ•  canonical URL์„ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค.

๋งํฌ ์ฃผ๋ณ€ ํ™”๋ฉด ํƒ์ƒ‰

{
  "url": "https://www.figma.com/design/<file-key>/<name>?node-id=1-2",
  "limit": 50,
  "includeTextPreview": true,
  "refresh": false
}

์š”๊ตฌ์‚ฌํ•ญ ์ œ๋ชฉ์ด๋‚˜ ์„ค๋ช… node ๋งํฌ๊ฐ€ ์‹ค์ œ ๊ตฌํ˜„ ํ™”๋ฉด์ด ์•„๋‹ ๋•Œ devup_figma_explore๋ฅผ ๋จผ์ € ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. url์—๋Š” node-id๊ฐ€ ์žˆ์–ด์•ผ ํ•˜๊ณ (์—†์œผ๋ฉด DEVUP_FIGMA_NODE_NOT_FOUND), limit์€ 1~100 ๋ฒ”์œ„ ๋ฐ–์ด๋ฉด ๊ฑฐ์ ˆ๋ฉ๋‹ˆ๋‹ค. anchor์™€ ๊ฐ™์€ ๊ณต๊ฐ„ ๋ฌถ์Œ์˜ frame/component ํ›„๋ณด๋ฅผ ์‹œ๊ฐ ์ˆœ์„œ์™€ canonical URL๋กœ ๋ฐ˜ํ™˜ํ•˜๋ฉฐ, ๋‹ค์Œ ์š”๊ตฌ์‚ฌํ•ญ ์ œ๋ชฉ์—์„œ ํƒ์ƒ‰ ๋ฒ”์œ„๋ฅผ ๋๋ƒ…๋‹ˆ๋‹ค.

limit์€ ๋ฐ˜ํ™˜ํ•  ํ›„๋ณด ๊ฐœ์ˆ˜์ผ ๋ฟ์ด๊ณ  ๊ทธ ์ด์ƒ ์•„๋ฌด๊ฒƒ๋„ ์•„๋‹™๋‹ˆ๋‹ค. ๋””์ž์ธ์„ ์–ผ๋งˆ๋‚˜ ์ฝ์„์ง€๋Š” ๋ฐ”๊พธ์ง€ ์•Š์œผ๋ฏ€๋กœ(ํˆฌ์˜ ์˜ˆ์‚ฐ์€ ํ•ญ์ƒ ๊ณ ์ •์ž…๋‹ˆ๋‹ค), limit์„ ์˜ฌ๋ ค์„œ ํ•  ์ˆ˜ ์žˆ๋Š” ์ผ์€ ๋‹ต์„ ๊ธธ๊ฒŒ ๋งŒ๋“œ๋Š” ๊ฒƒ๋ฟ์ž…๋‹ˆ๋‹ค. ์˜ˆ์ „์—๋Š” limit์ด ํˆฌ์˜ ์˜ˆ์‚ฐ์œผ๋กœ 4๋ฐฐ ํ™•๋Œ€๋ผ 30โ†’33์œผ๋กœ ์˜ฌ๋ ธ๋”๋‹ˆ ํ›„๋ณด๊ฐ€ 23โ†’18๋กœ ์ค„์–ด๋“œ๋Š” ์ผ์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์ž˜๋ฆฐ ์ด์œ ๋„ ์ด์ œ ๋‘˜๋กœ ๋‚˜๋ˆ  ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค.

  • truncation.candidates โ€” limit์ด ๋ชฉ๋ก์„ ์ž˜๋ž๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค. limit์„ ์˜ฌ๋ฆฌ๋ฉด ๋‚˜๋จธ์ง€๊ฐ€ ๋‚˜์˜ต๋‹ˆ๋‹ค.
  • truncation.projection โ€” ์ˆ˜์ง‘ํ•œ ์Šค๋ƒ…์ƒท ์ž์ฒด๊ฐ€ ๋ถˆ์™„์ „ํ–ˆ๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค. limit์„ ์•„๋ฌด๋ฆฌ ์˜ฌ๋ ค๋„ ๊ทธ ํ™”๋ฉด๋“ค์€ ๋Œ์•„์˜ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ตœ์ƒ์œ„ truncated๋Š” ๋‘˜์˜ OR์ž…๋‹ˆ๋‹ค. ๋‘˜์„ ๊ตฌ๋ถ„ํ•˜์ง€ ๋ชปํ•˜๋ฉด ๊ณ ์น  ์ˆ˜ ์—†๋Š” ์ƒํ™ฉ์—์„œ limit๋งŒ ์˜ฌ๋ฆฌ๋ฉฐ ํ˜ธ์ถœ์„ ๋ฐ˜๋ณตํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

includeTextPreview๋Š” ๋ณ„๋„ ์˜ˆ์‚ฐ์„ ์”๋‹ˆ๋‹ค. ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋Š” ๊ตฌ์กฐ ํˆฌ์˜์ด ์“ฐ๊ณ  ๋‚จ๊ธด ๊ณต๊ฐ„๋งŒ ์†Œ๋น„ํ•˜๋ฏ€๋กœ, ์ผœ๊ณ  ๋„๋Š” ๊ฒƒ์ด ํ›„๋ณด ๊ฐœ์ˆ˜ยทIDยทtruncation.projection ์ค‘ ๋ฌด์—‡๋„ ๋ฐ”๊พธ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ฐ™์€ ํŒŒ์ผยท์˜ต์…˜์—์„œ ์ด๋ฏธ ์ˆ˜์ง‘ํ•œ ๋” ํฐ ํƒ์ƒ‰ ๊ฒฐ๊ณผ๋Š” exactยทrelated-nodeยทsuperset ๋ฒ”์œ„๋กœ ์žฌ์‚ฌ์šฉ๋˜๊ณ , ๋™์‹œ์— ๋“ค์–ด์˜จ ํ˜ธํ™˜ ์š”์ฒญ๋„ ๊ณต์‹ Figma ํ˜ธ์ถœ ํ•˜๋‚˜๋ฅผ ๊ณต์œ ํ•ฉ๋‹ˆ๋‹ค. refresh: true๋Š” ๋ชจ๋“  ์žฌ์‚ฌ์šฉ์„ ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค. ์›ํ•˜๋Š” ํ›„๋ณด์˜ canonical URL์„ devup_figma_export์— ๋„˜๊ฒจ ์ •ํ™•ํ•œ ํ™”๋ฉด๋งŒ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

ํƒ์ƒ‰๊ณผ ๊ฒ€์ƒ‰์€ ๋ณ€์ˆ˜ catalog๋ฅผ ์ˆ˜์ง‘ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ •ํ™•ํ•œ UI ๋ณ€ํ™˜ ๋‹จ๊ณ„์—์„œ ์„ ํƒ subtree์˜ ๋ชจ๋“  ๋ณด์กด ํ•„๋“œ์— ์žˆ๋Š” VARIABLE_ALIAS์™€ paint/text/effect/grid style ID๋ฅผ ์žฌ๊ท€์ ์œผ๋กœ ์Šค์บ”ํ•˜๊ณ , ์‹ค์ œ ์‚ฌ์šฉ๋œ ID๋งŒ ๊ณต์‹ Figma API๋กœ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค. outputs: ["devupJson"]์— scope: "file"์„ ํ•จ๊ป˜ ์ค€ ๊ฒฝ์šฐ์—๋งŒ file ์ „์ฒด ๋กœ์ปฌ catalog๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ , ๊ทธ ๋’ค ๋…ธ๋“œ๊ฐ€ ์“ฐ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋ณ€์ˆ˜ยท์Šคํƒ€์ผ๊ณผ ๋ณ€์ˆ˜๊ฐ€ alias๋กœ ๊ฐ€๋ฆฌํ‚ค๋Š” ๋ณ€์ˆ˜๋ฅผ ๊ฐ™์€ ๋ฐฉ์‹์œผ๋กœ ์ฝ์–ด ํ•ฉ์นฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๋ฌธ์„œ๋ฅผ ํ•จ๊ป˜ ์ˆ˜์ง‘ํ•˜๋Š” ํŒŒ์ผ ์ „์ฒด export(rawPayload ๋“ฑ)๋„ ํ…Œ๋งˆ๋งŒ ๋ฐ›๋Š” export์™€ ๊ฐ™์€ ํ…Œ๋งˆ๋ฅผ ๋ƒ…๋‹ˆ๋‹ค. Figma๊ฐ€ ์›์‹œ ๊ฐ’์— ๋งž์ถฐ ์ œ์•ˆํ•˜๋Š” ๋ณ€์ˆ˜(inferredVariables)๋Š” ๋ฐ”์ธ๋”ฉ์ด ์•„๋‹ˆ๋ฏ€๋กœ ์‚ฌ์šฉ๋œ ๊ฒƒ์œผ๋กœ ์„ธ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

sourcePolicy ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” auto์™€ direct ๋‘˜ ๋‹ค ๊ฐ™์€ ๋™์ž‘์ด์—ˆ์œผ๋ฏ€๋กœ ์ œ๊ฑฐํ–ˆ์Šต๋‹ˆ๋‹ค โ€” ๋ถ„๊ธฐํ•˜์ง€ ์•Š๋Š” ์„ ํƒ์ง€๋Š” ํ˜ธ์ถœ์ž์—๊ฒŒ ํ‹€๋ฆด ๊ธฐํšŒ๋งŒ ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. ๊ฒฝ๋กœ ์„ ํƒ์€ ํŒŒ๋ผ๋ฏธํ„ฐ๊ฐ€ ์•„๋‹ˆ๋ผ ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ๋ถ™์–ด ์žˆ๋Š”์ง€๊ฐ€ ์ •ํ•ฉ๋‹ˆ๋‹ค: ๋ถ™์–ด ์žˆ์œผ๋ฉด ๋ธŒ๋ฆฌ์ง€, ์•„๋‹ˆ๋ฉด direct์ž…๋‹ˆ๋‹ค. direct ๊ฒฝ๋กœ๋Š” ์—ฐ๊ฒฐ๊ณผ read-only capability catalog ์กฐํšŒ๋ฅผ ๊ฐ๊ฐ 30์ดˆ, ๊ฐœ๋ณ„ tool ํ˜ธ์ถœ์„ 5๋ถ„์œผ๋กœ ์ œํ•œํ•ฉ๋‹ˆ๋‹ค. deadline์„ ๋„˜๊ธฐ๋ฉด ํ•ด๋‹น remote session์„ ํ๊ธฐํ•˜๊ณ  ๋””์ž์ธ ์›๋ฌธ ์—†์ด retryable timeout ๋‹จ๊ณ„๋งŒ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์ •ํ™•ํ•œ node ๋งํฌ์˜ UI ๋ณ€ํ™˜์€ ํ•˜๋‚˜ ์ด์ƒ์˜ ๊ณต์‹ use_figma ํ˜ธ์ถœ ์•ˆ์—์„œ subtree์™€ ์‹ค์ œ ์‚ฌ์šฉ ๋ฆฌ์†Œ์Šค๋ฅผ ์ˆ˜์ง‘ํ•ฉ๋‹ˆ๋‹ค. ์ˆ˜์ง‘ ์Šคํฌ๋ฆฝํŠธ๋Š” checked-in manifest(devup-ui ๋ณ€ํ™˜๊ธฐ๊ฐ€ ์‹ค์ œ๋กœ ์ฝ๋Š” ํ•„๋“œ๋งŒ)๋งŒ ํ™•์ธํ•˜๊ณ  โ€” ํ”„๋กœํ† ํƒ€์ž… ์ฒด์ธ ์ „์ฒด๋ฅผ ํ›‘๊ฑฐ๋‚˜ ๋ฏธ๋ถ„๋ฅ˜ ํ•„๋“œ๋ฅผ extra์— ๋‹ด์ง€ ์•Š์Šต๋‹ˆ๋‹ค โ€” null/๋นˆ ๋ฐฐ์—ด/๋ฏธ๋ฐ”์ธ๋”ฉ style ID ๊ฐ™์€ ๊ธฐ๋ณธ๊ฐ’์€ ๋ด‰ํˆฌ์—์„œ ์ƒ๋žตํ•ฉ๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ๋Š” ํ•ญ์ƒ ํ…์ŠคํŠธ(devupFastSnapshotEnvelope)์ด๋ฉฐ PNG ๊ฐ™์€ ๋ฐ”์ด๋„ˆ๋ฆฌ transport๋Š” ์—†์Šต๋‹ˆ๋‹ค. ํ•œ subtree๊ฐ€ 15KB ํ…์ŠคํŠธ ํ•œ๋„๋ฅผ ๋„˜์œผ๋ฉด ๊ฐ™์€ ์Šคํฌ๋ฆฝํŠธ๋ฅผ offset์„ ์˜ฎ๊ฒจ ๋‹ค์‹œ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ํ…์ŠคํŠธ ํŽ˜์ด์ง€๋„ค์ด์…˜ํ•ฉ๋‹ˆ๋‹ค โ€” ๊ฐ ๋ผ์šด๋“œ๋Š” ๊ทธ ๋ผ์šด๋“œ๊ฐ€ ๋ณด๋‚ธ node์—์„œ๋งŒ ๋ฆฌ์†Œ์Šค๋ฅผ ์Šค์บ”ํ•ด ์ž๊ธฐ ์™„๊ฒฐ์ ์ด๋ฉฐ, Rust๊ฐ€ ์—ฌ๋Ÿฌ ๋ผ์šด๋“œ์˜ node์™€ ๋ฆฌ์†Œ์Šค๋ฅผ ๋ณ‘ํ•ฉํ•ฉ๋‹ˆ๋‹ค. Rust๋Š” schemaยท๋Œ€์ƒ IDยทnode graphยท๋ฆฌ์†Œ์Šค ์ฐธ์กฐยท(ํŽ˜์ด์ง€ ์ค‘์ด ์•„๋‹ ๋•Œ์˜) ์ž์‹ ์™„์ „์„ฑ์„ ๋ชจ๋‘ ๊ฒ€์ฆํ•œ ๋’ค์—๋งŒ ๊ฒฐ๊ณผ๋ฅผ ์ฑ„ํƒํ•ฉ๋‹ˆ๋‹ค. ํ•œ ํ•ญ๋ชฉ์ด๋ผ๋„ ๋ถˆ์ผ์น˜ํ•˜๋ฉด fast ๊ฒฐ๊ณผ ์ „์ฒด๋ฅผ ๋ฒ„๋ฆฌ๊ณ  ๊ธฐ์กด cursor ์ˆ˜์ง‘์„ 0๋ถ€ํ„ฐ ์žฌ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. Section multi-root์—์„œ๋Š” ์„ฑ๊ณตํ•œ root์™€ resource๋Š” ๊ทธ๋Œ€๋กœ ๋ณด์กดํ•˜๊ณ  ์‹คํŒจํ•˜๊ฑฐ๋‚˜ ์ƒํ•œ์„ ๋„˜์€ root๋งŒ legacy๋กœ ๋‹ค์‹œ ์ˆ˜์ง‘ํ•œ ๋’ค ์›๋ž˜ ์‹œ๊ฐ ์ˆœ์„œ๋กœ ํ•ฉ์นฉ๋‹ˆ๋‹ค. direct upstream์€ ์—ฐ๊ฒฐ๊ณผ read-only tool catalog๋ฅผ ํ•œ session์—์„œ ์žฌ์‚ฌ์šฉํ•˜๊ณ  30์ดˆ TTL, ์—ฐ๊ฒฐ ์ข…๋ฃŒ ๋˜๋Š” transport ์˜ค๋ฅ˜ ๋•Œ๋งŒ ์žฌ์—ฐ๊ฒฐยท์žฌ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ์˜ stats์—๋Š” figmaToolCalls, transport(text | text-paginated | legacy-cursor), fallbackUsed, node/variable/style ์ˆ˜์™€ byte ์ˆ˜๋งŒ ํฌํ•จ๋˜๋ฉฐ ์›๋ณธ ๋””์ž์ธ์ด๋‚˜ ์ธ์ฆ ์ •๋ณด๋Š” ํฌํ•จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์™„์ „์„ฑ ๋“ฑ๊ธ‰์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • full-local-plus-used-remote: ๋กœ์ปฌ ์ „์ฒด์™€ ์‚ฌ์šฉ๋œ ์™ธ๋ถ€ token์„ ๋ชจ๋‘ ํ™•์ธ
  • used-tokens: ํ™•๋ณดํ•œ token๋งŒ ๋ณ€ํ™˜ํ–ˆ์œผ๋ฉฐ ์™ธ๋ถ€ ์ „์ฒด๋ฅผ ๋ณด์žฅํ•˜์ง€ ์•Š์Œ
  • resolved-values-only: ์˜๋ฏธ ์žˆ๋Š” token binding ์—†์ด ๊ณ„์‚ฐ๊ฐ’๋งŒ ํ™•๋ณด

ํ”„๋กœ์ ํŠธ ์ชฝ ๋„๊ตฌ

devup_project_context โ€” ๋ฌด์—‡์„ ์ฝ๊ณ  ๋ฌด์—‡์„ ๊ฑด๋„ˆ๋›ฐ๋Š”๊ฐ€

scope๋Š” theme(ํ”„๋กœ์ ํŠธ devup.json), api(openapi.json), db(Vespertide models/*.json), all์ž…๋‹ˆ๋‹ค. ํ˜ธ์ถœ ์‹œ์ ์— ๋””์Šคํฌ์— ์‹ค์ œ๋กœ ์žˆ๋Š” ํŒŒ์ผ๋งŒ ์ฝ๊ณ , ์„ธ์…˜ ๊ฐ„ ์บ์‹œํ•˜์ง€ ์•Š์œผ๋ฉฐ, ์—†๋Š” ๊ฒƒ์„ ์ถ”์ธกํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์Šค์บ” ๊นŠ์ด๋Š” 4๋‹จ๊ณ„์ž…๋‹ˆ๋‹ค.

๋‹ค์Œ ๋””๋ ‰ํ„ฐ๋ฆฌ๋Š” ์Šค์บ”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

  • ๋นŒ๋“œยท์˜์กด์„ฑ ์‚ฐ์ถœ๋ฌผ: node_modules, target, dist, build, out, .next, .turbo, .nuxt, .venv, venv, __pycache__, .cache, coverage, .git
  • ์ค‘์ฒฉ ์ฒดํฌ์•„์›ƒ: .worktrees, .worktree, .git-worktrees

์ œ์™ธ๋Š” ์กฐ์šฉํžˆ ์ผ์–ด๋‚˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ฌด์–ธ๊ฐ€๋ฅผ ๊ฑด๋„ˆ๋›ฐ์—ˆ์œผ๋ฉด excludedPaths[]์— path์™€ reason(nested-checkout-directory ๋˜๋Š” nested-git-checkout)์œผ๋กœ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ์„ ํ•˜๋‚˜๋„ ๋ชป ์ฐพ์•˜๋Š”๋ฐ ์ œ์™ธํ•œ ๊ฒƒ์ด ์žˆ์œผ๋ฉด ๊ทธ ์‚ฌ์‹ค๋„ ํ•จ๊ป˜ ๋ณด๊ณ ํ•˜๋ฏ€๋กœ, "์—†์Œ"๊ณผ "์ œ์™ธํ•ด์„œ ์•ˆ ๋ณด์ž„"์„ ๊ตฌ๋ถ„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ์ „์—๋Š” ์Šคํ…Œ์ผ ๋ธŒ๋žœ์น˜์˜ devup.json์ด ๊ถŒ์œ„ ์žˆ๋Š” ๊ฒƒ๊ณผ ์„ž์—ฌ 4๊ฐœ๊ฐ€ ๋ฐ˜ํ™˜๋๊ณ , ์–ด๋А ๊ฒƒ์ด ๋งž๋Š”์ง€ ํ‘œ์‹œ๊ฐ€ ์—†์—ˆ์Šต๋‹ˆ๋‹ค.

๊ฐ™์€ ์ข…๋ฅ˜์˜ ํŒŒ์ผ์ด ์—ฌ๋Ÿฌ ๊ฐœ์ผ ๋•Œ๋Š” ๊ฐ ํ•ญ๋ชฉ์ด authority(project-root | package-local)์™€ appliesTo(์ ์šฉ ๋””๋ ‰ํ„ฐ๋ฆฌ)๋ฅผ ๊ฐ–๊ณ , authorityNote๊ฐ€ ์–ด๋А ํ•˜๋‚˜๋„ ํ”„๋กœ์ ํŠธ ์ „์ฒด๋ฅผ ์ง€๋ฐฐํ•˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์‚ฌ์‹ค์„ ์•Œ๋ ค์ค๋‹ˆ๋‹ค. ์ฝ”๋“œ๋ฅผ ์“ฐ๋Š” ํŒŒ์ผ์ด ์–ด๋А appliesTo ์•ˆ์— ์žˆ๋Š”์ง€๋กœ ๊ณจ๋ผ์•ผ ํ•ฉ๋‹ˆ๋‹ค.

devup_ui_validate โ€” ์‹ฌ๊ฐ๋„๋กœ ํŒ์ •ํ•œ๋‹ค

์‘๋‹ต์€ ๋‹ค์Œ์„ ๋‹ด์Šต๋‹ˆ๋‹ค.

ํ•„๋“œ ๋‚ด์šฉ
ok ์‹ฌ๊ฐ๋„๋กœ ํŒ์ •. error๊ฐ€ ์žˆ์œผ๋ฉด ์‹คํŒจ, strict: true๋ฉด warning๋„ ์‹คํŒจ
okReason clean ยท clean-unverified-tokens ยท info-only ยท warnings-only ยท warnings-and-info ยท strict-warnings ยท error-violations
violations[] rule, severity(info|warning|error), byteRange, message, suggestion, ๊ทธ๋ฆฌ๊ณ  ์œ„๋ฐ˜์ด ๋†“์ธ JSX ์—ฌ๋Š” ํƒœ๊ทธ์˜ ๋ฌธ์„œ ์ˆœ์„œ ๋ฒˆํ˜ธ elementIndex(0๋ถ€ํ„ฐ)์™€ elementยทattribute
violationCounts error / warning / info ๊ฐœ์ˆ˜
themeNotes[] ๋น„์–ด ์žˆ๋Š” ํ† ํฐ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ์ข…๋ฅ˜๋งˆ๋‹ค ํ•œ ๋ฒˆ์”ฉ๋งŒ ์š”์•ฝ
tokens referencedByTsx, definedByTheme, unknownTokenCheckRan
checkedTokens / availableTokenCount ์œ„ ๋‘ ๊ฐ’๊ณผ ๊ฐ™์€ ์ˆ˜๋ฅผ ์˜ˆ์ „ ์ด๋ฆ„์œผ๋กœ ์œ ์ง€
themeAvailable / themeGuardrail ํ…Œ๋งˆ๋ฅผ ์ฐพ์•˜๋Š”์ง€, ๋ชป ์ฐพ์•˜์œผ๋ฉด ์™œ
layoutRisks[] ํญ์ด ๋ฐ”๋€Œ๋ฉด ๋“œ๋Ÿฌ๋‚˜๋Š” ์ •์  ๋ ˆ์ด์•„์›ƒ ์œ„ํ—˜(anchor-mismatch, right-aligned-but-held-left, centered-but-held-left, fixed-length-line-mask)
verificationScope ์ •์ ์œผ๋กœ ๊ฒ€์‚ฌํ•œ ๊ฒƒ(static: ์‹คํ–‰ํ•œ ๊ทœ์น™, ํ† ํฐ ๊ฒ€์‚ฌ ์—ฌ๋ถ€, layout ๊ฒ€์‚ฌ์™€ referenceWidth)๊ณผ ๋ Œ๋”ํ•˜์ง€ ์•Š์€ ๊ฒƒ(runtime.state: "not-performed")
sourceMapLink sourceMap์„ ๋„˜๊ฒผ์„ ๋•Œ๋งŒ. linked(linkedViolations, unlinkedViolations) ๋˜๋Š” unavailable

๊ทœ์น™์€ ๋‘ ๊ฐˆ๋ž˜์ž…๋‹ˆ๋‹ค. ์•ž์˜ ์—ฌ์„ฏ์€ tsx ํ•˜๋‚˜๋งŒ ์ค˜๋„ ๋Œ๊ณ , ๋’ค์˜ ๋‹ค์„ฏ์€ files ๋ฒˆ๋“ค๋กœ ์ค„ ๋•Œ๋งŒ ๋•๋‹ˆ๋‹ค โ€” ํŒŒ์ผ ๊ฒฝ๋กœ์™€ ํŒŒ์ผ ์‚ฌ์ด์˜ ๊ด€๊ณ„๋ฅผ ๋ด์•ผ ํŒ์ •ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๊ฐˆ๋ž˜ ๊ทœ์น™
ํ•ญ์ƒ invalid-syntax, unknown-token, hardcoded-color, hardcoded-length, unknown-prop, runtime-value
files ๋ฒˆ๋“ค์—์„œ๋งŒ file-placement, one-component-per-file, client-boundary, inline-style, react-query-states

file-placement๋Š” ํ”„๋ ˆ์ž„์›Œํฌ๊ฐ€ ๊ฐ•์ œํ•˜๋Š” ํŒŒ์ผ์„ ๋ฉด์ œํ•ฉ๋‹ˆ๋‹ค. app/layout.tsx์— default export๊ฐ€ ์—†์œผ๋ฉด ๋นŒ๋“œ๊ฐ€ ๊นจ์ง€๋ฏ€๋กœ, ๊ทธ๊ฑธ ์ง€์ ํ•˜๋Š” ๊ฒƒ์€ ๋”ฐ๋ฅผ ์ˆ˜ ์—†๋Š” ๊ถŒ๊ณ ์ด๊ณ  warning์ด๋ผ strict: true์—์„œ ๋ฉ€์ฉกํ•œ Next.js ์•ฑ์„ ํƒˆ๋ฝ์‹œ์ผฐ์Šต๋‹ˆ๋‹ค. ๋ฉด์ œ ๋Œ€์ƒ์€ App Router์˜ segment ํŒŒ์ผ(page, layout, loading, error, global-error, not-found, template, default)๊ณผ metadataยท์ด๋ฏธ์ง€ ๊ทœ์•ฝ(sitemap, robots, manifest, icon, apple-icon, opengraph-image, twitter-image), Pages Router์˜ _appยท_document, ๊ทธ๋ฆฌ๊ณ  middleware์ž…๋‹ˆ๋‹ค. ๊ทธ ์ด๋ฆ„์ด ์˜ˆ์•ฝ๋œ ๋””๋ ‰ํ„ฐ๋ฆฌ ์•ˆ์—์„œ๋งŒ ๋ฉด์ œ๋˜๋ฏ€๋กœ src/components/error.tsx๋Š” ์—ฌ์ „ํžˆ ์ผ๋ฐ˜ ๋ชจ๋“ˆ๋กœ ํŒ์ •ํ•ฉ๋‹ˆ๋‹ค.

ํŽ˜์ด์ง€ ์ด๋ฆ„ ๊ทœ์น™์€ ๋ผ์šฐํŠธ๊ฐ€ ์‹ค์ œ๋กœ ๋ Œ๋”ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ, ์ฆ‰ default export๋งŒ ๋ด…๋‹ˆ๋‹ค. ์˜ˆ์ „์—๋Š” ํŒŒ์ผ ์•ˆ์˜ ๋ชจ๋“  ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฒ€์‚ฌํ•ด์„œ, ์˜ฌ๋ฐ”๋ฅธ export default function NoticePage๋ฅผ ๊ฐ€์ง„ ํŒŒ์ผ์ด ๊ทธ ์•ˆ์˜ PageContent ํ—ฌํผ ๋•Œ๋ฌธ์— ๊ฒฝ๊ณ ๋ฅผ ๋ฐ›์•˜์Šต๋‹ˆ๋‹ค. page๊ฐ€ ๋ผ์šฐํŠธ ์ด๋ฆ„์ธ ๊ฒƒ๋„ ๋ผ์šฐํ„ฐ ๋””๋ ‰ํ„ฐ๋ฆฌ ์•ˆ์—์„œ๋ฟ์ด๋ผ, src/components/page.tsx๋Š” ๋ผ์šฐํŠธ๋กœ ํŒ์ •ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

one-component-per-file๋„ ๋ผ์šฐํŠธ segment ํŒŒ์ผ์—์„œ๋Š” ์ ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ ํŒŒ์ผ์€ ์ปดํฌ๋„ŒํŠธ ๋ชจ๋“ˆ์ด ์•„๋‹ˆ๋ผ ํ”„๋ ˆ์ž„์›Œํฌ ์ง„์ž…์ ์ด๋ผ ์•ˆ์˜ ํ—ฌํผ๋ฅผ ์ด๋ฆ„์œผ๋กœ importํ•  ์ˆ˜ ์žˆ๋Š” ๊ณณ์ด ์—†๊ณ , ๊ทธ๋ž˜์„œ "๊ฐ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ž๊ธฐ ํŒŒ์ผ๋กœ ์˜ฎ๊ธฐ๋ผ"๊ฐ€ ์†Œ๋น„์ž์—๊ฒŒ ์ฃผ๋Š” ๊ฒƒ์ด ์—†์Šต๋‹ˆ๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ ๋ฐ”๋กœ ์œ„ ๊ทœ์น™๊ณผ ์ถฉ๋Œํ–ˆ์Šต๋‹ˆ๋‹ค โ€” src/app/ ์•„๋ž˜ ํ˜•์ œ ํŒŒ์ผ์€ ๊ทธ ์ž์ฒด๋กœ ์œ„๋ฐ˜์ด๋ฏ€๋กœ, ํ•œ ๋ผ์šฐํŠธ๋งŒ ์“ฐ๋Š” ํ—ฌํผ๋ฅผ ์˜ฎ๊ธธ ์ˆ˜ ์žˆ๋Š” ๊ณณ์ด src/components/๋ฟ์ด์—ˆ์Šต๋‹ˆ๋‹ค. ์ผ๋ฐ˜ ์ปดํฌ๋„ŒํŠธ ํŒŒ์ผ์€ ๊ทธ๋Œ€๋กœ์ด๊ณ , ๋น„๊ณต๊ฐœ ํ—ฌํผ๋„ ์—ฌ์ „ํžˆ ์žก์Šต๋‹ˆ๋‹ค.

tsx๋„ files๋„ ์ฃผ์ง€ ์•Š์€ ํ˜ธ์ถœ์€ ๊ฑฐ์ ˆํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ์ „์—๋Š” ๋นˆ ๋ฌธ์ž์—ด์„ ๊ฒ€์ฆํ•ด ok: true, okReason: "clean"์„ ๋Œ๋ ค์คฌ๋Š”๋ฐ, ๋ณด๋‚ธ ์ ๋„ ์—†๋Š” ์ฝ”๋“œ๊ฐ€ ํ†ต๊ณผํ•œ ๊ฒƒ์ฒ˜๋Ÿผ ์ฝํ˜”์Šต๋‹ˆ๋‹ค.

clean๊ณผ clean-unverified-tokens๋Š” ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ์•ž์€ ๊ฒ€์‚ฌํ–ˆ๊ณ  ๋ฌธ์ œ๊ฐ€ ์—†์—ˆ๋‹ค๋Š” ๋œป์ด๊ณ , ๋’ค๋Š” ์œ„๋ฐ˜์ด ์—†์—ˆ์ง€๋งŒ devup.json์„ ์ฐพ์ง€ ๋ชปํ•ด ์ด ์ฝ”๋“œ๊ฐ€ ์“ด $token์„ ์• ์ดˆ์— ๊ฒ€์‚ฌํ•˜์ง€ ๋ชปํ–ˆ๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค.

๋ชจ๋…ธ๋ ˆํฌ์—์„œ ์–ด๋А devup.json์„ ์“ฐ๋Š”๊ฐ€

ํ”„๋กœ์ ํŠธ ๋ฃจํŠธ์— devup.json์ด ์žˆ์œผ๋ฉด ๊ทธ๊ฒƒ์„ ์”๋‹ˆ๋‹ค. ์—†์œผ๋ฉด ํ”„๋กœ์ ํŠธ ์•ˆ์„ 4๋‹จ๊ณ„๊นŒ์ง€ ์ฐพ๋Š”๋ฐ, ํ›„๋ณด๊ฐ€ ๋‘˜ ์ด์ƒ์ด๋ฉด ์•„๋ฌด๊ฒƒ๋„ ๊ณ ๋ฅด์ง€ ์•Š๊ณ  ๊ฑฐ์ ˆํ•ฉ๋‹ˆ๋‹ค. ๋ฃจํŠธ์— ์—†๋Š” ์—ฌ๋Ÿฌ ํ…Œ๋งˆ ์ค‘ ๋ฌด์—‡๋„ ํ”„๋กœ์ ํŠธ ์ „์ฒด๋ฅผ ์ง€๋ฐฐํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ "๊ทธ" ํ…Œ๋งˆ๋ผ๋Š” ๊ฒƒ์ด ์กด์žฌํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์˜ˆ์ „์—๋Š” ์ •๋ ฌ ์ˆœ์„œ์ƒ ์ฒซ ๋ฒˆ์งธ๋ฅผ ์กฐ์šฉํžˆ ๊ณจ๋ž๊ณ , ๊ทธ ๊ฒฐ๊ณผ ๋ชจ๋…ธ๋ ˆํฌ ๋ฃจํŠธ๋ฅผ ๋„˜๊ธฐ๋ฉด apps/front ์ฝ”๋“œ๋ฅผ apps/admin ํ…Œ๋งˆ๋กœ ๊ฒ€์ฆํ•ด ์‹ค์žฌํ•˜๋Š” ํ† ํฐ์„ unknown-token error๋กœ ๋‹จ์ •ํ•˜๊ณ  ๋‹ค๋ฅธ ์•ฑ์˜ ํ† ํฐ์„ ๊ณ ์น˜๋ผ๊ณ  ์ œ์•ˆํ–ˆ์Šต๋‹ˆ๋‹ค.

์ง€๊ธˆ์€ themeAvailable: false์™€ ํ•จ๊ป˜ themeGuardrail.candidateProjectRoots๊ฐ€ ํ›„๋ณด๋งˆ๋‹ค devupJsonยทprojectRootยทauthorityยทappliesTo๋ฅผ ๋Œ๋ ค์ค๋‹ˆ๋‹ค. ๊ฑฐ๊ธฐ ์ ํžŒ projectRoot ๊ฐ’์„ ๊ทธ๋Œ€๋กœ ๋„˜๊ฒจ ๋‹ค์‹œ ๋ถ€๋ฅด๋ฉด ๋ฉ๋‹ˆ๋‹ค. ํ›„๋ณด๊ฐ€ ํ•˜๋‚˜๋ฟ์ด๋ฉด ์˜ˆ์ „์ฒ˜๋Ÿผ ๊ทธ๋Œ€๋กœ ํ•ด์„ํ•˜๋ฏ€๋กœ ๋‹จ์ผ ํŒจํ‚ค์ง€ ๋ ˆ์ด์•„์›ƒ์˜ ๋™์ž‘์€ ๋ฐ”๋€Œ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํ•˜๋“œ์ฝ”๋”ฉ ๊ฐ’์˜ ํŒ์ • ๊ธฐ์ค€์€ ์ผ์น˜ํ•˜๋Š” ํ† ํฐ์ด ์‹ค์ œ๋กœ ์žˆ๋Š”์ง€์ž…๋‹ˆ๋‹ค. ์žˆ์œผ๋ฉด warning์œผ๋กœ ์˜ฌ๋ฆฌ๊ณ  ํ† ํฐ ์ด๋ฆ„์„ ์•Œ๋ ค์ค๋‹ˆ๋‹ค. ์—†์œผ๋ฉด info๋กœ ๋‚ฎ์ถ”๊ณ  ๊ถŒ๊ณ  ์—†์ด ์‚ฌ์‹ค๋งŒ ์ ์œผ๋ฉฐ strict ๋ชจ๋“œ์—์„œ๋„ ์‹คํŒจ์‹œํ‚ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ length ํ† ํฐ์ด ํ•˜๋‚˜๋„ ์—†๋Š” ํ…Œ๋งˆ๊ฐ€ "ํ† ํฐ์„ ์“ฐ๋ผ"๋Š” ๊ถŒ๊ณ ๋ฅผ ๋ฐ›๋Š” ์ผ์€ ์—†๊ณ , ๋Œ€์‹  themeNotes๊ฐ€ "์ด ํ…Œ๋งˆ์—๋Š” length ํ† ํฐ์ด ์—†๋‹ค"๊ณ  ํ•œ ๋ฒˆ ์•Œ๋ ค์ค๋‹ˆ๋‹ค.

checkedTokens์™€ availableTokenCount๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ๊ฒƒ์„ ์…‰๋‹ˆ๋‹ค โ€” ์•ž์€ ์ด TSX๊ฐ€ ์ฐธ์กฐํ•œ $token ์ˆ˜, ๋’ค๋Š” devup.json์ด ์ •์˜ํ•œ ํ† ํฐ ์ˆ˜์ž…๋‹ˆ๋‹ค. ๋น„์œจ์ด ์•„๋‹™๋‹ˆ๋‹ค. tokens๊ฐ€ ๊ทธ ์‚ฌ์‹ค์„ ์ด๋ฆ„์œผ๋กœ ๋‹ค์‹œ ๋งํ•ด ์ค๋‹ˆ๋‹ค.

์œ„๋ฐ˜์„ Figma ๋…ธ๋“œ์— ์ž‡๊ธฐ โ€” sourceMap๊ณผ layoutRisks

devup_figma_export๊ฐ€ ๋Œ๋ ค์ค€ sourceMap์„ ๊ทธ๋Œ€๋กœ sourceMap์œผ๋กœ ๋„˜๊ธฐ๋ฉด, ๊ฐ ์œ„๋ฐ˜์— ๊ทธ ์†์„ฑ์„ ๋งŒ๋“  Figma ๋…ธ๋“œ์™€ ํ•„๋“œ๊ฐ€ source[](์ตœ๋Œ€ 3๊ฐœ: nodeId, property, generatedProperty, resolution)๋กœ ๋ถ™์Šต๋‹ˆ๋‹ค. ํ•ญ๋ชฉ์€ ํ™”๋ฉด ํ•˜๋‚˜๋ฉด tsx, Section ํ”„๋ ˆ์ž„์ด๋ฉด entries ์•„๋ž˜์— ์žˆ๊ณ  ๋‘˜ ๋‹ค ๋ฐ›์Šต๋‹ˆ๋‹ค. tsx์™€ componentTsx๋ฅผ ํ•จ๊ป˜ ์š”์ฒญํ–ˆ๋‹ค๋ฉด ๊ฒ€์ฆํ•˜๋Š” ์ฝ”๋“œ์˜ sourceMap.byOutput.<output>์„ {"tsx": [...]}๋กœ ๋„˜๊ธฐ์„ธ์š”. ๋ฐ”์ดํŠธ ์˜คํ”„์…‹ ์—†์ด elementIndex์™€ ์†์„ฑ ์ด๋ฆ„์œผ๋กœ ์ž‡์Šต๋‹ˆ๋‹ค โ€” sourceMap ํ•ญ๋ชฉ๋„ ๊ฐ™์€ ๋ฒˆํ˜ธ์˜ elementIndex๋ฅผ ๊ฐ–์Šต๋‹ˆ๋‹ค. elementIndex๊ฐ€ ์—†๋Š” ์˜ˆ์ „ sourceMap์ด๋ฉด sourceMapLink.state๊ฐ€ unavailable์ž…๋‹ˆ๋‹ค. files ๋ฒˆ๋“ค๊ณผ ํ•จ๊ป˜ ๋„˜๊ฒจ๋„ unavailable์ž…๋‹ˆ๋‹ค โ€” ๋ฒˆ๋“ค์€ ํŒŒ์ผ๋งˆ๋‹ค ์š”์†Œ๋ฅผ 0๋ถ€ํ„ฐ ์„ธ๋ฏ€๋กœ, ํ•œ TSX์˜ sourceMap์„ ์–ด๋А ํŒŒ์ผ์˜ ์œ„๋ฐ˜์— ์ด์„์ง€ ์ •ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์ด์–ด์ง€์ง€ ์•Š์€ ์œ„๋ฐ˜์€ unlinkedViolations๋กœ ์„ธ๊ณ  ์ด์œ ๋ฅผ ๋ถ™์ž…๋‹ˆ๋‹ค. sourceMap์ด ๊ทธ ์†์„ฑ์„ ๋งคํ•‘ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ์ด๊ณ , export๋Š” ๊ทธ๋Ÿฐ ์†์„ฑ์„ projectionIssues(DEVUP_CODEGEN_PROPERTY_UNMAPPED)์— ๊ฐ™์€ elementIndex๋กœ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค. WQUW-151 ์‹ค์ œ ํ™”๋ฉด์—์„œ๋Š” 161๊ฑด ์ค‘ 158๊ฑด, 300๊ฑด ์ค‘ 296๊ฑด์ด ์ด์–ด์กŒ๊ณ , ๋‚˜๋จธ์ง€๋Š” ์—ฌ๋Ÿฌ ์Šคํƒ€์ผ ๊ตฌ๊ฐ„์„ ๊ฐ€์ง„ ํ…์ŠคํŠธ ์•ˆ์ชฝ <Text>์˜ color์˜€์Šต๋‹ˆ๋‹ค.

layoutRisks๋Š” ์œ„์น˜ ์ง€์ •๋œ ํ˜•์ œ๋“ค์ด ๊ฐ๊ฐ ์–ด๋А ๊ฐ€์žฅ์ž๋ฆฌ์— ๊ณ ์ •๋๋Š”์ง€ ์ฝ์–ด ์„ธ ๊ฐ€์ง€๋ฅผ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค.

  • anchor-mismatch: ์น ํ•ด์ง„ ํ˜•์ œ ์•ˆ์— ๊ทธ๋ ค์กŒ๋Š”๋ฐ ๋‹ค๋ฅธ ๊ฐ€์žฅ์ž๋ฆฌ์— ๊ณ ์ •๋œ ์ƒ์ž
  • fixed-length-line-mask: ๋Š˜์–ด๋‚˜๋Š” ์ƒ์ž ์•ˆ์—์„œ ๊ทธ๋ฆฐ ๊ธธ์ด๋ฅผ ์ง€ํ‚ค๋Š” mask ์ง์„ 
  • right-aligned-but-held-leftยทcentered-but-held-left: ํญ์ด viewport๋ฅผ ๋”ฐ๋ผ๊ฐ€๋Š” ์ปจํ…Œ์ด๋„ˆ ์•ˆ์—์„œ ์™ผ์ชฝ์— ๊ณ ์ •๋์ง€๋งŒ ์˜ค๋ฅธ์ชฝ ๊ฐ€์žฅ์ž๋ฆฌ๋‚˜ ๊ฐ€์šด๋ฐ์— ๊ทธ๋ ค์ง„ ์ƒ์ž. ์†์œผ๋กœ ๋„“ํžŒ ์‹ค์ œ ๊ณต์ง€ ํ™”๋ฉด์—์„œ Chromium์ด 408px ์–ด๊ธ‹๋‚จ์„ ์žฐ ๋ฐ”๋กœ ๊ทธ ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค. ๋”ฐ๋ผ๊ฐ„๋‹ค๊ณ  ๋ณด๋Š” ์ปจํ…Œ์ด๋„ˆ๋Š” ๋ณด์ˆ˜์ ์œผ๋กœ ๊ณ ๋ฆ…๋‹ˆ๋‹ค: px ํญ์ด ์—†๋Š” ๋ฃจํŠธ, ๊ทธ๋ฆฌ๊ณ  ๊ฐ€๋กœ ์—ฌ๋ฐฑยทpadding ์—†์ด ๊ทธ ์•ˆ์„ ๊ฝ‰ ์ฑ„์šฐ๋Š” in-flow ์ž์‹๋ฟ์ž…๋‹ˆ๋‹ค. ์‹ค์ œ ํ™”๋ฉด 17๊ฐœ๋ฅผ export ๊ทธ๋Œ€๋กœ ๊ฒ€์‚ฌํ–ˆ์„ ๋•Œ๋Š” ํ•˜๋‚˜๋„ ๋ณด๊ณ ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

๋ถ€๋ชจ๊ฐ€ px ํญ์„ ๋งํ•˜์ง€ ์•Š์œผ๋ฉด referenceWidth๋กœ ๊ณ„์‚ฐํ•˜๊ณ , ๊ทธ๊ฒƒ๋„ ์—†์œผ๋ฉด ์ถ”์ธกํ•˜์ง€ ์•Š๊ณ  verificationScope.static.groupsWithoutReferenceWidth๋กœ ์…‰๋‹ˆ๋‹ค.

์ƒˆ ๊ฒ€์‚ฌ๋Š” ok, okReason, strict ํŒ์ •์„ ๋ฐ”๊พธ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด info๊ฐ€ error๋กœ ๋ฐ”๋€Œ๋Š” ์ผ๋„ ์—†์Šต๋‹ˆ๋‹ค โ€” ๊ฒฐ๊ณผ๋Š” violations ๋ฐ–์˜ ๋ณ„๋„ ํ‚ค์—๋งŒ ์‹ค๋ฆฝ๋‹ˆ๋‹ค.

๋น„์šฉ: ์œ„๋ฐ˜๋งˆ๋‹ค elementIndexยทelementยทattribute๊ฐ€ ๋ถ™์–ด ๊ธฐ๋ณธ ์‘๋‹ต์˜ violations๊ฐ€ ์•ฝ 19% ์ปค์ง€๊ณ (WQUW-151์˜ 300๊ฑด ํ™”๋ฉด์—์„œ 88.6 โ†’ 105.5 KB), ๊ฒ€์ฆ ์‹œ๊ฐ„์€ 1.12 โ†’ 1.36 ms๊ฐ€ ๋์Šต๋‹ˆ๋‹ค. sourceMap ์—ฐ๊ฒฐ์€ ๋„˜๊ธธ ๋•Œ๋งŒ ์ผ์–ด๋‚˜๋ฉฐ, ๊ฐ™์€ ํ™”๋ฉด์—์„œ ์š”์ฒญ 176 KBยท์‘๋‹ต 155 KBยทstdio ์™•๋ณต 12.6 ms(๋„˜๊ธฐ์ง€ ์•Š์œผ๋ฉด 5.2 ms)์˜€์Šต๋‹ˆ๋‹ค.

devup_stack_diff โ€” ํœด๋ฆฌ์Šคํ‹ฑ์ด๊ณ , ๊ทธ๋ ‡๊ฒŒ ๋งํ•œ๋‹ค

๋ ˆ์ด์–ด๋Š” db-entity, entity-route, route-openapi, openapi-client ๋„ท์ด๋ฉฐ ์ƒ๋žตํ•˜๋ฉด ์ „๋ถ€ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์•„๋‹ˆ๋ผ ํ…์ŠคํŠธ/JSON ํœด๋ฆฌ์Šคํ‹ฑ์ด๋ฏ€๋กœ ๋ชจ๋“  ๋ฐœ๊ฒฌ์€ confidence๋ฅผ ๋‹ฌ๊ณ  ๋‚˜์˜ค๊ณ , ๊ทธ ๊ฐ’์€ low ๋˜๋Š” medium์ž…๋‹ˆ๋‹ค โ€” high๋Š” ์—†์Šต๋‹ˆ๋‹ค.

_์™€ -์˜ ์ฐจ์ด๋Š” ๋Œ€์กฐํ•  ๋•Œ๋งŒ ์ ‘์Šต๋‹ˆ๋‹ค. Vespera๊ฐ€ openapi.json์— kebab-case๋ฅผ ์“ฐ๊ณ  ์ฝ”๋“œ๋Š” ์›๋ฌธ์„ ์“ฐ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. ๋ณด๊ณ ๋˜๋Š” ๋“œ๋ฆฌํ”„ํŠธ๋Š” ๊ฐ ๋ ˆ์ด์–ด๊ฐ€ ์‹ค์ œ๋กœ ์“ด ์ฒ ์ž ๊ทธ๋Œ€๋กœ ์‹ฃ๊ณ , ์ •๊ทœํ™”๋กœ ์ผ์น˜์‹œํ‚จ ๊ฑด์ˆ˜๋Š” spellingNormalizedMatches๋กœ ๋”ฐ๋กœ ๊ณต๊ฐœํ•ฉ๋‹ˆ๋‹ค. ๊ฐ™์€ ๋ผ์šฐํŠธ๊ฐ€ ์–ธ๋”์Šค์ฝ”์–ดํŒ๊ณผ ํ•˜์ดํ”ˆํŒ์œผ๋กœ ์–‘์ชฝ์— ์ค‘๋ณต ์‹ ๊ณ ๋˜๋˜ ๋ฌธ์ œ๋Š” ์ด ๋ฐฉ์‹์œผ๋กœ ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค.

devup_visual_compare โ€” ๋ Œ๋”ํ•˜์ง€ ์•Š๋Š”๋‹ค

actual.png ํ•˜๋‚˜์™€ reference PNG ํ•˜๋‚˜๋ฅผ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค. reference๋Š” ๊ฒฝ๋กœ๋กœ ์ฃผ๊ฑฐ๋‚˜ artifactId๋กœ ์บ์‹œ๋œ ๊ฒƒ์„ ๊ฐ€๋ฆฌํ‚ต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ threshold๋Š” 0.005(0.5%)์ด๊ณ , ์›ํ•˜๋ฉด diff PNG๋ฅผ auto | inline | resource๋กœ ๋ฐ›์Šต๋‹ˆ๋‹ค.

์ด ๋„๊ตฌ๋Š” ์Šค์Šค๋กœ ๊ทธ๋ฆฌ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋ธŒ๋ผ์šฐ์ €๋ฅผ ๋„์šฐ์ง€๋„, ๋ช…๋ น์„ ์‹คํ–‰ํ•˜์ง€๋„ ์•Š์Šต๋‹ˆ๋‹ค. actual.png๋Š” ์†Œ๋น„์ž repository๊ฐ€ ์ž๊ธฐ fontยทassetยทDevupUI ํ™˜๊ฒฝ์—์„œ ๋งŒ๋“ค์–ด ๊ฑด๋„ค๋Š” ๊ฒƒ์ด๊ณ , ์—ฌ๊ธฐ์„œ๋Š” ์ˆœ์ˆ˜ Rust๋กœ ํ”ฝ์…€๋งŒ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค. ๊ฒฝ๋กœ๋Š” allowlist ์•ˆ์ด์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ํ†ต๊ณผ ์—ฌ๋ถ€๋ณด๋‹ค ํŒ์ • ๊ทผ๊ฑฐ๊ฐ€ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. renderer ํ™˜๊ฒฝ manifest๊ฐ€ ์—†๊ฑฐ๋‚˜ ๋ถˆ์™„์ „ํ•˜๊ฑฐ๋‚˜ ์œ ํšจํ•˜์ง€ ์•Š์œผ๋ฉด, ํ”ฝ์…€์ด ์ผ์น˜ํ•ด visual.passed๊ฐ€ true์—ฌ๋„ ์ตœ์ข… ํŒ์ •์€ inconclusive์ž…๋‹ˆ๋‹ค โ€” ๋ฌด์—‡์œผ๋กœ ๊ทธ๋ฆฐ ๊ทธ๋ฆผ์ธ์ง€ ๋ชจ๋ฅด๋Š” ์ฑ„ "๊ฐ™๋‹ค"๊ณ  ๋งํ•˜๋Š” ๊ฒƒ์€ ๋ณด์ฆ์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์„œ๋ฒ„๊ฐ€ ์Šค์Šค๋กœ ์ฆ๋ช…ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์€ ์„œ๋ฒ„๊ฐ€ ์ฑ„์›๋‹ˆ๋‹ค. reference๋ฅผ artifactId๋กœ ์ฃผ๊ณ  manifest์— referenceResource๋ฅผ ๋น ๋œจ๋ฆฌ๋ฉด, ์ด ์„œ๋ฒ„๊ฐ€ Figma์—์„œ ๋ฐ›์•„ ๋“ค๊ณ  ์žˆ๋Š” ๊ทธ PNG์˜ ํ•ด์‹œ๋กœ ์ฑ„์šฐ๊ณ  environment.filledByServer์— ์ ์Šต๋‹ˆ๋‹ค. ๊ฒฝ๋กœ๋กœ ์ค€ reference๋Š” ์†Œ๋น„์ž์˜ ํŒŒ์ผ์ด๋ผ ์ฑ„์šฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‚˜๋จธ์ง€ manifest ํ•ญ๋ชฉ์€ ์—ฌ์ „ํžˆ ์†Œ๋น„์ž๊ฐ€ ๋งํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

devup_feature_trace โ€” anchor๊ฐ€ ์—†์œผ๋ฉด ๊ฑฐ์ ˆํ•œ๋‹ค

ํ™”๋ฉด๋ถ€ํ„ฐ ์ปฌ๋Ÿผ๊นŒ์ง€ ํ•œ ๊ธฐ๋Šฅ์˜ ์Šฌ๋ผ์ด์Šค๋ฅผ ์ฝ์–ด acceptance matrix๋กœ ๋Œ๋ ค์ค๋‹ˆ๋‹ค. ์ถœ๋ฐœ์ ์€ ๋ช…์‹œ์  anchor์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค โ€” routePath, figmaNodeId ๋˜๋Š” artifactId, operationId ๋˜๋Š” apiPath+method, componentPath, tableName.

์š”๊ตฌ์‚ฌํ•ญ ์‚ฐ๋ฌธ์€ anchor๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. "๋กœ๊ทธ์ธ ํ™”๋ฉด ๋งŒ๋“ค์–ด์ค˜" ๊ฐ™์€ ๋ฌธ์žฅ๋งŒ ์ฃผ๋ฉด ์ถ”์ธกํ•ด์„œ ๋‹ตํ•˜์ง€ ์•Š๊ณ  ๊ฑฐ์ ˆํ•ฉ๋‹ˆ๋‹ค. ๊ทผ๊ฑฐ ์—†์ด ์ด์–ด ๋ถ™์ธ ์Šฌ๋ผ์ด์Šค๋Š” ํ‹€๋ ธ์„ ๋•Œ ์–ด๋””์„œ ํ‹€๋ ธ๋Š”์ง€ ์•Œ ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๊ฐ hop์€ ๊ทผ๊ฑฐ๊ฐ€ ์žˆ๊ฑฐ๋‚˜ UNVERIFIED๋กœ ํ‘œ์‹œ๋˜๋ฉฐ, ์ƒ์„ฑ ์‚ฐ์ถœ๋ฌผ์˜ ์†Œ์œ ๊ถŒ(๊ทธ ํŒŒ์ผ์„ ์†์œผ๋กœ ๊ณ ์น˜๋ฉด ์•ˆ ๋˜๋Š”์ง€), ์ˆœ์œ„๊ฐ€ ๋งค๊ฒจ์ง„ UI ์žฌ์‚ฌ์šฉ ํ›„๋ณด, ๋””์ž์ธ์˜ ๋ฌธ์ž์—ด๊ณผ request/response ํ•„๋“œ์˜ ๋Œ€์‘, ํ•„์ˆ˜ ์ƒํƒœ ์ปค๋ฒ„๋ฆฌ์ง€, ๊ทธ๋ฆฌ๊ณ  ์ž˜๋ฆผ์ด ์ผ์–ด๋‚ฌ๋‹ค๋ฉด ๊ทธ ์ƒํ•œ์„ ํ•จ๊ป˜ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค. requirement์™€ acceptanceCriteria๋ฅผ ๋„˜๊ธฐ๋ฉด ํ•ด์„ํ•˜์ง€ ์•Š๊ณ  ๊ทธ๋Œ€๋กœ ์‹ค์–ด ๋Œ๋ ค์ค๋‹ˆ๋‹ค.

์ •์  ํŒŒ์‹ฑ์€ ๋Ÿฐํƒ€์ž„ ๋™์ž‘์„ ์ฆ๋ช…ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด ๋„๊ตฌ๊ฐ€ "์—ฐ๊ฒฐ๋ผ ์žˆ๋‹ค"๊ณ  ๋งํ•˜๋Š” ๊ฒƒ์€ ์ฝ”๋“œ๊ฐ€ ๊ทธ๋ ‡๊ฒŒ ์ ํ˜€ ์žˆ๋‹ค๋Š” ๋œป์ด์ง€, ์‹คํ–‰ํ–ˆ์„ ๋•Œ ๊ทธ๋ ‡๊ฒŒ ๋™์ž‘ํ•œ๋‹ค๋Š” ๋œป์ด ์•„๋‹™๋‹ˆ๋‹ค.

devup_runtime_audit โ€” ๊ณ„ํšํ•˜๊ณ  ๊ฒ€์ฆํ•  ๋ฟ, ์‹คํ–‰ํ•˜์ง€ ์•Š๋Š”๋‹ค

์ƒ์„ฑํ•œ ํ™”๋ฉด์ด ๊ทธ๋ฆฐ ํญ์ด ์•„๋‹Œ ํญ์—์„œ ์–ด๋–ป๊ฒŒ ๊ทธ๋ ค์ง€๋Š”์ง€๋Š” ๋ Œ๋”ํ•ด ๋ด์•ผ ์••๋‹ˆ๋‹ค. ์ด ๋„๊ตฌ๋Š” ๊ทธ ์‹คํ–‰์„ ๊ณ„ํšํ•˜๊ณ  ๊ฒ€์ฆํ•˜๋ฉฐ, ์‹คํ–‰์€ ์†Œ๋น„์ž ํ”„๋กœ์ ํŠธ๊ฐ€ ํ•ฉ๋‹ˆ๋‹ค. ์„œ๋ฒ„๋Š” ๋ธŒ๋ผ์šฐ์ €๋ฅผ ๋„์šฐ์ง€๋„, ๋ช…๋ น์„ ์‹คํ–‰ํ•˜์ง€๋„, URL์„ ๊ฐ€์ ธ์˜ค์ง€๋„ ์•Š์Šต๋‹ˆ๋‹ค. Playwright๋Š” MCP ์„œ๋ฒ„์˜ ์˜์กด์„ฑ์ด ์•„๋‹ˆ๋ผ ์†Œ๋น„์ž๊ฐ€ ์„ค์น˜ํ•˜๋Š” ์‹คํ–‰๊ธฐ harness/runtime-audit์˜ ์˜์กด์„ฑ์ž…๋‹ˆ๋‹ค.

  1. plan โ€” viewport, ํ™”๋ฉด(routePath, ๋˜๋Š” role๊ณผ ์ ‘๊ทผ ๊ฐ€๋Šฅํ•œ ์ด๋ฆ„ยทtestIdยทํ…์ŠคํŠธ๋กœ ์š”์†Œ๋ฅผ ์ฐพ๋Š” clickยทfill ๋‹จ๊ณ„), ๊ธฐํ•˜ ๊ฒ€์‚ฌ(no-horizontal-overflow, contained, aligned, spans, visible)๋ฅผ ๋‹ด์€ ์„ ์–ธํ˜• ๊ณ„ํš์„ ๊ฒ€์ฆํ•ด ํ—ˆ์šฉ๋œ root ์•„๋ž˜ outputPath์— ์”๋‹ˆ๋‹ค. ๋ชจ๋“  ๊ฐ์ฒด๊ฐ€ ๋ชจ๋ฅด๋Š” ํ•„๋“œ๋ฅผ ๊ฑฐ์ ˆํ•˜๋ฏ€๋กœ ๋ช…๋ น์ด๋‚˜ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‹ค์„ ์ž๋ฆฌ๊ฐ€ ์—†๊ณ , URL์€ ์ž๊ฒฉ์ฆ๋ช… ์—†๋Š” http(s)์—ฌ์•ผ ํ•˜๋ฉฐ, ์‹คํ–‰ ์ค‘ allowedOrigins ๋ฐ–์œผ๋กœ ๊ฐ€๋Š” ์š”์ฒญ์€ ๋ง‰ํž™๋‹ˆ๋‹ค. planId๋Š” ์ •๊ทœํ™”ํ•œ ๊ณ„ํš์˜ SHA-256์ž…๋‹ˆ๋‹ค.
  2. ์‹คํ–‰ โ€” ์†Œ๋น„์ž ํ”„๋กœ์ ํŠธ์—์„œ node run.mjs --plan plan.json --out out. viewport๋งˆ๋‹ค ํ”„๋กœํ•„ยท์ฟ ํ‚คยท์ €์žฅ์†Œ ์—†๋Š” ์ƒˆ ๋ธŒ๋ผ์šฐ์ € context๋ฅผ ์“ฐ๊ณ  report.json๊ณผ ์Šคํฌ๋ฆฐ์ƒท์„ ์”๋‹ˆ๋‹ค. ์‹œ๊ฐ„ ์ƒํ•œ์— ๊ฑธ๋ฆฌ๊ฑฐ๋‚˜ ์ค‘๋‹จ๋˜๋ฉด ๋๋‚ธ ๋งŒํผ์„ partialยทaborted๋กœ ์“ฐ๋ฉฐ, ์ด์–ด์„œ ์‹คํ–‰ํ•˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.
  3. ingest โ€” ๋ณด๊ณ ์„œ๋ฅผ ๊ทธ๋Œ€๋กœ ๋ฏฟ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ณ„ํš id์™€ ๊ณ„ํš ํŒŒ์ผ ํ•ด์‹œ์— ๋ฌถ์ด์ง€ ์•Š์€ ๋ณด๊ณ ์„œ๋‚˜ ๋ฐ”๋€ ์Šคํฌ๋ฆฐ์ƒท์€ rejected(plan-edited, report-for-another-plan, plan-file-differs, artifact-changed)์ด๊ณ , ๋ชจ๋“  ๊ฐœ์ˆ˜๋ฅผ ํ•ญ๋ชฉ์—์„œ ๋‹ค์‹œ ์…‰๋‹ˆ๋‹ค. ์—†๋Š” ํ•ญ๋ชฉ์€ not-run์ด์ง€ ํ†ต๊ณผ๊ฐ€ ์•„๋‹ˆ๋ฉฐ, ํ™˜๊ฒฝ ์˜ค๋ฅ˜(์ฝ˜์†”ยท๋„คํŠธ์›Œํฌ ์˜ค๋ฅ˜, ๋น ์ง„ ํฐํŠธ, ๋‹ค๋ฅธ viewport ํญ)๊ฐ€ ์žˆ์œผ๋ฉด ์‹คํŒจ๋„ inconclusive์ž…๋‹ˆ๋‹ค. ์‘๋‹ต์˜ evidence๊ฐ€ ์„œ๋ฒ„๊ฐ€ ์ง์ ‘ ํ™•์ธํ•œ ๊ฒƒ๊ณผ ์‹คํ–‰๊ธฐ๊ฐ€ ์‹ ๊ณ ๋งŒ ํ•œ ๊ฒƒ์„ ๋‚˜๋ˆ•๋‹ˆ๋‹ค.

์‹œ๊ฐ ์›๋ณธ์€ Figma ํ”„๋ ˆ์ž„์„ ๊ทธ๋ฆฐ ํญ์—๋งŒ ์žˆ์Šต๋‹ˆ๋‹ค(figmaReferences[].visualOriginalAt). ๊ทธ ํญ์˜ ์Šคํฌ๋ฆฐ์ƒท์€ devup_visual_compare๋กœ ๋น„๊ตํ•˜๊ณ , ๋‚˜๋จธ์ง€ ํญ์—์„œ๋Š” ๋ ˆ์ด์•„์›ƒ ๊ฒ€์‚ฌ๋งŒ ์˜๋ฏธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

ingest๋Š” ๊ทธ๋ฆฐ ํญ์—์„œ ์ฐํ˜€ ๊ฒ€์ฆ๋œ ์Šคํฌ๋ฆฐ์ƒท๋งˆ๋‹ค ๋ฐ”๋กœ ๋ถ€๋ฅผ ์ˆ˜ ์žˆ๋Š” devup_visual_compare ํ˜ธ์ถœ์„ visualComparisons[]๋กœ ๋Œ๋ ค์ค๋‹ˆ๋‹ค. audit์ด ์•„๋Š” ๊ฒƒ โ€” ์Šคํฌ๋ฆฐ์ƒท ๊ฒฝ๋กœ(actualยทactualPng), viewport, ์‹คํ–‰๊ธฐ๊ฐ€ ์‹ ๊ณ ํ•œ ๋ธŒ๋ผ์šฐ์ € ๋ฒ„์ „(renderer), OSยทlocaleยทtimezone, ๊ทธ run์— ํ™˜๊ฒฝ ์˜ค๋ฅ˜๊ฐ€ ์žˆ์—ˆ๋Š”์ง€(environmentStatus) โ€” ์€ ์ฑ„์›Œ์ ธ ์žˆ๊ณ , ์†Œ๋น„์ž๋งŒ ๋งํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ(reference์˜ artifactId, tsxResource, themePath, assetDirectory, devupUiVersion, fontManifest)์€ consumerMustAdd๊ฐ€ ์ด๋ฆ„์œผ๋กœ ๋Œ‘๋‹ˆ๋‹ค. ๊ทธ๋Œ€๋กœ ๋ถˆ๋Ÿฌ๋„ inconclusive๊ฐ€ ๋  ์‚ฌ์ • โ€” scale 1์ด ์•„๋‹Œ ์บก์ฒ˜, viewport๋ณด๋‹ค ๊ธด ํŽ˜์ด์ง€์˜ full-page ์บก์ฒ˜, ํ™˜๊ฒฝ ์˜ค๋ฅ˜ โ€” ์€ blockers๊ฐ€ ๋ฏธ๋ฆฌ ๋งํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ์ „์—๋Š” audit ์Šคํฌ๋ฆฐ์ƒท์„ ๋น„๊ต๊ธฐ์— ๋„ฃ์œผ๋ฉด ์ด ํ•ญ๋ชฉ๋“ค์ด ๋น„์–ด ์–ธ์ œ๋‚˜ inconclusive์˜€์Šต๋‹ˆ๋‹ค.

360ยท768ยท1200ยท1440 ํญ ร— ํ™”๋ฉด 2๊ฐœ ร— ๊ฒ€์‚ฌ 8๊ฐœ ๊ณ„ํš์—์„œ plan์€ 4.5 ms(structured content 9.2 KB), ์Šคํฌ๋ฆฐ์ƒท 8์žฅ์„ ํ™•์ธํ•˜๋Š” ingest๋Š” 18.9 ms(12.5 KB)์˜€์Šต๋‹ˆ๋‹ค. ์ค‘๋‹จ๋œ ์‹คํ–‰ ์ด์–ด๊ฐ€๊ธฐ(resume), ๋ถ€์ˆ˜ํšจ๊ณผ๊ฐ€ ์žˆ๋Š” ๋‹จ๊ณ„, ๋ฐ๋ชจ ๋…นํ™”ยทTTSยท์ž๋ง‰, ์ธ๊ณ„ ํŒจํ‚ค์ง€๋Š” ๊ตฌํ˜„ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

devup_screen_plan โ€” ํ™”๋ฉด ๊ณ„ํš ๋จผ์ €, ํ™•์ธ์€ ํ•œ ๋ฒˆ, ๊ตฌํ˜„์€ ๊ทธ ๋‹ค์Œ

"์ด ๋””์ž์ธ์„ ๊ตฌํ˜„ํ•ด์ค˜"๋ฅผ ๋ฐ›์€ ์—์ด์ „ํŠธ๊ฐ€ ๊ณง๋ฐ”๋กœ ํŽ˜์ด์ง€๋ฅผ ์“ฐ๋ฉด, ์–ด๋А Figma ํ™”๋ฉด์ด ์–ด๋А URL์ด ๋๋Š”์ง€ ์‚ฌ์šฉ์ž๋Š” ๋๋‚ด ๋ชจ๋ฆ…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ˆœ์„œ๋ฅผ ๊ณ ์ •ํ•ฉ๋‹ˆ๋‹ค.

  1. draft โ€” Section/frame ๋งํฌ(๋˜๋Š” ๋ธŒ๋ฆฌ์ง€ ์„ ํƒ, ๋˜๋Š” ์•ž์„  devup_figma_explore์˜ screens)์™€ projectRoot๋ฅผ ๋ฐ›์•„, ์ด๋ฆ„์ด ๊ฐ™์€ ํ”„๋ ˆ์ž„์„ ๊ทธ๋ฃน์œผ๋กœ ๋ชจ์œผ๊ณ (ํ•œ ํŽ˜์ด์ง€์ธ์ง€ ์—ฌ๋Ÿฌ ํŽ˜์ด์ง€์ธ์ง€๋Š” ๊ทธ๋ฃน๋งˆ๋‹ค ๋ฌป๋˜, ๊ฐ™์€ Section ์•ˆ์˜ ์—ฌ๋Ÿฌ ํญ์ด๋ฉด "One responsive page", ๋‹ค๋ฅธ Section์ด๋ฉด "Separate pages"๋ฅผ ์ถ”์ฒœ), ํ™”๋ฉด๋งˆ๋‹ค proposedRouteยทrouteEvidenceยทexistingFileยทsharedPartsยทopenDecisions๋ฅผ ๋‹ด์€ .devup/screen-plan.json๊ณผ ๊ฐ™์€ ๋‚ด์šฉ์˜ .devup/screen-plan.md๋ฅผ ์”๋‹ˆ๋‹ค. ๊ทผ๊ฑฐ ์—†๋Š” ๋ผ์šฐํŠธ๋Š” proposedRoute: null, routeEvidence: "UNVERIFIED"์ด๊ณ  ์งˆ๋ฌธ์ด ๋ฉ๋‹ˆ๋‹ค. ์‘๋‹ต์˜ questions๋Š” ์งˆ๋ฌธ ๋„๊ตฌ(OpenCode question, Claude Code AskUserQuestion)์— ๊ทธ๋Œ€๋กœ ๋„ฃ๋Š” ๋ชจ์–‘ โ€” id, 30์ž ์ดํ•˜ header, question, ์ถ”์ฒœ์„ ๋งจ ์•ž์— ๋‘๊ณ  " (Recommended)"๋ฅผ ๋ถ™์ธ options[{label, description}], multiple โ€” ์ด๊ณ , ํ•œ ๋ฒˆ์— ์ตœ๋Œ€ 4๊ฐœ์”ฉ ๋‚˜๋ˆˆ questionRounds์™€ askInstruction์ด ํ•จ๊ป˜ ์˜ต๋‹ˆ๋‹ค. ๋ผ์šด๋“œ๋Š” ๊ตฌํ˜„ ์ „์— ์—ฐ๋‹ฌ์•„ ๋ชจ๋‘ ๋ฌป์Šต๋‹ˆ๋‹ค. when์ด ๋ถ™์€ ์งˆ๋ฌธ(์˜ˆ: "Separate pages"๋ฅผ ๊ณ ๋ฅธ ๊ทธ๋ฃน์˜ ํ”„๋ ˆ์ž„๋ณ„ ๋ผ์šฐํŠธ, "Review individually"๋ฅผ ๊ณ ๋ฅธ ์ด๋ฆ„ ์ถ”์ • ํ•ญ๋ชฉ)์€ ๊ทธ ๋‹ต์ผ ๋•Œ๋งŒ ๋ฌป์Šต๋‹ˆ๋‹ค. ๋ ˆ์ด์–ด ์ด๋ฆ„์œผ๋กœ ์ถ”์ •ํ•œ ๋ผ์šฐํŠธ๋Š” "Accept all (Recommended)" / "Review individually" ํ•œ ์งˆ๋ฌธ์œผ๋กœ ๋ฌถ์–ด ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
  2. confirm โ€” ํ•ด๋‹นํ•˜๋Š” ์งˆ๋ฌธ id๋ณ„ ๋‹ต์„ ํ•œ ๋ฒˆ์— ๋ฐ›์Šต๋‹ˆ๋‹ค. ํ•˜๋‚˜๋ผ๋„ ๋น ์ง€๋ฉด ๋น ์ง„ id๋ฅผ ์ด๋ฆ„์œผ๋กœ ๋Œ€๋ฉฐ ๊ฑฐ์ ˆํ•˜๊ณ , ์•„๋ฌด๊ฒƒ๋„ ๋ฐ”๊พธ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ™•์ • ๋’ค ๊ตฌํ˜„ ์ค‘์—๋Š” ์‚ฌ์šฉ์ž์—๊ฒŒ ์•„๋ฌด๊ฒƒ๋„ ๋ฌป์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ƒˆ๋กœ ์ƒ๊ธด ๊ฒฐ์ •์€ ์ถ”์ฒœ์•ˆ์ด๋‚˜ ๊ฐ€์žฅ ๋ณด์ˆ˜์ ์ธ ์„ ํƒ์„ ๋”ฐ๋ฅด๊ณ  assume์œผ๋กœ ๊ณ„ํš์˜ assumptions์— ๋‚จ๊ธฐ๋ฉฐ, status์™€ ์ตœ์ข… ๋ณด๊ณ ์— ํ•จ๊ป˜ ๋‚˜์˜ต๋‹ˆ๋‹ค.
  3. status โ€” ํ™•์ •๋œ ๊ณ„ํš์„ devup_project_context scope:"routes"์˜ ์‹ค์ œ ํŽ˜์ด์ง€์™€, ์„ ํƒ์ ์œผ๋กœ devup_runtime_audit์˜ plan/report์— ๋Œ€์กฐํ•ฉ๋‹ˆ๋‹ค. URL๋งˆ๋‹ค ๊ตฌํ˜„ ์—ฌ๋ถ€์™€ ํญ๋ณ„ passยทfail(๊ฐ€๋กœ ๋„˜์นจ, ํŽ˜์ด์ง€ ์˜ค๋ฅ˜ ๋“ฑ ์ด์œ )ยทnot-run์„ ๋‚ด๊ณ , ์š”์•ฝ ํ•œ ์ค„(7 URLs: 5 implemented, 3 verified at all widths, /about overflows at 390)๊ณผ URL | Figma screen | file | status | widths verified | screenshots ํ‘œ๋ฅผ ์ค๋‹ˆ๋‹ค. audit ํ•ญ๋ชฉ์ด ์—†๋Š” ํญ์€ not-run์ด์ง€ pass๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค.

devup_figma_export๋Š” projectRoot๊ฐ€ ์žˆ๊ณ  ํŽ˜์ด์ง€๋ฅผ ์“ฐ๋Š” ํ˜ธ์ถœ(pageScaffold write, app/ ์•„๋ž˜ outputPaths, outputPaths๊ฐ€ ์žˆ๋Š” ์—ฌ๋Ÿฌ ํ”„๋ ˆ์ž„ยทallScreens)์ธ๋ฐ ๊ทธ ํ”„๋ ˆ์ž„์„ ๋ฎ๋Š” ํ™•์ • ๊ณ„ํš์ด ์—†์œผ๋ฉด ์“ฐ์ง€ ์•Š๊ณ  status: "plan_required"์™€ devup_screen_plan draft๋กœ ๊ฐ€๋Š” nextAction์„ ๋Œ๋ ค์ค๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ ํ•˜๋‚˜, ์“ฐ์ง€ ์•Š๋Š” ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋Š” ๋ง‰์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ™•์ • ๊ณ„ํš์ด ์žˆ์œผ๋ฉด pageScaffold.route๋Š” ๊ณ„ํš์—์„œ ๊ฐ€์ ธ์˜ค๊ณ , ๋‹ค๋ฅธ ๋ผ์šฐํŠธ๋ฅผ ๋„˜๊ธฐ๋ฉด routeMismatch๋กœ ๊ฑฐ์ ˆํ•ฉ๋‹ˆ๋‹ค. ์ „์ฒด ํ๋ฆ„์€ devup_skills๋กœ ์„ค์น˜ํ•˜๋Š” devup-implementation-plan ์Šคํ‚ฌ์— ์žˆ์Šต๋‹ˆ๋‹ค.

๋””์ž์ด๋„ˆ๋งˆ๋‹ค ์ด๋ฆ„ ์ง“๋Š” ๋ฐฉ์‹์ด ๋‹ฌ๋ผ์„œ, ํ”„๋กœ์ ํŠธ๋Š” ์„ ํƒ์ ์œผ๋กœ .devup/screen-plan.config.json์— modalKeywords(๋ชจ๋‹ฌ๋กœ ๋ณผ ๋ ˆ์ด์–ด ์ด๋ฆ„ ๋‹จ์–ด, ๊ธฐ๋ณธ ๋ชฉ๋ก์„ ๋Œ€์ฒด), routeAliases(๋ ˆ์ด์–ด ์ด๋ฆ„ โ†’ ๋ผ์šฐํŠธ), widthGrouping(ํ•œ Section ์•ˆ์˜ ์—ฌ๋Ÿฌ ํญ: responsive | separate | ask, ๊ธฐ๋ณธ ask), excludeKeywords(๊ตฌํ˜„ ๋Œ€์ƒ์ด ์•„๋‹Œ ์˜์—ญ์„ ํ‘œ์‹œํ•˜๋Š” ๋‹จ์–ด, ๊ธฐ๋ณธ ๋ชฉ๋ก์„ ๋Œ€์ฒด), includeKeywords(์ด ๋‹จ์–ด๊ฐ€ ๋“  ์ด๋ฆ„์€ ์ œ์™ธํ•˜์ง€ ์•Š์Œ)๋ฅผ ๋‘˜ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์žˆ๋Š” ํ•ญ๋ชฉ์€ ๊ทผ๊ฑฐ๋กœ ์“ฐ์ด๊ณ  ๊ทธ์— ํ•ด๋‹นํ•˜๋Š” ์งˆ๋ฌธ์„ ๋Œ€์‹ ํ•ฉ๋‹ˆ๋‹ค.

๋ฒ”์œ„๋Š” ๋งํฌ๊ฐ€ ๊ฐ€๋ฆฌํ‚จ ๋…ธ๋“œ ์•„๋ž˜๋กœ๋งŒ ํ•œ์ •ํ•ฉ๋‹ˆ๋‹ค. ํŒŒ์ผ ์ „์ฒด๋‚˜ ๋‹ค๋ฅธ ํŽ˜์ด์ง€๋กœ ๋„“ํžˆ์ง€ ์•Š๊ณ (outOfScope์— ๊ธฐ๋ก), ์ด๋ฆ„์ด ํœด์ง€ํ†ต/trash, ์•„์นด์ด๋ธŒ/archive, ๊ตฌ๋ฒ„์ „/old, ์‚ฌ์šฉ์•ˆํ•จ/deprecated, ๋ฐฑ์—…/backup, ์ž‘์—…์ค‘/draft/WIP, ํ…Œ์ŠคํŠธ/test, sandbox, playground, ์‚ฌ๋ณธ/copy์ธ ํŽ˜์ด์ง€ยทSectionยทํ”„๋ ˆ์ž„๊ณผ ์ˆจ๊ฒจ์ง„ ํ”„๋ ˆ์ž„์€ ์ง“์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ œ์™ธํ•œ ๊ฒƒ์€ excluded์— ์ด์œ ์™€ ํ•จ๊ป˜ ๋‚จ๊ธฐ๊ณ  .md์™€ ํ™•์ธ ์งˆ๋ฌธ("These N were excluded ... Accept the exclusions (Recommended) / Review individually")์— ๋ณด์—ฌ ์ค๋‹ˆ๋‹ค. ๋งํฌ ์ž์ฒด๊ฐ€ ๊ทธ๋Ÿฐ ์˜์—ญ ์•ˆ์„ ๊ฐ€๋ฆฌํ‚ค๋ฉด ์ œ์™ธํ•˜์ง€ ์•Š๊ณ  ํ•œ ๋ฒˆ ๋ฌป์Šต๋‹ˆ๋‹ค.

๊ฐ™์€ ์ด๋ฆ„์˜ ํ”„๋ ˆ์ž„์ด ์„œ๋กœ ๋‹ค๋ฅธ Section์— ์žˆ์œผ๋ฉด(PC/Mobile์ฒ˜๋Ÿผ ๊ธฐ๊ธฐ ์ด๋ฆ„ Section ํฌํ•จ) ์„œ๋ฒ„๊ฐ€ ์ •ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. Section ์ด๋ฆ„ยทํ”„๋ ˆ์ž„ ์ด๋ฆ„ยทํญยทnodeId์™€ ๊ด€๋ จ ์žˆ์–ด ๋ณด์ด๋Š” ์ด์œ ๋ฅผ ์ ์€ ์งˆ๋ฌธ์œผ๋กœ "Separate pages: the sections are different pages", "One responsive page: the sections only split by device"(๋ชจ๋“  Section ์ด๋ฆ„์ด ๊ธฐ๊ธฐ ์ด๋ฆ„์ผ ๋•Œ๋งŒ ์ถ”์ฒœ), "The Figma structure is wrong: I will fix it" ์ค‘์—์„œ ๋ฌป์Šต๋‹ˆ๋‹ค. ํ•œ ํ™”๋ฉด์ด ์—ฌ๋Ÿฌ Section์— ๋‚˜๋‰œ ๊ฒฝ์šฐ, Section ๋ฐ–์— ํ™€๋กœ ์žˆ๋Š” ํ”„๋ ˆ์ž„, ํ•œ Section ์•ˆ์˜ ๊ฐ™์€ ์ด๋ฆ„ยท๊ฐ™์€ ํญ ์ค‘๋ณต๋„ ๊ฐ™์€ "๊ณ ์น˜๊ฒ ๋‹ค" ์„ ํƒ์ง€๋กœ ๋ฌป์Šต๋‹ˆ๋‹ค. ๊ณ ์น˜๊ฒ ๋‹ค๊ณ  ํ•œ ํ”„๋ ˆ์ž„์€ ์ง“์ง€ ์•Š๊ณ  status์™€ ์ตœ์ข… ๋ณด๊ณ ์˜ "Design fixes requested"์— ์˜ฎ๊ธฐ๊ฑฐ๋‚˜ ์ด๋ฆ„์„ ๋ฐ”๊ฟ€ ํ”„๋ ˆ์ž„๊ณผ Section์„ ์ •ํ™•ํžˆ ์ ์Šต๋‹ˆ๋‹ค. ์—์ด์ „ํŠธ๋Š” ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ๋‚˜๋จธ์ง€๋ฅผ ๊ณ„์†ํ•ฉ๋‹ˆ๋‹ค. export gate๋Š” ์ œ์™ธ๋˜์—ˆ๊ฑฐ๋‚˜ ๋””์ž์ธ ์ˆ˜์ • ๋Œ€์ƒ์ธ ํ”„๋ ˆ์ž„์˜ ํŽ˜์ด์ง€ ์“ฐ๊ธฐ๋ฅผ ๊ฐ€์ •(assume)์ด ์žˆ์–ด๋„ ๊ฑฐ์ ˆํ•ฉ๋‹ˆ๋‹ค.

์‚ฌ์ดํŠธ ์™„์„ฑ๋„: Figma๊ฐ€ ๊ทธ๋ฆฐ ๊ฒƒ๋งŒ์œผ๋กœ ๋งŒ๋“  ์‚ฌ์ดํŠธ๋Š” hoverยทfocusยท์ „ํ™˜ยทmetadataยท์•„์ด์ฝ˜์ด ์—†์–ด "์ฃฝ์–ด" ๋ณด์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๊ณ„ํš์€ ๋‘˜๋กœ ๋‚˜๋ˆ•๋‹ˆ๋‹ค. ์—์ด์ „ํŠธ๊ฐ€ ๋ฌป์ง€ ์•Š๊ณ  ์ฑ„์šฐ๋Š” ๊ฒƒ(defaults์— ๊ธฐ๋ก, ์ตœ์ข… ๋ณด๊ณ ์— ํ‘œ์‹œ): ๋ชจ๋“  ๋งํฌยท๋ฒ„ํŠผยท์นด๋“œยทํƒญยท์ž…๋ ฅ์˜ _hoverยท_focusVisibleยท_activeยท_disabledยทcursor(Figma ์ƒํƒœ variant ์šฐ์„ , ์—†์œผ๋ฉด ๊ธฐ์กด ํ…Œ๋งˆ ํ† ํฐ์œผ๋กœ๋งŒ ๋งŒ๋“  ์ž‘์€ ๋Œ€์ฒด โ€” ์ƒˆ ์ƒ‰์€ ๋งŒ๋“ค์ง€ ์•Š์Œ), ํ•œ ๋ฒˆ๋งŒ ์ •์˜ํ•œ ์•ฝ 0.2s transition, _motionReduce, ๋ฃจํŠธยทํŽ˜์ด์ง€๋ณ„ metadata(openGraph ํฌํ•จ, devfive-frontend ๊ทœ์น™), <html lang>, alt, ์ด๋ฏธ์ง€ ํฌ๊ธฐ, not-found ํŽ˜์ด์ง€. ๊ฐ™์€ ์‚ฌ์ „ ์งˆ๋ฌธ ๋ผ์šด๋“œ์—์„œ ๋ฌป๋Š” ๊ฒƒ: ํŒŒ๋น„์ฝ˜ยท๊ณต์œ (OG) ์ด๋ฏธ์ง€(๊ธฐ์กด ํŒŒ์ผ, ๋ฒ”์œ„ ์•ˆ์˜ Figma ๋กœ๊ณ /og ๋…ธ๋“œ, ์ง์ ‘ ์ œ๊ณต ์ค‘์—์„œ โ€” ์ง์ ‘ ๊ทธ๋ฆฌ๊ฑฐ๋‚˜ ์ƒ์„ฑํ•˜์ง€ ์•Š์Œ), ํ”„๋กœ์ ํŠธ์— ์—†์„ ๋•Œ๋งŒ ์‚ฌ์ดํŠธ ์ด๋ฆ„ยท์šด์˜ URL, ํ”์ ์ด ์žˆ์„ ๋•Œ๋งŒ ๋ถ„์„/์ธ์ฆ ํƒœ๊ทธ, hover ์ด์ƒ์˜ ๋ชจ์…˜("None" ์ถ”์ฒœ). .devup/screen-plan.config.json์˜ faviconยทogImageยทsiteNameยทsiteUrlยทmotion์œผ๋กœ ๋ฏธ๋ฆฌ ๋‹ตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. status๋Š” ํŒŒ๋น„์ฝ˜, URL๋ณ„ metadata, OG ์ด๋ฏธ์ง€, hover/focus ์ ์šฉ ๋น„์œจ, reduced-motion์„ filled / asked-and-answered / missing์œผ๋กœ ๋ณด๊ณ ํ•˜๊ณ , devup_ui_validate๋Š” ์ƒํƒœ ์—†๋Š” ์ƒํ˜ธ์ž‘์šฉ ์š”์†Œ(interactive-states), transition ์—†๋Š” _hover(hover-transition), metadata ์—†๋Š” page/layout(page-metadata), projectRoot์— ์•„์ด์ฝ˜์ด ์—†๋Š” ์•ฑ(app-icon)์„ ์•Œ๋ฆฝ๋‹ˆ๋‹ค.

์ƒํ˜ธ์ž‘์šฉ์€ ์—ญํ• ๋ณ„ ๋ ˆ์‹œํ”ผ๋กœ ์ฑ„์›๋‹ˆ๋‹ค(๋ฒ„ํŠผยท๋ณด์กฐ ๋ฒ„ํŠผยทํ…์ŠคํŠธ ๋งํฌยท๋งํฌ ์นด๋“œยท๋‚ด๋น„/ํƒญยท์•„์ด์ฝ˜ ๋ฒ„ํŠผยท์ž…๋ ฅยท์•„์ฝ”๋””์–ธยท๋ชจ๋ฐ”์ผ ๋ฉ”๋‰ด๋งˆ๋‹ค hover, pressed, focus ๋ง, ํ˜„์žฌ ํŽ˜์ด์ง€(aria-current), ํŽผ์นจ(aria-expanded), disabled, loading). ์ ˆ์ œ๊ฐ€ ๊ทœ์น™์ž…๋‹ˆ๋‹ค: Figma ์ƒํƒœ variant ์šฐ์„ , ๊ธฐ์กด ํ† ํฐ๋งŒ, ์ƒํƒœ๋‹น ์†์„ฑ 2๊ฐœ ์ดํ•˜, 150โ€“250ms ํ•œ ๋ฒˆ ์ •์˜, bounceยท๋ฌดํ•œ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ธˆ์ง€, ์ด๋™ 4pxยทscale 0.97โ€“1.05 ์ด๋‚ด, _motionReduce์—์„œ ๋ชจ๋‘ ๋”, ์ƒํ˜ธ์ž‘์šฉ ์—†๋Š” ์š”์†Œ๋Š” hover ์—†์Œ. ํŽ˜์ด์ง€ ์ „ํ™˜(View Transitions)์€ ํ•ญ์ƒ ์ผญ๋‹ˆ๋‹ค: ์˜์กด์„ฑ ์—…๋ฐ์ดํŠธ๊ฐ€ ๋จผ์ € ๋Œ์•„ ํ•ญ์ƒ ์ตœ์‹  NextยทReact์ด๋ฏ€๋กœ ๋ฒ„์ „์„ ๋ณด์ง€ ์•Š๊ณ  import { ViewTransition } from 'react'๋ฅผ ๊ฐ page.tsx์— ์”๋‹ˆ๋‹ค(์งง์€ cross-fade, reduced-motion์—์„œ ๋”). ์„ค์ • ํŒŒ์ผ์˜ viewTransitions: false๋‚˜, updateDependencies: false๋กœ React 19.3 ๋ฏธ๋งŒ์— ๋จธ๋ฌธ ๊ฒฝ์šฐ์—๋งŒ ๊ฑด๋„ˆ๋œ๋‹ˆ๋‹ค. ๋™์ž‘์€ ์˜์‹ฌํ•˜์ง€ ์•Š๊ณ  ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค: runtime audit์˜ view-transition ๊ฒ€์‚ฌ๊ฐ€ ๋งํฌ๋ฅผ ๋”ฐ๋ผ๊ฐ€ document.startViewTransition ํ˜ธ์ถœ(๋˜๋Š” cross-document pagereveal)์„ ํ™•์ธํ•˜๊ณ , ์‹คํŒจ๋Š” ๊ณ ์น  ๋ฒ„๊ทธ์ž…๋‹ˆ๋‹ค. ์ €์žฅ์†Œ์— ์•ฑ์ด ์—ฌ๋Ÿฟ์ด๋ฉด ์ด๋ฆ„ ์ˆœ์„œ๊ฐ€ ์•„๋‹ˆ๋ผ ์˜๋ฏธ๋กœ ๊ณ ๋ฆ…๋‹ˆ๋‹ค: ๋งˆ์ผ€ํŒ… ํ™”๋ฉด์€ ๊ณต๊ฐœ ์‚ฌ์ดํŠธ(apps/front), "Admin - ํšŒ์›๊ด€๋ฆฌ" ๊ฐ™์€ ํ™”๋ฉด์€ apps/admin, ์„ž์ธ ํŒŒ์ผ์€ ํ™”๋ฉด๋งˆ๋‹ค ์•ฑ์„ ์ •ํ•ฉ๋‹ˆ๋‹ค(route ํ‘œ์˜ app ์—ด). ๊ธฐ์ค€์€ "๊ด€๋ฆฌ์ž๋ผ๋Š” ๋ถ„๋ช…ํ•œ ๊ทผ๊ฑฐ๊ฐ€ ์—†์œผ๋ฉด ๊ณต๊ฐœ ์‚ฌ์ดํŠธ"๋ผ์„œ, ๋‹จ์„œ ์—†๋Š” ํ™”๋ฉด๋„ ๊ณต๊ฐœ ์‚ฌ์ดํŠธ๋กœ ๊ฐ€๊ณ  ๊ณต๊ฐœ ์‚ฌ์ดํŠธ๋กœ ๋ณด์ด๋Š” ์•ฑ์ด ํ•˜๋‚˜๋„ ์—†์„ ๋•Œ๋งŒ ๋ฌป์Šต๋‹ˆ๋‹ค. ์—์ด์ „ํŠธ ์ž์‹ ์˜ ์งˆ๋ฌธ์€ extraQuestions๋กœ ๊ฐ™์€ ์‚ฌ์ „ ๋ผ์šด๋“œ์— ๋„ฃ๊ณ , ๋์€ ์ œ์ถœ ์ฒดํฌ๋ฆฌ์ŠคํŠธ(๋ชจ๋“  URL ๊ตฌํ˜„, buildยทlintยทtypecheck ํ†ต๊ณผ, validate ์˜ค๋ฅ˜ ์—†์Œ, ์•„๋ž˜ audit์˜ ์‹คํŒจ 0, ์‚ฌ์ดํŠธ ์™„์„ฑ๋„ ์ฑ„์›€, ์ตœ์ข… ๋ณด๊ณ ์„œ)์ž…๋‹ˆ๋‹ค.

๋ชจ๋“  ๊ฒƒ์„ ์‹œ๊ฐ์œผ๋กœ ๊ฒ€์ฆํ•˜๊ณ , ๋ฉˆ์ถ”์ง€ ์•Š๋Š”๋‹ค

๊ทœ์น™์€ ์…‹์ž…๋‹ˆ๋‹ค: ๋ชจ๋“  ํ™”๋ฉดยท์„น์…˜ยท์—์…‹์„ ๊ทธ๋ ค์ง„ ๋ชจ๋“  ํญ์—์„œ ์‹œ๊ฐ์œผ๋กœ ๊ฒ€์ฆํ•œ๋‹ค, ๊ธฐ๋‹ค๋ฆฌ๋А๋ผ ๋ฉˆ์ถ”์ง€ ์•Š๊ณ  ์ž‘์—… ์ˆœ์„œ๋ฅผ ๋ฐ”๊พผ๋‹ค, ์™„๋ฃŒ = ์‹คํŒจ 0, ์•„๋‹ˆ๋ฉด ์ฆ๊ฑฐ์™€ ํ•จ๊ป˜ unresolved๋กœ ๋‚จ๊ธด๋‹ค.

  • devup_screen_plan action: "auditPlan"(ํ™•์ • ๋’ค)์€ ํ™•์ •๋œ URL๋งˆ๋‹ค runtime audit ๊ณ„ํš์„ .devup/runtime-audit/<slug>/plan.json์— ์“ฐ๊ณ  ์‹คํ–‰๊ธฐ๋ฅผ .devup/runtime-audit/runner/์— ํ•œ ๋ฒˆ ์”๋‹ˆ๋‹ค. artifacts์— ๊ทธ ๊ณ„ํš ํ”„๋ ˆ์ž„๋“ค์˜ devup_figma_export artifactId๋ฅผ ๋ชจ๋‘ ๋„˜๊น๋‹ˆ๋‹ค. ํญ์€ ๊ทธ๋ ค์ง„ ํญ(๋ทฐํฌํŠธ ๋†’์ด = ํ”„๋ ˆ์ž„ ๋†’์ด) + 1440/1100/858/390์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  ํญ์—์„œ page health(ํŽ˜์ด์ง€ ์˜ค๋ฅ˜ยท์ฝ˜์†” ์˜ค๋ฅ˜ยท"This page couldn't load"/"Application error" ํ…์ŠคํŠธ ์—†์Œ), ๊ฐ€๋กœ ๋„˜์นจ, ์ž˜๋ฆฐ ํ…์ŠคํŠธ, ๋‚ด๋น„ ์ž˜๋ฆผ์„ ๋ณด๊ณ , ๊ทธ๋ ค์ง„ ํญ์—์„œ๋Š” ํŽ˜์ด์ง€ ์ „์ฒด๋ฅผ referencePng์™€, ํ”„๋ ˆ์ž„์˜ ์ตœ์ƒ์œ„ ์ž์‹(์„น์…˜)๊ณผ ์—์…‹์„ ๊ฐ์ž์˜ native-node ๋ Œ๋”์™€ ๋น„๊ตํ•˜๊ณ , ์„น์…˜ยท์นด๋“œยท์—์…‹์˜ ํฌ๊ธฐ์™€ ์œ„์น˜(bounds ๊ฒ€์‚ฌ; ์นด๋“œ ์ˆœ์„œ๊ฐ€ ํ‹€๋ฆฌ๋ฉด ์‹คํŒจ)๋ฅผ ๋ด…๋‹ˆ๋‹ค. ๋งํฌยท๋ฒ„ํŠผ ์ „์ฒด์˜ hover/focus์™€ ๋‹ค์Œ URL๋กœ์˜ view-transition ๊ฒ€์‚ฌ๋„ ๋“ค์–ด๊ฐ€๋ฉฐ, view-transition์€ ์ฝ”๋“œ๊ฐ€ ๋งจ ๋’ค๋กœ ๋ณด๋‚ด๊ณ  ์‹คํ–‰๊ธฐ๊ฐ€ ๋๋‚˜๋ฉด ์›๋ž˜ URL๋กœ ๋Œ์•„์˜ต๋‹ˆ๋‹ค. ๋น ์ง„ referencePngยท๋ Œ๋”ยทํ”„๋ ˆ์ž„์€ missingReferences์— ๋งŒ๋“œ๋Š” export ํ˜ธ์ถœ๊ณผ ํ•จ๊ป˜ ๋‚˜์˜ต๋‹ˆ๋‹ค โ€” ์กฐ์šฉํžˆ ๊ฑด๋„ˆ๋›ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ์„น์…˜ยท์นด๋“œยท์—์…‹์€ data-figma-node-id๋กœ ์ฐพ์Šต๋‹ˆ๋‹ค. projectRoot๊ฐ€ ์žˆ๋Š” export๋Š” ํŽ˜์ด์ง€ ๋ฃจํŠธ, ์„น์…˜, ๊ทธ ์ž์‹, ์—์…‹์— ์ด ์†์„ฑ์„ ์”๋‹ˆ๋‹ค(projectRoot ์—†๋Š” export๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ๊ณผ ๊ฐ™์€ ์ถœ๋ ฅ ๊ทธ๋Œ€๋กœ). ๋„๋ ค๋ฉด ์„ค์ • figmaNodeIds: false, ํ”„๋กœ๋•์…˜์—์„œ ๋นผ๋ ค๋ฉด Next์˜ compiler.reactRemoveProperties: { properties: ["^data-figma-node-id$"] }.
  • status์— auditPlanPathยทauditReportPath๋ฅผ ์ฃผ๋ฉด ๋ชจ๋“  ์‹คํŒจ๋ฅผ ๋ถ„๋ฅ˜ํ•ฉ๋‹ˆ๋‹ค: runtime-error, overflow, layout-position, layout-size, asset-mismatch(๋ฉ€ํ‹ฐ์ปฌ๋Ÿฌ SVG์— mask, ์ž˜๋ชป๋œ ํŒŒ์ผ, crop/objectFit ์ˆœ์œผ๋กœ ์›์ธ ์ œ์‹œ), section-visual, page-visual, interaction, view-transition โ€” ๊ฐ๊ฐ Figma ๋…ธ๋“œ, ์ฝ”๋“œ ํŒŒ์ผ, ๊ตฌ์ฒด์ ์ธ ์ˆ˜์ • ํžŒํŠธ๋ฅผ ๋‹ด์Šต๋‹ˆ๋‹ค. ๋ฐ˜ํ™˜๋œ visualComparisons ํ˜ธ์ถœ(thresholdยทdiffPath ํฌํ•จ)์„ ๋Œ๋ฆฌ๊ณ  ๊ฒฐ๊ณผ๋ฅผ visualResults๋กœ ๋„˜๊น๋‹ˆ๋‹ค. ์•ˆํ‹ฐ์•จ๋ฆฌ์–ด์‹ฑ์€ ์‹คํŒจ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค: ๋ฐ”๋€ ํ”ฝ์…€ ๋น„์œจ ํ—ˆ์šฉ์น˜๊ฐ€ ํŽ˜์ด์ง€ยท์„น์…˜ 3%, ์—์…‹ 5%์ด๊ณ , ์„ค์ • visualThresholds(page, section, asset, ๋…ธ๋“œ๋ณ„ nodes)๋กœ ์กฐ์ •ํ•ฉ๋‹ˆ๋‹ค.
  • ๊ฐ™์€ finding์€ ์ƒˆ ์‹คํ–‰๋งˆ๋‹ค ํ•œ ๋ฒˆ๋งŒ ์‹œ๋„๋กœ ์…‰๋‹ˆ๋‹ค. maxFixAttempts(์„ค์ •, ๊ธฐ๋ณธ 3)๋ฒˆ ๊ณ ์น˜๊ณ ๋„ ์‹คํŒจํ•˜๋ฉด ์Šคํฌ๋ฆฐ์ƒทยทdiff ๊ฒฝ๋กœ์™€ ํ•จ๊ป˜ unresolved๋กœ ๊ธฐ๋กํ•˜๊ณ  ์ž‘์—… ํ์—์„œ ๋บ๋‹ˆ๋‹ค. workQueue๋Š” URL๋งˆ๋‹ค build โ†’ audit โ†’ fix โ†’ compare ์ˆœ์ด๊ณ  ๋ง‰ํžŒ ์ผ์€ ๋’ค๋กœ ๊ฐ‘๋‹ˆ๋‹ค.
  • ํ™•์ • ๋’ค ์ƒ๊ธด ๊ฒฐ์ •์€ action: "defer"(decision: {id, about, question, options, recommended, destructive})๋กœ ๋‚จ๊ธฐ๊ณ  ๊ณ„์† ์ผํ•ฉ๋‹ˆ๋‹ค. ๋งˆ์ง€๋ง‰์— assume(about: ๊ฒฐ์ • id)์œผ๋กœ ์ถ”์ฒœ์•ˆ์„ ํƒํ•˜๊ณ , ํŒŒ๊ดด์ ์ธ ๊ฒฐ์ •์€ ๋งŒ๋“ค์ง€ ์•Š์€ ์ฑ„ ๋ณด๊ณ ์„œ์— ๋‚จ์Šต๋‹ˆ๋‹ค. ์‹คํ–‰๊ธฐ๋ฅผ ์„ค์น˜ํ•  ์ˆ˜ ์—†์œผ๋ฉด toolingUnavailable๋กœ ๊ทธ URL์˜ audit๋ฅผ inconclusive๋กœ ๋‘๊ณ  ๊ณ„์†ํ•ฉ๋‹ˆ๋‹ค.
  • status์˜ finalReport: URL ร— ํญ ํ‘œ(health, geometry, drift / converter bugs, page, section, asset, interaction, ์Šคํฌ๋ฆฐ์ƒท, diff), converter bug์™€ repro ํŒŒ์ผ, unresolved finding๊ณผ ์ฆ๊ฑฐ, ์ฒ˜๋ฆฌ๋œ ๋ณด๋ฅ˜ ๊ฒฐ์ •, ๊ฐ€์ •, ๋””์ž์ธ ์ˆ˜์ • ์š”์ฒญ, ์ œ์™ธ, ์˜์กด์„ฑ ์ค„.

๋ ˆ์ด์•„์›ƒ์€ ์ •ํ™•ํžˆ ๋งž์•„์•ผ ํ•œ๋‹ค

Figma ํ”„๋ ˆ์ž„์€ ์ •์ ์œผ๋กœ ๋ณ€ํ™˜๋˜๋ฏ€๋กœ, ๊ทธ๋ ค์ง„ ํญ์—์„œ ํฌ๊ธฐยท์œ„์น˜ยทpaddingยทgap์€ "๋Œ€์ถฉ ๋งž๋Š”๊ฐ€"๊ฐ€ ์•„๋‹ˆ๋ผ ๋งž๊ฑฐ๋‚˜ ํ‹€๋ฆฌ๊ฑฐ๋‚˜์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ bounds ๊ฒ€์‚ฌ์—๋Š” ์„ค์ • ๊ฐ€๋Šฅํ•œ ํ—ˆ์šฉ์น˜๊ฐ€ ์—†๊ณ  ๋ฐ˜์˜ฌ๋ฆผ 0.5px๋งŒ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค(Chromium์˜ getBoundingClientRect๋Š” 1/64px LayoutUnit์„ DPR 1์—์„œ ํ”ฝ์…€์— ๋งž์ถ”์ง€ ์•Š๊ณ  ๋Œ๋ ค์ฃผ๊ณ , Figma์˜ ์†Œ์ˆ˜ px๋Š” ๋ณ€ํ™˜๊ธฐ๊ฐ€ ๋ฐ˜์˜ฌ๋ฆผํ•ด ์”๋‹ˆ๋‹ค). ๊ณ„ํš๋œ ํฐํŠธ๊ฐ€ ๋จผ์ € ๋กœ๋“œ๋˜๋ฏ€๋กœ ํ…์ŠคํŠธ ๋ฐ•์Šค๋„ ์ •ํ™•ํ•ด์•ผ ํ•˜๋ฉฐ, ํฐํŠธ๊ฐ€ ์—†์œผ๋ฉด ๋ ˆ์ด์•„์›ƒ ์‹คํŒจ๊ฐ€ ์•„๋‹ˆ๋ผ ํ™˜๊ฒฝ ์˜ค๋ฅ˜์ž…๋‹ˆ๋‹ค. ์ด๋ฏธ์ง€ยทํ”ฝ์…€ ๋น„๊ต๋งŒ ์•ˆํ‹ฐ์•จ๋ฆฌ์–ด์‹ฑ ์ž„๊ณ„๊ฐ’(ํŽ˜์ด์ง€ยท์„น์…˜ 3%, ์—์…‹ 5%)์„ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ ค์ง„ ํญ ์‚ฌ์ด์—๋Š” ๋””์ž์ธ์ด ์—†์œผ๋ฏ€๋กœ health, ๋„˜์นจ, ํฌํ•จ, ์ž˜๋ฆฐ ํ…์ŠคํŠธ, ๋‚ด๋น„๋งŒ ๋ด…๋‹ˆ๋‹ค.

๋ถˆ์ผ์น˜๋Š” ์Šน์ธํ•˜์ง€ ์•Š๊ณ  ์›์ธ์œผ๋กœ ๋ถ„๋ฅ˜ํ•ฉ๋‹ˆ๋‹ค. projectRoot๊ฐ€ ์žˆ๋Š” export๋Š” ๋…ธ๋“œ๋งˆ๋‹ค ์“ด ๋‚ด์šฉ์„ .devup/exports/<๋…ธ๋“œ>.json์— ๊ธฐ๋กํ•˜๊ณ , status๊ฐ€ ์‹คํŒจํ•œ ์š”์†Œ์˜ ์ฝ”๋“œ๋ฅผ ๊ทธ ๊ธฐ๋ก๊ณผ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค.

  • code-drift: export๊ฐ€ ์“ด ์†์„ฑ์ด ๋น ์กŒ๊ฑฐ๋‚˜ ๋ฐ”๋€Œ์—ˆ๊ฑฐ๋‚˜, export๊ฐ€ ์“ฐ์ง€ ์•Š์€ ๋ ˆ์ด์•„์›ƒ ์†์„ฑ์ด ๋”ํ•ด์กŒ์Šต๋‹ˆ๋‹ค(ํ•ธ๋“ค๋Ÿฌยทhover ์Šคํƒ€์ผยท๋งํฌ ์ถ”๊ฐ€๋Š” drift๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค). ํ•ด๋ฒ•์€ export ๊ฐ’ ๋ณต์›์ด๋ฉฐ, ๋ ˆ์ด์•„์›ƒ์„ ์†์œผ๋กœ ์กฐ์ •ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • converter-bug: ์ฝ”๋“œ๊ฐ€ export ๊ทธ๋Œ€๋กœ์ธ๋ฐ ๋ธŒ๋ผ์šฐ์ € ๋ฐ•์Šค๊ฐ€ Figma์™€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. devup-mcp ๊ฒฐํ•จ์ด๋ฏ€๋กœ ์žฌํ˜„ ์ •๋ณด(file key, node id, Figma ๋ฐ•์Šค, DOM ๋ฐ•์Šค, ์ƒ์„ฑ๋œ props, sourceMap ํ•ญ๋ชฉ)๋ฅผ .devup/converter-bugs/<๋…ธ๋“œ>.json์— ์“ฐ๊ณ  converterBugs์™€ ์ตœ์ข… ๋ณด๊ณ ์„œ์— ๋‚จ๊น๋‹ˆ๋‹ค. ๊ฒฐ์ •์ ์ธ ๊ฒฐ๊ณผ๋ผ ์žฌ์‹œ๋„ํ•˜์ง€ ์•Š์œผ๋ฉฐ maxFixAttempts๋„ ์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์‚ฌ์ดํŠธ๋ฅผ ๋งˆ์น˜๋ ค๊ณ  ๋กœ์ปฌ override๋ฅผ ๋‘˜ ์ˆ˜๋Š” ์žˆ์ง€๋งŒ repro ํŒŒ์ผ์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ์ฃผ์„์ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

ํญ๋งˆ๋‹ค ํ”„๋ ˆ์ž„์ด ๋”ฐ๋กœ ์žˆ๋Š” ํŽ˜์ด์ง€๋ฅผ responsiveTsx๋กœ ํ•ฉ์น˜๋ฉด ๊ฐ ์š”์†Œ๊ฐ€ data-figma-node-id(๊ฐ€์žฅ ๋„“์€ ํญ์˜ ๋…ธ๋“œ)์™€ data-figma-node-ids="1440:12:34 390:56:78"(๊ทธ๋ ค์ง„ ํญ๋งˆ๋‹ค ํญ:๋…ธ๋“œ, ๊ณต๋ฐฑ ๊ตฌ๋ถ„, [data-figma-node-ids~="390:56:78"]๋กœ ์„ ํƒ)๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค. audit ๊ณ„ํš์€ ํญ๋งˆ๋‹ค ๊ทธ id๋กœ ์„น์…˜ยท์นด๋“œยท์—์…‹์„ ์ฐพ๊ณ , ์–ด๋–ค ํญ์—์„œ ๊ทธ๋ฆฌ์ง€ ์•Š์€ ์š”์†Œ๋Š” ๊ทธ ํญ์—์„œ ์ˆจ๊ฒจ์ ธ ์žˆ๋Š”์ง€(hidden ๊ฒ€์‚ฌ) ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

๋™์  ๋ผ์šฐํŠธ([slug], [id], catch-all)๋Š” notAudited๋กœ ๋„˜๊ธฐ์ง€ ์•Š๊ณ  ์‹ค์ œ URL๋กœ ๊ฒ€์‚ฌํ•ฉ๋‹ˆ๋‹ค: ๋ผ์šฐํŠธ์˜ generateStaticParams(๋ฆฌํ„ฐ๋Ÿด, ๋˜๋Š” ์ฝ๋Š” ๋กœ์ปฌ ๋ฐ์ดํ„ฐ ํŒŒ์ผ) โ†’ ์ด๋ฏธ ๊ฒ€์‚ฌํ•œ ํŽ˜์ด์ง€์˜ a[href](์‹คํ–‰๊ธฐ๊ฐ€ ํŽ˜์ด์ง€๋งˆ๋‹ค ๋ชจ์œผ๊ณ  ๋‹ค์Œ auditPlan์ด ์‚ฌ์šฉ) โ†’ ๋งคํ•‘๋œ Figma ์ƒ์„ธ ํ”„๋ ˆ์ž„์˜ ์ œ๋ชฉ์œผ๋กœ ๋งŒ๋“  ์ƒ˜ํ”Œ(๊ฐ€์ •์œผ๋กœ ๊ธฐ๋ก) ์ˆœ์„œ์ด๋ฉฐ, 10๊ฐœ ์ดํ•˜๋ฉด ์ „๋ถ€ ๊ฒ€์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์…‹ ๋‹ค ์—†์„ ๋•Œ๋งŒ ์ด์œ ์™€ ํ•จ๊ป˜ notAudited์ž…๋‹ˆ๋‹ค.

data-figma-node-id(s)๋Š” ํ”„๋กœ๋•์…˜์— ๋‚จ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. vinext์™€ devup-ui Vite ํ”Œ๋Ÿฌ๊ทธ์ธ์—๋Š” ์†์„ฑ์„ ์ง€์šฐ๋Š” ์˜ต์…˜์ด ์—†์–ด์„œ, auditPlan์ด ๊ฐ ์•ฑ์˜ vite.config ์˜†์— ์ž‘์€ Vite ํ”Œ๋Ÿฌ๊ทธ์ธ(vite-plugin-strip-figma-node-ids.mjs)์„ ๋‘ก๋‹ˆ๋‹ค. enforce: 'pre', apply๊ฐ€ vite build์ผ ๋•Œ๋งŒ์ด๋ฉฐ DEVUP_AUDIT=1์ด๋ฉด ๋‚จ๊น๋‹ˆ๋‹ค. Rolldown์€ transform ํ›… ๋’ค์— TSX/JSX๋ฅผ ๋ณ€ํ™˜ํ•˜๋ฏ€๋กœ ์ด ๋‹จ๊ณ„์—์„œ๋Š” ์†์„ฑ์ด ๊ทธ๋Œ€๋กœ JSX ํ…์ŠคํŠธ์ž…๋‹ˆ๋‹ค. plugins: [stripFigmaNodeIds(), vinext()]๋กœ ์—ฐ๊ฒฐํ•˜๊ณ , audit๋Š” dev ์„œ๋ฒ„๋‚˜ DEVUP_AUDIT=1 bun run build๋กœ ๋Œ๋ฆฌ๋ฉฐ, ์ œ์ถœ ์ฒดํฌ๋ฆฌ์ŠคํŠธ๋Š” ๋นŒ๋“œ ์ถœ๋ ฅ์—์„œ grep -rl data-figma-node-id dist๊ฐ€ ์•„๋ฌด๊ฒƒ๋„ ์ฐพ์ง€ ์•Š์•„์•ผ ํ†ต๊ณผํ•ฉ๋‹ˆ๋‹ค. next CLI์—์„œ๋Š” compiler.reactRemoveProperties๋ฅผ ์”๋‹ˆ๋‹ค.

์ตœ์‹  ์Šคํƒ์ด ๊ธฐ๋ณธ์ž…๋‹ˆ๋‹ค: Next App Router๋ฅผ vinext(Vite)๋กœ ์‹คํ–‰ํ•˜๊ณ , bun workspaces(apps/*), devup-ui, oxlint(eslint-plugin-devup), ์ตœ์‹  ๋ฒ„์ „. vinext ์˜์กด์„ฑยทvinext ์Šคํฌ๋ฆฝํŠธยทvinext๋ฅผ importํ•˜๋Š” vite.config๊ฐ€ ์žˆ์œผ๋ฉด next ์˜์กด์„ฑ์ด ์—†์–ด๋„ App Router ์•ฑ์œผ๋กœ ๋ณด๊ณ , devup_project_context์˜ ui/routes๊ฐ€ ์•ฑ๋ณ„ frameworks๋ฅผ ์•Œ๋ ค ์ค๋‹ˆ๋‹ค. ๊ตฌํ˜„ ์ „์—๋Š” ๋ฌป์ง€ ์•Š๊ณ  ์ €์žฅ์†Œ ๋ฃจํŠธ์—์„œ bunx @dependency-check-updates/cli -d -u --rm(-d: ๋ฃจํŠธ ์•„๋ž˜ ๋ชจ๋“  manifest, -u: ๋ณต๊ตฌ ๊ฐ€๋Šฅํ•œ ์ผ๊ด„ ์ ์šฉ, --rm: ๊ฐ manifest ์˜†์˜ lockfileยท์„ค์น˜ ๋””๋ ‰ํ„ฐ๋ฆฌ ์‚ญ์ œ โ€” Cargo.lockยทtarget/ ํฌํ•จ) โ†’ bun install โ†’ buildยทlintยทtypecheck๋ฅผ ํ•˜๊ณ , ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ API๋Š” ๊ธฐ์–ต์ด ์•„๋‹ˆ๋ผ ํ˜„์žฌ ๋ฌธ์„œ๋ฅผ ๋ด…๋‹ˆ๋‹ค. ๊ฒฐ๊ณผ๋Š” assume์˜ dependencyUpdate๋กœ ๋‚จ๊ธฐ๊ณ  status์— dependencies ์ค„๋กœ ๋‚˜์˜ต๋‹ˆ๋‹ค. ์•ฑ์ด ์—†์œผ๋ฉด dev-five-git/service-template์—์„œ ์‹œ์ž‘ํ•˜๊ณ (์ด๋ฆ„ยท๊ตฌ์„ฑ๋งŒ ์งˆ๋ฌธ), next CLI ํ”„๋กœ์ ํŠธ์—๋Š” "Migrate to vinext (Recommended) / Keep next"๋ฅผ ํ•œ ๋ฒˆ ๋ฌป์Šต๋‹ˆ๋‹ค. ์„ค์ • ํŒŒ์ผ์˜ frameworkยทupdateDependencies: false๋กœ ๋ฏธ๋ฆฌ ๋‹ตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ช…์‹œ์  ์‹œ์ž‘์  /devup [figma url] [notes]: devup_skills install์ด devup ์Šคํ‚ฌ์„ ๋‹ค๋ฅธ ์Šคํ‚ฌ๊ณผ ํ•จ๊ป˜ ์“ฐ๊ณ (Claude Code๋Š” ํ”„๋กœ์ ํŠธ ์Šคํ‚ฌ์„ ๊ฐ™์€ ์ด๋ฆ„์˜ ์Šฌ๋ž˜์‹œ ๋ช…๋ น์œผ๋กœ ์ œ๊ณตํ•˜๋ฏ€๋กœ .claude/skills/devup/SKILL.md๊ฐ€ ๊ณง /devup), OpenCode์—๋Š” .opencode/command/devup.md๋„ ์”๋‹ˆ๋‹ค. ๋ช…๋ น ์œ„์น˜๊ฐ€ ๊ฒ€์ฆ๋˜์ง€ ์•Š์€ ๋‹ค๋ฅธ ๋Ÿฐํƒ€์ž„์€ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค๊ณ  ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค. devup_skills status์˜ command๊ฐ€ ์„ค์น˜ ์—ฌ๋ถ€๋ฅผ ์•Œ๋ ค ์ค๋‹ˆ๋‹ค. /devup ์—†์ด๋„ Figma๋กœ ํŽ˜์ด์ง€๋‚˜ ์‚ฌ์ดํŠธ๋ฅผ ๋งŒ๋“ค์–ด ๋‹ฌ๋ผ๋Š” ์š”์ฒญ์ด๋ฉด ๊ฐ™์€ ํ๋ฆ„์„ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.

์งˆ๋ฌธ ๋„๊ตฌ์— ํ˜ธ์ถœ๋‹น ๊ฐœ์ˆ˜ ์ œํ•œ์ด ์—†์œผ๋ฉด(OpenCode question) ํ‰ํ‰ํ•œ questions ๋ชฉ๋ก์„ ํ•œ ๋ฒˆ์— ๋ฌป๊ณ , ์ œํ•œ์ด ์žˆ์œผ๋ฉด(Claude Code AskUserQuestion: 4) maxQuestionsPerRound(๊ธฐ๋ณธ 4)๋กœ ๋‚˜๋ˆˆ questionRounds๋ฅผ ์—ฐ๋‹ฌ์•„ ๋ฌป์Šต๋‹ˆ๋‹ค.

์ฝ๊ธฐ ์ „์šฉยท๊ฐœ์ธ์ •๋ณด ๋ณดํ˜ธ

  • upstream ํ˜ธ์ถœ์€ get_metadata, get_variable_defs, get_design_context, get_code_connect_map, get_screenshot๊ณผ ๋‚ด์žฅ๋œ read-only use_figma script๋กœ ๋‹ซํ˜€ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ์ž ์ž…๋ ฅ JavaScript๋ฅผ ๋ฐ›์ง€ ์•Š์œผ๋ฉฐ Figma document mutation API๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. figma.io.write๋Š” asset export(devup_figma_export์˜ assetRequests)์—๋งŒ read-only๋กœ ์‚ฌ์šฉํ•˜๋ฉฐ Figma ํŒŒ์ผ์„ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. fast snapshot/theme envelope๋Š” ํ•ญ์ƒ ํ…์ŠคํŠธ๋กœ๋งŒ ๋ฐ˜ํ™˜๋˜๋ฉฐ ๋ฐ”์ด๋„ˆ๋ฆฌ transport๋ฅผ ์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • stdout์—๋Š” MCP frame๋งŒ ์ถœ๋ ฅํ•˜๊ณ  trace๋Š” stderr๋กœ ๋ณด๋ƒ…๋‹ˆ๋‹ค.
  • access token, refresh token, OAuth code, PKCE verifier๋Š” Debug, trace์™€ MCP error์— ํฌํ•จํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • Figma snapshot๊ณผ screenshot์„ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋””์Šคํฌ์— ์ €์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • devup_runtime_audit๋Š” ๋ธŒ๋ผ์šฐ์ €๋ฅผ ๋„์šฐ๊ฑฐ๋‚˜ ๋ช…๋ น์„ ์‹คํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ณ„ํš์€ ์‹คํ–‰๋˜์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ์ด๊ณ , ์‹คํ–‰๊ธฐ์˜ ์Šคํฌ๋ฆฐ์ƒท์€ ์†Œ๋น„์ž์˜ ๋ณด๊ณ ์„œ ๋””๋ ‰ํ„ฐ๋ฆฌ์—๋งŒ ๋‚จ์œผ๋ฉฐ ์‘๋‹ต์—๋Š” ํ•ด์‹œ์™€ ๊ฒ€์ฆ ์ƒํƒœ๋งŒ ์‹ค๋ฆฝ๋‹ˆ๋‹ค.
  • screenshot, asset, TSX resource๋Š” bounded memory artifact์™€ ๊ฐ™์€ TTL์„ ๊ฐ€์ง€๋ฉฐ resource manifest์—๋Š” ์ด๋ฆ„ยทMIMEยทํฌ๊ธฐยทhash๋งŒ ๋…ธ์ถœ๋ฉ๋‹ˆ๋‹ค.
  • ํ˜ธํ™˜์„ฑ fixture๋Š” ๊ณ ์ •ํ•œ JavaScript ํ”Œ๋Ÿฌ๊ทธ์ธ์˜ 268๊ฐœ synthetic ์ž…๋ ฅ์ž…๋‹ˆ๋‹ค. ๋ณ„๋„์˜ WQUW-151 ํšŒ๊ท€ fixture๋Š” ๊ณต์‹ MCP์—์„œ read-only๋กœ ์ˆ˜์ง‘ํ•œ ๋””์ž์ธ node/ํ…์ŠคํŠธ/token ์ด๋ฆ„๋งŒ ํฌํ•จํ•˜๋ฉฐ OAuth token, header, callback parameter, ์‚ฌ์šฉ์ž ๊ณ„์ •ยทemail์€ ํฌํ•จํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

ํ”Œ๋Ÿฌ๊ทธ์ธ ํ˜ธํ™˜์„ฑ corpus

fixtures/devup-figma-plugin์€ dev-five-git/devup-figma-plugin์˜ ๊ณ ์ • commit 243db650f1d635ab5385546a2a297eae4ea93515์—์„œ ์ˆ˜์ง‘ํ•œ 54๊ฐœ test file๊ณผ 978๊ฐœ passing-test inventory๋ฅผ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค. upstream test 252๊ฐœ๊ฐ€ ๋งŒ๋“  JSON/golden 268์Œ์€ Rust serde/codegen ๊ฒฝ๋กœ์—์„œ byte parity๋ฅผ ์ „๋ถ€ ์‹คํ–‰ํ•˜๊ณ , 666๊ฐœ๋Š” ๊ฐ™์€ ๋™์ž‘ ์˜์—ญ์˜ ์‹ค์ œ Rust assertion์— ์—ฐ๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋จธ์ง€๋Š” plugin module/codegen handler/iframe/notify/browser download ์ˆ˜๋ช…์ฃผ๊ธฐ 38๊ฐœ์™€ read-only MCP๊ฐ€ ์˜๋„์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š๋Š” Figma document/style/import write 22๊ฐœ์ž…๋‹ˆ๋‹ค. not_ported๋Š” 0๊ฐœ์ด๋ฉฐ, ๋น„-parity ํ•ญ๋ชฉ๋„ ๊ตฌ์ฒด์ ์ธ MCP ๊ฒฝ๊ณ„ test๋ฅผ ๊ฐ€๋ฆฌํ‚ต๋‹ˆ๋‹ค. ์ฆ‰ ์ •ํ™•ํ•œ ๋ณด์žฅ์€ โ€œ268/268 snapshot byte parity, 666๊ฐœ ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ๋Œ€ํ‘œ Rust assertion ์—ฐ๊ฒฐ, 60๊ฐœ ๋ช…์‹œ์  runtime/write ๊ฒฝ๊ณ„, 978-entry inventoryโ€์ด๊ณ  JavaScript assertion 978๊ฐœ๋ฅผ ๊ฐ๊ฐ ๋ณ„๋„ fixture๋กœ ๋ณต์ œํ–ˆ๋‹ค๋Š” ๋œป์€ ์•„๋‹™๋‹ˆ๋‹ค. manifest๋Š” LF๋กœ ์ •๊ทœํ™”ํ•œ fixture์™€ snapshot 536๊ฐœ ํŒŒ์ผ์˜ SHA-256์„ ๊ฒ€์ฆํ•˜๊ณ , coverage registry๋Š” ledger๊ฐ€ ์‹ค์ œ Rust test symbol ๋˜๋Š” ๊ทผ๊ฑฐ๊ฐ€ ์žˆ๋Š” ๋น„-parity ๋ถ„๋ฅ˜๋งŒ ์ฐธ์กฐํ•˜๋„๋ก ๊ฐ•์ œํ•ฉ๋‹ˆ๋‹ค. ์ƒ์„ธ ๋ถ„๋ฅ˜์™€ ์‹คํ–‰ ๋ฐฉ๋ฒ•์€ fixtures/devup-figma-plugin/README.md๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.

์‹ค์ œ Figma JSON contract gate

crates/devup-mcp/tests/live_figma_contract.rs๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ignore๋ฉ๋‹ˆ๋‹ค. DEVUP_MCP_LIVE_FIGMA=1์„ ์„ค์ •ํ•˜๊ณ  ๊ณต์‹ MCP์˜ fast use_figma ๊ฒฐ๊ณผ๋ฅผ stdin์— ํ•œ ์ค„๋กœ ์ „๋‹ฌํ•˜๋ฉด ์‹ค์ œ payload๋ฅผ ๋””์Šคํฌ์— ์“ฐ๊ฑฐ๋‚˜ ์ถœ๋ ฅํ•˜์ง€ ์•Š๊ณ  envelope ๋ฌด๊ฒฐ์„ฑ, serde round-trip, ์š”์ฒญ context, node/๋ฆฌ์†Œ์Šค ์ˆ˜์™€ DevupUI codegen์„ ๊ฒ€์ฆํ•˜๊ณ  ์•ˆ์ „ํ•œ count/hash ์š”์•ฝ๋งŒ ์ถœ๋ ฅํ•ฉ๋‹ˆ๋‹ค. ๋ณ„๋„์˜ ๋น„-ignore corruption test๋Š” ๊นจ์ง„ fast ์‘๋‹ต์ด legacy metadata ์ˆ˜์ง‘์œผ๋กœ ์›์ž์ ์œผ๋กœ ํด๋ฐฑํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

crates/devup-mcp-figma/tests/explore_script_behavior.mjs๋Š” compile-in explore.js ์ž์ฒด๋ฅผ mock Figma scene graph์—์„œ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๋‘ ๋‹จ๊ณ„ ์ด์ƒ ์ค‘์ฒฉ๋œ ํ™”๋ฉด์˜ parent chain, ํ™”๋ฉด์ด ์—†๋Š” 1,000-node Section์˜ projectionLimit * 8 ๋ฐฉ๋ฌธ ์ƒํ•œ, ํ•„์ˆ˜ node๋งŒ ๋‚จ๊ธฐ๋Š” 14,000์ž ์ดํ•˜ fallback์„ ๊ฒ€์ฆํ•˜๋ฉฐ CI์˜ Node ๋‚ด์žฅ test runner๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์ œํ’ˆ binary์™€ ๊ธฐ๋ณธ Cargo test์—๋Š” JavaScript runtime ์˜์กด์„ฑ์ด ์ถ”๊ฐ€๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

legacy ๊ฒฝ๋กœ์—์„œ ์‹ค์ œ ํ™•์ธ๋œ ๊ณต์‹ metadata๋Š” XML text content envelope์ด๋ฉฐ, local ๋ณ€์ˆ˜/style์€ catalog ํ›„ resource ๋‹จ์œ„๋กœ ์ˆ˜์ง‘ํ•ฉ๋‹ˆ๋‹ค. style์˜ consumers์ฒ˜๋Ÿผ ๋‹จ์ผ field๊ฐ€ ๊ณต์‹ MCP์˜ text ์ƒํ•œ(์‹ค์ธก 20,480 UTF-8 ๋ฐ”์ดํŠธ, ๋„˜๋Š” ๋งŒํผ ์ž˜๋ฆฌ๊ณ  // truncated to 20kb๊ฐ€ ๋ถ™์Œ)์„ ๋„˜์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, base field์™€ 320๊ฐœ ๋‹จ์œ„์˜ compact consumer relation์„ ๋ถ„๋ฆฌํ•ด ์ฝ๊ณ  Rust์—์„œ ์›๋ž˜ exhaustive JSON shape๋กœ ์žฌ์กฐ๋ฆฝํ•ฉ๋‹ˆ๋‹ค. legacy node snapshot๋„ byte budget๊ณผ cursor๋ฅผ ์‚ฌ์šฉํ•ด ๊ฐ™์€ ์ƒํ•œ ์•„๋ž˜์—์„œ ์ž๋™ ์žฌ๊ฐœํ•ฉ๋‹ˆ๋‹ค. range์˜ ๋ˆ„๋ฝยท์ค‘๋ณต์ด๋‚˜ ์ˆ˜์ง‘ ์ค‘ ๋ชฉ๋ก ๋ณ€๊ฒฝ์€ ์„ฑ๊ณต์œผ๋กœ ์ˆจ๊ธฐ์ง€ ์•Š๊ณ  ์˜ค๋ฅ˜๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

Server module ownership

server/mod.rs๋Š” MCP tool router, service construction, handoff ์—ฐ๊ฒฐ๊ณผ ServerHandler๋งŒ ์†Œ์œ ํ•ฉ๋‹ˆ๋‹ค. projection.rs๋Š” TSX/theme/source-map/asset/reference output ์ƒ์„ฑ๊ณผ delivery transaction์„, validation.rs๋Š” output/schema/artifact capability ์ž…๋ ฅ ๊ฒ€์ฆ์„ ๋‹ด๋‹นํ•ฉ๋‹ˆ๋‹ค. delivery.rs, artifacts.rs, output.rs, resources.rs, quality.rs๋Š” ๊ฐ๊ฐ ํฌ๊ธฐ ๊ฒฐ์ •, memory artifact, allowlisted filesystem transaction, MCP resource protocol, typed ํ’ˆ์งˆ ์ง‘๊ณ„๋ฅผ ๋‹ด๋‹นํ•˜๋ฉฐ source-level boundary test๊ฐ€ generator์™€ filesystem ๊ตฌํ˜„์ด router๋กœ ๋˜๋Œ์•„์˜ค๋Š” ๊ฒƒ์„ ๋ง‰์Šต๋‹ˆ๋‹ค.

2026-09-01 ์‹ค์ œ ํŒŒ์ผ ๊ฒ€์ฆ์—์„œ๋Š” 13๊ฐœ page ์ „์ฒด ๊ฒ€์ƒ‰์œผ๋กœ [FR-026] ๋ณธ์—ฐ์ฒด Section (4217:7743)์„ ์ฐพ๊ณ , ๊ทธ ์•ˆ์˜ 360ร—740 ํ™”๋ฉด 10๊ฐœ๋ฅผ ์‹œ๊ฐ ์ˆœ์„œ๋Œ€๋กœ ์ธ๋ฑ์‹ฑํ•ด A : STORY-F-PROOFREAD (3879:35518)๋ฅผ ์ •ํ™•ํ•œ ๋Œ€์ƒ์œผ๋กœ ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค. Section ์ „์ฒด fast envelope๋Š” 8 MiB ์•ˆ์ „ ์ƒํ•œ์„ ๋„˜์–ด์„œ๋ฏ€๋กœ ์„ฑ๊ณต์œผ๋กœ ์˜ค์ธํ•˜์ง€ ์•Š๊ณ , ๊ฐ ํ™”๋ฉด์„ ๊ณต์‹ read-only MCP๋กœ ๊ฐœ๋ณ„ ์ˆ˜์ง‘ํ–ˆ์Šต๋‹ˆ๋‹ค. ์—ด ํ™”๋ฉด์€ ๊ฐ๊ฐ 15210๊ฐœ node๋ฅผ ๊ฐ€์ง€๋ฉฐ ๋ชจ๋“  child, styled text segment, ๋ณ€์ˆ˜ 325๊ฐœ์™€ text style 2~13๊ฐœ์˜ ์ฐธ์กฐ ์™„์ „์„ฑ์„ ์‹ค์ œ JSON fixture์™€ DevupUI TSX snapshot์œผ๋กœ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค. ๋Œ€ํ‘œ proofread ํ™”๋ฉด์€ ๊ณต์‹ read-only MCP 1ํšŒ, 3๊ฐœ PNG envelope ์ฒญํฌ์—์„œ 144๊ฐœ node, ๋ณ€์ˆ˜ 20๊ฐœ์™€ text style 11๊ฐœ๋ฅผ ์ˆ˜์ง‘ํ–ˆ๊ณ  ํด๋ฐฑ์€ ์—†์—ˆ์Šต๋‹ˆ๋‹ค. instance children, concrete boolean property, mixed typography, nested [1. ์ด๋ฆ„], token binding๊ณผ ๊ฐœ๋ณ„ footer stroke๋„ Rust snapshot/live contract๋กœ ๊ฒ€์ฆํ–ˆ์Šต๋‹ˆ๋‹ค. ๊ฐ™์€ ํŒŒ์ผ์˜ ์ „์ฒด theme export๋Š” legacy ๊ณต์‹ read-only ํ˜ธ์ถœ 89๊ฐœ๋ฅผ ํ†ตํ•ด collection 1๊ฐœ, variable 49๊ฐœ, style 37๊ฐœ, mode 2๊ฐœ๋ฅผ ์ˆ˜์ง‘ํ•ด 42,794์ž devup.json์„ ์ƒ์„ฑํ–ˆ์œผ๋ฉฐ diagnostics๋Š” 0๊ฐœ์˜€์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ full-theme fast collector๋Š” ๊ฐ™์€ collection/variable/style ์ „์ฒด๋ฅผ ๋‹จ์ผ read-only use_figma ํ˜ธ์ถœ๋กœ ์ˆ˜์ง‘ํ•˜๊ณ , envelope ๊ฒ€์ฆ ์‹คํŒจ ์‹œ์—๋งŒ ์ด legacy ๊ฒฝ๋กœ๋ฅผ 0๋ถ€ํ„ฐ ๋‹ค์‹œ ์‹œ์ž‘ํ•˜๋„๋ก contract test๋กœ ๊ณ ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

Snapshot ์˜๋ฏธ์™€ ํ˜„์žฌ ํ•œ๊ณ„

Figma Remote MCP์—์„œ๋Š” JSON_REST_V1 export๊ฐ€ ํ—ˆ์šฉ๋˜์ง€ ์•Š์œผ๋ฏ€๋กœ host object๋ฅผ ๊ทธ๋Œ€๋กœ REST JSON์œผ๋กœ ๋งŒ๋“ค ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋Œ€์‹  checked-in property manifest์™€ runtime prototype/enumerable ํƒ์ƒ‰์„ ํ•จ๊ป˜ ์‚ฌ์šฉํ•ด ๋ชจ๋“  ๋ฐœ๊ฒฌํ•œ data field์˜ key์™€ ์ฝ๊ธฐ ๊ฒฐ๊ณผ๋ฅผ ๋ณด์กดํ•ฉ๋‹ˆ๋‹ค. ํ•จ์ˆ˜, ์ˆœํ™˜ node object๋Š” ์ œ์™ธํ•˜๊ฑฐ๋‚˜ id๋กœ ๋ฐ”๊พธ๊ณ , binary asset์€ bytes ๋Œ€์‹  metadata๋กœ ๋‚˜ํƒ€๋‚ด๋ฉฐ, ํƒ€ plugin private data์™€ ์˜ค๋ฅ˜๋ฅผ ๋‚ด๋Š” getter๋Š” ์ฝ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋‹จ์ผ ๊ฐ’์ด byte budget์„ ๋„˜์œผ๋ฉด key๋ฅผ ์—†์• ์ง€ ์•Š๊ณ  { "$truncated": ..., "byteLength": ... }์™€ DEVUP_FIELD_VALUE_TRUNCATED๋ฅผ ๋‚จ๊ธฐ๋ฉฐ, characters, styled segment, resource binding์ฒ˜๋Ÿผ UI ๋ณ€ํ™˜์— ํ•„์š”ํ•œ ๊ฐ’์€ ์šฐ์„  ๋ณด์กดํ•ฉ๋‹ˆ๋‹ค.

ํ˜„์žฌ private MVP์˜ ๋‚จ์€ ํ•œ๊ณ„๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

  • ๊ณต์‹ get_metadata์˜ file-level page ๋ชฉ๋ก์€ ์‹ค์ œ page ์ „์ฒด๋ณด๋‹ค ์ ๊ฒŒ ๋ฐ˜ํ™˜๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋ฆ„ ๊ฒ€์ƒ‰์€ Plugin API page catalog์™€ per-page projection์œผ๋กœ ์šฐํšŒํ•˜๋ฉฐ ์‹ค์ œ 13๊ฐœ page ํŒŒ์ผ์—์„œ ๊ฒ€์ฆํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๋งค์šฐ ํฐ computed field(์˜ˆ: vector fillGeometry)๋Š” ํ˜„์žฌ ๊ฐ’ ์ „์ฒด ๋Œ€์‹  ๋ช…์‹œ์ ์ธ byte-length marker๋กœ ๋ณด์กด๋ฉ๋‹ˆ๋‹ค. ๋ชจ๋“  ๋Œ€์šฉ๋Ÿ‰ field ๊ฐ’์„ losslessํ•˜๊ฒŒ exportํ•˜๋Š” ๊ธฐ๋Šฅ์€ ํ›„์† wire-format ๊ฐœ์„  ๋Œ€์ƒ์ž…๋‹ˆ๋‹ค.
  • exact-node fast envelope๊ฐ€ 8 MiB ์•ˆ์ „ ์ƒํ•œ์„ ๋„˜๊ฑฐ๋‚˜ ๊ณต์‹ MCP๊ฐ€ image transport๋ฅผ ๋ฐ”๊พธ๋ฉด ์ž๋™ legacy fallback์ด ์—ฌ๋Ÿฌ cursor call์„ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ subtree ํฌ๊ธฐ์— ๋”ฐ๋ผ ์‹œ๊ฐ„์ด ๋Š˜์–ด๋‚  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • direct OAuth registration์€ Figma MCP Catalog ์Šน์ธ์ด ์—†๋Š” client_name์œผ๋กœ๋Š” ๊ฑฐ์ ˆ๋ฉ๋‹ˆ๋‹ค. ์Šน์ธ๋œ ์ด๋ฆ„(๊ธฐ๋ณธ๊ฐ’ Codex)์œผ๋กœ๋งŒ ๋“ฑ๋ก์ด ์„ฑ๋ฆฝํ•˜๋ฉฐ, ๊ทธ ๋“ฑ๋ก์€ Figma์—๊ฒŒ ํ•ด๋‹น ์ œํ’ˆ์œผ๋กœ ๊ธฐ๋ก๋ฉ๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ๋˜์ง€ ์•Š์€ ์™ธ๋ถ€ Figma library ๋ณ€์ˆ˜ ์ „์ฒด๋Š” Remote MCP๊ฐ€ ์ œ๊ณตํ•˜์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • node/page theme scope๋Š” ๋กœ์ปฌ ๋ณ€์ˆ˜ API์˜ file-wide ๊ฒฐ๊ณผ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•˜๋ฉฐ ์„ธ๋ฐ€ํ•œ ์‚ฌ์šฉ ๋ฒ”์œ„ ํ•„ํ„ฐ๋Š” ํ›„์† ๋ณด๊ฐ• ๋Œ€์ƒ์ž…๋‹ˆ๋‹ค.
  • vector, mask, image, absolute layout๊ณผ ์ผ๋ถ€ effect๋Š” diagnostics๋ฅผ ํฌํ•จํ•œ ์ œํ•œ์  fallback์ž…๋‹ˆ๋‹ค.
  • Figma Remote MCP์˜ use_figma tool contract๊ฐ€ ๋ฐ”๋€Œ๋ฉด live smoke test์™€ adapter ๊ฐฑ์‹ ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์ƒ์„ฑ ์ฝ”๋“œ์˜ ๋ฐฐ์น˜ ๊ณ„์•ฝ

absolute ์ž์‹์ด ์žˆ๋Š” ํ”„๋ ˆ์ž„์€ ์ƒ์„ฑ ์ฝ”๋“œ๊ฐ€ containing block์„ ๋งŒ๋“ญ๋‹ˆ๋‹ค. standalone์—์„œ๋Š” PAGE/SECTION ์•„๋ž˜์˜ ๋ฃจํŠธ๋„ FIXED ์ถ•์˜ ์›๋ณธ ํฌ๊ธฐ๋ฅผ ๋ณด์กดํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด WQUW-120์˜ 360ร—740 ์ˆ˜์ง ํ”„๋ ˆ์ž„์€ VStack pos="relative" w="360px" h="740px"๊ฐ€ ๋˜์–ด ๋ชจ๋‹ฌ์˜ left="50%", w="100%"๊ฐ€ ์ด ๋ฃจํŠธ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๊ณ„์‚ฐ๋ฉ๋‹ˆ๋‹ค. absolute ์ž์‹์ด ์—†๋Š” ํ™”๋ฉด์˜ ๊ธฐ์กด ์œ ๋™ ํฌ๊ธฐ ์ •์ฑ…์€ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.

placementContracts๋Š” includeDiagnostics ์—†์ด๋„ ๋ฐ˜ํ™˜ํ•˜๋ฉฐ SECTION ์‘๋‹ต์—๋Š” ํ”„๋ ˆ์ž„๋ณ„ ๋ชฉ๋ก๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ ๊ณ„์•ฝ์˜ details.output, generatedRoot, sourceSize, containingBlock, parentCollected, parentIncludedInOutput, hostRequirements๋ฅผ ํ•จ๊ป˜ ํ™•์ธํ•˜์„ธ์š”. ์บก์ฒ˜ ๋ฐ– ๋ถ€๋ชจ๊ฐ€ ์—†์œผ๋ฉด ๊ทธ ์‚ฌ์‹ค์„ ๋ช…์‹œํ•˜๋ฉฐ, ์ผ๋ฐ˜ ๋ฃจํŠธ๋Š” ์•ฑ์˜ normal flow์— ์‚ฝ์ž…๋ฉ๋‹ˆ๋‹ค. Figma canvas์˜ x/y๊ฐ€ ์•ฑ ์•ˆ์˜ ์œ„์น˜๋ฅผ ๋œปํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

embedded์—์„œ๋„ absolute ์ž์‹์˜ ๊ธฐ์ค€ ์š”์†Œ๋Š” ์œ ์ง€ํ•˜์ง€๋งŒ ๋ฃจํŠธ ํฌ๊ธฐ๋Š” ํ˜ธ์ŠคํŠธ/๋‚ด์šฉ์— ์˜์กดํ•ฉ๋‹ˆ๋‹ค. ์ด ์˜์กด์ด๋‚˜ absolute ๋ฃจํŠธ์˜ ์™ธ๋ถ€ ๋ถ€๋ชจ ์˜์กด์€ hostRequirements์™€ projectionIssues์— ๊ทผ์‚ฌ๋กœ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ์ถฉ์กฑํ•˜๊ฑฐ๋‚˜ containing parent๋ฅผ standalone์œผ๋กœ exportํ•œ ํ›„ ๋ฐฐ์น˜๋ฅผ ์ˆ˜์šฉํ•˜์„ธ์š”. ์ด ๊ณ„์•ฝ์€ CSS ์ƒ์„ฑ ๊ทผ๊ฑฐ์ด๋ฉฐ ๋ธŒ๋ผ์šฐ์ € ํ”ฝ์…€ ๋น„๊ต๋‚˜ ๋ฐ˜์‘ํ˜• ๋™์ผ์„ฑ ๋ณด์ฆ์ด ์•„๋‹™๋‹ˆ๋‹ค.

R1 ์‘๋‹ต ์ผ๊ด€์„ฑ

projectionIssues๋Š” includeDiagnostics ์—†์ด๋„ ๊ทผ์‚ฌยท์†์‹ค์˜ ๋…ธ๋“œ, ํ•„๋“œ, ์‚ฌ์œ ์™€ ์›๋ณธ/์ ์šฉ ๊ทผ๊ฑฐ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๋ถ„๋ฅ˜ ๊ทผ๊ฑฐ ์—†๋Š” uncoveredLayout์€ lossy์ด๋ฉฐ exact๋‚˜ ์ตœ์ข… ์‚ฐ์ถœ๋ฌผ๋กœ ํ‘œ์‹œํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ˆจ๊น€ ์ž์‚ฐ ์ฐธ์กฐ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ , ์ œ์™ธ ์ž์‚ฐ์ด ์—ฌ์ „ํžˆ ํ•„์š”ํ•œ ์ถœ๋ ฅ์€ ์‹คํŒจ ์‚ฌ์œ ์™€ ํ•จ๊ป˜ ๋ณด๋ฅ˜ํ•ฉ๋‹ˆ๋‹ค.

quality.assets๋Š” ์ž์‚ฐ ๋ฐ”์ดํŠธ ์ˆ˜์ง‘ ํ’ˆ์งˆ์ด๋ฉฐ manifest ์ƒ์„ฑ ์„ฑ๊ณต์„ ๋œปํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. assetSummary.description๊ณผ excludedCount๊ฐ€ ๋ฏธ์ˆ˜์ง‘ยท๋ถ€๋ถ„ ์ˆ˜์ง‘ยท์ˆจ๊น€ ์ œ์™ธ๋ฅผ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ”์ดํŠธ ์ˆ˜์ง‘์„ ์š”์ฒญํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด not-collected ์š”์•ฝ๊ณผ quality.assets: not-requested๊ฐ€ ํ•จ๊ป˜ ์˜ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฐฐ์น˜ ์‘๋‹ต์˜ recommendedBatchSize๋Š” ์ตœ๋Œ€ 3, maxFrameCount๋Š” 6, maxFrameOutputUnits๋Š” 12์ž…๋‹ˆ๋‹ค.

R8 provenance, scrolling and acquisition

sourceMap.version=2๋Š” ๊ณต๊ฐœ ์˜คํ”„์…‹์„ ์ œ๊ฑฐํ•œ ๊ณ„์•ฝ์ž…๋‹ˆ๋‹ค. exact๋Š” ์›๋ณธ ํ•„๋“œ์— ๋Œ€์‘ํ•˜๋Š” ์ƒ์„ฑ ์†์„ฑ/ํƒœ๊ทธ/ํ…์ŠคํŠธ ๋งคํ•‘์„ ๊ฒ€์ฆํ–ˆ๋‹ค๋Š” ๋œป์ž…๋‹ˆ๋‹ค. ์ค„ ์œ„์น˜, ๋ธŒ๋ผ์šฐ์ € ํ”ฝ์…€ ์ผ์น˜, ๋ฐ˜์‘ํ˜• ๋™๋“ฑ์„ฑ ๋˜๋Š” ํ™”๋ฉด ์ „์ฒด ์™„์„ฑ๋„๋ฅผ ๋ณด์ฆํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. generatedProperty๋Š” h="740px", flex="1", aspectRatio="320 / 48"์ฒ˜๋Ÿผ ์ถœ๋ ฅ์— ์žˆ๋Š” ์†์„ฑ์„ ๋‹ด์Šต๋‹ˆ๋‹ค. characters๋Š” children, ์•”๋ฌต์  cross-axis stretch๋Š” implicit:align-self:stretch๋กœ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. grow๋Š” ์‹ค์ œ flex ์†์„ฑ๊ณผ accounted-for-implicit-flex-grow๋ฅผ ํ•จ๊ป˜ ์ฝ์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด sizing ๊ฒ€์ฆ๊ณผ ์ง„๋‹จ์€ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค. ๋‚ด๋ถ€ renderer/validator์˜ ์œ„์น˜ ์ •๋ณด๋Š” ๊ณต๊ฐœ sourceMap์— ์ง๋ ฌํ™”ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

overflowDirection์€ ์บก์ฒ˜ manifest์— ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. NONE์€ ๋ช…์‹œ์  ๋น„์Šคํฌ๋กค, JSON null์€ ์ˆ˜์ง‘ ์‹œ ์›๋ณธ ์†์„ฑ์ด ์—†์Œ, ํ‚ค ๋ถ€์žฌ๋Š” ์ด์ „ ์ˆ˜์ง‘๊ธฐ์˜ ๋ฏธ์ˆ˜์ง‘, fieldErrors.overflowDirection์€ ์ฝ๊ธฐ ์‹คํŒจ์ž…๋‹ˆ๋‹ค. ์„ธ๋กœ/๊ฐ€๋กœ/์–‘๋ฐฉํ–ฅ ์Šคํฌ๋กค์€ ๊ฐ๊ฐ overflowY="auto"/overflowX="auto"/overflow="auto"๋กœ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. clipsContent=true๋Š” ์Šคํฌ๋กค์„ ์ทจ์†Œํ•˜์ง€ ์•Š์œผ๋ฉฐ, ํ•œ ์ถ• ์Šคํฌ๋กค์—์„œ๋Š” ๋‹ค๋ฅธ ์ถ•์„ clipํ•ฉ๋‹ˆ๋‹ค. ABSOLUTE FIXED ๋†’์ด๋Š” ํŒŒ์ƒ padding์ด ์žˆ์–ด๋„ ๋ช…์‹œ์  ๋†’์ด๋กœ ๋ณด์กดํ•ฉ๋‹ˆ๋‹ค. FILL/HUG๋ฅผ px๋กœ ๊ณ ์ •ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ผ๋ฐ˜ ํ™”๋ฉด export๋„ ์ˆ˜์ง‘์ด ๊ธธ์–ด์ง€๋ฉด ์•ฝ 1์ดˆ์˜ ์ดˆ๊ธฐ ๋Œ€๊ธฐ ํ›„ exportJob์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค(assetJob์€ exportJob์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ํ˜ธํ™˜ ์ฐธ์กฐ์ด๋ฉฐ ๋ณธ๋ฌธ์„ ๋ฐ˜๋ณตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค). jobId๋กœ ์ƒํƒœ๋ฅผ ์กฐํšŒํ•˜๊ณ (์ž์‚ฐ ํ–‰์€ assetIdยทstatusยทbyteLengthยทsha256ยทfileState ์œ„์ฃผ๋กœ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ๋ฐ˜ํ™˜๋˜๋ฉฐ, ์ „์ฒด ํ–‰์€ ์กฐํšŒ์— responseFields:["exportJob.assets"]๋ฅผ ๋ถ™์—ฌ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค) paused ์ƒํƒœ๋Š” jobAction:"resume"์œผ๋กœ ์žฌ๊ฐœํ•ฉ๋‹ˆ๋‹ค. calls์˜ frame/root IDยทpaginationยทelapsedMs์™€ projection ์‹œ๊ฐ„์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ์‚ฐ์€ frame-output unit๋‹น 5โ€“20์ดˆ์˜ ๊ณ„ํš์šฉ ์ถ”์ •์ด๋ฉฐ ๋ณด์žฅ์ด ์•„๋‹™๋‹ˆ๋‹ค. ์š”์ฒญ ํ•œ๋„ 6 frames/12 units์™€ ๋ณ„๊ฐœ๋กœ upstream ์ง€์—ฐ์€ job์œผ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์ง€์—ฐ ํ”„๋ ˆ์ž„์„ ๋ถ„๋ฆฌํ•˜๋ ค๋ฉด ํ•œ ํ”„๋ ˆ์ž„์”ฉ ํ˜ธ์ถœํ•˜์‹ญ์‹œ์˜ค. ์ž‘์—…์€ ์„œ๋ฒ„ ํ”„๋กœ์„ธ์Šค ์•ˆ์—์„œ 30๋ถ„, ์™„๋ฃŒ ์‘๋‹ต์€ 5๋ถ„ ๋ณด์กด๋˜๋ฉฐ ์žฌ์‹œ์ž‘์„ ๋„˜๊ฒจ ๋ณด์กด๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

artifact์˜ artifactState๋Š” expired(ํ™•์ธ๋œ TTL ๋งŒ๋ฃŒ), evicted(ํ™•์ธ๋œ ์บ์‹œ ์ œ๊ฑฐ), issued-by-another-process(๋‹ค๋ฅธ devup-mcp ํ”„๋กœ์„ธ์Šค๊ฐ€ ๋ฐœ๊ธ‰ํ•œ id โ€” ์žฌ์‹œ์ž‘ ์ „์˜ ์„œ๋ฒ„๋‚˜ ๋‹ค๋ฅธ ์„ธ์…˜์˜ ์„œ๋ฒ„), unknown(ํ˜„์žฌ ์„œ๋ฒ„์—์„œ ์›์ธ ํŒ๋‹จ ๋ถˆ๊ฐ€)์„ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. artifactId์˜ ์•ž 6๋ฐ”์ดํŠธ๊ฐ€ ๋ฐœ๊ธ‰ํ•œ ํ”„๋กœ์„ธ์Šค์˜ ํ‘œ์‹์ด๋ผ ๋””์Šคํฌ์— ์•„๋ฌด๊ฒƒ๋„ ์“ฐ์ง€ ์•Š๊ณ  ๊ตฌ๋ถ„ํ•˜๋ฉฐ, id๋Š” ๊ทธ๋Œ€๋กœ 43์ž์ž…๋‹ˆ๋‹ค. ์ตœ๊ทผ ์ œ๊ฑฐ 64๊ฐœ ์ด๋‚ด์˜ ๋ณต๊ตฌ metadata๊ฐ€ ์žˆ์œผ๋ฉด canonical URL๊ณผ ์„ ํƒ์„ nextAction.arguments๋กœ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. URL ์›๋ฌธ์˜ ํŒŒ์ผ ์ œ๋ชฉ์€ ์ €์žฅํ•˜์ง€ ์•Š์•„๋„ ๊ฐ™์€ ๋Œ€์ƒ์„ ๊ฐ€๋ฆฌํ‚ค๋Š” canonical URL์„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. metadata๊ฐ€ ์—†์œผ๋ฉด recoveryState:"unrecoverable"๋กœ ๋ช…์‹œํ•˜๋ฉฐ, ์„œ๋ฒ„ ์žฌ์‹œ์ž‘์ด๋ผ๊ณ  ๋‹จ์ •ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์˜ค๋ฅ˜์˜ details.recovery๋Š” mode(recollect | reproject | reread-manifest | fix-uri), cause, reprojectable, requiresOriginalUrl์„ ์ค๋‹ˆ๋‹ค. resource ์ฝ๊ธฐ ์‹คํŒจ๋Š” ๊ณ„์† resource not found์ด์ง€๋งŒ data์— ๊ฐ™์€ recovery๊ฐ€ ์‹ค๋ฆฝ๋‹ˆ๋‹ค โ€” artifact๊ฐ€ ์‚ด์•„ ์žˆ๋Š”๋ฐ output์ด ์—†์œผ๋ฉด reproject, ์ฒญํฌ ๋ฒˆํ˜ธ๊ฐ€ ๋ฒ”์œ„ ๋ฐ–์ด๋ฉด chunkCountยทmanifestUri์™€ ํ•จ๊ป˜ reread-manifest์ž…๋‹ˆ๋‹ค. artifact์˜ capability๊ฐ€ ์š”์ฒญํ•œ output์„ ๋ฎ์ง€ ๋ชปํ•ด ๊ฑฐ์ ˆํ•  ๋•Œ๋Š” recovery.missing์ด ๋น ์ง„ ๊ฒƒ(referencePng, asset-captures ๋“ฑ)์„, nextAction์ด artifactId ๋Œ€์‹  ์›๋ž˜ url๋กœ ๋‹ค์‹œ ๋ถ€๋ฅด๋Š” ์ธ์ž๋ฅผ ๋‹ด์Šต๋‹ˆ๋‹ค. ์„ฑ๊ณต ์‘๋‹ต์˜ cache.reuse๋Š” ์ด artifactId๋กœ Figma ํ˜ธ์ถœ ์—†์ด ๋” ํˆฌ์˜ํ•  ์ˆ˜ ์žˆ๋Š” output(withArtifactId)๊ณผ ์›๋ž˜ url์ด ํ•„์š”ํ•œ ๊ฒƒ(needsUrl)์„ ๋ฏธ๋ฆฌ ๋‚˜์—ดํ•ฉ๋‹ˆ๋‹ค.

ํฐ ์‘๋‹ตยทํฐ ๋…ธ๋“œ๋ฅผ ๋‹ค๋ฃจ๋Š” ๋ฒ• (ํด๋ผ์ด์–ธํŠธ ๊ฐ€์ด๋“œ)

  • responseLimit/responseOffset์€ ์ด๋ฏธ ์ˆ˜์ง‘๋œ ๊ฒฐ๊ณผ๋ฅผ ์ž๋ฅด๋Š” ๋ฐ˜ํ™˜ ํ•œ๋„์ž…๋‹ˆ๋‹ค(compact.rs๊ฐ€ ์ˆ˜์ง‘๋œ ๋…ธ๋“œ ๋ชฉ๋ก์— skip/take). Figma๋ฅผ ์–ผ๋งˆ๋‚˜ ์ฝ๋Š”์ง€๋Š” ๋ฐ”๊พธ์ง€ ์•Š์œผ๋ฏ€๋กœ ์ˆ˜์ง‘ ๋น„์šฉ์˜ ์ƒํ•œ์ด ์•„๋‹™๋‹ˆ๋‹ค. ๋งค์šฐ ํฐ ๋…ธ๋“œ(outputs:["outline"]์ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ๊ฒฝ์šฐ)๋Š” ๊ฐ’์„ ์กฐ์ ˆํ•˜์ง€ ๋ง๊ณ  devup_figma_explore๋กœ ๋‚ด๋ถ€ ํ”„๋ ˆ์ž„์„ ์ฐพ์•„ ๊ทธ ํ”„๋ ˆ์ž„๋งŒ exportํ•˜๊ฑฐ๋‚˜, ์น˜์ˆ˜๋งŒ ํ•„์š”ํ•˜๋ฉด devup_figma_measure๋ฅผ ์“ฐ์„ธ์š”. ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ์ž‘์—…์€ jobAction:"cancel"๋กœ ์ทจ์†Œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์งง์€ ์ƒํƒœ ์กฐํšŒ: ํด๋ง์— responseFields๋ฅผ ๋ถ™์ด๋ฉด ํ•„์š”ํ•œ ๋ถ€๋ถ„๋งŒ ๋ฐ›์Šต๋‹ˆ๋‹ค. ์˜ˆ: {"jobId":"<id>","responseFields":["exportJob.state","exportJob.elapsedMs","exportJob.completedCalls","exportJob.nextAction"]}. ํ•„๋“œ ์ด๋ฆ„์€ ์‹ค์ œ ์‘๋‹ต์˜ ํ‚ค๋ฅผ ๋”ฐ๋ฅด๋ฏ€๋กœ ๋จผ์ € ํ•œ ๋ฒˆ ์ „์ฒด ์‘๋‹ต์œผ๋กœ ํ™•์ธํ•˜์„ธ์š”. devup_figma_auth์˜ status๋Š” ์ตœ์ƒ์œ„ summary ํ•œ ์ค„์ด ์ง€๊ธˆ ์“ฐ๋Š” ๊ฒฝ๋กœ(๋ธŒ๋ฆฌ์ง€/metered direct)์™€ ๋น„์šฉ์„ ๋จผ์ € ๋งํ•ฉ๋‹ˆ๋‹ค.
  • resource ์†Œ๋น„: delivery:"resource"(๋˜๋Š” ์ž๋™ ์ „ํ™˜)์˜ ๊ฒฐ๊ณผ๋Š” devup://artifact/{artifactId}/outputs/{outputId}/manifest๋ฅผ ๋จผ์ € ์ฝ๊ณ , manifest์˜ chunkUris๋ฅผ ์ˆœ์„œ๋Œ€๋กœ ์ฝ์–ด ์ด์–ด ๋ถ™์ž…๋‹ˆ๋‹ค. ์ฒญํฌ ๋ฒˆํ˜ธ๊ฐ€ ๋ฒ”์œ„ ๋ฐ–์ด๋ฉด ์˜ค๋ฅ˜์˜ recovery๊ฐ€ manifest๋ฅผ ๋‹ค์‹œ ์ฝ์œผ๋ผ๊ณ  ์•ˆ๋‚ดํ•ฉ๋‹ˆ๋‹ค.
  • ๋ฌธ์ž์—ด/๊ฐ์ฒด ๋ฐ˜ํ™˜ ์ฐจ์ด: ๋„๊ตฌ ๊ฒฐ๊ณผ๋Š” content์˜ JSON ํ…์ŠคํŠธ์™€ structuredContent๋กœ ๊ฐ™์€ ๋‚ด์šฉ์ด ํ•จ๊ป˜ ์˜ฌ ์ˆ˜ ์žˆ๊ณ , read_mcp_resource์˜ ๋ฐ˜ํ™˜ํ˜•์€ ํ˜ธ์ŠคํŠธ ์–ด๋Œ‘ํ„ฐ๋งˆ๋‹ค contents[0].text(๋ฌธ์ž์—ด)์ด๊ฑฐ๋‚˜ ์ด๋ฏธ ํŒŒ์‹ฑ๋œ ๊ฐ์ฒด์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. typeof raw === "string" ? JSON.parse(raw) : raw์ฒ˜๋Ÿผ ์ •๊ทœํ™”ํ•ด์„œ ์ฝ์œผ์„ธ์š”. ์ด ์ฐจ์ด๋Š” ์„œ๋ฒ„๊ฐ€ ์•„๋‹ˆ๋ผ ํ˜ธ์ŠคํŠธ ์–ด๋Œ‘ํ„ฐ ๋ชซ์ผ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์„œ๋ฒ„ ๋ฒ„๊ทธ๋กœ ํ™•์ •๋œ ์‚ฌ๋ก€๋Š” ์—†์Šต๋‹ˆ๋‹ค.
  • ์—ฐ๊ฒฐ ์ƒํƒœ ์ฝ๊ธฐ: activePath:"direct"์ด๋ฉด์„œ paths.bridge.available:false๋Š” ์„œ๋ฒ„ ์ ‘์† ์‹คํŒจ๊ฐ€ ์•„๋‹ˆ๋ผ "ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์—ฐ๊ฒฐ๋˜์ง€ ์•Š์•„ metered direct๋กœ ์ฝ๋Š” ์ค‘"์ด๋ผ๋Š” ๋œป์ž…๋‹ˆ๋‹ค. summary์™€ paths.bridge.reason์„ ๋ณด์„ธ์š”.

R9 sourceMap ํ•„๋“œ์™€ resolution

TSX ํ•ญ๋ชฉ์˜ ๊ธฐ๋ณธ ํ•„๋“œ๋Š” nodeId, property, generatedProperty, resolution์ž…๋‹ˆ๋‹ค. ์„ ํƒ์  variableId๋Š” ์›๋ณธ ๋ณ€์ˆ˜ ํ† ํฐ ์ฐธ์กฐ, styleId๋Š” typography/style ํ† ํฐ ์ฐธ์กฐ, assetId๋Š” ์ด๋ฏธ์ง€ยท๋ฒกํ„ฐ ์ž์‚ฐ ์ฐธ์กฐ๋ฅผ ์‹๋ณ„ํ•  ๋•Œ ๋ถ™์Šต๋‹ˆ๋‹ค. ํ•œ ํ•ญ๋ชฉ์— ์—ฌ๋Ÿฌ ์‹๋ณ„์ž๊ฐ€ ํ•จ๊ป˜ ์žˆ์„ ์ˆ˜ ์žˆ์œผ๋ฉฐ ํ•ด๋‹น ์ฐธ์กฐ๊ฐ€ ์—†์œผ๋ฉด ์ƒ๋žตํ•ฉ๋‹ˆ๋‹ค. elementIndex๋Š” ๊ทธ ํ•ญ๋ชฉ์ด ๋งคํ•‘ํ•œ JSX ์—ฌ๋Š” ํƒœ๊ทธ์˜ ๋ฌธ์„œ ์ˆœ์„œ ๋ฒˆํ˜ธ(0๋ถ€ํ„ฐ)์ด๊ณ , devup_ui_validate๊ฐ€ ์œ„๋ฐ˜์— ๋ถ™์ด๋Š” ๋ฒˆํ˜ธ์™€ ๊ฐ™์Šต๋‹ˆ๋‹ค โ€” ์˜คํ”„์…‹ ์—†์ด ๋‘˜์„ ์ž‡๋Š” ํ‚ค์ž…๋‹ˆ๋‹ค. devup.json ๋งคํ•‘์€ jsonPointer๋กœ ํ…Œ๋งˆ ์œ„์น˜๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋ฉฐ TSX ๊ธฐ๋ณธ ํ•„๋“œ๊ฐ€ ๋ชจ๋‘ ์žˆ๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค.

resolution ์˜๋ฏธ์™€ ๊ฒ€์ฆ ๋ฒ”์œ„
exact ์›๋ณธ ํ•„๋“œ์™€ ์ƒ์„ฑ ํƒœ๊ทธยทํ…์ŠคํŠธ์˜ ์˜๋ฏธ ๋งคํ•‘. ํ™”๋ฉด ์ „์ฒด์˜ ํ”ฝ์…€/๋ฐ˜์‘ํ˜• ๋™๋“ฑ์„ฑ ๋ณด์ฆ์€ ์•„๋‹˜.
raw-fallback ํ† ํฐ ๋˜๋Š” ์ „์šฉ ๋งคํ•‘ ๋ผ๋ฒจ์ด ์—†๋Š” ์ผ๋ฐ˜ ์›๋ณธ ๊ฐ’ยท์ƒ์„ฑ ์ •์ฑ… ๋งคํ•‘ ๊ฒฝ๋กœ. raw ๊ฐ’์˜ ๋ฌด๋ณ€ํ™˜ ๋ณต์‚ฌ๋‚˜ ๋‚ฎ์€ ์‹ ๋ขฐ๋„๋ฅผ ๋œปํ•˜์ง€ ์•Š์œผ๋ฉฐ, ๋ณ„๋„ ABSOLUTE component๊ฐ€ verified์—ฌ๋„ ์ด ๋ผ๋ฒจ์€ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.
verified-explicit-dimension ์ฝ๊ธฐ ์˜ค๋ฅ˜ ์—†๋Š” ์›๋ณธ width/height ์ˆ˜์น˜์™€ ์ƒ์„ฑ w/h/boxSize์˜ px ๊ฐ’์ด ์ •ํ™•ํžˆ ๊ฐ™์Œ์„ ํ™•์ธ. ๋ฐ˜์‘ํ˜• ๋ฐฐ์น˜๊นŒ์ง€ ๊ฒ€์ฆํ•œ ๊ฒƒ์€ ์•„๋‹˜.
accounted-for-content-sizing textAutoResize์— ๋”ฐ๋ฅธ ๊ณ ์ • ํฌ๊ธฐ ์ƒ๋žต์˜ ์˜๋ฏธ ๋งคํ•‘. ํฐํŠธ ๋ฉ”ํŠธ๋ฆญ๊ณผ ๋ธŒ๋ผ์šฐ์ € ํ”ฝ์…€์€ ๋ฏธ์ธก์ •.
verified-layout-sizing ์›๋ณธ FIXED/FILL/layoutGrow ์˜๋„์™€ ์ƒ์„ฑ h/boxSize/flex ๊ด€๊ณ„๋ฅผ sizing ๊ฒ€์ฆ๊ธฐ๋กœ ํ™•์ธ.
accounted-for-implicit-flex-stretch ์‹ค์ œ ์ƒ์„ฑ ๋ถ€๋ชจ์˜ cross-axis stretch์™€ ํฌ๊ธฐ ๊ธฐ์ค€์œผ๋กœ ์ƒ๋žต๋œ ํฌ๊ธฐ๋ฅผ ์„ค๋ช….
accounted-for-implicit-flex-grow ์‹ค์ œ ์ƒ์„ฑ ๋ถ€๋ชจ์˜ main-axis flex-grow์™€ ๋‚จ์€ ๊ณต๊ฐ„์œผ๋กœ ํฌ๊ธฐ๋ฅผ ์„ค๋ช….
restored-hug-after-mask-child-folding ์ ‘ํžŒ mask ์ž์‹์˜ ์›๋ณธ ํฌ๊ธฐ์™€ ๋…๋ฆฝ์ ์ธ ํฌ๊ธฐ ๊ธฐ์ค€์œผ๋กœ HUG/aspectRatio ๋ณต์›. -from-absoluteBoundingBox ์ ‘๋ฏธ์‚ฌ๋Š” bounding box์—์„œ ํฌ๊ธฐ๋ฅผ ์–ป์€ ๊ฒฝ์šฐ.
variable-token / style-token ์›๋ณธ ๋ณ€์ˆ˜/์Šคํƒ€์ผ ID์™€ ์ƒ์„ฑ ํ† ํฐ ์ฐธ์กฐ์˜ ๋งคํ•‘.
asset ์›๋ณธ ์ž์‚ฐ ID์™€ ์ƒ์„ฑ ์ž์‚ฐ ์†์„ฑ์˜ ๋งคํ•‘. ๋ฐ”์ด๋„ˆ๋ฆฌ ์ˆ˜์ง‘ ์„ฑ๊ณต ์—ฌ๋ถ€๋Š” assetSummary์—์„œ ๋ณ„๋„ ํ™•์ธ.
variant-selector ์›๋ณธ variant ์„ ํƒ์„ ์ƒ์„ฑ selector ์†์„ฑ๊ณผ ์—ฐ๊ฒฐ.
unverified-property-mapping ์›๋ณธ์— ๋Œ€์‘ํ•˜๋Š” ์ƒ์„ฑ ์†์„ฑ์„ ํ™•์ธํ•˜์ง€ ๋ชปํ•จ. ์ถ”๊ฐ€ ๊ฒ€์ฆ ํ•„์š”.
variable / alias / style devup.json์˜ ์ง์ ‘ ๋ณ€์ˆ˜ ๊ฐ’ / ํ•ด์„๋œ ๋ณ€์ˆ˜ alias / ์Šคํƒ€์ผ์—์„œ ๋‚˜์˜จ ํ…Œ๋งˆ ๊ฐ’๊ณผ JSON pointer.

node๋Š” ๋‚ด๋ถ€ ๋…ธ๋“œ ๋ฒ”์œ„์šฉ์ด๋ฉฐ ๊ณต๊ฐœ v2 property entries์—๋Š” ํฌํ•จ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

R16์˜ sourceMap.resolutionSemantics๋Š” ์ด ๋ผ๋ฒจ ์‚ฌ์ „๊ณผ axis="mapping-method"๋ฅผ ์‘๋‹ต์— ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. Section frame์€ dictionary="/resolutionSemantics"๋กœ ์ƒ์œ„ ์‘๋‹ต์˜ ๊ณตํ†ต ์‚ฌ์ „์„ ์ฐธ์กฐํ•˜๋ฉฐ, ์ด ์‚ฌ์ „์€ resource delivery์—๋„ ๋‚จ์Šต๋‹ˆ๋‹ค. diagnostics.details.components.*.state๋Š” ๋ณ„๋„์˜ ์ œํ•œ๋œ ๊ฒ€์ฆ ์ถ•์ž…๋‹ˆ๋‹ค. ๊ฐ™์€ screen/output ์•ˆ์—์„œ nodeId์™€ ์›๋ณธ property(width/height/x/y โ†’ width/height/horizontal/vertical)๋กœ ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด w="100%"์˜ ๋งคํ•‘์€ raw-fallback์ด์ง€๋งŒ, ํ™•์ •๋œ ๋™์ผ FIXED ๋ถ€๋ชจ ํญ์—์„œ ํ•ด์„ํ•œ ๊ฐ’์ด ์›๋ณธ ํญ๊ณผ ๊ฐ™์œผ๋ฉด ABSOLUTE width๋Š” verified์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋‘ ๋ผ๋ฒจ ๋ชจ๋‘ ํ™”๋ฉด ์ „์ฒด์˜ ํ”ฝ์…€ยท๋ฐ˜์‘ํ˜• ๋“ฑ๊ฐ€์„ฑ ์ธก์ •์€ ์•„๋‹™๋‹ˆ๋‹ค.

R16์€ ์‹คํŒจํ•œ ์น˜์ˆ˜ ์ฆ๋ช…์˜ blockedBy๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. ๋ถ€๋ชจ ํญ ๋ถˆ๋ช…(parent-width-unknown), ๋ถ€๋ชจ ํญ ๋ถˆ์ผ์น˜(parent-width-unequal), ์ฝ๊ธฐ ์˜ค๋ฅ˜(read-error), ๋น„FIXED sizing(non-fixed-sizing), dimension ์ถฉ๋Œ(conflicting-dimension-props)์„ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. ์„ฑ๊ณต ์‹œ null์ด๋ฉฐ widthPreservation์—๋„ ๊ฐ™์€ ๊ฐ’์ด ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค. ์ฒซ ์ฐจ๋‹จ ์ด์œ ๋งŒ ๋ณด๊ณ ํ•˜๋ฏ€๋กœ ๋‚˜๋จธ์ง€ ์กฐ๊ฑด์˜ ํ†ต๊ณผ๋‚˜ ๋‹ค๋ฅธ ์‹คํŒจ ๋ถ„๊ธฐ์˜ ์‹คํ–‰์„ ๋œปํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ž์‚ฐ์€ ๋ถ€๋ชจ percentage ์ฆ๋ช… ๋Œ€์‹  ์‹ค์ œ ์„ ํƒ๋œ render-boundary ์ฆ๋ช…์˜ ์‹คํŒจ ์ด์œ ๋ฅผ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค.

์ตœ์ข… ์‘๋‹ต๊ณผ ๊ฐ frame์˜ verdictScope๋Š” status/projection์„ ๊ทธ๋Œ€๋กœ ํ‘œ์‹œํ•˜๊ณ  statusCauses์™€ projectionCauses๋กœ ์›์ธ์„ ์š”์•ฝํ•ฉ๋‹ˆ๋‹ค. projection ์›์ธ์€ nodeId/screenId/output/code/property์™€ ๋ฏธํ•ด๊ฒฐ component๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋ฏ€๋กœ, width/height๊ฐ€ verified์—ฌ๋„ horizontal/vertical์ด approximated์ธ ํ™”๋ฉด์˜ ์ „์ฒด ํŒ์ •์„ ๋ฐ”๋กœ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. diagnosticIndex๋Š” ๊ฐ™์€ ๊ฐ์ฒด์˜ projectionIssues ์ธ๋ฑ์Šค์ž…๋‹ˆ๋‹ค. includeDiagnostics=false ๋ฐ resource delivery์—๋„ ์š”์•ฝ์ด ์œ ์ง€๋ฉ๋‹ˆ๋‹ค.

ABSOLUTE ์ง„๋‹จ์˜ components๋Š” height/width/horizontal/vertical๋ณ„ stateยทfidelityImpactยท์›๋ณธ ๊ฐ’ยท์ƒ์„ฑ ๊ฐ’ยท๊ฒ€์ฆ ์ด์œ ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. resolutionConditions์—๋Š” ๋‚จ์€ ๊ทผ์‚ฌ ํ•ญ๋ชฉ์˜ ํ•ด์†Œ ์กฐ๊ฑด๋งŒ ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค. CENTER๋Š” ์›๋ณธ ์ค‘์‹ฌ ์˜คํ”„์…‹, left/top 50%, translate ๋ฐ ์‹ค์ œ ์ƒ์„ฑ ๋ถ€๋ชจ ์ขŒํ‘œ ๊ธฐ์ค€๊นŒ์ง€ ํ™•์ธํ•ด์•ผ ๊ฒ€์ฆ๋ฉ๋‹ˆ๋‹ค. MIN์€ ์›๋ณธ ์˜คํ”„์…‹๊ณผ ์ƒ์„ฑ px๋ฅผ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค. MAX๋Š” ํฌ๊ธฐ ๊ฒ€์ฆ์„ ์ „์ œ๋กœ parentSize-offset-size์™€ right/bottom ์—ฌ๋ฐฑ์„ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค. ์ œ์•ฝ ํ•„๋“œ ์ „์ฒด๊ฐ€ ์—†๋Š” ์ด์ „ ์บก์ฒ˜๋Š” constraintDeclared=false๋กœ ํ‘œ์‹œํ•˜๋ฉฐ ์ƒ์„ฑ๋œ ๋กœ์ปฌ ์˜คํ”„์…‹๋งŒ ๋น„๊ตํ•˜๊ณ  ๋ฐ˜์‘ํ˜• ์ œ์•ฝ์„ ์ถ”์ •ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ฆ๋ช…๋˜์ง€ ์•Š์€ MAX/STRETCH/SCALE, ๋ˆ„๋ฝ geometry ๋˜๋Š” ํ•„๋“œ ์˜ค๋ฅ˜๋Š” ๊ทผ์‚ฌ๋กœ ๋‚จ์Šต๋‹ˆ๋‹ค. ๋ช…์‹œ์  FIXED ๋†’์ด ๋ณด์กด์€ percentage ๋„ˆ๋น„์˜ ๋™๋“ฑ์„ฑ์„ ์ฆ๋ช…ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

R14๋Š” ABSOLUTE ์ž์‹์˜ FIXED ํญ๊ณผ ์ƒ์„ฑ๋œ ์ง๊ณ„ containing block์˜ ํ™•์ • px ํญ์ด ๊ฐ™์€ ๊ฒฝ์šฐ w="100%"์˜ ๋Œ€์‘์„ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค. ํŠน์ • ํญ์— ํ•œ์ •ํ•˜์ง€ ์•Š์œผ๋ฉฐ, ์ƒ์„ฑ ์ฝ”๋“œ๋Š” ๋ฐ”๊พธ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ์ฆ๋ช… ๋ฒ”์œ„๋Š” ์ฝ๊ธฐ ์˜ค๋ฅ˜๊ฐ€ ์—†๋Š” ๋™์ผ ํฌ๊ธฐ์˜ FIXED ๋ถ€๋ชจ, ๋ช…์‹œ์  positioning, ๋ถ€๋ชจ padding/border ๋ฐ ํฌ๊ธฐ override๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค. ๋ถ€๋ชจ ํญ์ด ์œ ๋™์ ์ด๊ฑฐ๋‚˜ ๊ฐ’์ด ๋‹ค๋ฅด๋ฉด ๊ทผ์‚ฌ๋กœ ๋‚จ์Šต๋‹ˆ๋‹ค. appliedValue.widthPreservation์— components.width์™€ ๊ฐ™์€ ๊ทผ๊ฑฐ, ๋ถ€๋ชจ ์ƒ์„ฑ ํญ, resolvedPixels, sourceFields, ๊ณ„์‚ฐ์‹์ด ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค. componentSummary.verified/unresolved๋Š” ๋„ค ํ•ญ๋ชฉ ์ค‘ ๊ฒ€์ฆ๋œ ๊ฒƒ๊ณผ ๋‚จ์€ ๊ฒƒ์„ ๋‚˜๋ˆ•๋‹ˆ๋‹ค.

ABSOLUTE ์ž์‚ฐ์˜ components๋Š” boundary(์„ ํƒํ•œ export ๊ฒฝ๊ณ„ยท์›๋ณธ bounding box์™€์˜ ์ฐจ์ดยทsourceFieldsยท๊ณ„์‚ฐ์‹), rounding(์ž…๋ ฅยท์ƒ์„ฑ๊ฐ’ยท์˜ค์ฐจ), constraintInterpretation(์ง์ ‘ ์ œ์•ฝยท์ฒซ ์ž์‹ ์ƒ์†ยท๊ธฐ๋ณธ๊ฐ’)์„ ๋ถ„๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜์˜ฌ๋ฆผ์€ round(value*100)/100์™€ ์ƒ์„ฑ๊ฐ’์˜ ์ผ์น˜ ๋ฐ ์ตœ๋Œ€ 0.005px ์˜ค์ฐจ๋ฅผ ํ™•์ธํ•˜๋ฉฐ ๋ถ€๋™์†Œ์ˆ˜์  ๋น„๊ต ์—ฌ์œ ๋Š” 1e-12px์ž…๋‹ˆ๋‹ค. ๊ฒฝ๊ณ„ ์ฐจ์ด๋ฅผ ๋ฐ˜์˜ฌ๋ฆผ ์˜ค์ฐจ๋กœ ํก์ˆ˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ž์‚ฐ ์น˜์ˆ˜์˜ verified๋Š” ์ˆ˜์ง‘๋œ render bounds ํˆฌ์˜์˜ ๊ฒ€์ฆ์ด๋ฉฐ ์›๋ณธ layout box์™€์˜ ๋™์ผ์„ฑ์ด๋‚˜ SVG/CSS ํ•ฉ์„ฑ ์ธก์ •์€ ์•„๋‹™๋‹ˆ๋‹ค. GROUP์˜ null/๋ˆ„๋ฝ ์ œ์•ฝ์„ ์ฒซ ์ž์‹์—์„œ ๊ฐ€์ ธ์˜ค๊ฑฐ๋‚˜ MIN์œผ๋กœ ๊ธฐ๋ณธ ์ฒ˜๋ฆฌํ•œ ๊ฒฝ์šฐ ๊ทธ ์ •์ฑ…์„ assumed๋กœ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. ์œ„์น˜ ๊ณ„์‚ฐ๊ณผ ๋ฐ˜์˜ฌ๋ฆผ์ด ๋งž์•„๋„ GROUP์˜ ๋ฐฐ์น˜ ์˜๋„์™€ ๊ฐ™๋‹ค๋Š” ์ฆ๋ช…์ด ์—†์œผ๋ฉด ํ•ด๋‹น ์œ„์น˜ ์ถ•์€ approximated๋กœ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

๋น„๋ Œ๋” ์ง„๋‹จ์€ ์˜ค๋ฅ˜ ์—†์ด ์ฝ์€ visible=false ๋˜๋Š” ๋ช…์‹œ์  absoluteRenderBounds=null์„ accounted-for-non-rendering / fidelityImpact=none์œผ๋กœ ๊ธฐ๋กํ•ฉ๋‹ˆ๋‹ค. verification์˜ fieldยทfieldPresentยทvalueยทreadError๊ฐ€ ๊ทผ๊ฑฐ์ž…๋‹ˆ๋‹ค. ํ•„๋“œ ๋ถ€์žฌ๋‚˜ ์ฝ๊ธฐ ์‹คํŒจ๋Š” unverified-non-rendering / approximated๋กœ ์œ ์ง€ํ•˜๋ฉฐ ์žฌ์ˆ˜์ง‘ ์กฐ๊ฑด์„ ์•ˆ๋‚ดํ•ฉ๋‹ˆ๋‹ค.

HUG ๊ฒ€์ฆ์€ ์›๋ณธ๊ณผ ์ผ์น˜ํ•˜๋Š” ์ƒ์„ฑ auto-layout์—์„œ ํฌ๊ธฐ override ์—†์ด intrinsic sizing ์˜๋„๋ฅผ ํ‘œํ˜„ํ–ˆ๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. FIXED percentage ํฌ๊ธฐ๋Š” ์ผ์น˜ํ•˜๋Š” auto-layout๊ณผ ๋ช…์‹œ์ ์œผ๋กœ ๊ฐ™์€ ํฌ๊ธฐ์ธ FIXED ๋ถ€๋ชจ, ํ…Œ๋‘๋ฆฌยทpaddingยทmin/max override ๋ถ€์žฌ๊ฐ€ ํ™•์ธ๋œ ์ œํ•œ๋œ ๊ฒฝ์šฐ๋งŒ ๊ฒ€์ฆํ•ฉ๋‹ˆ๋‹ค. CENTER ๊ฒ€์ฆ์€ ์›๋ณธ์˜ ์ค‘์‹ฌ ์˜คํ”„์…‹ 0๊ณผ ์ƒ์„ฑ CSS์˜ ์ค‘์‹ฌ ๊ด€๊ณ„๋ฅผ ๋œปํ•ฉ๋‹ˆ๋‹ค. ๋ถ€๋ชจ๊ฐ€ ์ปค์งˆ ๋•Œ ์›๋ž˜ x๋ฅผ ๊ณ ์ •ํ•œ๋‹ค๋Š” ๋œป์ด ์•„๋‹ˆ๋ฉฐ, ๋ถ€๋ชจ์˜ ๋ช…์‹œ์  px ๊ฐ’์ด ๋ฐ”๋€Œ๋ฉด ๋ถ€๋ชจ ์ž์‹ ์˜ dimension coverage ์žฌ๊ฒ€์ฆ์ด ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค. ๊ฒ€์ฆ๋œ ๋ช…์‹œ์  ํฌ๊ธฐ ๋งคํ•‘์€ canvas/derived-padding์˜ ์ผ๋ฐ˜ ์ƒ๋žต ๊ทœ์น™์œผ๋กœ ์žฌ๊ฒ€์ฆ ์˜๋ฌด๋ฅผ ๋ฉด์ œํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

projectionEvidence๋Š” ํ™•์ธ๋œ ๋น„๋ Œ๋”์™€ ๊ฒ€์ฆ ์™„๋ฃŒ ABSOLUTE ํ•ญ๋ชฉ์˜ ๊ทผ๊ฑฐ๋ฅผ ๋‹ด์œผ๋ฉฐ, includeDiagnostics=false์—ฌ๋„ ์ตœ์ƒ์œ„ ๋ฐ ํ”„๋ ˆ์ž„ ์‘๋‹ต์— ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋ฏธํ•ด๊ฒฐ ๊ทผ์‚ฌ๋Š” projectionIssues์— ๋‚จ์œผ๋ฏ€๋กœ ๋‘ ๋ฐฐ์—ด์„ ๊ตฌ๋ถ„ํ•ด ์ฝ์Šต๋‹ˆ๋‹ค.

R13 ์ƒ์„ฑ ์†์„ฑ provenance์™€ ์ถœ๋ ฅ ์ „๋‹ฌ

์ตœ์ข… TSX์˜ ๋ชจ๋“  JSX ์†์„ฑ(ํ‘œํ˜„์‹ยทbooleanยทspread ํฌํ•จ)์„ ํŒŒ์„œ๋กœ ๊ฒ€์‚ฌํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ์กด sourceMap์— ์—†๋Š” ์†์„ฑ์€ ์ƒ์„ฑ ๋‹จ๊ณ„์˜ ์›๋ณธ ํ•„๋“œ์™€ ๊ณ„์‚ฐ์œผ๋กœ ๋Œ€์กฐํ•˜๋ฉฐ, ํ™•์ธ๋œ ํ•ญ๋ชฉ์€ DEVUP_CODEGEN_PROPERTY_EVIDENCE๋กœ projectionEvidence์— ๋‚จ๊น๋‹ˆ๋‹ค. sourceFields, originalValue, calculation, stage, generatedProperty, elementIndex, assetId ๋ฐ details.output์„ ํ•จ๊ป˜ ์ฝ์Šต๋‹ˆ๋‹ค. ์ž์‚ฐ ๊ฒฝ๊ณ„ ๋ณด์ •์€ render bounds์™€ bounding box์˜ ์ฐจ์ด, ํšจ๊ณผ๋Š” ์›๋ณธ effects๋ฅผ ๊ทผ๊ฑฐ๋กœ ๋‚จ๊น๋‹ˆ๋‹ค. ์ด ๊ทผ๊ฑฐ๋Š” ์ƒ์„ฑ ๊ณ„์‚ฐ์˜ ๋Œ€์‘์ด๋ฉฐ SVG/CSS ํšจ๊ณผ์˜ ๋ธŒ๋ผ์šฐ์ € ํ•ฉ์„ฑ ์‹ค์ธก์ด ์•„๋‹™๋‹ˆ๋‹ค.

์„ค๋ช…ํ•  ์ˆ˜ ์—†๋Š” ์†์„ฑ์€ DEVUP_CODEGEN_PROPERTY_UNMAPPED, mappingComplete=false๋กœ ํ•ญ์ƒ ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค. ๊ฐ’์€ ์œ ์ง€๋˜๋ฏ€๋กœ ์ด ์ง„๋‹จ์˜ fidelityImpact๋Š” none์ž…๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์†์‹คยท๊ทผ์‚ฌ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ quality.projection=mapping-incomplete๊ฐ€ ๋˜๋ฉฐ exact์™€ ์ตœ์ข… ์™„๋ฃŒ ํŒ์ •์„ ํ—ˆ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ธฐ์กด lossyยทapproximated ํŒ์ •์€ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜์‘ํ˜• ๋ณ‘ํ•ฉ์€ ๊ฐ ํญ์˜ slot์—์„œ ๋ณ‘ํ•ฉ ๋ชจ๋“ˆ์„ ์ฝ์–ด(๋ฐฐ์—ด์€ devup-ui๊ฐ€ ์ฝ๋Š” ๋Œ€๋กœ, ๊ทธ ํญ์—์„œ ์ˆจ๊ธด ์š”์†Œ๋Š” ๋นผ๊ณ ) ๊ทธ ํญ๋งŒ ๋”ฐ๋กœ ๋ณ€ํ™˜ํ•œ tsx์™€ ๋Œ€์กฐํ•ฉ๋‹ˆ๋‹ค. ๊ฐ™์€ ์š”์†Œ, ๊ฐ™์€ ๊ฐ’(๊ทธ ํญ์˜ ๊ณ„์•ฝ์ด ๊ฒ€์ฆํ•œ ๊ฐ’), ๊ฐ™์€ ๊ธ€์ด๊ณ  ๊ทธ ํญ์— ๋‚จ๋Š” ์š”์†Œ๊ฐ€ ์—†์„ ๋•Œ๋งŒ mergedMappingVerified๊ฐ€ true์ด๊ณ , ์•„๋‹ˆ๋ฉด mergeGaps๊ฐ€ ์–ด๋А ํญ์˜ ์–ด๋А ๋…ธ๋“œยท์†์„ฑ์ธ์ง€ ๋Œ€๋ฉฐ ๊ฐ™์€ ์ง„๋‹จ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ปดํฌ๋„ŒํŠธ ์ฐธ์กฐ๋Š” ์ด๋ฆ„๊ณผ ๊ทธ ํญ์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ๊ณ ๋ฅธ variant๊นŒ์ง€ ๋Œ€์กฐํ•˜์ง€๋งŒ, ๊ทธ๋ฆฌ๋Š” ๊ฒƒ์€ ์ด ๋ชจ๋“ˆ์— ์—†๋Š” ์ปดํฌ๋„ŒํŠธ ํŒŒ์ผ์ด๊ณ  ์ธ์Šคํ„ด์Šค๋Š” ๊ทธ ๊ธ€ยทํฌ๊ธฐ๋ฅผ ๋ฎ์–ด์“ธ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ์ฐธ์กฐ๋ฅผ ๋ณด์—ฌ ์ฃผ๋Š” ๋ชจ๋“ˆ์€ ๊ฒ€์ฆ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค(component-body-unverified). ์ฝ”๋“œ๋ฅผ ์ฝ๋Š” ๋Œ€์กฐ์ด์ง€ ๋ Œ๋”๋ง์ด ์•„๋‹™๋‹ˆ๋‹ค. strict๋Š” ๋งคํ•‘ ๋ˆ„๋ฝ๋„ ๊ฑฐ์ ˆํ•ฉ๋‹ˆ๋‹ค. tsx์™€ componentTsx๋ฅผ ํ•จ๊ป˜ ์š”์ฒญํ•œ ๊ฒฝ์šฐ sourceMap.byOutput์ด ์ถœ๋ ฅ๋ณ„ map์„ ๊ตฌ๋ถ„ํ•˜๋ฉฐ ๊ธฐ์กด ๋‹จ์ผ map ํ•„๋“œ๋Š” ํ˜ธํ™˜์„ฑ์„ ์œ„ํ•ด ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค.

outputPaths์˜ ํ”„๋ ˆ์ž„ ์ถœ๋ ฅ ํ‚ค๋Š” frame:<nodeId>:<output>์ž…๋‹ˆ๋‹ค. ์˜ˆ: {"frame:3997:46715:tsx":"C:/allowed/Screen.tsx","frame:3997:46715:sourceMap":"C:/allowed/Screen.map.json"}. outputPathResults.supportedKeys๋Š” ์ด๋ฒˆ ํˆฌ์˜์—์„œ ์‹ค์ œ ์“ธ ์ˆ˜ ์žˆ๋Š” ํ‚ค๋ฅผ ์—ด๊ฑฐํ•ฉ๋‹ˆ๋‹ค. ํ”„๋ ˆ์ž„ ์‘๋‹ต์— ์ผ๋ฐ˜ tsx/sourceMap ํ‚ค๋ฅผ ์“ฐ๊ฑฐ๋‚˜ ๋ฏธ์ง€์›ยท๋ฏธ์ƒ์„ฑ ์ถœ๋ ฅ ํ‚ค๋ฅผ ์“ฐ๋ฉด outputPathResults.diagnostics์— ๋ช…์‹œํ•˜๋ฉฐ ์กฐ์šฉํžˆ ๋ฌด์‹œํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ตœ์ƒ์œ„ ๋‹จ์ผ ์ถœ๋ ฅ์€ ๊ธฐ์กด ์ผ๋ฐ˜ ํ‚ค๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. outputPaths ์‘๋‹ต์€ ์„ฑ๊ณต์ ์œผ๋กœ ์ปค๋ฐ‹ํ•œ ํŒŒ์ผ๋งŒ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ์ž์‚ฐ binary๋Š” ๊ณ„์† assetRequests[].outputPath๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

๋ช…์‹œ์  resource์™€ ์ž๋™ resource ์ „ํ™˜ ๋ชจ๋‘ nextAction.tool/arguments๋กœ ๋™์ผ artifact์˜ ์ž‘์€ ๋ณธ๋ฌธ ๋Œ€์กฐ ์˜ˆ์‹œ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ผ๋ฐ˜ ํ™”๋ฉด์€ ๋‹จ์ผ frameยท๋‹จ์ผ ์ถœ๋ ฅ์ด๋ฉฐ ํŒŒ์ผ์„ ๋‹ค์‹œ ์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. sizeEstimate.outputBytes๋Š” ๊ธฐ์กด ์„ ํƒ ์ถœ๋ ฅ์˜ UTF-8 ๋ฐ”์ดํŠธ ์ˆ˜(PNG๋Š” binary ๋ฐ”์ดํŠธ ์ˆ˜)์ด๊ณ  minimumWireBytes๋Š” ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์ œ์™ธํ•œ JSON ์ค‘๋ณต ์ „์†ก ํ•˜ํ•œ์ž…๋‹ˆ๋‹ค. ์žฌํˆฌ์˜ ์‘๋‹ต์˜ ์ „์ฒด ํฌ๊ธฐ๋Š” ์‹ค์ธกํ•˜์ง€ ์•Š์•˜์œผ๋ฏ€๋กœ ํ•œ๋„ ๋ฏธ๋งŒ์˜ ๊ฒฝ์šฐ์—๋„ inlineFit=unknown์ž…๋‹ˆ๋‹ค. ํ™•์‹คํžˆ ํ•œ๋„๋ฅผ ๋„˜๋Š” ๊ฒฝ์šฐ resource๋ฅผ ์•ˆ๋‚ดํ•˜๋ฉฐ, inline ํฌ๊ธฐ ์ดˆ๊ณผ ์‹œ ๊ธฐ์กด R12 ๊ต์ • ์ธ์ž๋ฅผ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. ์ž์‚ฐ ์š”์ฒญยท์ถœ๋ ฅ ๊ฒฝ๋กœ๊ฐ€ ์ฝ”๋“œ URL์„ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ์›๋ž˜ ๊ฒฐํ•ฉ๋œ resource ์ธ์ž๋ฅผ ์œ ์ง€ํ•˜์—ฌ ์ž˜๋ชป๋œ ๋ณธ๋ฌธ ๋Œ€์กฐ๋ฅผ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.

Project intake comes first

/devup is the entry point for new projects, existing features, bug fixes, Figma page implementation and refactors. Figma screen planning is a branch of the confirmed project plan; complete this section before the screen workflow below.

Read sources before asking: devup_project_context for every supported scope, Atlassian MCP getJiraIssue / searchJiraIssuesUsingJql for issues, epic children and dependencies, referenced Confluence/spec documents, and Figma exploration (screen names, field labels, forms, tables and detail screens). Pass each read as evidence: [{source, facts}] to devup_project_plan. Facts use goal, parts, figma (links), screens ([{name, fields}]), and <featureId>.screens, .states, .permissions, .forms, .notifications. Backend decisions use erd, tenancy, auth, lifecycle, indexes, storage; delivery uses jiraPolicy, branchPolicy, prPolicy. A sourced fact is a default with its provenance, not another question. Sources are read by the runtime's connectors, never by treating a URL as its contents.

  1. devup_project_plan intake with projectRoot: always ask the four initial questions with the runtime question tool (recommended option first): work type (refactor may be a custom answer), optional Figma/Jira/spec sources, one-sentence goal, and parts. Existing apis/ recommends front + admin + API; apps/admin recommends front + admin. Retain planId on every later call.
  2. Read newly named sources. draft with all initial answers, source-labelled evidence and features: [{id,scope,screens,acceptance}] or issues: [{key,scope,dependencies,acceptance}] (epic children are issues too). Each unresolved feature gets concrete screen/role, state, permission, validation, notification/integration rounds. Put all remaining unknowns into extraQuestions. No implementation starts while a question remains unanswered.
  3. API in scope: inspect existing vespertide.json and apis/*/models/*.json. Draft complete entities as vespertide model objects and relations with from/to, cardinality oneToMany|manyToMany, column/joinTable and onDelete. Signup/post Figma evidence seeds user/post entities and a user FK, but the agent must expand every actual domain from the source evidence. Review the ERD round, ownership/tenancy, auth with integer roles (0,10,50,100), status enums, soft delete, audit timestamps, nullable fields, unique/composite keys, filter/sort indexes, file/image storage and cascade/restrict/set_null actions. Read https://github.com/dev-five-git/vespertide/blob/main/SKILL.md and schemas/. Use snake_case, $schema on every model, inline PK/FK/unique/index constraints, and table constraints only for CHECK. A manyToMany relation generates its join table. modelsDir is relative to projectRoot; set it explicitly for multiple APIs. Re-draft structured entities for requested corrections before confirming.
  4. When Figma is present, run the existing screen-plan draft/rounds/confirm workflow (routes, groups, exclusions, site facts) before project confirm.
  5. Ask all delivery questions now: Jira none/comment/transition, branch, PR. devup_project_plan confirm requires every applicable answer and writes source models plus confirmed state in one transaction. Then in modelsWorkingDirectory run the returned commands, in order: vespertide diff, vespertide sql, vespertide revision -m "confirmed project plan", vespertide export --orm seaorm. The tool returns commands; the runtime executes and checks them. Never hand-write migrations/ or src/models/. Build vespera CRUD and feature routes, openapi.json, and the devup-api client; devup_stack_diff must show zero drift before done.
  6. Latest-first updates, vinext, exact layout, all interaction/site defaults, runtime/visual audits and the existing never-wait rules below still apply. After confirm, use project-plan defer with id/reason/recommended/destructive; continue independent work. At the end assume with no decisions resolves all pending recommendations; destructive decisions remain unbuilt. Status includes ERD Mermaid, decisions and the unit table; retain unresolved evidence in reports.

Supervisor execution

devup_project_plan split splits work, not files. Jira input produces one unit per issue/sub-task with its explicit dependencies (reject cycles/unknown IDs). Without Jira, it produces shared layout/theme first, then each feature's models -> API -> client -> front chain; front-only features depend on shared. Review cross-feature dependencies before dispatch; the supervisor sequences shared database work to avoid concurrent schema edits. Each unit has acceptance criteria and build/lint/typecheck/tests/validate/runtime/visual/stack-diff checks. .devup/units/<id>.md contains scope, decisions, ERD and screen context, and checklist.

Default unitQuestions: "supervisor" means workers never ask the user: questions were settled across all units during intake, and supervisor ownership avoids repeated questions and keeps decisions consistent. unitQuestions: "worker" is an explicit alternative, still subject to the confirmed never-wait policy. Load orca-cli and orchestration skills to create one worktree per unit, dispatch the DAG in parallel where independent, collect worker_done, and rerun each unit's checks yourself before merging. New decisions use orchestration ask/reply to the supervisor: answer from recorded decisions, or defer with a recommended option while the worker continues other work. Send failed checks back to the same worker session. Update unit states with status unitUpdates (pending -> running -> verifying -> merged, or unresolved); merged needs recorded supervisor verification. Without Orca, execute the same units sequentially in one session. Only under the recorded Jira/PR policy, comment results and PR link and transition issues. Final report includes the unit table, DAG, ERD and unresolved items; never claim completion from split or command generation alone.

Source organization: Rust source under crates/ is limited to 1000 lines per file, including tests. The existing size-contract test checks this alongside all embedded JSON text assets. Source sections use include! inside the original module namespace to retain narrow privacy and existing test names. Long tool text is in crates/devup-mcp/src/server/text/; other crates use src/text/. include_str! keeps these assets inside the binary.

App selection keyword overrides in .devup/screen-plan.config.json:

{"appSelectionKeywords":{"publicApps":["storefront"],"admin":["controlroom"],"publicScreens":["catalogue"],"adminRoutes":["control"]}}

Each supplied category replaces its defaults; omitted categories retain the vendored defaults. An empty category explicitly disables its keyword matches.

Connected URLs: run links-resolve at every width. Each same-origin anchor contributes source URL, accessible label, href, resolved pathname and width to linkTable (also Markdown in finalReport). GET each distinct target once, including concrete dynamic hrefs, and reject HTTP failures, error pages and unplanned-target URLs absent from the confirmed plan and existing routes. Any failing link row blocks done.

Header navigation wider than a narrow viewport emits a navigation-fit risk and a validation warning. Exploration carries measured navigation widths into the screen plan; without a mobile-menu frame, draft proposes a drawer behind a menu button below the navigation content-fit breakpoint.