---
description: Figma URL과 출력 경로를 받아 Code Connect 매핑·Storybook MCP 조회를 우선 활용해 page.tsx + page.module.scss를 생성한다.
---

주어진 Figma URL의 디자인을 아래 규칙에 따라 코드로 변환하고, 지정한 경로에 `page.tsx`와 `page.module.scss`를 생성한다.

## 입력

- **Figma URL**: $ARGUMENTS 의 첫 번째 인자
- **출력 경로**: $ARGUMENTS 의 두 번째 인자 (예: `apps/docs/app/external/my-page`)

> **⚠️ 파일 생성 규칙**: `page.tsx`와 `page.module.scss` 외 어떤 파일도 생성하지 않는다.
> 모든 컴포넌트·로직은 `page.tsx` 한 파일 안에 작성한다.

## 작업 흐름

이 가이드를 실행하는 AI는 아래 순서를 따른다:

1. **Figma MCP**(`get_metadata` + `get_design_context`)로 Figma 노드 분석.
2. **사용할 DS 컴포넌트 사전 학습 (필수)**: 1단계 응답에서 사용 예정 디자인 시스템 컴포넌트 목록을 추출하고, 각 컴포넌트의 도큐먼트를 **Storybook MCP**(`storybook-mcp`)로 조회한다. props 시그니처·실제 사용 예시·Compound 패턴(예: `Chip.Root` + `Chip.Item`)을 파악한 뒤 변환을 시작한다. 이렇게 하면 환각·임의 prop 추가가 원천 차단된다.
   - **Compound 휴리스틱 감지 시 Storybook MCP 선조회 의무**: Code Connect 스니펫의 JSX가 다음 중 하나에 해당하면 Compound 컴포넌트로 간주하고, 변환 전 반드시 해당 컴포넌트의 Storybook 문서를 조회해 Root + 자식 조립 패턴을 학습한다.
     - 최상위 요소가 `X.Root` 형태 (예: `<Accordion.Root>`, `<Tab.Root>`, `<Modal.Root>`)
     - 자식 JSX에 동일 네임스페이스 자식 사용 (예: `<Modal.Header>`, `<Tab.Item>`, `<Select.Option>`)
     - JSDoc/설명에 "Compound" 표기가 있는 경우
3. 응답에 `<CodeConnectSnippet>` 래퍼가 포함된 영역 → **Code Connect 매핑이 완료된 컴포넌트**. 그 안의 JSX(컴포넌트 이름·props·import 경로)를 그대로 사용한다. 임의 변형 금지.
4. `<CodeConnectSnippet>` 밖의 wrapper/레이아웃 영역은 Figma MCP가 원본 디자인을 **Tailwind 클래스**로 표현해 응답한다 → 본 가이드의 **디자인 토큰 매핑** · **SCSS 설정**으로 변환.
5. 매핑되지 않은 디자인 시스템 컴포넌트가 필요하면 아래 **컴포넌트 사용 우선순위** 절차로 진입.
6. **이미지 대조 검증 (필수)**: 코드 생성을 마치면 **Figma MCP**의 `get_screenshot`으로 원본 이미지를 받아 생성된 JSX 구조와 시각적으로 대조한다. 다음을 체크:

   - **레이아웃**: flex 방향(row/column), 자식 정렬, 자식 개수와 순서
   - **간격**: 토큰(`padding(small/medium/large)` 등)이 디자인의 시각적 간격과 맞는지
   - **색상**: 배경·텍스트·보더 색이 토큰 매핑 표대로 적용됐는지
   - **타이포**: `apply-typography(...)` 토큰이 디자인의 타이포 스타일과 일치하는지
   - **누락**: 원본에 있는데 코드에 빠진 요소 없는지

   불일치 발견 시 보정 후 다시 비교. **최대 2회까지만 보정**, 그래도 불일치 시 사람에게 차이점 리스트업해서 보고.

## 컴포넌트 사용 우선순위

### 1순위: Code Connect 매핑

