@su-record/vibe 2.8.47 → 2.8.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,8 +11,15 @@ tier: standard
11
11
  **Claude는 시맨틱 판단(태그 선택, 컴포넌트 분리, 인터랙션)만 담당한다.**
12
12
 
13
13
  ```
14
+ ⛔ 불변 규칙 — 복잡한 UI도 반드시 HTML로 구현한다:
15
+ "복잡하니까 이미지로 처리하자" → 이 사고방식 자체가 금지.
16
+ 카드 그리드, 보상 목록, 교환소, 가격 표시 → 전부 HTML+CSS.
17
+ <img>는 순수 이미지 에셋(아이콘, 썸네일, 벡터 글자)에만 사용.
18
+ BG는 CSS background-image만 사용. <img> 태그 금지.
19
+
14
20
  ❌ 스크린샷을 보고 CSS 추정 (범용 LLM의 약점)
15
21
  ❌ Figma 레이어를 무분별하게 div soup로 변환
22
+ ❌ 복잡한 섹션을 screenshot으로 이미지화 (코드 생성 중 screenshot 호출 금지)
16
23
  ✅ Figma Auto Layout → CSS Flexbox 1:1 매핑 (기계적)
17
24
  ✅ Figma CSS 속성 → SCSS 직접 변환 (추정 없음)
18
25
  ✅ Claude → 시맨틱 태그 선택 + 컴포넌트 설계 + 인터랙션
@@ -55,11 +62,45 @@ component-index (/tmp/{feature}/component-index.json) 에서 매칭되는 컴포
55
62
  ### 1-1. 노드 → HTML 매핑 규칙 (기계적)
56
63
 
57
64
  ```
65
+ ⛔ 코드 작성 전: 이미지 vs HTML 판별 테이블을 먼저 작성한다 (BLOCKING).
66
+ 섹션의 모든 1~2depth 노드를 순회하여 각각의 처리 방법을 결정.
67
+ 판별 없이 코드를 작성하면 안 된다.
68
+
69
+ 판별 규칙 (순서대로 적용, 하나라도 YES → HTML):
70
+ Q1. 이 노드의 자식 트리에 TEXT 노드가 있는가?
71
+ YES → HTML로 구현 (이미지에 텍스트를 넣지 않는다)
72
+ ※ "가격 1,000", "보상 교환하기", "이벤트 기간" 등이 있으면 무조건 HTML
73
+ Q2. 같은 부모 아래 동일 구조 INSTANCE가 2개 이상인가?
74
+ YES → HTML 반복 구조 (v-for/.map) — 내부 아이콘/썸네일만 <img>
75
+ ※ 카드 4개 그리드를 이미지 1장으로 렌더링하면 절대 안 됨
76
+ Q3. 클릭/인터랙션이 필요한가? (btn, CTA, link, tab)
77
+ YES → HTML <button>/<a>
78
+ Q4. 동적으로 변경되는 데이터인가? (가격, 수량, 기간, 상태)
79
+ YES → HTML 텍스트
80
+ 모두 NO → 이미지 렌더링 가능 (벡터 글자, 래스터 에셋, 합성 BG 등)
81
+
58
82
  각 노드에 대해 아래 규칙을 순서대로 적용:
59
83
 
60
- 1. 타입별 기본 매핑:
84
+ 1. 배경 레이어 (BG 프레임 — 가장 먼저 처리):
85
+ BG 프레임 (name에 "BG"/"bg" 또는 부모와 동일 크기)
86
+
87
+ ❌ 절대 금지 패턴 (이렇게 쓰면 안 됨):
88
+ <img src="hero-bg.webp" class="bg-img" />
89
+ <div class="bg"><img src="bg.webp" /></div>
90
+ position: absolute; inset: 0; → 이미지 배치
91
+
92
+ ✅ 유일하게 허용되는 패턴:
93
+ 부모 요소의 SCSS에 background-image로 처리:
94
+ .heroSection {
95
+ background-image: url('/images/{feature}/hero-bg.webp');
96
+ background-size: cover;
97
+ background-position: center top;
98
+ }
99
+ HTML에는 BG 관련 요소를 아무것도 넣지 않음.
100
+
101
+ 2. 타입별 기본 매핑:
61
102
  TEXT 노드 → <span> (Claude가 <h1>~<h6>, <p>, <button> 등으로 승격)
