디자인 도구에서 코드로 넘어올 때 같은 것에 이름이 두 번 지어진다.

디자인 도구   「면」 · 「Frame 89」 · 「상태=열기」
코드          AdminSelect · admin-select-arrow · isOpen

두 이름은 반드시 갈라진다. 한쪽을 고쳐도 다른 쪽은 그대로이고, 어느 것이 맞는지 아무도 모르게 된다.

그런데 도구 쪽에는 코드용 이름을 적는 자리가 이미 있다. 거기 적어 두면 코드 생성 결과에 그대로 실려 나온다. 이 글은 무엇을 어디에 적는지를 정한다.


먼저: 인접한 표준과 겹치지 않는 자리

프론트엔드 표준이 이미 여럿 있는데, 그것들은 전부 「코드 안에서」의 규칙이다.

컴포넌트 자리 표준     이 컴포넌트를 어느 폴더에 두나
컴포넌트 추출 표준     이 덩어리를 언제 별도 파일로 빼나
CSS 자리 표준          이 스타일을 어디에 쓰나
토큰 공유 표준         앱이 둘일 때 토큰을 어떻게 나누나

이 표준은 그 앞에 선다 — 코드 밖에서 코드로 들어오는 자리다. 디자인 도구에 무엇을 적어야 코드 쪽이 이름을 다시 짓지 않는가를 정한다. 어느 폴더에 둘지는 안 정한다 — 그것은 컴포넌트 자리 표준의 일이고, 이 표준은 그때 쓸 이름만 마련해 준다.

CSS 자리 표준과는 한 자리에서 맞닿는다. 그 표준이 CSS Modules 로 가기로 했으므로 레이어 이름에 적는 클래스는 빌드 뒤 이름이 아니라 소스에 적힌 이름이다(원칙 2). 도구가 가리키는 것은 실행 결과가 아니라 코드의 그 줄이다.


먼저: 공식 연결 경로가 플랜에 막혀 있다

Figma 에는 컴포넌트와 코드를 이어 붙이는 Code Connect 라는 공식 기능이 있다. 그것이 이 문제의 정답이다.

쓸 수 없다.

"You need a Dev or Full seat on an Organization or Enterprise plan to use Code Connect."

이 표준이 존재하는 이유가 그것이다. 정답이 막혀 있어서, 도구가 이미 코드로 실어 보내는 자리들을 대신 쓴다. 플랜이 올라가면 이 표준의 상당 부분은 Code Connect 로 대체된다 — 대체재라는 것을 알고 쓴다.


원칙 1. 이름을 지어내지 않는다

이 표준의 뼈대다. 조사하면서 가장 크게 배운 것이 코드에 이미 이름이 다 있었다는 것이다.

CSS 클래스     관리자 231개. 접두사 붙은 kebab-case
토큰 변수      --space-4 · --radius-md · --color-primary
라우트         파일 경로가 곧 화면 주소다
화면 키        DB 에 있고 권한이 걸려 있다

그리고 태그와 클래스의 짝은 코드에서 그대로 긁힌다.

grep -rnoE '<[A-Za-z][A-Za-z0-9.]*[^>]*className="[a-z0-9 -]*"' src --include=*.js

한 줄이 <p className="admin-empty-row"> · <span className="admin-badge"> · <aside className="admin-sidebar"> 를 다 뱉는다.

그래서 규칙이 「무엇을 적을지 정한다」가 아니라 「어디서 옮겨 적을지 정한다」가 된다. 디자인 도구에 적는 이름은 전부 코드에서 온 것이어야 하고, 코드에 없는 이름을 도구에서 새로 만들지 않는다.

지어내면 그 순간 두 벌이 된다. 이 표준이 없애려던 바로 그 상태다.


원칙 2. 여섯 자리에 나눠 적는다

도구가 코드로 실어 보내는 자리가 여섯이고, 자리마다 실려 나오는 꼴이 다르다. 실제로 되읽어 확인한 것이다.

적는 자리 무엇을 적나 코드에 나오는 꼴
컴포넌트 이름 <앱>/<PascalCase> function AdminActionableCard
변형 이름 prop=value prop 이름과 값 breakpoint?: "mobile" \| "desktop"
레이어 이름 태그.클래스 data-name="span.admin-select-arrow"
컴포넌트 설명 @키 블록 설명 절에 통째로
개발자 주석 변환할 때 반드시 알 것 data-development-annotations="…"
변수 코드 표기 진짜 CSS 변수명 bg-[var(--color-background)]