- Figma MCP 응답의 `<CodeConnectSnippet>` JSX를 베이스로 사용
- 매핑된 props 그대로 활용 (override·재해석 금지)
- **누락 요소 검토**: 스니펫만으로 Figma 디자인의 모든 요소(아이콘·보조 텍스트 등)가 표현되는지 확인. 누락이 있으면 2순위 Storybook MCP로 해당 컴포넌트 Story를 조회해 누락된 props 사용법을 가져온다. 예: Code Connect로 매핑된 Button 스니펫에 `icon`/`iconDir`가 빠져 있는데 디자인에 아이콘이 있으면 Story에서 `icon` prop 사용 예시 확인 후 추가.

> ⚠️ **PLACEHOLDER 교체**: 스니펫에 `dsp-icons-icons-sub-ui-PLACEHOLDER` 같은 문자열이 포함되어 있으면 반드시 Figma 노드의 instance swap 이름을 확인해 실제 아이콘 className으로 교체한다. PLACEHOLDER가 남은 코드 커밋 금지.

> 💡 **단일 enum이 여러 boolean으로 분리되는 패턴**: Figma의 단일 State property가 React 측 여러 boolean prop으로 갈라지는 매핑이 존재한다. 예) TextField의 Figma `State`(normal/active/readonly/disabled/error) → `readOnly` / `disabled` / `error` 세 boolean으로 분리. 스니펫 상에는 매칭된 props만 노출되므로 그대로 사용하면 된다.

> 🧩 **Compound 컴포넌트의 Figma 단위 ≠ React 단위**: Accordion·Modal 등 복잡한 Compound는 디자이너가 헤더·바디 등으로 노드를 분해하지만, React는 Root/Item/Header/Content 단위로 조립한다. 1:1 매핑 시도하지 말고 **Root에만 매핑 + example에 풀 골격(Root + Item + Header + Content) 노출**한 뒤 자식 구성은 디자인에 맞춰 개발자가 조립한다. Figma의 시각 `state` enum이 React Root props와 매칭되지 않으면 enum 매핑은 빼고 example 골격만 채운다.
>
> Compound 감지 휴리스틱(다음 중 하나라도 해당하면 Compound로 간주):
>
> - 스니펫 최상위가 `X.Root` 형태
> - 자식에 동일 네임스페이스 자식 (`X.Item` / `X.Option` / `X.Header` / `X.Content` 등)
> - JSDoc/설명에 "Compound" 표기
>
> 감지되면 변환 전 **Storybook MCP**(`storybook-mcp`)로 해당 컴포넌트 스토리를 반드시 선조회해 Root + 자식 props · 자식 조립 순서 · 이벤트 핸들러 패턴을 학습한다. 스니펫은 출발점일 뿐, 디자인이 표현하는 모든 자식을 풀 골격으로 재구성한다.

> 🏷️ **Label vs Fieldset 분기**: Figma `label` 노드는 `<Label>`로 매핑되어 있지만, **멀티 입력 그룹**(체크박스 그룹, 칩 그룹, 라디오 그룹 등)에 붙는 라벨이라면 `<Fieldset.Root>` + `<Fieldset.Legend>` + `<Fieldset.Items>` 조합으로 교체한다. 단일 입력에는 `<Label>` 그대로. 상세 props는 Storybook MCP로 `Label` / `Fieldset` 조회.

> 🔎 **Code Connect example이 단순한데 디자인은 조립 패턴인 경우**: SearchField·Select·DatePicker 등 일부 매핑은 Figma Dev Mode에 노출되는 example이 핵심 컴포넌트(트리거) 한 조각만 보여준다. 디자인이 dropdown·결과 리스트·캘린더 등 부속을 포함한다면 example 그대로 옮기지 말고, Storybook MCP로 다음을 추가 조회해 조립을 완성한다:
>
> 1. 트리거 컴포넌트의 ref/triggerRef 연결 방법 (예: `useDropDown` 훅 반환값)
> 2. 결과·dropdown wrapper 컴포넌트의 props 시그니처와 자식 구조 (예: `DropDown`, `SearchResult.Root/List/Item`, `ComboList`)
> 3. 사용 예시 Story가 있다면 그 props 조합·자식 구성·이벤트 핸들러 패턴을 그대로 따른다
>
> example은 출발점이고, 최종 코드는 항상 디자인이 표현하는 모든 요소를 포함해야 한다.