62
- IMAGE fill → <img src="다운로드된 경로" />
103
+ IMAGE fill → <img src="다운로드된 경로" /> (판별 통과한 순수 에셋만)
63
104
  VECTOR/GROUP → 크기가 작으면(≤64px) 아이콘 후보 → <img> (렌더링 이미지)
64
105
  크기가 크면 장식 요소 → <div> + background
65
106
  FRAME/INSTANCE:
@@ -67,20 +108,21 @@ component-index (/tmp/{feature}/component-index.json) 에서 매칭되는 컴포
67
108
  Auto Layout 없음 → <div> + position:relative (자식은 absolute)
68
109
  children 없음 → 빈 div 또는 스킵
69
110
 
70
- 2. 배경 레이어 판별:
71
- BG 프레임 (name에 "BG"/"bg" 또는 부모와 동일 크기)
72
- ❌ <img> 태그로 배경 처리 금지
73
- ❌ position:absolute + inset:0 으로 이미지 배치 금지
74
- ✅ 부모 요소에 CSS background-image로 처리:
75
- background-image: url('/images/{feature}/{section}-bg.webp');
76
- background-size: cover;
77
- background-position: center top;
78
- ✅ BG 프레임은 HTML에 아무것도 렌더링하지 않음 (CSS만)
79
-
80
111
  3. 반복 패턴 감지:
81
- 같은 부모 아래 동일 타입 + 유사 구조(children 수 동일) 노드 3개 이상
112
+ 같은 부모 아래 동일 타입 + 유사 구조(children 수 동일) INSTANCE 2개 이상
82
113
  → v-for (Vue) 또는 .map() (React)
83
114
  → 첫 번째 노드를 기준으로 템플릿 생성
115
+ → 카드 내부의 이미지 에셋(아이콘, 썸네일)만 <img>
116
+ → 카드 레이아웃, 텍스트, 버튼, 가격은 HTML로 구현
117
+
118
+ ❌ 잘못된 예: <img src="exchange-section1.webp" /> (카드 4개가 한 이미지)
119
+ ✅ 올바른 예:
120
+ <div v-for="card in weeklyCards" :key="card.id" class="card">
121
+ <img :src="card.icon" :alt="card.name" class="card__icon" />
122
+ <span class="card__name">{{ card.name }}</span>
123
+ <span class="card__price">{{ card.price }}</span>
124
+ <button class="card__btn">보상 교환하기</button>
125
+ </div>
84
126
 
85
127
  4. 스킵 대상:
86
128
  크기 0px 노드
@@ -99,14 +141,29 @@ tree.json의 css 객체를 SCSS로 직접 변환한다. 추정하지 않는다.
99
141
  node.css.justifyContent → justify-content
100
142
  node.css.alignItems → align-items
101
143
  node.css.gap → gap (→ vw 변환)
144
+ node.css.flexGrow → flex-grow (값 그대로)
102
145
  node.css.padding → padding (→ vw 변환)
103
146
  node.css.width → width (→ vw 또는 % 변환)
104
147
  node.css.height → height (→ vw 변환, 또는 auto)
105
148
  node.css.overflow → overflow
106
149
  node.css.position → position
150
+ node.css.top → top (→ vw 변환, absolute 노드만)
151
+ node.css.left → left (→ vw 변환, absolute 노드만)
152
+ node.css.transform → transform (rotate — 값 그대로)
153
+
154
+ layoutSizing 처리 (node.layoutSizingH / node.layoutSizingV):
155
+ HUG → css에 width/height 없음 (auto). SCSS에서 생략하면 auto 동작.
156
+ FILL → 부모 flex-direction 확인 후 결정:
157
+ 부모 row + 자식 FILL-H → flex: 1 0 0 (또는 width: 100%)
158
+ 부모 column + 자식 FILL-V → flex: 1 0 0 (또는 height: 100%)
159
+ 부모 row + 자식 FILL-V → align-self: stretch
160
+ 부모 column + 자식 FILL-H → align-self: stretch
161
+ FIXED → css.width/height의 px 값을 vw로 변환 (기존 방식)
107
162
 
108
163
  비주얼 (components/ 파일):
109
164
  node.css.backgroundColor → background-color
165
+ node.css.backgroundImage → background-image (gradient — 값 그대로)
166
+ node.css.backgroundBlendMode → background-blend-mode (값 그대로)
110
167
  node.css.color → color
111
168
  node.css.fontFamily → font-family
112
169
  node.css.fontSize → font-size (→ clamp 변환)