변형은 prop 이 된다 — 타입까지 붙어서 나온다. 그래서 변형 이름과 값은 영문으로 적는다. 한국어 변형명을 남기면 상태?: "열기" | "닫기" 가 되어 그대로는 못 쓴다.

레이어 이름의 클래스는 코드에 실제로 있는 것만 적는다. 클래스가 없는 레이아웃용 프레임은 태그만 적는다. CSS Modules 를 쓰는 앱이라면 빌드 뒤 이름이 아니라 소스에 적힌 이름을 적는다 — 도구가 가리키는 것은 실행 결과가 아니라 코드의 그 줄이다.

함정이 하나 있다 — 텍스트 노드의 레이어 이름은 안 실린다. 프레임과 도형은 data-name 으로 나오는데 텍스트는 안 나온다. 태그가 중요한 글자는 프레임으로 한 겹 감싼다. 직접 부딪혀 확인한 것이고, 모르면 「적었는데 왜 안 나오지」로 시간을 쓴다.


원칙 3. 화면에도 코드를 주되 번호를 붙이지 않는다

컴포넌트 이름만으로는 「이게 어느 화면에 쓰이나」가 안 남는다. 그래서 화면에도 코드를 준다.

<앱>.<화면>.<기능> — 세 마디 다 이미 있는 것에서 뽑는다(원칙 1).

마디 어디서 오나
저장소의 앱 디렉터리
화면 화면 키가 있으면 그것, 없으면 라우트 첫 마디
기능 라우트 세그먼트 그대로list · detail · new · edit
admin.caregivers.detail        화면 키 + /caregivers/[caregiverNo]
client.faq.detail              화면 키가 없어 라우트 첫 마디

처음 요구는 「페이지 번호 코드」였다. 화면마다 1번·2번을 붙이자는 것이었는데 버렸다.

번호는 화면이 하나 끼면 뒤가 전부 밀린다. 그리고 그때 이슈 트래커·디자인 도구·코드가 서로 다른 번호를 들게 된다 — 셋을 동시에 고쳐야 하는데 그런 일은 안 일어난다. 순서가 필요하면 정렬값을 본다. 이름은 순서를 나르는 자리가 아니다.

기능 코드는 라우트가 있는 것만 준다. 삭제·검색·내려받기처럼 라우트가 없는 동작에는 코드를 안 준다 — 근거를 코드에서 못 뽑기 때문이다. 원칙 1을 여기서도 지킨다. 지어낼 바에는 없는 채로 둔다.


원칙 4. 설명에 @키 블록을 넣고 existsextract 를 가른다

컴포넌트 설명은 자유 텍스트라 읽는 쪽이 매번 다르게 해석한다. 키를 정해 둔다.

<한 줄 — 이게 무엇이고 어디에 쓰나>

@app        어느 앱인가
@component  코드 컴포넌트명
@status     exists | extract
@file       (exists) 코드 파일 경로
@from       (extract) 지금 그 마크업이 있는 파일
@root       뿌리 요소 — 태그.클래스
@css        클래스가 정의된 파일
@usedOn     쓰이는 화면 코드
@size       쓰는 글자 토큰
@note       변환할 때 걸리는 것

@status 를 가르는 것이 이 블록의 핵심이다.

exists    코드에 그 컴포넌트가 이미 있다      →  만들지 말고 가져다 쓴다
extract   마크업과 클래스만 있고 아직 컴포넌트가 아니다  →  뽑아내야 한다

안 가르면 이미 있는 것을 또 만든다. 실제로 열넷 중 셋이 exists 였다 — 안 갈랐으면 셋을 중복으로 만들었을 것이다.

@file 로 경로를 적는 이유가 따로 있다. 도구에서 코드 파일로 링크를 거는 API 가 있는데 막혀 있다"addDevResourceAsync" is not a supported API. 그래서 링크 대신 경로를 글자로 적는다. 링크가 아니라서 자동으로 안 따라가고, 파일을 옮기면 손으로 고쳐야 한다. 그게 이 선택의 대가다.


원칙 5. 토큰에는 진짜 CSS 변수명을 붙인다

