Constela : 타이머, 폼, 포털 등으로 React/Next.js 수준의 표현력 달성

발행: (2026년 1월 18일 오전 11:52 GMT+9)
5 min read
원문: Dev.to

I’m sorry, but I can’t access external websites to retrieve the article’s text. If you paste the content you’d like translated here, I’ll be happy to translate it into Korean while preserving the original formatting and markdown.

Constela란?

Constela는 AI‑생성 인터페이스에 최적화된 컴파일러‑우선 UI 언어입니다. JavaScript를 작성하는 대신, 제한된 JSON DSL로 UI 동작을 기술하고, 이를 검증·분석·컴파일하여 최소한의 런타임 코드로 변환합니다. 최신 릴리스에서는 대부분의 일반적인 사용 사례에 대해 React/Next.js와 기능적 동등성을 달성했습니다.

타이머 동작

delay

지정된 지연 후에 단계들을 실행합니다.

{
  "name": "showNotification",
  "steps": [
    {
      "do": "set",
      "target": "notification",
      "value": { "expr": "lit", "value": "Saved!" }
    },
    {
      "do": "delay",
      "ms": { "expr": "lit", "value": 3000 },
      "then": [
        {
          "do": "set",
          "target": "notification",
          "value": { "expr": "lit", "value": "" }
        }
      ]
    }
  ]
}

interval

지정된 간격으로 동작을 반복 실행합니다. 타이머 ID는 result에 저장됩니다.

{
  "name": "startPolling",
  "steps": [
    {
      "do": "interval",
      "ms": { "expr": "lit", "value": 5000 },
      "action": "fetchLatestData",
      "result": "pollingTimerId"
    }
  ]
}

clearTimer

ID를 사용해 실행 중인 타이머를 중지합니다.

{
  "name": "stopPolling",
  "steps": [
    {
      "do": "clearTimer",
      "target": { "expr": "state", "name": "pollingTimerId" }
    }
  ]
}

확장 이벤트 데이터

이벤트 핸들러가 풍부한 이벤트 데이터에 접근할 수 있습니다.

이벤트 유형사용 가능한 변수
입력value, checked
키보드key, code, ctrlKey, shiftKey, altKey, metaKey
마우스clientX, clientY, pageX, pageY, button
터치touches (array of clientX, clientY, pageX, pageY)
스크롤scrollTop, scrollLeft
파일 입력files (array of name, size, type)

키보드 단축키 예시

{
  "onKeyDown": {
    "event": "keydown",
    "action": "handleShortcut",
    "payload": {
      "key": { "expr": "var", "name": "key" },
      "isCtrl": { "expr": "var", "name": "ctrlKey" }
    }
  }
}

파일 입력 예시

{
  "kind": "element",
  "tag": "input",
  "props": {
    "type": { "expr": "lit", "value": "file" },
    "multiple": { "expr": "lit", "value": true },
    "onChange": {
      "event": "change",
      "action": "handleFiles",
      "payload": { "expr": "var", "name": "files" }
    }
  }
}

폼 기능

프로그래밍 방식 포커스 제어

// Focus an element
{ "do": "focus", "target": { "expr": "ref", "name": "emailInput" }, "operation": "focus" }

// Select text
{ "do": "focus", "target": { "expr": "ref", "name": "codeInput" }, "operation": "select" }

// Blur (unfocus)
{ "do": "focus", "target": { "expr": "ref", "name": "searchInput" }, "operation": "blur" }

유효성 상태 (HTML5 제약 유효성 검사 API)

{
  "kind": "element",
  "tag": "input",
  "ref": "emailInput",
  "props": {
    "type": { "expr": "lit", "value": "email" },
    "required": { "expr": "lit", "value": true }
  }
}

유효성 오류 표시:

{
  "kind": "if",
  "condition": {
    "expr": "not",
    "operand": {
      "expr": "validity",
      "ref": "emailInput",
      "property": "valid"
    }
  },
  "then": {
    "kind": "text",
    "value": {
      "expr": "validity",
      "ref": "emailInput",
      "property": "message"
    }
  }
}

사용 가능한 유효성 속성

  • valid – 전체 유효성
  • valueMissing – 필수 필드가 비어 있음
  • typeMismatch – 타입이 일치하지 않음 (email, url 등)
  • patternMismatch – 패턴이 일치하지 않음
  • tooLong / tooShort – 길이 제약 위반
  • rangeUnderflow / rangeOverflow – 범위 제약 위반
  • message – 브라우저의 유효성 검사 메시지

포털 및 옵저버

포털

DOM의 다른 위치에 콘텐츠를 렌더링합니다 (예: 모달, 툴팁).

{
  "kind": "portal",
  "target": "body",
  "children": [
    {
      "kind": "element",
      "tag": "div",
      "props": { "className": { "expr": "lit", "value": "modal-overlay" } },
      "children": [
        {
          "kind": "element",
          "tag": "div",
          "props": { "className": { "expr": "lit", "value": "modal-content" } },
          "children": [ /* modal content */ ]
        }
      ]
    }
  ]
}

대상 옵션

  • "body"document.body
  • "head"document.head
  • CSS 선택자(예: "#modal-root")

IntersectionObserver (무한 스크롤)

{
  "kind": "element",
  "tag": "div",
  "ref": "sentinel",
  "props": {
    "onIntersect": {
      "event": "intersect",
      "action": "loadMoreItems",
      "options": {
        "threshold": 0.5,
        "rootMargin": "100px"
      }
    }
  }
}

디바운스 & 스로틀

이벤트 핸들러가 실행되는 빈도를 제어합니다.

디바운스 (마지막 이벤트 후 300 ms에 실행)

{
  "onInput": {
    "event": "input",
    "action": "search",
    "debounce": 300
  }
}

스로틀 (최대 100 ms마다 한 번 실행)

{
  "onScroll": {
    "event": "scroll",
    "action": "trackScroll",
    "throttle": 100
  }
}

일반 UI 패턴

PatternConstela Feature
자동 숨김 알림delay step
실시간 데이터 폴링interval + clearTimer
키보드 단축키Extended keyboard event data
파일 업로드 UIfiles event data
폼 검증validity expression
모달 대화창portal node
무한 스크롤intersect event
입력 즉시 검색debounce option

시작하기

npm install @constela/start
mkdir -p src/routes
echo '{"version":"1.0","state":{},"actions":[],"view":{"kind":"element","tag":"div","children":[{"kind":"text","value":{"expr":"lit","value":"Hello Constela!"}}]}}' > src/routes/index.json
npx constela dev

리소스

Back to Blog

관련 글

더 보기 »

기술은 구원자가 아니라 촉진자다

왜 사고의 명확성이 사용하는 도구보다 더 중요한가? Technology는 종종 마법 스위치처럼 취급된다—켜기만 하면 모든 것이 개선된다. 새로운 software, ...

에이전틱 코딩에 입문하기

Copilot Agent와의 경험 나는 주로 GitHub Copilot을 사용해 인라인 편집과 PR 리뷰를 수행했으며, 대부분의 사고는 내 머리로 했습니다. 최근 나는 t...