@@ -115,20 +172,35 @@ tree.json의 css 객체를 SCSS로 직접 변환한다. 추정하지 않는다.
115
172
  node.css.letterSpacing → letter-spacing (→ vw 변환)
116
173
  node.css.textAlign → text-align
117
174
  node.css.borderRadius → border-radius (→ vw 변환)
118
- node.css.border → border (width → vw 변환)
175
+ node.css.border → border (width → vw 변환, strokeAlign=INSIDE)
176
+ node.css.outline → outline (width → vw 변환, strokeAlign=OUTSIDE)
177
+ node.css.boxSizing → box-sizing (INSIDE stroke → border-box)
119
178
  node.css.boxShadow → box-shadow (px → vw 변환)
120
179
  node.css.opacity → opacity
121
180
  node.css.mixBlendMode → mix-blend-mode
122
- node.css.filter → filter (px → vw 변환)
181
+ node.css.filter → filter (blur px → vw 변환, grayscale/saturate → 값 그대로)
123
182
  node.css.backdropFilter → backdrop-filter (px → vw 변환)
124
183
 
184
+ 이미지 처리 (node.imageScaleMode):
185
+ FILL → background-size: cover
186
+ FIT → background-size: contain
187
+ CROP → background-size: cover; background-position: center
188
+ TILE → background-size: auto; background-repeat: repeat
189
+
190
+ 다중 fill (node.fills 배열, 2개 이상일 때):
191
+ 예: [IMAGE(grayscale) + GRADIENT_LINEAR] → 겹침 배경
192
+ CSS: background: url('img.webp'), linear-gradient(...);
193
+ background-blend-mode: multiply;
194
+ filter: grayscale(100%);
195
+ converter는 fills 배열 순서대로 CSS background 레이어링
196
+
125
197
  반응형 단위 변환 (scaleFactor 사용하지 않음):
126
198
  스토리보드 CONFIG에서 확보:
127
199
  designWidth: 디자인 너비 (예: 720px 모바일, 2560px PC)
128
200
  minWidth: 최소 지원 너비 (예: 340px)
129
201
  breakpoint: PC/모바일 분계 (예: 1025px)
130
202
 
131
- UI 요소 (width, height, padding, gap, border-radius, shadow 등):
203
+ UI 요소 (width, height, padding, gap, border-radius, shadow, top, left 등):
132
204
  → vw 비례: vw값 = (Figma px / designWidth) × 100
133
205
  → 예: gap: 24px / 720 × 100 = 3.33vw
134
206
  → width: 부모 대비 %도 가능 (620/720 = 86%)
@@ -154,7 +226,9 @@ tree.json의 css 객체를 SCSS로 직접 변환한다. 추정하지 않는다.
154
226
 
155
227
  변환하지 않는 속성:
156
228
  color, opacity, font-weight, font-family, z-index,
157
- line-height(단위 없을 때), text-align, mix-blend-mode
229
+ line-height(단위 없을 때), text-align, mix-blend-mode,
230
+ transform(rotate), background-blend-mode, flex-grow,
231
+ box-sizing, grayscale/saturate(filter 내)
158
232
 
159
233
  값이 없으면:
160
234
  → 해당 속성 생략 (추정 금지)