여섯 자리 중 값이 가장 큰 자리다. 변수마다 코드 표기를 붙여 두면 생성 코드에 우리 변수가 그대로 박혀 나온다.

className="bg-[var(--color-background,#fffbf6)]
           px-[var(--space-6,24px)]
           rounded-[var(--radius-md,14px)]"

색값을 손으로 옮겨 적을 일이 사라진다. 안 붙이면 생성 코드에 #fffbf6 이 박혀 나오고, 그것을 사람이 변수로 되돌려야 한다 — 그 되돌리는 작업이 곧 두 번째 이름을 짓는 일이다.

대응은 규칙으로 뽑는다.

색 원시값     →  --brand-<색>-<번호>
간격 N        →  --space-N
반경 X        →  --radius-X
의미 이름     →  --color-primary · --color-on-surface · --color-outline-variant

실제로 124개 중 104개가 규칙으로 풀렸다.

대응이 없는 것은 억지로 붙이지 않고 목록으로 남긴다. 남은 스물은 끊는 값·시간·레이아웃 계열이었다 — CSS 변수로 쓰는 자리가 아니거나 아직 안 정한 것들이다. 없는 대응을 지어내면 원칙 1을 여기서 어기는 것이고, 그 변수는 코드 어디에도 없어 아무 데도 안 걸린다.


열린 질문. 화면 크기 차이를 변형으로 만들지 모드로 만들지

변형(variant)으로 만들기 쉽다. 도구에서 모바일·데스크톱을 변형으로 두면 화면에서 바로 갈아 볼 수 있다.

그런데 코드에서 그것은 prop 이 아니다. 미디어 쿼리이고, 마크업은 한 벌이다.

디자인 도구   breakpoint = mobile | desktop   →  prop 으로 실려 나온다
코드          @media(min-width: 901px)        →  값만 갈아 끼운다

변형으로 만들면 코드에 없는 prop 이 생긴다. 받는 쪽이 그것을 지우고 미디어 쿼리로 옮겨야 하는데, 그 순간 도구와 코드의 구조가 갈라진다.

제대로 된 자리는 변수 컬렉션의 모드(mode)로 보인다. 모드를 모바일·데스크톱 둘로 두면 마크업 한 벌이 모드만 바꿔 두 크기를 다 낸다 — 코드의 미디어 쿼리와 모양이 같다.

규칙으로 못 박지 않는다. 아직 해 보지 않았다. 디자인 시스템 변수를 통째로 건드리는 일이라 별도 결정으로 뺐고, 해 보기 전에 규칙으로 적으면 이 저장소가 금지한 「검증하지 않은 단정」이 된다.

지금 정한 것은 하나뿐이다 — 변형을 만들었으면 그 이름과 값은 영문으로 적는다(원칙 2). 모드로 갈지는 실제로 해 본 뒤에 정한다.


판단 기준 정리

질문 결론
이름을 어디서 가져오나 코드에서 지어내면 그 순간 두 벌이 된다
공식 연결 기능은 플랜에 막혀 있다 이 표준은 대체재
컴포넌트 이름 <앱>/<PascalCase> 그대로 코드 컴포넌트명이 된다
변형 이름 영문으로 prop 이름과 값이 타입까지 붙어 나온다
레이어 이름 태그.클래스 코드에 실제로 있는 클래스만
글자에 태그를 주려면 프레임으로 감싼다 텍스트 노드의 이름은 안 실린다
화면 코드 <앱>.<화면>.<기능> 세 마디 다 코드에서 뽑는다
번호를 붙이나 안 붙인다 하나 끼면 뒤가 전부 밀린다
라우트 없는 동작은 코드를 안 준다 근거를 코드에서 못 뽑는다
이미 있는 컴포넌트는 @status: exists 안 가르면 또 만든다
토큰은 진짜 CSS 변수명을 붙인다 안 붙이면 색값이 박혀 나온다
대응 없는 토큰은 목록으로 남긴다 지어낸 변수는 아무 데도 안 걸린다
화면 크기 차이 아직 안 정했다 모드가 맞아 보이지만 해 보지 않았다

이 표준을 정하기까지

시작은 「페이지마다 번호 코드를 붙이자」였다. 화면을 1번·2번으로 부르면 디자인과 코드가 같은 말을 쓸 거라고 봤다.

