Skip to content

Latest commit

ย 

History

120 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

devup-mcp

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 ์ชฝ 4๊ฐœ, ํ”„๋กœ์ ํŠธ ์ชฝ 5๊ฐœ, ์Šคํ‚ฌ 1๊ฐœ, ๋ชจ๋‘ 10๊ฐœ์ž…๋‹ˆ๋‹ค.

  • 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: ์—ฐ๊ฒฐ ์ƒํƒœ ํ™•์ธ, ๋ธŒ๋ผ์šฐ์ € OAuth ๋กœ๊ทธ์ธ, ๋กœ๊ทธ์•„์›ƒ, ์‚ฌ์ „ ๋“ฑ๋ก ์ž๊ฒฉ์ฆ๋ช… ์ฃผ์ž…(configure), ์—ฐ๊ฒฐ ์‹คํŒจ ์›์ธ์„ ์‹ค์ธกํ•ด ๋ณด๊ณ ํ•˜๋Š” doctor
  • devup_skills: devup-mcp๊ฐ€ ๋‚ด๋†“๋Š” ์ฝ”๋“œ์— ํ•„์š”ํ•œ ์—์ด์ „ํŠธ ์Šคํ‚ฌ์ด ์ด ์›Œํฌ์ŠคํŽ˜์ด์Šค์— ์žˆ๋Š”์ง€ ๋ณด๊ณ (status)ํ•˜๊ณ , devup-mcp๊ฐ€ ํ’ˆ๊ณ  ์žˆ๋Š” ๊ฒƒ์„ ์„ค์น˜(install). ํ…์ŠคํŠธ๋ฅผ ์‘๋‹ต์— ์‹ค์–ด ๋ณด๋‚ด๋Š” ๊ฒŒ ์•„๋‹ˆ๋ผ ์Šคํ‚ฌ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์„ค์น˜ํ•ด์„œ ์—์ด์ „ํŠธ ์ž์‹ ์˜ ๋กœ๋”๊ฐ€ ์ฝ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค โ€” ํ•œ ๋ฒˆ ์ฝ์€ ๋ฌธ์„œ๋Š” ํ•œ ๋ฒˆ ์“ฐ์ด์ง€๋งŒ, ์„ค์น˜๋œ ์Šคํ‚ฌ์€ ์ดํ›„ ๋ชจ๋“  ์„ธ์…˜์— ๊ณ„์† ์ ์šฉ๋ฉ๋‹ˆ๋‹ค
  • 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_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 ๊ฐ™์€ ์ด๋ฆ„์˜ ํ‚ค
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์ผ ๋•Œ

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์ด ์—†์Œ)

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

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์— ๊ทธ๋Œ€๋กœ ์‹ค๋ฆฝ๋‹ˆ๋‹ค.

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๋ฅผ ์Šค์Šค๋กœ ๋ณต๊ตฌํ•  ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค.

์„œ๋ฒ„ ์‹œ์ž‘ ์‹œ 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
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์€ ํ•ด๋‹น ์ •๋ณด๋งŒ ์‚ญ์ œํ•ฉ๋‹ˆ๋‹ค.

์ธ์ฆ

{ "action": "status" }

action์€ status, login, logout, configure, doctor ์ค‘ ํ•˜๋‚˜์ด๋ฉฐ, ์Šคํ‚ค๋งˆ๊ฐ€ ์ด ๋ชฉ๋ก์„ ๊ทธ๋Œ€๋กœ ๊ฒŒ์‹œํ•ฉ๋‹ˆ๋‹ค. status/login/logout์˜ ์‘๋‹ต ํ˜•ํƒœ๋Š” ํ•ญ์ƒ { "status": "connected" | "disconnected" }์ž…๋‹ˆ๋‹ค. Figma์— ๋ถ™์ง€ ๋ชปํ•˜๋Š” ์ด์œ ๋ฅผ ์•Œ๊ณ  ์‹ถ์œผ๋ฉด doctor๋ฅผ ํ˜ธ์ถœํ•˜์„ธ์š”.