### 2순위: Storybook MCP 검증

- 매핑되지 않은 컴포넌트, 또는 매핑된 컴포넌트의 추가 props가 필요할 때
- **Storybook MCP**(`storybook-mcp`)로 해당 컴포넌트의 도큐먼트 조회
- 도큐먼트에 명시된 props만 사용. 임의 prop 추가 금지
- **유사 Story로 검증**: 컴포넌트의 Stories 중 변환 대상 Figma 디자인과 가장 가까운 사용 예시를 찾아 베이스 코드로 삼는다. props 조합·자식 구성·이벤트 핸들러 패턴을 Story 그대로 따르고, Figma의 실제 값(텍스트·variant·state 등)에 맞춰 조정한다.

> 📦 **조회 범위 한정**: 컴포넌트 매핑·props 학습은 반드시 `Design System/...` 또는 `Components/...` title prefix Stories만 사용한다. 다른 패키지·페이지 데모 Stories(예: `Utils/...`, `Hooks/...`, `API/...`, 페이지 시연용 stories 등)는 시각적 참고용일 뿐 매핑·props 베이스로 쓰지 않는다.

> 🚫 **환각 방지 절대 원칙**
>
> - 도큐먼트·Story에 없는 props 절대 사용 금지 (예: `<Button shadow="md">` 같이 추측한 prop)
> - 도큐먼트에 명시되지 않은 variant·size·appearance 값 절대 사용 금지
> - 컴포넌트 이름이 헷갈리면 Storybook MCP의 전체 컴포넌트 목록을 먼저 조회해 정확한 이름·존재 여부 확인 후 진행
> - "이런 prop이 있을 것 같다"는 추론으로 코드 생성 금지 — 항상 도큐먼트가 우선

### 3순위: 디자인 시스템 컴포넌트 미존재 시

2순위에서 디자인 시스템에 적합한 컴포넌트가 없다고 판단되면 HTML + module.scss로 직접 구현한다. Figma 노드의 레이아웃·토큰을 본 가이드의 **변환 규칙**에 따라 변환.

## 변환 규칙

### SCSS 설정

```scss
// sales-frontend-design-system 설치 후 상단에 선언(함수, 믹스인 제공)
@use 'sales-frontend-design-system/design-system-context' as *;
```

> 이 한 줄로 `colors()`, `padding()`, `margin()`, `radius-basic()`, `shadow()`, `@include hover`, `@include media`, `@include apply-typography` 등 본 가이드의 모든 토큰 함수·믹스인이 노출된다. 추가 `@use` 불필요.

### 디자인 토큰 매핑

#### 색상 — `colors(토큰명)`

| Figma 변수           | SCSS 함수                   |
| -------------------- | --------------------------- |
| `Surface/primary`    | `colors(surface-primary)`   |
| `Surface/secondary`  | `colors(surface-secondary)` |
| `Surface/neutral`    | `colors(surface-neutral)`   |
| `Surface/neutral_1`  | `colors(surface-neutral_1)` |
| `Surface/neutral_2`  | `colors(surface-neutral_2)` |
| `Surface/neutral_3`  | `colors(surface-neutral_3)` |
| `Surface/neutral_4`  | `colors(surface-neutral_4)` |
| `Text/primary`       | `colors(text-primary)`      |
| `Text/secondary`     | `colors(text-secondary)`    |
| `Text/body`          | `colors(text-body)`         |
| `Text/body_0`        | `colors(text-body_0)`       |
| `Text/body_1`        | `colors(text-body_1)`       |
| `Text/body_2`        | `colors(text-body_2)`       |
| `Text/body_3`        | `colors(text-body_3)`       |
| `Text/neutral_1`     | `colors(text-neutral_1)`    |
| `Text/neutral_2`     | `colors(text-neutral_2)`    |
| `Border/primary_1`   | `colors(border-primary_1)`  |
| `Border/primary_2`   | `colors(border-primary_2)`  |
| `Border/neutral_1`   | `colors(border-neutral_1)`  |
| `Border/neutral_2`   | `colors(border-neutral_2)`  |
| `Border/neutral_3`   | `colors(border-neutral_3)`  |
| `Border/neutral_4`   | `colors(border-neutral_4)`  |
| `Border/neutral_5`   | `colors(border-neutral_5)`  |
| `Border/neutral_6`   | `colors(border-neutral_6)`  |
| `Background/white`   | `colors(background-100)`    |
| `Background/neutral` | `colors(background-200)`    |
| `Background/dimmed`  | `colors(background-dimmed)` |