조사하니 번호가 제일 약한 이름이었다. 화면이 하나 끼면 뒤가 전부 밀리고, 그 순간 이슈 트래커·디자인 도구·코드가 서로 다른 번호를 든다. 셋을 동시에 고쳐야 하는 이름은 이름이 아니다. 그래서 번호를 버리고 <앱>.<화면>.<기능> 으로 갔다 — 세 마디 다 이미 있는 것이라 밀릴 일이 없다.

공식 경로가 막혀 있다는 것을 먼저 확인했다. Code Connect 가 이 문제의 정답인데 플랜이 안 된다. 정답이 막힌 것을 알고 대체재를 쓰는 것과, 정답이 있는 줄 모르고 다른 것을 만드는 것은 다르다. 그래서 그 사실을 글 맨 앞에 뒀다.

「이름을 지어내지 마라」가 조사 중에 뒤늦게 뼈대가 됐다. 처음엔 도구에 무엇을 적을지를 정하는 일인 줄 알았는데, 코드를 훑어보니 클래스 231개와 토큰과 라우트에 이름이 이미 다 있었다. 정할 것은 「무엇을 적나」가 아니라 「어디서 옮겨 적나」였다. 이 뒤집힘이 나머지 규칙을 전부 정했다 — 라우트 없는 동작에 코드를 안 주는 것도, 대응 없는 토큰을 목록으로 남기는 것도 같은 규칙의 다른 얼굴이다.

막힌 것 셋을 직접 부딪혀 확인했다. Code Connect 플랜 제약, 개발 리소스 링크 API 미지원, 텍스트 노드 이름 미전달이다. 셋 다 글에 남겼다 — 특히 텍스트 노드는 적어도 안 나오는 함정이라, 모르면 「적었는데 왜 안 나오지」로 시간을 쓴다.

반응형은 규칙으로 안 적었다. 모드가 맞아 보이는 근거는 뚜렷한데 아직 해 보지 않았다. 이 저장소가 「검증하지 않은 사실 단정」을 금지하고 있어, 해 보기 전에 적으면 6개월 뒤의 내가 근거 없는 규칙을 물려받는다. 열린 질문으로 남기고 장부에 올렸다.


정리

  • 이름을 지어내지 않는다. 코드에 이미 있는 것을 옮겨 적는다 — 이 표준의 뼈대다.
  • 공식 연결 기능이 플랜에 막혀 있어 이 표준이 대체재로 선다. 플랜이 올라가면 상당 부분이 대체된다.
  • 여섯 자리에 나눠 적고, 자리마다 실려 나오는 꼴이 다르다. 변형은 prop 이 되므로 이름과 값을 영문으로 적는다.
  • 텍스트 노드의 레이어 이름은 안 실린다. 태그가 중요한 글자는 프레임으로 감싼다.
  • 화면 코드는 <앱>.<화면>.<기능>. 번호를 붙이지 않는다 — 하나 끼면 뒤가 전부 밀린다.
  • @statusexistsextract 로 가른다. 안 가르면 이미 있는 것을 또 만든다.
  • 토큰에 진짜 CSS 변수명을 붙인다. 안 붙이면 색값이 박혀 나오고, 그것을 되돌리는 일이 곧 두 번째 이름 짓기다.
  • 대응이 없으면 목록으로 남긴다. 지어낸 변수는 코드 어디에도 없어 아무 데도 안 걸린다.

AI 코드 어시스턴트에 바로 적용하기

Claude Code · Codex — .claude/skills/figma-code-naming-standard/SKILL.md · .agents/skills/figma-code-naming-standard/SKILL.md

두 파일은 바이트까지 같아야 한다. .agents 쪽은 tools/agents-skills.rb 가 뽑는 생성물이다.

---
name: figma-code-naming-standard
description: 디자인 도구의 컴포넌트를 코드로 옮길 때 이름을 어디서 가져오는지 정한다. 도구가 코드로 실어 보내는 여섯 자리, 화면 코드 체계, 설명의 @키 블록, 토큰에 CSS 변수명을 붙이는 규칙을 담는다. Figma 컴포넌트를 만들 때, 디자인을 코드로 옮길 때, 토큰을 정리할 때 적용한다.
---

# 디자인 도구 명명 표준

