Konde Design Framework

i18n이 번역을 담당한다면,
KDF는 디자인을 담당하죠.
둘 다 JSON에 살고요.

서버사이드 웹 앱과 에이전트가 짜주는 UI를 위한 JSON 기반 디자인 코디네이션 레이어입니다. 레이아웃, 간격, 타이포그래피, 컴포넌트 스타일링 등 반복되는 결정사항을 단 하나의 진실 공급원에 모아두세요. 에이전트가 이 파일 저 파일 뒤지거나 과거 세션에서 삽질할 필요 없이 이 JSON 하나만 쓱 읽어오면 됩니다.

작동 원리 보기
kdf / homepage.json → 렌더링 결과
kdf/homepage.json
{
  "$layout": ["hero", "footer"],
  "hero": {
    "wrapper": "mx-auto max-w-6xl px-6 py-20",
    "title":   "@typography.h1",
    "cta-primary": "@button.cta"
  }
}
data-kdf="hero.title"
AI와 함께 빌드하고 배포하세요.
data-kdf="hero.body"

단 하나의 진실 공급원이 모든 페이지, 컴포넌트, 에이전트 세션의 일관성을 꽉 잡아줍니다.

data-kdf="hero.cta-primary"
시작하기 →

JSON이 정의하고 · 코드가 렌더링하며 · data-kdf 가 다시 연결해줍니다

라이선스
MIT
런타임
Node 파일시스템
의존성
런타임 의존성 제로
테스트 환경
Next · Astro · Hono
01 — 문제점

여러분의 컴포넌트 안에서
디자인 파편화가 일어나고 있습니다.

클래스명이 오로지 .tsx 파일 안에만 갇혀 있으면, 페이지가 늘어날수록 디자인은 서서히 어긋나게 됩니다. 사람 혼자 짤 땐 괜찮지만, 에이전트가 UI를 건드리는 순간 이 방식은 너무나 쉽게 박살나죠.

kdf 도입 전 · 파일 곳곳에 흩어진 모습
<section className="mx-auto max-w-6xl px-6 py-20">
  <h1 className="text-5xl font-semibold tracking-tight">
  // …and again, slightly different, on the next page

같은 버튼인데도 알게 모르게 5가지 버전이 생겨납니다. 페이지마다 간격이 들쭉날쭉해지고요. 새 에이전트 세션을 켤 때마다 디자인 룰을 처음부터 다시 가르쳐야 하죠.

결과적으로
  • 에이전트가 컬러, 간격, 폰트, 레이아웃을 마음대로 지어냅니다.
  • 매번 유저가 채팅으로 그걸 하나하나 짚어주며 고쳐야 해요.
  • 수정 한 번 하려면 컴포넌트 파일들을 싹 다 뒤져야 하죠.
  • 세션을 새로 열면 기존에 잡아둔 디자인 의도가 날아갑니다.

→ 끝없는 "좀 더 크게" · "더 파랗게" · "왼쪽으로 옮겨" 삽질의 연속

02 — 해결책

디자인을 밖으로 끄집어내세요.

KDF는 i18n이 텍스트에 한 일을 디자인에 똑같이 적용한 겁니다. 반복되는 결정들을 컴포넌트 파일 밖으로 빼내 JSON으로 옮기죠. 화면에 그려지는 UI는 여전히 평범한 CSS를 쓰지만, 주도권(ownership)이 어디에 있는지가 달라지는 겁니다.

1 — json에 룰 정의하기
{
  "hero": {
    "title": "text-5xl font-semibold tracking-tight"
  }
}
2 — 코드에서 렌더링하기
const d = getDesign("homepage");

<h1 data-kdf="hero.title" className={d("hero.title")}>
JSON이 정의
이 파일 하나가 디자인 방향을 전적으로 통제합니다. 디테일한 조정이 필요하면 유저가 직접 이걸 고치면 돼요.
코드가 렌더링
여러분의 컴포넌트는 토큰을 읽고 승인된 디자인을 일반 CSS로 화면에 뿌려줍니다. 밑단에 깔린 마법 같은 건 없어요.
에이전트가 구현
에이전트가 감으로 찍는 대신 JSON을 바탕으로 UI를 만듭니다. 간격, 색상, 폰트, 섹션 순서를 멋대로 지어내지 않죠.
유저가 JSON 수정
세션을 열 때마다 채팅으로 똑같은 수정을 지시하느라 진 빼지 말고, 그냥 소스 파일을 한 번 고치면 끝납니다.
03 — 컴포넌트 라이브러리가 아닙니다

라이브러리가 '무엇'인지 묻는다면,
KDF는 '어떤 것'.

디자인 라이브러리는 우리에게 컴포넌트를 던져주죠. 반면 KDF는 이 요소가 homepage.hero.cta-primary 이며, 버튼으로 그려지고 특정 토큰으로 스타일링된다는 걸 짚어줍니다. 라이브러리엔 쏙 빠져 있는 딱 한 가지, 즉 요소와 디자인 룰을 1:1로 매핑해주는 역할을 해요.

 디자인 라이브러리KDF
하는 말"이건 버튼입니다.""이건 homepage에 있는 hero.cta-primary입니다."
매핑 구조어디서나 쓰이는 범용 컴포넌트라 덮어쓸 구멍이 너무 많음.하나의 요소는 무조건 하나의 디자인 키와 매핑됨.
추적성요소에서 디자인 룰로 거슬러 올라가는 명확한 맵이 없음.data-kdf 속성 덕에 모든 DOM 노드를 JSON 경로까지 다이렉트로 추적할 수 있음.
어디에 얹어 쓰나요 shadcn · Bootstrap · Chakra · Tailwind · 쌩 CSS · 사내 자체 시스템 — KDF는 이런 도구들을 대체하는 게 아니라 그 위에 얹어 쓰는 겁니다.
04 — 문법 체계

6개의 기호.
하나의 문법.

KDF는 클래스명, 공유 참조(ref), 섹션 순서, CSS 커스텀 속성을 보관합니다. 비즈니스 로직이나 이벤트 핸들러, 데이터 페칭, 권한, 접근성 같은 건 절대 건드리지 않아요.

data-kdf
맵 (The map)

토큰을 쓰는 모든 요소는 그에 맞는 경로를 꼬리표처럼 달고 다닙니다. DOM 노드에서 JSON까지 한 큐에 추적할 수 있어서 스캐너나 테스트, 에이전트가 코드 리뷰할 때 딱이죠.

data-kdf="hero.title"
d(path)
접근자 (The accessor)

경로를 던져주면 그에 맞는 className 문자열로 풀어줍니다. d.css()를 쓰면 CSS 커스텀 속성 객체를 뱉어주고요.

className={d("hero.title")}
@
공유 참조

shared/ 폴더에 있는 재사용 가능한 토큰을 가리킵니다. 참조를 체이닝하거나 추가 클래스로 살을 붙일 수도 있어요.

"@button.cta" shadow-xl
$layout
순서 및 노출

섹션 키 배열입니다. 리스트에 적힌 순서대로 화면에 그려지고, 안 적힌 건 호스트 앱에서 깔끔하게 숨겨줍니다.

["hero", "features", "footer"]
$
컴포넌트 정체성

이 토큰을 렌더링할 컴포넌트가 뭔지, 어떤 variant나 사이즈가 쓰이는지 에이전트와 툴에게 슬쩍 알려주는 메타데이터입니다.

"$": "Button"
css
커스텀 속성

재사용 가능한 클래스로 딱 안 떨어지는 값들은 d.css()를 통해 인라인 스타일 변수로 먹여줍니다.

{ "--kdf-accent": "#4F46E5" }
05 — 아키텍처

다 같이 쓰는 공유 셋업.
페이지별 오버라이드.

디자인 토큰은 딱 두 군데 삽니다. 재사용 가능한 기본값들은 shared/ 에 두고, 페이지별 설정은 필요한 부분만 덮어쓰는 식이죠. 여러분이 쥐고 있는 두 개의 CSS 파일이 첫 페인트와 비상구 역할을 톡톡히 해냅니다.

프로젝트 구조
kdf/
  shared/
    button.json      ← reusable defaults
    card.json
    color.json
    typography.json
  homepage.json      ← page composition
  konde-server.css   ← critical, first paint
  konde.css          ← non-critical tweaks
다단계 캐스케이드

@button.cta 같은 참조는 해당 페이지의 shared/ 에서 먼저 뒤지고, 부모 shared/ 로 올라갔다가 최종적으로 페이지 토큰을 적용합니다. 템플릿은 필요한 것만 쏙쏙 덮어쓰고 나머진 다 물려받는 구조예요.

기본부터 가볍게

디자인 세팅은 서버에서 다 끝납니다. 브라우저는 완성된 클래스 문자열만 쏙 받아가죠. 크리티컬 CSS는 첫 페인트 때 바로 꽂히고 나머지는 천천히 불러오니까 똥컴에서도 렌더링을 갉아먹지 않아요.

직접 통제하는 2개의 CSS 파일 · 2번의 로딩 타이밍
konde-server.css
크리티컬 · 첫 페인트

앱이 가장 먼저 불러오기 때문에 디자인 변수와 FOUC(깜빡임) 방지용 오버라이드가 첫 페인트 때 즉시 반영됩니다. 화면이 깜빡일 틈도 안 주죠.

/* konde-server.css */
:root { --kdf-primary: #1F8F47; }
[data-kdf="hero.slider"] { display: none; }
konde.css
논크리티컬 · 앱 CSS 로딩 후

프레임워크나 앱 CSS가 로딩된 후에 짜잘한 수정이나 테스트, 비상구용으로 덮어쓸 때 씁니다. 당장 페인팅을 막을 필요가 없는 미세 조정용이에요.

/* konde.css */
[data-kdf="hero.title"] { letter-spacing: -0.02em; }
[data-kdf="hero.wrapper"] { gap: 3rem; }

→ KDF는 이 파일들을 처음 한 번만 만들고 다신 건드리지 않아요. 플러그인이 환경변수로 경로를 뚫어주면 앱에서 알아서 임포트하는 식입니다. 제멋대로 코드를 쑤셔넣지 않아요.

06 — 멀티 템플릿

언어를 바꾸듯
디자인을 휙휙 바꿔보세요.

i18n으로 언어를 갈아끼우듯, KDF는 디자인 템플릿을 통째로 갈아끼웁니다. 앱, 컴포넌트, 코드는 건드리지 말고 KDF_DIR 경로만 다른 디자인 폴더로 쓱 틀어주면 전체 룩이 확 달라지죠.

designs/ — 2개의 템플릿, 1개의 앱
designs/
  lander/
    shared/
    homepage.json
  newlander/
    shared/
    homepage.json
// next.config.ts
withKDF({ dir: "./designs/lander" })(nextConfig);
$layout 컴포넌트를 건드리지 않고도 섹션 순서를 바꾸거나 숨길 수 있어요.
@button 공유 토큰 하나로 모든 CTA 버튼 디자인을 한방에 갈아버리죠.
KDF_DIR 호스트 측에서 작정하고 디자인 템플릿 전체를 스위칭할 수 있습니다.
07 — 런타임 API

서버에서 싹 다 해석하고
깔끔한 문자열만 툭 던져줍니다.

KDF 코어는 디스크에서 JSON을 읽어오니 당연히 서버사이드에서 돕니다. Next.js 서버 컴포넌트, Astro 렌더링, Hono 핸들러 같은 곳이요. 거기서 복잡한 클래스 조합을 다 끝내고 클라이언트 컴포넌트엔 그저 깔끔한 문자열 하나 툭 던져주는 식이죠.

서버 컴포넌트
import { getDesign, cn } from "@kondeio/kdf";

const d = getDesign("homepage");

<button data-kdf="hero.cta" className={cn(d("hero.cta"), isActive)}>Start</button>
d("hero.title")

→ 완벽히 해석된 className 문자열

d.css("hero.title")

→ CSS 커스텀 속성 객체

cn · cx · dedupeClasses

조건부 클래스를 하나로 합치고, falsy 값은 쳐내고, 중복은 알아서 빼줍니다. 기본적으론 의미론적(semantic) 간섭 없이 담백하게 동작해요.

cache: auto · always · none

프로덕션에선 빡세게 캐싱하고, 개발 환경에선 파일 수정 시간(mtime)과 사이즈를 보고 그때그때 갱신합니다.

08 — 찰떡 호환성

여러분의 스택 위에
살포시 얹어 쓰세요.

스타일링 — CSS가 아니라 클래스명을 씁니다

TailwindshadcnBootstrapChakraCSS ModulesPlain CSSYour own system

KDF는 CSS 엔진이 아니에요. 여러분의 스타일링 시스템이 알아먹는 클래스 문자열이면 뭐든 토큰에 때려 넣을 수 있죠. 쓰시는 프레임워크가 소스 파일을 스캔하는 방식이라면 JSON도 같이 스캔하라고 @source "../kdf/**/*.json" 처럼 한 줄 쓱 넣어주기만 하면 끝입니다.

런타임 — 서버사이드 자바스크립트

Next.js 테스트 완료 · 플러그인 제공
Astro 테스트 완료
Hono 테스트 완료
시작하기

설치는 딱 한 번.
나머진 init으로 알아서 세팅.

설치할 때 kdf/ 폴더가 없으면 알아서 기본 뼈대를 쫙 깔아줍니다. 이미 짜놓은 파일은 절대 안 덮어쓰니까 걱정 마시고요.

$npm install @kondeio/kdf
$pnpm add @kondeio/kdf
$bun add @kondeio/kdf
$npm exec -- kdf init # 수동으로 스캐폴딩 쳐보기
next.config.ts
import withKDF from "@kondeio/kdf/plugin";

export default withKDF({ dir: "./designs" })(nextConfig);