#### 패딩 — `padding(토큰명)`

| Figma 변수          | SCSS 함수            |
| ------------------- | -------------------- |
| `Padding/zero`      | `padding(zero)`      |
| `Padding/xxxxSmall` | `padding(xxxxsmall)` |
| `Padding/xxxSmall`  | `padding(xxxsmall)`  |
| `Padding/xxSmall`   | `padding(xxsmall)`   |
| `Padding/xSmall`    | `padding(xsmall)`    |
| `Padding/small`     | `padding(small)`     |
| `Padding/medium`    | `padding(medium)`    |
| `Padding/large`     | `padding(large)`     |
| `Padding/xLarge`    | `padding(xlarge)`    |
| `Padding/xxLarge`   | `padding(xxlarge)`   |
| `Padding/xxxLarge`  | `padding(xxxlarge)`  |
| `Padding/xxxxLarge` | `padding(xxxxlarge)` |

#### 마진 — `margin(토큰명)`

| Figma 변수                     | SCSS 함수                       |
| ------------------------------ | ------------------------------- |
| `Margin/zero`                  | `margin(zero)`                  |
| `Margin/inputfield-vertical`   | `margin(inputfield-vertical)`   |
| `Margin/inputfield-horizontal` | `margin(inputfield-horizontal)` |
| `Margin/button-large`          | `margin(button-large)`          |
| `Margin/button-medium`         | `margin(button-medium)`         |
| `Margin/button-small`          | `margin(button-small)`          |
| `Margin/card`                  | `margin(card)`                  |
| `Margin/divider-xlarge`        | `margin(divider-xlarge)`        |
| `Margin/divider-large`         | `margin(divider-large)`         |
| `Margin/divider-medium`        | `margin(divider-medium)`        |
| `Margin/divider-small`         | `margin(divider-small)`         |
| `Margin/divider-xsmall`        | `margin(divider-xsmall)`        |
| `Margin/title-xlarge`          | `margin(title-xlarge)`          |
| `Margin/title-large`           | `margin(title-large)`           |
| `Margin/title-medium`          | `margin(title-medium)`          |
| `Margin/title-small`           | `margin(title-small)`           |
| `Margin/title-xsmall`          | `margin(title-xsmall)`          |
| `Margin/textarea-large`        | `margin(textarea-large)`        |
| `Margin/textarea-medium`       | `margin(textarea-medium)`       |
| `Margin/textarea-small`        | `margin(textarea-small)`        |
| `Margin/textarea-xsmall`       | `margin(textarea-xsmall)`       |
| `Margin/textarea-xxsmall`      | `margin(textarea-xxsmall)`      |
| `Margin/card/around_large`     | `margin(card-around-large)`     |
| `Margin/card/around_small`     | `margin(card-around-small)`     |
| `Margin/modal`                 | `margin(modal)`                 |

#### 보더 라디우스 — `radius-basic(토큰명)`

| Figma 변수      | SCSS 함수              |
| --------------- | ---------------------- |
| `Corner/small`  | `radius-basic(small)`  |
| `Corner/medium` | `radius-basic(medium)` |
| `Corner/large`  | `radius-basic(large)`  |
| `Corner/xlarge` | `radius-basic(xlarge)` |
| `Corner/full`   | `radius-basic(full)`   |

