값의 원천은 코드다. 이 문서는 값을 갖지 않고, 어디를 봐야 하는지와 무엇을 하면 안 되는지만 정한다.
- 시맨틱 토큰 —
primary,surface,border,danger… - 원시 토큰 —
blue-100등. 1에 마땅한 게 없을 때만 - 임의값 —
text-[17px],#3B82F6
- 값의 정의는 전부
tailwind.config.js(+src/app/globals.css)에 있다. 이 문서에 값을 적지 않는다. - 위에서부터 가능한 단계를 쓴다.
디자인 파일을 우선시하며, 토큰과 임의값의 사용은 디자인파일의 구현을 따른다. 예를들어서 피그마 페이지에 폰트가 17px이고 시멘틱 토큰과 css변수가 없다면 임의값을 사용한다.
- 공통 컴포넌트는
src/components/<도메인>/(layout,place,map,settings…)에 있다. 있으면 쓴다 - 특정 페이지 전용 컴포넌트는
src/app/**/_components/에 둔다 - 없으면 Tailwind로 직접 작업한다
Design System 파일은 확정된 내용만 모아 둔 곳이다. 퍼블리싱할 때 해당 페이지의 컴포넌트·토큰을 기준으로 맞춘다.
- 00 Foundation — Color(Primitive·Semantic), Typography, Icon, Spacing, Shadow, Radius, Grid, Logo
- 01 Button — Button, Button Base (Size·State·Layout)
- 02 Text Field — Text Field Emphasis, Textarea Field, Search Bar, Input Base
- 03·04·05 Header · Footer · Side Bar — Header(Login·Logout), Footer, Side Bar
- 06 Navigation — Bottom Navigation
- 07·08 Select Field · Dropdown — Select Field, Select Field_Emphasis, Dropdown, Item
- 09·10 Date Picker · Time Picker — Date Picker, Date Cell, Time Picker, Time Picker Modal, Period
- 11 Tabs — Tabs Line, Tabs Chip, Tab Item
- 12 Toast — Toast
- 13 Alert Dialog — Alert Dialog
- 14·15 Chip · Badge — Chip(Solid·Outline), Badge(Weak·Solid·Outline, 톤 6종)
- Update Log — 디자인 시스템 변경 기록
- 아이콘은 Figma 디자인 시스템 Icon을 우선 사용한다
- 필요한 아이콘이 Figma에 없으면 임의로 만들거나 다른 라이브러리에서 가져오지 말고, 디자이너/개발자에게 추가를 요청한다
- SVGR로 SVG를 React 컴포넌트로 만들어 쓴다.
<img src>·next/image·CSSmask로 SVG를 직접 쓰지 않는다 - 크기 규격: Figma 권장 사이즈(12 / 16 / 20 / 24 / 28 / 32)를 따른다
- stroke는 1.8로 고정한다. 아이콘 크기를 줄여도 선 굵기는 1.8px로 유지된다(
createIcon이 처리) - 기존
lucide-react,src/components/icons/,public/icons/의 SVG는 Figma 아이콘 컴포넌트로 점진적으로 전환한다. 신규 코드에서 새로 추가하지 않는다
- Figma에서 SVG를 받는다
- 인스턴스가 아니라 디자인 시스템의 원본 컴포넌트(24×24) 를 받는다. 16·20 크기로 쓰인 인스턴스를 받으면 viewBox가 달라진다
src/assets/icons/에 넣는다- 파일명은 kebab-case로, Figma 이름을 따른다. variant가 있으면
<컴포넌트 세트>-<type>(예:chevron-left.svg,calendar-add.svg,reaction-star.svg).type=default는 붙이지 않는다 (close.svg)
- 파일명은 kebab-case로, Figma 이름을 따른다. variant가 있으면
- 파일 값을 3가지 수정한다
<!-- Figma에서 받은 원본 --> <svg width="24" height="24" viewBox="0 0 24 24" fill="none"> <path d="M6 9L12 15L18 9" stroke="#3B3F4E" stroke-width="1.8" .../> </svg> <!-- 수정 후 --> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"> <path d="M6 9L12 15L18 9" stroke="currentColor" stroke-width="1.8" .../> </svg>
width,height삭제 (viewBox는 반드시 남긴다)stroke="#3B3F4E"→stroke="currentColor". 색이fill에 있는 아이콘이면fill="currentColor"viewBox가0 0 24 24인지 확인한다
src/assets/icons/index.tsx에 추가한다import MapPinSvg from "@/assets/icons/map-pin.svg"; // 위쪽 import 구역 export const MapPinIcon = createIcon(MapPinSvg, "MapPinIcon"); // 아래쪽 export 구역
Figma 디자인 시스템 Icon에 없어 임시로 Tabler Icons(outline)에서 받아 쓰는 아이콘이다. 디자이너가 디자인 시스템에 추가하면 원본으로 바꾸고 이 목록에서 지운다.
- 파일 형식은 2-1의 규칙(24×24 viewBox,
currentColor, stroke 1.8)에 맞춰 수정해 넣는다
| 아이콘 | 파일 | 출처 | 쓰는 곳 |
|---|---|---|---|
| 경로 보기 | route.svg (RouteIcon) |
Tabler route |
모바일 지도 경로 보기 버튼 |
| 보내기(종이비행기) | send.svg (SendIcon) |
Tabler send |
헤더 초대하기 (PR #220) |
import { MapPinIcon } from "@/assets/icons";
<MapPinIcon /> // 24px, 색은 부모 글자색
<MapPinIcon size={16} className="text-primary" />- 크기는
size(기본 24), 색은text-*토큰으로 준다 (currentColor) - 장식용이라 기본으로
aria-hidden이다. 아이콘만 있는 버튼은 버튼에aria-label을 단다 - SVGR 설정은
next.config.ts의turbopack.rules에 있고,src/assets/icons/안의 SVG에만 적용된다 - 테스트(Vitest)에서는
vitest.config.ts의vite-plugin-svgr가 같은 폴더를 같은 방식으로 변환한다
- 서버 상태 → TanStack Query
- 클라이언트 상태 → Zustand
- 서버에서 온 데이터를 Zustand에 복사해두지 않는다
데이터를 다루는 화면은 다음을 빠짐없이:
- loading
- error
- empty
- 사용자 액션에는 결과 피드백이 있어야 한다
- 현재 alert 는 toast를 사용하고, confirm은 시스템confirm 과 dialog가 혼용되고 있는 상태이다. 새로 작성하는 코드는 dialog를 우선시하되 요구조건에 맞춰서 해라
- 비동기 동작 중 중복 실행 방지
작업 완료 후 자가 점검:
- Design Token이 있는데 인라인 tailwind를 사용한 값이 없는지
- 이벤트 피드백/실패처리가 없는 이벤트는 없는지
- 다크모드: 사용하지 않음