디자인 도구에서 코드로 넘어올 때 같은 것에 이름이 두 번 지어진다.
디자인 도구 「면」 · 「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을 여기서도 지킨다. 지어낼 바에는 없는 채로 둔다.
원칙 3-1. 화면 안 영역 번호에는 화면 코드를 앞에 단다
원칙 3은 화면에 번호를 안 붙인다고 정했다. 그런데 화면 안 영역은 안 정해뒀고, 그 빈자리에서 문제가 실제로 터졌다.
기획 판에서는 화면 하나를 영역으로 쪼개 번호를 매긴다. 어느 영역을 말하는지 핀과 카드가 그 번호로 이어진다.
핀 (화면 위) #4
카드 제목 #4 하단 고정 바
본문 참조 나가는 길 상담 신청 (#4 · #7)
화면마다 1부터 다시 센다. 그래서 #4로 전체 검색을 하면 화면 수만큼 걸리고 전부 다른 것이다. 실제로 아리맘 디자인 파일에서 화면 17개가 각자 #4를 갖고 있었다. 어느 것인지 열어봐야 안다.
정해야 했던 건 이거였다 — 무엇을 유니크하게 만들 것인가.
안 A — 번호 대신 이름을 쓴다. #4 → sticky-bar. 얻는 것은 뜻이 바로 보인다는 것이다. 버린 이유는 핀이 작다는 것이다. 화면 위에 얹는 핀은 24px 높이의 알약인데 거기에 이름이 들어가면 화면을 가린다. 그리고 카드 제목에는 이미 「하단 고정 바」가 옆에 적혀 있어 이름을 또 넣으면 같은 말이 두 번이다.
안 B — 번호를 유지하고 화면 코드를 앞에 단다. #4 → client.home-004.
골랐다 — 안 B. 안 A가 못 지켜주는 것은 핀과 카드와 본문이 같은 짧은 문자열로 이어지는 구조다. 번호는 그 셋을 잇는 실이라 없애면 연결이 끊긴다. 화면 코드를 앞에 다는 것만으로 유니크해지고, 그 화면이 어디인지도 같이 보인다.
세 자리로 채운다. client.home-004. 두 자리면 -10과 -100이 정렬에서 섞이고, 영역이 열 개를 넘는 화면이 실제로 있었다(client.info.detail은 11개다).
전 후
핀 #4 client.home-004
카드 #4 하단 고정 바 client.home-004 하단 고정 바
본문 (#4 · #7) (client.home-004 · client.home-007)
셋이 모두 같은 문자열을 쓴다. 이게 규칙의 핵심이다 — 한 곳만 바꾸면 다시 갈린다.
대가는 핀이 넓어진다는 것이다. 68px 알약이 127px가 됐고, 화면 코드가 긴 관리자 쪽은 164px다. 작업판 여백에 놓이므로 화면을 가리지는 않지만 공짜는 아니다.
이 번호는 코드로 나가지 않는다. 그 화면 안의 지시용이다. 코드에 옮길 이름은 레이어 이름의 태그.클래스다(원칙 2).
원칙 4. 설명에 @키 블록을 넣고 exists 와 extract 를 가른다
컴포넌트 설명은 자유 텍스트라 읽는 쪽이 매번 다르게 해석한다. 키를 정해 둔다.
<한 줄 — 이게 무엇이고 어디에 쓰나>
@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을 여기서 어기는 것이고, 그 변수는 코드 어디에도 없어 아무 데도 안 걸린다.
원칙 6. 화면 크기 차이는 변형으로 가른다
처음엔 모드(mode)가 답으로 보였다. 그리고 해보고 나서 뒤집혔다.
논리는 이랬다. 변형으로 만들면 breakpoint 가 prop 으로 실려 나오는데 코드에서 그건 prop 이 아니라 미디어 쿼리다. 받는 쪽이 지우고 옮겨야 하고 그 순간 도구와 코드가 갈라진다. 반면 변수 컬렉션의 모드를 둘로 두면 마크업 한 벌이 모드만 바꿔 두 크기를 낸다 — 코드의 미디어 쿼리와 모양이 같다.
규칙으로 못 박지 않고 해보기로 했다. 그리고 블로그 화면 열한 벌을 폰 390 · 폴드 816 · 데스크톱 1280 세 폭으로 그린 뒤에 쟀다.
세 폭이 경로까지 같은 노드 값만 다른 노드 구조가 다른 노드
홈 2 1 46
글 상세 11 6 32
값 차이 대 구조 차이가 홈 1 대 46, 글 상세 6 대 32 — 두 화면을 합하면 7 대 78, 약 1 대 11 이다. 그리고 구조가 다른 자리는 우연이 아니라 정한 것들이었다.
목차·카테고리 폰·폴드 = 아래에서 올라오는 시트 데스크톱 = 옆에 붙어 따라다님
표 폰·폴드 = 행을 카드로 편다 데스크톱 = 진짜 표
하단 엄지 바 폰·폴드 = 있다 데스크톱 = 없다
사이드바 폰 = 없다 폴드·데스크톱 = 있다
여기서 모드가 무너진다. 모드는 값만 바꾸지 노드를 만들거나 없애지 못한다. 하단 바를 데스크톱에서 지우는 일을 모드로는 표현할 수 없다. 46개와 32개가 통째로 남는다.
골랐다 — 변형. 실제로 컴포넌트 세트로 묶어 스위치를 돌려보니 구조까지 갈렸다.
breakpoint = mobile 390 × 652 하단 엄지 바 O · 사이드바 X
breakpoint = fold 816 × 701 하단 엄지 바 O · 사이드바 O
breakpoint = desktop 1280 × 693 하단 엄지 바 X · 사이드바 O
모드를 걱정하며 피하려던 대가는 그대로 남는다 — breakpoint 가 prop 으로 실려 나간다. 그런데 그건 코드로 옮길 때 한 번 처리하는 일이고, 그 자리는 이미 화면별 대응 표준이 받고 있다. 거기서 「구조가 바뀌는 것은 예외 — 두 벌로 가되 이유를 파일에 적는다」고 정해뒀다. 도구와 코드가 같은 말을 한다.
도구 값만 다르면 모드로 묶을 수 있다. 구조가 다르면 변형이다
코드 내용이 같으면 마크업 하나. 구조가 바뀌면 두 벌이다
원칙 6-1. 변형의 prop 이름과 기본값을 고정한다
무엇으로 가를지를 정했으면 어떻게 짓는지가 남는다. 셋을 못 박는다.
prop 이름은 breakpoint, 값은 영문 소문자다 — mobile · fold · desktop. 원칙 2가 이미 정한 것이 여기서도 그대로다. 한국어로 지으면 코드에 폭?: "폴드 816" 으로 나와 그대로 못 쓴다.
기본 변형은 가장 많이 보는 폭을 첫 자리에 둔다. 컴포넌트 세트는 첫 변형이 기본값이 되므로 순서가 곧 규칙이다. 이 블로그는 폴드로 읽는 일이 제일 많아 fold 를 앞에 뒀고, 새 인스턴스를 뽑으면 816 으로 나온다.
값만 다른 것은 변수 모드로 묶어도 된다. 글자 크기와 여백처럼 숫자만 갈리는 것은 모드가 정확히 맡는 자리다. 다만 그것만으로는 화면이 안 되므로 변형이 주된 수단이고 모드는 보조다. 순서를 뒤집으면 원칙 6에서 무너진 자리로 돌아간다.
판단 기준 정리
| 질문 | 답 | 결론 |
|---|---|---|
| 이름을 어디서 가져오나 | 코드에서 | 지어내면 그 순간 두 벌이 된다 |
| 공식 연결 기능은 | 플랜에 막혀 있다 | 이 표준은 대체재다 |
| 컴포넌트 이름 | <앱>/<PascalCase> |
그대로 코드 컴포넌트명이 된다 |
| 변형 이름 | 영문으로 | prop 이름과 값이 타입까지 붙어 나온다 |
| 레이어 이름 | 태그.클래스 |
코드에 실제로 있는 클래스만 |
| 글자에 태그를 주려면 | 프레임으로 감싼다 | 텍스트 노드의 이름은 안 실린다 |
| 화면 코드 | <앱>.<화면>.<기능> |
세 마디 다 코드에서 뽑는다 |
| 번호를 붙이나 | 안 붙인다 | 하나 끼면 뒤가 전부 밀린다 |
| 라우트 없는 동작은 | 코드를 안 준다 | 근거를 코드에서 못 뽑는다 |
| 이미 있는 컴포넌트는 | @status: exists |
안 가르면 또 만든다 |
| 토큰은 | 진짜 CSS 변수명을 붙인다 | 안 붙이면 색값이 박혀 나온다 |
| 대응 없는 토큰은 | 목록으로 남긴다 | 지어낸 변수는 아무 데도 안 걸린다 |
| 화면 크기 차이 | 변형으로 가른다 | 모드는 노드를 만들거나 없애지 못한다 — 홈·글 상세 합산 값 7 대 구조 78 |
이 표준을 정하기까지
시작은 「페이지마다 번호 코드를 붙이자」였다. 화면을 1번·2번으로 부르면 디자인과 코드가 같은 말을 쓸 거라고 봤다.
조사하니 번호가 제일 약한 이름이었다. 화면이 하나 끼면 뒤가 전부 밀리고, 그 순간 이슈 트래커·디자인 도구·코드가 서로 다른 번호를 든다. 셋을 동시에 고쳐야 하는 이름은 이름이 아니다. 그래서 번호를 버리고 <앱>.<화면>.<기능> 으로 갔다 — 세 마디 다 이미 있는 것이라 밀릴 일이 없다.
공식 경로가 막혀 있다는 것을 먼저 확인했다. Code Connect 가 이 문제의 정답인데 플랜이 안 된다. 정답이 막힌 것을 알고 대체재를 쓰는 것과, 정답이 있는 줄 모르고 다른 것을 만드는 것은 다르다. 그래서 그 사실을 글 맨 앞에 뒀다.
「이름을 지어내지 마라」가 조사 중에 뒤늦게 뼈대가 됐다. 처음엔 도구에 무엇을 적을지를 정하는 일인 줄 알았는데, 코드를 훑어보니 클래스 231개와 토큰과 라우트에 이름이 이미 다 있었다. 정할 것은 「무엇을 적나」가 아니라 「어디서 옮겨 적나」였다. 이 뒤집힘이 나머지 규칙을 전부 정했다 — 라우트 없는 동작에 코드를 안 주는 것도, 대응 없는 토큰을 목록으로 남기는 것도 같은 규칙의 다른 얼굴이다.
막힌 것 셋을 직접 부딪혀 확인했다. Code Connect 플랜 제약, 개발 리소스 링크 API 미지원, 텍스트 노드 이름 미전달이다. 셋 다 글에 남겼다 — 특히 텍스트 노드는 적어도 안 나오는 함정이라, 모르면 「적었는데 왜 안 나오지」로 시간을 쓴다.
반응형은 규칙으로 안 적었다. 모드가 맞아 보이는 근거는 뚜렷한데 아직 해 보지 않았다. 이 저장소가 「검증하지 않은 사실 단정」을 금지하고 있어, 해 보기 전에 적으면 6개월 뒤의 내가 근거 없는 규칙을 물려받는다. 열린 질문으로 남기고 장부에 올렸다.
마지막으로 화면 크기 차이를 해보고 정했다. 글을 쓸 때는 모드가 답으로 보여 「해 보기 전에는 규칙으로 안 적는다」로 남겨뒀다. 화면 열한 벌을 세 폭으로 그린 뒤 재보니 세 폭이 공유하는 노드가 홈 기준 2개, 구조가 다른 노드가 46개였다. 모드는 값만 바꾸므로 그 46개를 담지 못한다. 미뤄둔 덕에 추측으로 반대 결론을 박제하지 않았다.
정리
- 이름을 지어내지 않는다. 코드에 이미 있는 것을 옮겨 적는다 — 이 표준의 뼈대다.
- 공식 연결 기능이 플랜에 막혀 있어 이 표준이 대체재로 선다. 플랜이 올라가면 상당 부분이 대체된다.
- 여섯 자리에 나눠 적고, 자리마다 실려 나오는 꼴이 다르다. 변형은 prop 이 되므로 이름과 값을 영문으로 적는다.
- 텍스트 노드의 레이어 이름은 안 실린다. 태그가 중요한 글자는 프레임으로 감싼다.
- 화면 코드는
<앱>.<화면>.<기능>. 번호를 붙이지 않는다 — 하나 끼면 뒤가 전부 밀린다. @status를exists와extract로 가른다. 안 가르면 이미 있는 것을 또 만든다.- 토큰에 진짜 CSS 변수명을 붙인다. 안 붙이면 색값이 박혀 나오고, 그것을 되돌리는 일이 곧 두 번째 이름 짓기다.
- 대응이 없으면 목록으로 남긴다. 지어낸 변수는 코드 어디에도 없어 아무 데도 안 걸린다.
- 화면 크기 차이는 변형으로 가른다. 모드는 값만 바꾸지 노드를 만들거나 없애지 못한다 — 실측에서 홈과 글 상세를 합해 값 7 대 구조 78(약 1 대 11) 이었다
자신만의 철학을 만들어가는 중입니다.
댓글남기기