@@ -434,7 +508,15 @@ function handleShare(): void {
434
508
  조건: BG 프레임 (name에 BG/bg 또는 부모와 크기 동일)
435
509
  ❌ <img> 태그 금지
436
510
  ✅ CSS background-image로만 처리:
437
- 부모 { background-image: url('...'); background-size: cover; }
511
+ 부모 { background-image: url('...'); }
512
+ imageScaleMode에 따라:
513
+ FILL → background-size: cover (기본)
514
+ FIT → background-size: contain
515
+ CROP → background-size: cover; background-position: center
516
+ TILE → background-size: auto; background-repeat: repeat
517
+ 다중 fill (node.fills 배열):
518
+ background: url('img.webp'), linear-gradient(...);
519
+ background-blend-mode: multiply;
438
520
 
439
521
  콘텐츠 이미지:
440
522
  조건: imageRef 있음 + 독립적 크기 + TEXT 형제 없음
@@ -454,21 +536,58 @@ function handleShare(): void {
454
536
 
455
537
  ---
456
538
 
457
- ## 5. 반응형 추가 (데스크탑 URL)
539
+ ## 5. 반응형 추가 (MO↔PC 매칭)
458
540
 
459
541
  ```
460
- 번째 URL의 tree.json을 확보한 후:
542
+ remapped.json의 pcDiff를 사용하여 @media 오버라이드 추가.
543
+ MO 기본 코드 삭제 금지. PC는 항상 @media 블록 안에만 작성.
544
+
545
+ SCSS 구조:
546
+ .heroSection {
547
+ // MO 기본 (vw = px / 720 × 100)
548
+ width: 100%;
549
+ height: 177.78vw;
550
+ background-image: url('/images/{feature}/hero-bg.webp');
551
+
552
+ @media (min-width: #{$bp-desktop}) {
553
+ // PC 오버라이드 (vw = px / 2560 × 100, 또는 고정 px)
554
+ height: 32.66vw; // 836 / 2560 × 100
555
+ background-image: url('/images/{feature}/hero-bg-pc.webp');
556
+ }
557
+ }
558
+
559
+ CSS diff 처리 규칙:
560
+
561
+ 같은 값 → MO만 유지 (PC에 쓰지 않음)
562
+
563
+ 다른 px 값:
564
+ → @media { 속성: PC값/pcDesignWidth × 100 vw; }
565
+ → 예: MO gap:24px → 3.33vw, PC gap:60px → @media { gap: 2.34vw; }
566
+
567
+ 다른 레이아웃:
568
+ → @media { flex-direction: row; }
569
+ → @media { justify-content: space-between; }
570
+
571
+ 다른 이미지:
572
+ → @media { background-image: url('/images/{feature}/pc-xxx.webp'); }
573
+
574
+ layoutSizing diff:
575
+ MO: HUG → PC: FIXED → @media { width: Xvw; }
576
+ MO: FILL → PC: FIXED → @media { width: Xvw; flex: initial; }
577
+ MO: FIXED → PC: FILL → @media { flex: 1 0 0; width: auto; }
578
+
579
+ MO에만 있는 속성 → MO 기본값 유지
580
+ PC에만 있는 속성 → @media에서 추가
581
+ PC에서 사라진 속성 → @media { display: none; } 또는 생략
461
582
 
462
- 모바일 tree vs 데스크탑 tree 비교:
463
- 동일한 name의 노드를 매칭
464
- CSS 속성 차이만 @media 오버라이드로 추가
583
+ absolute 위치 diff:
584
+ @media { top: PC값/pcDesignWidth × 100 vw; left: ...; }
465
585
 
466
- 같은 값 → 유지
467
- 다른 px 값 → @include pc { width: {desktop값 × pcScaleFactor}px; }
468
- 다른 레이아웃 → @include pc { flex-direction: row; }
469
- 다른 이미지 → @include pc { content: url(/images/{feature}/desktop-xxx.webp); }
586
+ rotation diff:
587
+ → @media { transform: rotate(PC각도deg); }
470
588
 
471
589
  기존 모바일 코드 삭제 금지.
590
+ PC 오버라이드는 항상 @media 블록 안에만.
472
591
  ```
473
592
 
474
593
  ---
@@ -53,6 +53,19 @@ Q4. 이 노드의 콘텐츠가 동적 데이터인가? (가격, 수량, 기간,
53
53
 
54
54
  위 체크에서 하나라도 YES → HTML로 구현
55
55
  모두 NO → 이미지(벡터 글자, BG, 래스터, 복잡 그래픽)로 렌더링
56
+
57
+ 실제 테스트에서 발생한 잘못된 패턴:
58
+
59
+ ❌ <img src="hero-bg.webp" class="bg-img" /> → BG를 img 태그로
60
+ ❌ <img src="exchange-section1.webp" /> → 카드 4개를 이미지 1장으로
61
+ ❌ <img src="daily-step2-list.webp" /> → 보상 목록을 통째 이미지로
62
+ ❌ <img src="hero-period-bg-mo.webp" class="period-bg"> → Period BG를 img로
63
+
64
+ ✅ .heroSection { background-image: url('hero-bg.webp'); background-size: cover; }
65
+ ✅ <div v-for="card in cards" :key="card.id">
66
+ <img :src="card.icon" /> <span>{{ card.price }}</span>
67
+ <button>보상 교환하기</button>
68
+ </div>
56
69
  ```
57
70
 
58
71
  ## CSS 직접 매핑 규칙
@@ -107,6 +120,9 @@ BEM 패턴: `.sectionName__childName`
107
120
 
108
121
  ## 자가 검증 (코드 작성 후)
109
122
 
123
+ - [ ] ⛔ BG 프레임이 <img> 태그로 처리되지 않았는가? (CSS background-image만 허용)
124
+ - [ ] ⛔ TEXT 자식이 있는 프레임이 통째 이미지로 처리되지 않았는가?
125
+ - [ ] ⛔ INSTANCE 반복 패턴(카드/아이템)이 이미지 1장으로 처리되지 않았는가?
110
126
  - [ ] template 클래스 ↔ SCSS 클래스 1:1 일치
111
127
  - [ ] 모든 img src가 static/에 실제 존재
112
128
  - [ ] Auto Layout 노드 → SCSS에 flex 속성 존재
@@ -9,6 +9,18 @@ tier: standard
9
9
 
10
10
  Figma REST API(`src/infra/lib/figma/`)를 사용하여 **구조적 코드 생성에 필요한 모든 데이터**를 추출.
11
11
 
12
+ ```
13
+ ⛔ 불변 규칙 — screenshot 명령의 허용 범위:
14
+ ✅ BG 프레임 렌더링 (TEXT 자식이 없는 순수 배경만)
15
+ ✅ 벡터 글자 GROUP (웹폰트 없는 장식 타이틀)
16
+ ✅ 개별 아이콘/썸네일 (50~300px 독립 에셋)
17
+ ✅ 섹션별 전체 스크린샷 → sections/ (Phase 6 검증용)
18
+ ❌ TEXT 자식이 있는 프레임 (가격, 수량, 설명 등)
19
+ ❌ INSTANCE 반복 패턴 프레임 (카드 그리드, 보상 목록)
20
+ ❌ 버튼/인터랙티브 요소가 있는 프레임
21
+ ❌ "섹션 콘텐츠"를 통째로 렌더링
22
+ ```
23
+
12
24
  ```
13
25
  추출 우선순위:
14
26
  1순위: 노드 트리 + CSS (코드 생성의 PRIMARY 소스)
@@ -53,27 +65,53 @@ Bash:
53
65
 
54
66
  트리 추출 도구가 자동 변환하는 속성. **이 값들이 SCSS에 직접 매핑된다:**
55
67
 
68
+ **레이아웃:**
69
+
56
70
  | Figma 속성 | CSS | vw 변환 |
57
- |-----------|-----|-----------------|
71
+ |-----------|-----|---------|
58
72
  | `layoutMode=VERTICAL` | `display:flex; flex-direction:column` | ❌ |
59
73
  | `layoutMode=HORIZONTAL` | `display:flex; flex-direction:row` | ❌ |
60
74
  | `primaryAxisAlignItems` | `justify-content` | ❌ |
61
75
  | `counterAxisAlignItems` | `align-items` | ❌ |
62
76
  | `itemSpacing` | `gap` | ✅ |
77
+ | `layoutGrow=1` | `flex-grow: 1` | ❌ |
63
78
  | `padding*` | `padding` | ✅ |
64
79
  | `absoluteBoundingBox.width/height` | `width/height` | ✅ |
65
- | `layoutPositioning=ABSOLUTE` | `position: absolute` | |
80
+ | `layoutPositioning=ABSOLUTE` | `position: absolute` + `top/left` (부모 상대 좌표) | |
81
+ | `layoutSizingHorizontal=HUG` | width 삭제 (auto) | — |
82
+ | `layoutSizingHorizontal=FILL` | 메타데이터 `layoutSizingH` (converter가 flex:1/100% 결정) | — |
83
+ | `layoutSizingVertical=HUG` | height 삭제 (auto) | — |
84
+ | `layoutSizingVertical=FILL` | 메타데이터 `layoutSizingV` (converter가 결정) | — |
66
85
  | `clipsContent` | `overflow: hidden` | ❌ |
67
- | `fills[].color` | `background-color` | ❌ |
68
- | `fills[].type=IMAGE` | `imageRef` (다운로드 대상) | — |
86
+
87
+ **비주얼:**
88
+
89
+ | Figma 속성 | CSS | vw 변환 |
90
+ |-----------|-----|---------|
91
+ | `fills[].SOLID` | `background-color` | ❌ |
92
+ | `fills[].IMAGE` | `imageRef` + `imageScaleMode` (FILL/FIT/CROP/TILE) | — |
93
+ | `fills[].GRADIENT_LINEAR` | `background-image: linear-gradient(...)` | ❌ |
94
+ | `fills[].GRADIENT_RADIAL` | `background-image: radial-gradient(...)` | ❌ |
95
+ | `fills[] (2개 이상)` | `fills` 배열 (type, color, imageRef, gradient, blendMode, filters) | — |
96
+ | `fills[].blendMode` | `background-blend-mode` | ❌ |
97
+ | `fills[].filters.saturation` | `filter: grayscale(X%)` / `saturate(X%)` | ❌ |
69
98
  | `fills[].color` (TEXT) | `color` | ❌ |
70
- | `strokes[].color` + `strokeWeight` | `border` | ✅ (width만) |
99
+ | `strokes[] + strokeAlign=INSIDE` | `border` + `box-sizing: border-box` | ✅ (width만) |
100
+ | `strokes[] + strokeAlign=OUTSIDE` | `outline` | ✅ (width만) |
101
+ | `strokes[] + strokeAlign=CENTER` | `border` | ✅ (width만) |
71
102
  | `effects[].DROP_SHADOW` | `box-shadow` | ✅ (px만) |
72
- | `effects[].LAYER_BLUR` | `filter: blur()` | ✅ |
103
+ | `effects[].INNER_SHADOW` | `box-shadow` (inset) | ✅ (px만) |
104
+ | `effects[].LAYER_BLUR` | `filter: blur()` (누적) | ✅ |
73
105
  | `effects[].BACKGROUND_BLUR` | `backdrop-filter: blur()` | ✅ |
74
106
  | `cornerRadius` | `border-radius` | ✅ |
75
107
  | `opacity` | `opacity` | ❌ |
76
- | `blendMode` | `mix-blend-mode` | ❌ |
108
+ | `rotation` | `transform: rotate(Xdeg)` | ❌ |
109
+ | `blendMode` (노드 레벨) | `mix-blend-mode` | ❌ |
110
+
111
+ **텍스트:**
112
+
113
+ | Figma 속성 | CSS | vw 변환 |
114
+ |-----------|-----|---------|
77
115
  | `style.fontFamily` | `font-family` | ❌ |
78
116
  | `style.fontSize` | `font-size` | ✅ |
79
117
  | `style.fontWeight` | `font-weight` | ❌ |
@@ -82,6 +120,17 @@ Bash:
82
120
  | `style.textAlignHorizontal` | `text-align` | ❌ |
83
121
  | `characters` | 텍스트 내용 | — |
84
122
 
123
+ ### FigmaNode 메타데이터 필드 (css 외)
124
+
125
+ converter가 의사결정에 사용하는 추가 필드:
126
+
127
+ | 필드 | 타입 | 용도 |
128
+ |------|------|------|
129
+ | `layoutSizingH` | `'FIXED'\|'HUG'\|'FILL'` | 부모 context로 width 결정 |
130
+ | `layoutSizingV` | `'FIXED'\|'HUG'\|'FILL'` | 부모 context로 height 결정 |
131
+ | `imageScaleMode` | `'FILL'\|'FIT'\|'CROP'\|'TILE'` | background-size 결정 |
132
+ | `fills` | `array` | 다중 fill 레이어 상세 (2개 이상일 때만) |
133
+
85
134
  ---
86
135
 
87
136
  ## 2. 이미지 에셋 — 노드 렌더링 기반 (imageRef 개별 다운로드 금지)
@@ -4,7 +4,15 @@
4
4
 
5
5
  ```
6
6
  ❌ imageRef 개별 다운로드 금지 (텍스처 fill 공유 문제)
7
+ ❌ 섹션/그룹을 통째 이미지로 렌더링 금지 (내부 TEXT/INSTANCE 포함 시)
7
8
  ✅ 모든 이미지는 Figma screenshot API로 노드 렌더링
9
+ ✅ 렌더링 전 반드시 2-1.5 판별 규칙 확인 → HTML 대상은 렌더링하지 않음
10
+
11
+ 실제 테스트에서 발생한 잘못된 렌더링:
12
+ ❌ exchange-section1.webp (카드 4개 그리드를 이미지 1장으로)
13
+ ❌ daily-step2-list.webp (보상 목록을 텍스트 포함하여 통째로)
14
+ ❌ prize-section1.webp (응모 아이템을 텍스트 포함하여 통째로)
15
+ ✅ 올바른 접근: 카드 내부의 아이콘/썸네일만 개별 렌더링
8
16
  ```
9
17
 
10
18
  ## Render Methods