{ "action": "doctor" }
{
  "status": "disconnected",
  "preferredPath": "bridge",
  "preferredPathNote": "Two paths reach Figma and they are not equals. ...",
  "paths": {
    "bridge": {
      "available": false,
      "listening": true,
      "port": 1993,
      "attachedFiles": [],
      "attachedFilesNote": "File keys the attached plugins have open. ...",
      "reason": "The bridge is listening on 127.0.0.1:1993 but no plugin is attached. ..."
    },
    "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": "์ €์žฅ๋œ ์ž๊ฒฉ์ฆ๋ช… ์—†์Œ. ..."
    }
  },
  "clientSetup": { "constraints": { ... }, "opencode": { ... }, "claudeCode": "...", "codex": "..." }
}

doctor๋Š” ๋„คํŠธ์›Œํฌ ํ˜ธ์ถœ์„ ์ „ํ˜€ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

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

  • listening: false โ€” ์ด ํ”„๋กœ์„ธ์Šค๊ฐ€ ๋ธŒ๋ฆฌ์ง€ ํฌํŠธ๋ฅผ ์•„์˜ˆ ์žก์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. DEVUP_FIGMA_BRIDGE_PORT๊ฐ€ off์ด๊ฑฐ๋‚˜, ๋‹ค๋ฅธ devup-mcp๊ฐ€ ์ด๋ฏธ ๊ทธ ํฌํŠธ๋ฅผ ์ฅ๊ณ  ์žˆ๋Š” ๊ฒฝ์šฐ์ž…๋‹ˆ๋‹ค(MCP ํด๋ผ์ด์–ธํŠธ๋ฅผ ์—ฌ๋Ÿฌ ๊ฐœ ๋„์šฐ๋ฉด ์ •์ƒ์ž…๋‹ˆ๋‹ค). ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์•„๋ฌด๋ฆฌ ์‹คํ–‰ํ•ด๋„ ์ด ํ”„๋กœ์„ธ์Šค๋กœ๋Š” ์˜ค์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • listening: true, attachedFiles: [] โ€” ๋ฌธ์€ ์—ด๋ ค ์žˆ๋Š”๋ฐ ์•„๋ฌด๋„ ๋“ค์–ด์˜ค์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋Œ€์ƒ ํŒŒ์ผ์—์„œ Devup Bridge ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์‹คํ–‰ํ•˜์„ธ์š”.
  • attachedFiles์— ํŒŒ์ผ ํ‚ค๊ฐ€ ์žˆ์Œ โ€” ์ •์ƒ ๋™์ž‘์ž…๋‹ˆ๋‹ค. ์ด ์ƒํƒœ๋ฉด ๋กœ๊ทธ์ธ ์—†์ด ๊ทธ ํŒŒ์ผ์˜ ์ˆ˜์ง‘์ด ๊ทธ๋Œ€๋กœ ๋ฉ๋‹ˆ๋‹ค.

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"}์ด ๋„คํŠธ์›Œํฌ ์—†์ด ์Šคํ‚ฌ ๋””๋ ‰ํ„ฐ๋ฆฌ์— ์”๋‹ˆ๋‹ค
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ํ•œ ๋ชจ๋“  ๊ธฐ๊ณ„์—์„œ ์ž„์˜ ์‹คํ–‰์ด ๋ฉ๋‹ˆ๋‹ค.

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

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

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