**이름을 지어내지 않는다. 코드에 이미 있는 것을 옮겨 적는다.**

지어내면 같은 것에 이름이 둘 생기고 **두 이름은 반드시 갈라진다.** 코드에는 CSS 클래스·토큰 변수·라우트·화면 키가 이미 다 있다.

```bash
# 태그와 클래스의 짝은 코드에서 그대로 긁힌다
grep -rnoE '<[A-Za-z][A-Za-z0-9.]*[^>]*className="[a-z0-9 -]*"' src --include=*.js
```

**공식 연결 기능(Code Connect)은 플랜에 막혀 있다.** 이 표준은 그 **대체재**다 — 플랜이 올라가면 상당 부분이 대체된다.

## 여섯 자리에 나눠 적는다

| 적는 자리 | 무엇을 | 코드에 나오는 꼴 |
|---|---|---|
| 컴포넌트 이름 | `<앱>/<PascalCase>` | `function AdminActionableCard` |
| 변형 `prop=value` | prop 이름과 값 | `breakpoint?: "mobile" \| "desktop"` |
| 레이어 이름 | `태그.클래스` | `data-name="span.admin-select-arrow"` |
| 컴포넌트 설명 | `@키` 블록 | 설명 절에 통째로 |
| 개발자 주석 | 변환할 때 반드시 알 것 | `data-development-annotations` |
| 변수 코드 표기 | 진짜 CSS 변수명 | `bg-[var(--color-background)]` |

- **변형 이름과 값은 영문으로 적는다.** prop 이름과 값이 **타입까지 붙어** 나온다 — 한국어면 `상태?: "열기"` 가 되어 그대로 못 쓴다.
- **레이어 이름의 클래스는 코드에 실제로 있는 것만.** 클래스 없는 레이아웃 프레임은 태그만 적는다. CSS Modules 라면 **빌드 뒤 이름이 아니라 소스에 적힌 이름**을 적는다.
- **텍스트 노드의 레이어 이름은 안 실린다.** 프레임·도형만 나온다. **태그가 중요한 글자는 프레임으로 한 겹 감싼다.**

## 화면 코드는 `<앱>.<화면>.<기능>`

```
admin.caregivers.detail        화면 키 + 라우트
client.faq.detail              화면 키가 없으면 라우트 첫 마디
```

- 앱은 저장소의 앱 디렉터리, 화면은 화면 키(없으면 라우트 첫 마디), **기능은 라우트 세그먼트 그대로**(`list` · `detail` · `new` · `edit`).
- **번호를 붙이지 않는다.** 화면이 하나 끼면 뒤가 전부 밀리고, 그때 이슈 트래커·디자인 도구·코드가 **서로 다른 번호를 든다.** 순서가 필요하면 정렬값을 본다.
- **기능 코드는 라우트가 있는 것만 준다.** 삭제·검색·내려받기처럼 라우트가 없는 동작에는 안 준다 — **근거를 코드에서 못 뽑는다.** 지어낼 바에는 없는 채로 둔다.

## 설명에 `@키` 블록을 넣는다

```
<한 줄 — 이게 무엇이고 어디에 쓰나>

@app @component @status @file @from @root @css @usedOn @size @note
```

- **`@status` 를 `exists` 와 `extract` 로 반드시 가른다.** `exists` 는 코드에 이미 있는 것(가져다 쓴다), `extract` 는 마크업만 있고 아직 컴포넌트가 아닌 것(뽑아낸다). **안 가르면 이미 있는 것을 또 만든다.**
- **`@file` 로 경로를 글자로 적는다.** 코드 파일로 링크를 거는 API 가 막혀 있어서다(`addDevResourceAsync` 미지원). **링크가 아니라 자동으로 안 따라가고, 파일을 옮기면 손으로 고쳐야 한다.**

## 토큰에는 진짜 CSS 변수명을 붙인다

```
색 원시값 → --brand-<색>-<번호>      간격 N → --space-N
반경 X   → --radius-X                의미   → --color-primary 등
```

- 안 붙이면 생성 코드에 **색값이 박혀 나오고**, 그것을 사람이 변수로 되돌려야 한다 — **그 되돌리기가 곧 두 번째 이름 짓기다.**
- **대응이 없으면 억지로 붙이지 말고 목록으로 남긴다.** 지어낸 변수는 코드 어디에도 없어 아무 데도 안 걸린다.

