brief.ing.gg

BRIEFING

Tidbyt의 시계는 왜 정확하기 어렵고 Starlark는 왜 실시간 UI에 어울리지 않는가

결론

Tidbyt에서 시계가 실제 시간과 약간 어긋날 수 있는 핵심 이유는 Starlark 자체의 시간 정확도 문제가 아니라, Pixlet 앱이 현재 시각을 읽어 화면을 계속 갱신하는 장기 실행 프로그램이 아니라 그 시점의 화면을 WebP/GIF로 렌더링하는 구조이기 때문이다.

Starlark 역시 본래 이런 실시간 UI를 위해 설계된 언어가 아니다. Starlark는 Bazel의 빌드 정의 언어에서 출발했으며, 짧게 실행되고 외부 부작용을 제한하며 같은 입력에 같은 결과를 내는 결정적·hermetic한 임베디드 언어를 지향한다. Pixlet은 여기에 HTTP, 시간, 렌더링 위젯 같은 호스트 기능을 추가해 디스플레이 앱 DSL로 사용한다.

따라서 Tidbyt의 선택은 날씨·주가·캘린더처럼 주기적으로 새 스냅샷을 만드는 콘텐츠에는 잘 맞지만, 시계·타이머처럼 시간의 연속적인 흐름 자체가 상태인 UI에는 구조적으로 어색하다.

1. Tidbyt/Pixlet의 실행 모델

Pixlet 앱은 대략 다음 순서로 동작한다.

Starlark applet 실행
        ↓
HTTP / time 등 데이터 읽기
        ↓
render 위젯 트리 생성
        ↓
WebP 또는 GIF 애니메이션 렌더링
        ↓
Tidbyt로 전달
        ↓
이미 만들어진 프레임 표시

공식 Pixlet 문서도 pixlet render가 Starlark 코드를 실행해 WebP를 만들고, pixlet push가 그 WebP를 장치로 전달한다고 설명한다. 앱을 계속 최신 상태로 유지하려면 이 과정을 반복해야 한다.

즉 Tidbyt 장치가 일반적인 GUI 앱처럼 Starlark 프로그램을 계속 실행하면서 상태를 갱신하는 것이 아니다.

2. 시계에서는 왜 문제가 드러나는가

Pixlet의 공식 시계 예제는 다음과 같은 형태다.

now = time.now().in_location(timezone)

return render.Animation(
    children = [
        render.Text(content = now.format("3:04 PM")),
        render.Text(content = now.format("3 04 PM")),
    ],
)

여기서 두 프레임은 콜론을 깜빡이게 하지만, 두 프레임 모두 같은 **now**에서 만들어진다.

예를 들어 12:34:48에 렌더링했다면 생성된 애니메이션은 12:34라는 값을 가진다. 이후 장치가 그 애니메이션을 표시하는 동안 실제 시간이 12:35로 넘어가도 이미지 자체가 12:35를 계산하지는 않는다.

다음 렌더링이 수행되고 새 이미지가 전달되어야 숫자가 바뀐다.

따라서 화면에 보이는 시각은 대략 다음 세 시점의 영향을 받는다.

렌더링 시각
  + 서버 스케줄링 지연
  + 네트워크/전달 지연
  + 장치의 실제 표시 시점

렌더링이 분 경계와 정확히 동기화되지 않으면 사람 눈에는 한동안 1분 느린 시계처럼 보일 수 있다.

이것은 RTC나 NTP가 부정확해서 생기는 문제가 아니라 snapshot rendering과 실시간 clock semantics의 불일치다.

3. Starlark는 원래 어떤 언어인가

Starlark 공식 명세는 이 언어를 Python과 유사한 configuration language로 정의한다.

핵심 설계 특성은 다음과 같다.

  • 짧게 실행되는 프로그램
  • 결정적(deterministic) 실행
  • 기본적으로 외부 부작용이 없는 hermetic 실행
  • 제한된 언어 기능
  • 호스트 애플리케이션에 임베드되는 구조
  • 프로그램 결과로 구조화된 데이터나 호스트 측 효과를 생성

공식 설명은 Starlark가 일반 애플리케이션을 작성하기보다는 configuration을 표현하기 위한 언어이며, 실행이 유한하도록 설계됐다고 명시한다.

특히 기본 Starlark는 파일 시스템, 네트워크, 시스템 시계에 접근할 수 없다. Pixlet의 time.star, HTTP 기능 등은 Pixlet 호스트가 Starlark에 추가로 노출한 API다.

따라서 “Starlark는 시간을 읽을 수 없는 언어”라고 말하는 것은 부정확하다. 더 정확한 표현은 다음과 같다.