์œ ๋„๋Š” ๋‘ ๊ณณ์—์„œ๋งŒ ํ•ฉ๋‹ˆ๋‹ค. ์„ธ์…˜๋งˆ๋‹ค ์‹ค๋ฆฌ๋Š” instructions์˜ ํ•œ ์ค„, ๊ทธ๋ฆฌ๊ณ  devup_ui_validate๊ฐ€ ์œ„๋ฐ˜์„ ์ฐพ์•˜๋Š”๋ฐ devup-ui ์Šคํ‚ฌ์ด ์‹ค์ œ๋กœ ์—†์„ ๋•Œ๋งŒ ๋ถ™๋Š” skillGap์ž…๋‹ˆ๋‹ค. ์ด๋ฏธ ๊น”๋ ค ์žˆ๋Š” ์‚ฌ๋žŒ์—๊ฒŒ ๊น”๋ผ๊ณ  ํ•˜๋Š” ๊ฒƒ์€ ๊ทธ ํ•„๋“œ๋ฅผ ๋ฌด์‹œํ•˜๊ฒŒ ๋งŒ๋“œ๋Š” ์†Œ์Œ์ž…๋‹ˆ๋‹ค.

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

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

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

๋ธŒ๋ฆฌ์ง€๋ฅผ ๋จผ์ € ์“ฐ์‹ญ์‹œ์˜ค. direct๋Š” Figma๊ฐ€ ์‚ฌ์šฉ๋Ÿ‰์„ ์„ธ๋Š” ๊ฒฝ๋กœ์ด๊ณ , ํ™”๋ฉด ํ•˜๋‚˜๊ฐ€ ์—ฌ๋Ÿฌ ๋ฒˆ์˜ ์ฝ๊ธฐ๋ฅผ ์“ฐ๋ฏ€๋กœ ํ•œ๋„๊ฐ€ ๊ธˆ๋ฐฉ ๋ฐ”๋‹ฅ๋‚ฉ๋‹ˆ๋‹ค.

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

์ง€๊ธˆ ์–ด๋А ๊ฒฝ๋กœ๊ฐ€ ์‚ด์•„ ์žˆ๋Š”์ง€๋Š” devup_figma_auth { action: "doctor" }์˜ paths.bridge/paths.direct๋กœ ํ™•์ธํ•˜์„ธ์š”.

ํ˜„์žฌ ๋ธŒ๋ฆฌ์ง€๊ฐ€ ๋ชป ํ•˜๋Š” ์ฝ๊ธฐ๋Š” ๋‘˜์ž…๋‹ˆ๋‹ค โ€” scope: "file"์˜ ๋…ธ๋“œ ์—†๋Š” metadata ์ฝ๊ธฐ(์ตœ์ƒ์œ„ ํŽ˜์ด์ง€ ๋ชฉ๋ก์ด๋ผ ๊ณ„์•ฝ์ด ๋‹ค๋ฆ…๋‹ˆ๋‹ค)์™€ referencePng์˜ get_screenshot. ๊ทธ ๋ฐ–์˜ tsx export ๊ฒฝ๋กœ๋Š” ์ „๋ถ€ ๋ธŒ๋ฆฌ์ง€๋กœ ๊ฐ‘๋‹ˆ๋‹ค.

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

๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๊ฐ€ ์ง์ ‘ ๋งŒ๋“  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_FIGMA_BRIDGE_PORT=off.
  • ์ฝ๊ธฐ ์ „์šฉ์ž…๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์‹คํ–‰ํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ๋Š” ๋นŒ๋“œ ์‹œ์ ์— ํ”Œ๋Ÿฌ๊ทธ์ธ ์•ˆ์— ๋ฐ•ํ˜€ ์žˆ๊ณ , devup-mcp๋Š” ๊ทธ์ค‘ ์–ด๋А ๊ฒƒ์„ ์‹คํ–‰ํ• ์ง€ ์ด๋ฆ„๋งŒ ๋ณด๋ƒ…๋‹ˆ๋‹ค. ์†Œ์ผ“์œผ๋กœ ์ฝ”๋“œ๊ฐ€ ์˜ค๊ฐ€์ง€ ์•Š์œผ๋ฉฐ Figma ๋ฌธ์„œ๋ฅผ ๋ฐ”๊พธ๋Š” ํ˜ธ์ถœ์€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ์ด ๊ธฐ๊ธฐ์—์„œ๋งŒ ๋ฉ๋‹ˆ๋‹ค. ๋Œ€๊ธฐ ์ฃผ์†Œ๋Š” 127.0.0.1์ด๋ผ ๋‹ค๋ฅธ ๊ธฐ๊ธฐ์—์„œ๋Š” ๋ถ™์„ ์ˆ˜ ์—†๊ณ , ์›๊ฒฉ CI์—์„œ๋Š” ๋ธŒ๋ฆฌ์ง€๊ฐ€ ์—†์œผ๋‹ˆ ์ž๋™์œผ๋กœ ์›๊ฒฉ ๊ฒฝ๋กœ๋ฅผ ์”๋‹ˆ๋‹ค.
  • ํŒŒ์ผ์€ ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์ž๊ธฐ ํŒŒ์ผ ํ‚ค๋ฅผ ๋ณด๊ณ ํ•˜์ง€ ๋ชปํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์–ด(Dev Mode ๋“ฑ), ํ‚ค ์—†๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ํ˜ผ์ž ๋ถ™์–ด ์žˆ์„ ๋•Œ๋งŒ ์ฝ๊ธฐ๋ฅผ ๋ฐ›์Šต๋‹ˆ๋‹ค. ๋‘ ๊ฐœ ์ด์ƒ์ด๋ฉด ์–ด๋А ํŒŒ์ผ์ธ์ง€ ์•Œ ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ์›๊ฒฉ ๊ฒฝ๋กœ๋กœ ๋„˜์–ด๊ฐ‘๋‹ˆ๋‹ค.
  • ํฌํŠธ๋ฅผ ๋ฐ”๊พธ๋ ค๋ฉด ์„ธ ๊ณณ์„ ๊ฐ™์ด ๊ณ ์ณ์•ผ ํ•ฉ๋‹ˆ๋‹ค. Figma๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์ ‘์†ํ•  ์ˆ˜ ์žˆ๋Š” ์ฃผ์†Œ๋ฅผ manifest์— ๋ฏธ๋ฆฌ ์ ์–ด ๋‘๊ฒŒ ํ•˜๊ณ  ์‹คํ–‰ ์ค‘์—๋Š” ๋ฐ”๊ฟ€ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. manifest.json์˜ allowedDomains, src/code.ts์˜ PORT, ๊ทธ๋ฆฌ๊ณ  ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ๋ชจ๋‘ ๊ณ ์นœ ๋’ค ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋‹ค์‹œ ๋นŒ๋“œยท์„ค์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ•˜๋‚˜๋งŒ ๋ฐ”๊พธ๋ฉด ์•„๋ฌด ๋ง ์—†์ด ์›๊ฒฉ ๊ฒฝ๋กœ๋กœ ๋Œ์•„๊ฐ€ ํ•œ๋„๋ฅผ ๊ทธ๋Œ€๋กœ ์”๋‹ˆ๋‹ค.
  • ws://127.0.0.1์€ ์“ธ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. Figma์˜ allowedDomains๊ฐ€ ๊ทธ ์ฃผ์†Œ๋ฅผ ๊ฑฐ๋ถ€ํ•ด manifest ์ „์ฒด๊ฐ€ ๋ฌดํšจ๊ฐ€ ๋˜๊ณ  ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์•„์˜ˆ ๋กœ๋“œ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. localhost์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • Figma ์ฐฝ์„ ๋’ค๋กœ ๋ณด๋‚ด๋ฉด ๋А๋ ค์ง‘๋‹ˆ๋‹ค. ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ๋ถ„๋‹น ํ•œ ๋ฒˆ๊ผด๋กœ ๊นจ์šฐ๊ธฐ ๋•Œ๋ฌธ์—, ์•ž์—์„œ 160ms์ธ snapshot์ด ๋’ค์—์„œ๋Š” 11์ดˆ๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ์ˆ˜์ง‘ ์ค‘์—๋Š” Figma๋ฅผ ์•ž์— ๋‘์„ธ์š”.