#### 그림자 — `shadow(토큰명)`

| Figma 변수      | SCSS 함수        |
| --------------- | ---------------- |
| `Shadow/small`  | `shadow(small)`  |
| `Shadow/medium` | `shadow(medium)` |
| `Shadow/large`  | `shadow(large)`  |

```scss
.card {
  box-shadow: shadow(medium);
}
```

#### 호버 — `@include hover { ... }`

터치 디바이스에서 sticky hover를 방지하는 믹스인. 데스크톱(마우스)에서는 `:hover`, 터치 디바이스에서는 `:active`로 자동 분기된다.

```scss
.card {
  @include hover {
    background-color: colors(surface-neutral_1);
  }
}
```

#### 반응형 — `@include media(디바이스) { ... }`

| 디바이스  | 브레이크포인트      | 설명         |
| --------- | ------------------- | ------------ |
| `tablet`  | `min-width: 768px`  | 태블릿 이상  |
| `pc`      | `min-width: 1281px` | PC 이상      |
| `pc-wide` | `min-width: 1441px` | PC Wide 이상 |

```scss
.container {
  padding: padding(small);

  @include media(tablet) {
    padding: padding(medium);
  }

  @include media(pc) {
    padding: padding(large);
  }
}
```

#### 타이포그래피 — `@include apply-typography(토큰명)`

| Figma 변수                | SCSS 믹스인                      |
| ------------------------- | -------------------------------- |
| `Typography/title1`       | `apply-typography(title1)`       |
| `Typography/title2`       | `apply-typography(title2)`       |
| `Typography/title3`       | `apply-typography(title3)`       |
| `Typography/title4`       | `apply-typography(title4)`       |
| `Typography/title5`       | `apply-typography(title5)`       |
| `Typography/subtitle1`    | `apply-typography(subtitle1)`    |
| `Typography/subtitle2`    | `apply-typography(subtitle2)`    |
| `Typography/subtitle3`    | `apply-typography(subtitle3)`    |
| `Typography/body1`        | `apply-typography(body1)`        |
| `Typography/body2`        | `apply-typography(body2)`        |
| `Typography/body3`        | `apply-typography(body3)`        |
| `Typography/body4`        | `apply-typography(body4)`        |
| `Typography/caption`      | `apply-typography(caption)`      |
| `Typography/link_large`   | `apply-typography(link_large)`   |
| `Typography/link_medium`  | `apply-typography(link_medium)`  |
| `Typography/link_small`   | `apply-typography(link_small)`   |
| `Typography/input_large`  | `apply-typography(input_large)`  |
| `Typography/input_medium` | `apply-typography(input_medium)` |
| `Typography/input_small`  | `apply-typography(input_small)`  |

### 아이콘

피그마에서 아이콘 노드(`data-name`이 `sub-ui/*`, `main-ui/*`, `illust/*` 등)를 발견하면 `base.md`의 **아이콘 시스템** 섹션에 정의된 규칙을 따른다.

피그마 노드의 `data-name`에서 아이콘 경로를 추출하여 적절한 방식으로 변환한다:

- 시스템 아이콘 (`sub-ui/*`, `main-ui/*`) → `sales-frontend-assets`에서 SVG React 컴포넌트 import
- 그래픽/일러스트 (`graphics/*`, `images/*`, `illust/*`) → 아이콘 CSS 클래스 사용

```tsx
// 시스템 아이콘 — Button의 icon prop 예시
import { IconSubUiArrowRightWhite } from 'sales-frontend-assets';

<Button
  variant="primary"
  size="medium"
  appearance="filled"
  icon={<IconSubUiArrowRightWhite width={20} height={20} />}
  iconDir="right"
>
  다음
</Button>;

// 그래픽/일러스트 — CSS 클래스 예시
<span className={cx('dsp-icons', 'dsp-icons-graphics-avatar-man-60')} />;
```