## 아직 안 정한 것

**화면 크기 차이를 변형으로 만들지 모드로 만들지.** 변형은 prop 으로 실려 나오는데 코드에서 그것은 미디어 쿼리라 구조가 갈라진다. 변수 컬렉션의 **모드**가 맞아 보이지만 **아직 해 보지 않았다.** 지금 정한 것은 「변형을 만들었으면 이름과 값을 영문으로」 하나뿐이다.

## 하지 않는 것

- **코드에 없는 이름을 도구에서 새로 만들지 않는다.**
- **한국어 변형 이름을 남기지 않는다.**
- **코드에 없는 클래스를 레이어 이름에 적지 않는다.**
- **화면에 번호를 붙이지 않는다.**
- **라우트 없는 동작에 기능 코드를 주지 않는다.**
- **`@status` 를 빼놓지 않는다.**
- **대응 없는 토큰에 변수명을 지어 붙이지 않는다.**

GitHub Copilot — .github/instructions/figma-code-naming-standard.instructions.md

---
description: 디자인 도구의 컴포넌트를 코드로 옮길 때 이름을 어디서 가져오는가
applyTo: "**"
---

# 디자인 도구 명명

- **이름을 지어내지 않는다. 코드에 이미 있는 것을 옮겨 적는다.** 지어내면 같은 것에 이름이 둘 생기고 **두 이름은 반드시 갈라진다.** 코드에는 CSS 클래스·토큰 변수·라우트·화면 키가 이미 다 있고, 태그와 클래스의 짝은 `grep -rnoE '<[A-Za-z][A-Za-z0-9.]*[^>]*className="[a-z0-9 -]*"'` 한 줄로 긁힌다.
- **공식 연결 기능(Code Connect)은 플랜에 막혀 있다.** 이 표준은 그 **대체재**이고, 플랜이 올라가면 상당 부분이 대체된다.
- **여섯 자리에 나눠 적는다** — 컴포넌트 이름(`<앱>/<PascalCase>`) · 변형 `prop=value` · 레이어 이름(`태그.클래스`) · 컴포넌트 설명(`@키` 블록) · 개발자 주석 · 변수 코드 표기. **변형 이름과 값은 영문으로** 적는다(prop 이름과 값이 **타입까지 붙어** 나온다). 레이어 이름의 클래스는 **코드에 실제로 있는 것만**, 클래스 없는 레이아웃 프레임은 태그만.
- **텍스트 노드의 레이어 이름은 안 실린다.** 프레임·도형만 나오므로 **태그가 중요한 글자는 프레임으로 한 겹 감싼다.**
- **화면 코드는 `<앱>.<화면>.<기능>`.** 앱은 앱 디렉터리, 화면은 화면 키(없으면 라우트 첫 마디), 기능은 라우트 세그먼트 그대로. **번호를 붙이지 않는다** — 화면이 하나 끼면 뒤가 전부 밀리고 트래커·도구·코드가 서로 다른 번호를 든다. **기능 코드는 라우트가 있는 것만** 준다(근거를 코드에서 못 뽑으면 안 준다).
- **컴포넌트 설명에 `@키` 블록을 넣고 `@status` 를 `exists`/`extract` 로 가른다.** 안 가르면 **이미 있는 것을 또 만든다.** `@file` 은 링크가 아니라 글자로 적는다 — 링크 API 가 막혀 있어서이고, 파일을 옮기면 손으로 고쳐야 한다.
- **토큰에 진짜 CSS 변수명을 붙인다.** 안 붙이면 생성 코드에 색값이 박혀 나오고 **그것을 변수로 되돌리는 일이 곧 두 번째 이름 짓기다.** **대응이 없으면 목록으로 남긴다** — 지어낸 변수는 코드 어디에도 없어 아무 데도 안 걸린다.
- **화면 크기 차이를 변형으로 만들지 모드로 만들지는 아직 안 정했다.** 변형은 prop 으로 나오는데 코드에서는 미디어 쿼리라 구조가 갈라진다. 모드가 맞아 보이지만 **해 보지 않았다.**

자신만의 철학을 만들어가는 중입니다.
최상단으로 이동했습니다!
확대 이미지

댓글남기기