์ž์„ธํ•œ ๋‚ด๋ถ€ ๊ตฌ์กฐ์™€ ์Šคํฌ๋ฆฝํŠธ ์ƒ์„ฑ ๋ฐฉ์‹์€ 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์˜ ์™ธ๋ถ€ ํฌ๊ธฐยท์œ„์น˜ ์ œ์•ฝ๋งŒ ์ƒ๋žตํ•ฉ๋‹ˆ๋‹ค.

Figma โ†’ devup.json

{
  "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๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

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๊ฐœ/ํ•ญ๋ชฉ๋‹น 32 MiB/์ „์ฒด 128 MiB์ธ memory-only LRU cache๋ฅผ ์žฌ์‚ฌ์šฉํ•˜๋ฉฐ, refresh: true๋Š” ์™„๋ฃŒ cache๋ฟ ์•„๋‹ˆ๋ผ ์ง„ํ–‰ ์ค‘ ์š”์ฒญ ๊ณต์œ ๋„ ์šฐํšŒํ•ด URL์„ ์ƒˆ๋กœ ์ˆ˜์ง‘ํ•ฉ๋‹ˆ๋‹ค. ๋™์ผ 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`๋Š” ์„ ํƒํ–ˆ์„ ๋•Œ๋งŒ ๊ณต์‹ read-only `get_screenshot`์„ ์ •ํ™•ํžˆ ํ•œ ๋ฒˆ ์ถ”๊ฐ€ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. ๊ณต์‹ ๋„๊ตฌ๋Š” ๊ธฐ๋ณธ์œผ๋กœ 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๋กœ ๋น„๊ตํ•ฉ๋‹ˆ๋‹ค. renderer pinning, ๊ธฐ๋ณธ 0.5% threshold, diff PNG์™€ ๊ฐœ์ธ์ •๋ณด ์ทจ๊ธ‰ ๊ณ„์•ฝ์€ docs/visual-renderer-contract.md์— ์žˆ์Šต๋‹ˆ๋‹ค.

๋ Œ๋”๋ง ํ•˜๋„ค์Šค โ€” ์ƒ์„ฑ ์ฝ”๋“œ๋ฅผ 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์€ ์‹ค์ œ ํŒŒ์ผ ๋ฐ”์ดํŠธ๊นŒ์ง€ ํ™•์ธํ•œ ๊ฒฐ๊ณผ์ž…๋‹ˆ๋‹ค. ์ƒ์„ธ ๊ณ„์•ฝ์€ R3 ์ž์‚ฐ ์ž‘์—…๊ณผ ๊ทผ๊ฑฐ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

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๋กœ ๋ฐ”๊ฟ” ํ˜ธ์ถœํ•˜๋ฉด ์„ ํƒํ•œ ํ™”๋ฉด๋งŒ ์ˆ˜์ง‘ํ•ฉ๋‹ˆ๋‹ค.

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

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 ๊ฐ’์ด ๋†“์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํญ๋งˆ๋‹ค ์ค„๋ฐ”๊ฟˆ ์œ„์น˜๋งŒ ๋‹ค๋ฅธ ํ…์ŠคํŠธ๋Š” <Box as="br" display={[...]} />๋กœ ์“ฐ๊ณ , ์ปดํฌ๋„ŒํŠธ ์ธ์Šคํ„ด์Šค์˜ variant prop์ด ํญ๋งˆ๋‹ค ๋‹ค๋ฅด๋ฉด ๋ฐฐ์—ด๋กœ ์“ธ ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ๊ฐ€์žฅ ๋„“์€ ํญ์˜ ๊ฐ’์„ ์“ฐ๊ณ  responsiveUnrepresented์— ๋ณด๊ณ ํ•ฉ๋‹ˆ๋‹ค. ํ•จ๊ป˜ ๋ฐ˜ํ™˜๋˜๋Š” responsiveSlots, responsiveImports, responsiveComponents๊ฐ€ slot๊ณผ import ๋ชฉ๋ก์ž…๋‹ˆ๋‹ค.

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

์‹œ๊ฐ„ ํŠธ๋ฆฌ๊ฑฐ 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๋ฅผ ์ˆ˜์ง‘ํ•ฉ๋‹ˆ๋‹ค.

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 ยท info-only ยท warnings-only ยท warnings-and-info ยท strict-warnings ยท error-violations
violations[] rule, severity(info|warning|error), byteRange, message, suggestion
violationCounts error / warning / info ๊ฐœ์ˆ˜
themeNotes[] ๋น„์–ด ์žˆ๋Š” ํ† ํฐ ์นดํ…Œ๊ณ ๋ฆฌ๋ฅผ ์ข…๋ฅ˜๋งˆ๋‹ค ํ•œ ๋ฒˆ์”ฉ๋งŒ ์š”์•ฝ
tokens referencedByTsx, definedByTheme, unknownTokenCheckRan
checkedTokens / availableTokenCount ์œ„ ๋‘ ๊ฐ’๊ณผ ๊ฐ™์€ ์ˆ˜๋ฅผ ์˜ˆ์ „ ์ด๋ฆ„์œผ๋กœ ์œ ์ง€
themeAvailable / themeGuardrail ํ…Œ๋งˆ๋ฅผ ์ฐพ์•˜๋Š”์ง€, ๋ชป ์ฐพ์•˜์œผ๋ฉด ์™œ

๊ทœ์น™์€ invalid-syntax, unknown-token, hardcoded-color, hardcoded-length, unknown-prop, runtime-value์ž…๋‹ˆ๋‹ค.

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

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

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์ž…๋‹ˆ๋‹ค โ€” ๋ฌด์—‡์œผ๋กœ ๊ทธ๋ฆฐ ๊ทธ๋ฆผ์ธ์ง€ ๋ชจ๋ฅด๋Š” ์ฑ„ "๊ฐ™๋‹ค"๊ณ  ๋งํ•˜๋Š” ๊ฒƒ์€ ๋ณด์ฆ์ด ์•„๋‹ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค. renderer pinning๊ณผ ํ™˜๊ฒฝ manifest ๊ณ„์•ฝ์€ docs/visual-renderer-contract.md์— ์žˆ์Šต๋‹ˆ๋‹ค.

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

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

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

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

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

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

  • 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์„ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋””์Šคํฌ์— ์ €์žฅํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • 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 ๊ฐฑ์‹ ์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์ƒ์„ธ ์„ค๊ณ„๋Š” docs/superpowers/specs/2026-08-30-figma-remote-mcp-design.md๋ฅผ ์ฐธ๊ณ ํ•˜์„ธ์š”.

docs/superpowers/ ์•„๋ž˜์˜ planยทspec์€ ์ž‘์„ฑ ์‹œ์ ์˜ ๊ธฐ๋ก์ด์ง€ ํ˜„์žฌ API ๋ฌธ์„œ๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์œ„์—์„œ ์ œ๊ฑฐํ–ˆ๋‹ค๊ณ  ์ ์€ devup_figma_to_ui/devup_figma_to_json์„ ๊ทธ ๋ฌธ์„œ๋“ค์€ ์•„์ง ํ˜„์žฌ ๋„๊ตฌ์ฒ˜๋Ÿผ ๊ธฐ์ˆ ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ ๋„๊ตฌ ๋ชฉ๋ก๊ณผ ๋™์ž‘์˜ ๊ธฐ์ค€์€ ์ด README์™€ ์„œ๋ฒ„๊ฐ€ ๊ฒŒ์‹œํ•˜๋Š” ์Šคํ‚ค๋งˆยทtool description์ž…๋‹ˆ๋‹ค.

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

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์ž…๋‹ˆ๋‹ค. ์ž์„ธํ•œ ์ •์˜๋Š” R1 ์‘๋‹ต ๊ณ„์•ฝ์„ ์ฐธ๊ณ ํ•˜์„ธ์š”.

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์€ ํ˜ธํ™˜ ๋ณ„์นญ). jobId๋กœ ์ƒํƒœ๋ฅผ ์กฐํšŒํ•˜๊ณ  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(ํ™•์ธ๋œ ์บ์‹œ ์ œ๊ฑฐ), unknown(ํ˜„์žฌ ์„œ๋ฒ„์—์„œ ์›์ธ ํŒ๋‹จ ๋ถˆ๊ฐ€)์„ ๊ตฌ๋ถ„ํ•ฉ๋‹ˆ๋‹ค. ์ตœ๊ทผ ์ œ๊ฑฐ 64๊ฐœ ์ด๋‚ด์˜ ๋ณต๊ตฌ metadata๊ฐ€ ์žˆ์œผ๋ฉด canonical URL๊ณผ ์„ ํƒ์„ nextAction.arguments๋กœ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. URL ์›๋ฌธ์˜ ํŒŒ์ผ ์ œ๋ชฉ์€ ์ €์žฅํ•˜์ง€ ์•Š์•„๋„ ๊ฐ™์€ ๋Œ€์ƒ์„ ๊ฐ€๋ฆฌํ‚ค๋Š” canonical URL์„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค. metadata๊ฐ€ ์—†์œผ๋ฉด recoveryState:"unrecoverable"๋กœ ๋ช…์‹œํ•˜๋ฉฐ, ์„œ๋ฒ„ ์žฌ์‹œ์ž‘์ด๋ผ๊ณ  ๋‹จ์ •ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

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

TSX ํ•ญ๋ชฉ์˜ ๊ธฐ๋ณธ ํ•„๋“œ๋Š” nodeId, property, generatedProperty, resolution์ž…๋‹ˆ๋‹ค. ์„ ํƒ์  variableId๋Š” ์›๋ณธ ๋ณ€์ˆ˜ ํ† ํฐ ์ฐธ์กฐ, styleId๋Š” typography/style ํ† ํฐ ์ฐธ์กฐ, assetId๋Š” ์ด๋ฏธ์ง€ยท๋ฒกํ„ฐ ์ž์‚ฐ ์ฐธ์กฐ๋ฅผ ์‹๋ณ„ํ•  ๋•Œ ๋ถ™์Šต๋‹ˆ๋‹ค. ํ•œ ํ•ญ๋ชฉ์— ์—ฌ๋Ÿฌ ์‹๋ณ„์ž๊ฐ€ ํ•จ๊ป˜ ์žˆ์„ ์ˆ˜ ์žˆ์œผ๋ฉฐ ํ•ด๋‹น ์ฐธ์กฐ๊ฐ€ ์—†์œผ๋ฉด ์ƒ๋žตํ•ฉ๋‹ˆ๋‹ค. 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์—๋„ ์š”์•ฝ์ด ์œ ์ง€๋ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋ฒ”์œ„๋Š” R16 ์‘๋‹ต ๊ณ„์•ฝ์„ ์ฐธ๊ณ ํ•ฉ๋‹ˆ๋‹ค.

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 ํŒ์ •์€ ์œ ์ง€ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜์‘ํ˜• ๋ณ‘ํ•ฉ์€ ๊ฐœ๋ณ„ breakpoint source mapping๋งŒ์œผ๋กœ ๋ณ‘ํ•ฉ ์†์„ฑ์„ ์ฆ๋ช…ํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ ๊ฐ™์€ ์ง„๋‹จ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. 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 ์ธ์ž๋ฅผ ์œ ์ง€ํ•˜์—ฌ ์ž˜๋ชป๋œ ๋ณธ๋ฌธ ๋Œ€์กฐ๋ฅผ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages