SSOT Template Markup Language — 프레임워크 독립적인 UI 선언.

프론트엔드 코드는 두 가지를 섞는다: 당신의 결정프레임워크 배선. 어떤 API를 호출할지, 어떤 필드를 보여줄지, 어떤 순서로, 어떤 조건에서 — 이 결정들이 React 훅이나 Vue 컴포저블 안에 매몰된다. 프레임워크를 바꾸거나 리팩토링하면 결정이 재해석되거나 유실된다.

STML은 이를 분리한다. 결정은 data-* 속성이 붙은 표준 HTML에 남고, 프레임워크 배선은 생성하거나 위임한다.

GitHub 저장소

구조

┌─────────────────────────────────┐
│  STML (specs/)                  │  당신의 결정. 프레임워크 독립.
│  무엇을 가져오고, 보여주고,     │  어떤 리라이트도 살아남는다.
│  제출할지                       │
├─────────────────────────────────┤
│  코드 생성 / LLM                │  프레임워크 배선 생성.
│  React, Vue, Svelte, 무엇이든  │  교체 가능.
├─────────────────────────────────┤
│  런타임 (artifacts/)            │  React TSX, Vue SFC 등.
│  훅, 상태, 렌더링               │  생성물. 편집하지 않는다.
└─────────────────────────────────┘

보존되는 것

STML HTML의 모든 것이 사용자 결정이다:

  • 어떤 API 엔드포인트를 호출할지 (data-fetch, data-action)
  • 어떤 필드를 표시하거나 수집할지 (data-bind, data-field)
  • 어떤 순서로 요소가 나타날지 (DOM 구조)
  • 어떻게 보일지 (Tailwind 클래스, HTML 태그)
  • 어떤 텍스트를 사용자가 볼지 (제목, 플레이스홀더, 버튼 레이블)
  • 어떤 조건이 가시성을 제어할지 (data-state)
  • 어떤 컴포넌트가 특수 UX를 처리할지 (data-component)
  • 리스트 동작 — 페이지네이션, 정렬, 필터링

React도 아니고 Vue도 아니다. 페이지가 무엇을 하는지, 표준 HTML5로 선언한 것이다.

예시

<main class="max-w-4xl mx-auto p-6">
  <h1 class="text-2xl font-bold mb-6">내 예약</h1>

  <section data-fetch="ListMyReservations"
           data-paginate data-sort="StartAt:desc" data-filter="Status">
    <ul data-each="reservations" class="space-y-3">
      <li class="flex justify-between p-4 border rounded">
        <span data-bind="RoomID" class="font-semibold"></span>
        <span data-bind="Status" class="text-sm text-gray-500"></span>
      </li>
    </ul>
    <p data-state="reservations.empty" class="text-gray-400">예약이 없습니다</p>
  </section>

  <div data-action="CreateReservation">
    <input data-field="RoomID" type="number" placeholder="방 번호" />
    <button type="submit">예약</button>
  </div>
</main>

같은 파일에서 다른 타겟을 생성할 수 있다:

타겟방법
React TSXstml gen (내장 결정적 코드 생성)
Vue SFCSTML + OpenAPI를 LLM에게: “Vue로 구현해”
SvelteSTML + OpenAPI를 LLM에게: “Svelte로 구현해”
FlutterSTML + OpenAPI를 LLM에게: “Flutter로 구현해”

결정은 보존된다. 프레임워크 배선만 바뀐다.

검증

내장 검증기가 코드 생성 전에 STML을 OpenAPI 대비 교차 검증한다:

  • operationId가 존재하는가? HTTP 메서드가 맞는가?
  • 요청 필드, 응답 필드, 파라미터가 스키마와 일치하는가?
  • 정렬/필터/포함 컬럼이 허용 목록 안에 있는가?
  • 참조된 컴포넌트가 존재하는가?

프론트엔드-API 불일치를 런타임이 아닌 CI 시점에 잡는다.

stml validate specs/my-project    # OpenAPI 대비 12가지 심볼릭 검사

data-* 속성

속성선언하는 것
data-fetchGET 엔드포인트에서 데이터를 로드
data-actionPOST/PUT/DELETE 엔드포인트에 데이터를 제출
data-field요청 본문 필드를 수집하는 입력
data-bind응답 필드를 표시하는 요소
data-param-*경로/쿼리 파라미터가 필요한 작업
data-each배열 필드를 반복하는 컨테이너
data-state조건부로 표시되는 요소
data-component커스텀 컴포넌트에 위임하는 요소
data-paginate페이지네이션되는 리스트
data-sort정렬 가능한 리스트 (기본 컬럼과 방향)
data-filter필터 가능한 리스트 (어떤 컬럼)
data-include관련 리소스를 포함하는 쿼리

라이선스

MIT — GitHub