Starlark의 기본 실행 모델은 장기 실행 event loop나 timer-driven UI가 아니라, 호스트가 호출하면 제한된 계산을 수행하고 결과를 반환하는 임베디드 언어에 가깝다.

4. 그럼 Tidbyt가 Starlark를 선택한 것은 잘못인가

그렇지는 않다.

Tidbyt의 일반적인 콘텐츠에는 오히려 상당히 잘 맞는다.

콘텐츠서버 스냅샷 렌더링 적합성
날씨높음
주가·암호화폐 가격높음
스포츠 결과높음
RSS·뉴스높음
캘린더 일정높음
단순 애니메이션높음
분 단위 정확한 시계낮음
초 단위 시계매우 낮음
스톱워치·카운트다운매우 낮음
사용자 입력이 있는 인터랙티브 UI매우 낮음

Starlark는 제3자 코드를 제한된 환경에서 실행하기 쉽고, Pixlet은 그 결과를 작은 LED 디스플레이용 위젯 트리로 변환할 수 있다.

즉 **“데이터를 가져와 작은 화면 한 장을 만든다”**라는 문제에는 잘 맞는다.

문제는 시계가 같은 종류의 콘텐츠가 아니라는 점이다.

5. 시계에 더 자연스러운 설계

가장 깔끔한 방법은 서버와 장치의 역할을 분리하는 것이다.

방법 A: 장치가 시간을 직접 렌더링

서버
 └─ timezone, font, layout 전달

Tidbyt
 ├─ NTP / local clock
 ├─ 현재 시각 계산
 └─ 매 분 또는 매 초 직접 렌더링

이 방식이면 서버의 렌더링 및 전송 지연과 시계 정확도가 분리된다.

방법 B: semantic widget을 장치에 제공

서버가 픽셀 결과가 아니라 다음과 같은 의미 정보를 전달한다.

ClockWidget
├─ timezone: Asia/Seoul
├─ format: HH:mm
├─ font: ...
└─ position: ...

장치는 ClockWidget의 의미를 알고 로컬 시각으로 계속 다시 그린다.

방법 C: 하이브리드

날씨나 뉴스처럼 자주 변하지 않는 콘텐츠는 서버에서 WebP로 렌더링하고, 시계·타이머 같은 실시간 요소만 장치의 native primitive로 처리한다.

Tidbyt 같은 장치에는 이 방식이 가장 합리적이다.

6. 더 일반적인 설계 원칙

이 사례에서 중요한 것은 언어 선택 자체보다 실행 모델과 데이터의 시간적 특성이 맞는가다.

Snapshot model이 적합한 데이터

  • 특정 시점의 상태를 보여주면 충분함
  • 수초~수분의 지연이 허용됨
  • 서버에서 미리 렌더링하기 쉬움
  • 장치 로직을 최소화하고 싶음

Stateful / local runtime이 필요한 데이터

  • 시간이 흐르면서 자체적으로 값이 변함
  • 사용자 입력에 즉시 반응해야 함
  • 정확한 timer나 animation timing이 필요함
  • 네트워크 지연과 독립적으로 동작해야 함

시계는 대표적으로 두 번째 부류다.

따라서 이 문제를 단순히 “Starlark가 시계에 안 맞는다”고 표현하기보다는 다음처럼 정리하는 것이 정확하다.

Starlark는 Tidbyt의 서버 측 sandboxed applet DSL로서는 합리적인 선택이지만, Pixlet의 snapshot rendering 모델을 실시간 시계까지 동일하게 적용하면 시간의 연속성을 장치가 아니라 외부 재렌더링 스케줄러에 의존하게 된다.

설계상 시사점

임베디드 디스플레이나 사이니지 플랫폼을 설계할 때는 모든 UI를 동일한 렌더링 경로로 처리하기보다 다음 두 계층을 구분하는 편이 좋다.

  1. Snapshot content: 서버가 완성된 화면을 생성
  2. Live primitives: 장치가 시간·타이머·상태 변화를 로컬에서 계산

이렇게 하면 서버 렌더링의 단순성과 보안성을 유지하면서도 실시간 요소의 정확성을 확보할 수 있다.

출처

  • Tidbyt, Pixlet repository — Pixlet 실행 모델, WebP/GIF 렌더링, Clock 예제, 반복 render/push 필요성: https://github.com/tidbyt/pixlet
  • Starlark Language Specification — configuration language, short-lived execution, determinism, hermeticity: https://github.com/bazelbuild/starlark/blob/master/spec.md
  • Starlark README — 설계 원칙과 임베디드 언어 모델: https://github.com/bazelbuild/starlark
  • Starlark Language Design — configuration language를 목표로 한 설계 배경: https://github.com/bazelbuild/starlark/blob/master/design.md