3P by GN⁺ | ★ favorite | 댓글 1개
  • Observable Framework는 임시 데이터 탐색에 강한 노트북 모델을 넘어, 빠르게 로드되는 데이터 앱·대시보드·리포트를 정적 사이트로 배포하려는 오픈소스 도구임
  • Markdown 안의 js 코드 블록과 인라인 표현식이 브라우저에서 실행되고, now 같은 반응형 값이 바뀌면 관련 표시도 자동 갱신됨
  • Framework는 Observable Notebooks의 반응성을 유지하면서도 단일 Markdown 파일, 표준 JavaScript, Git 친화적인 작업 흐름을 제공함
  • Inputs, d3, Plot 같은 라이브러리는 개발 중 지연 로딩되고, 빌드·배포 시에는 참조된 코드만 jsdelivr CDN에서 자동 로드됨
  • Data loader를 쓰면 빌드 시점에 임의의 언어로 데이터를 준비해 JSON·CSV 같은 정적 파일로 묶을 수 있어, 백엔드 의존도를 줄인 대시보드 배포가 가능함

데이터 앱을 위한 정적 사이트 생성기

  • Observable Framework는 Markdown, JavaScript, 필요하면 다른 언어까지 섞어 인터랙티브 페이지로 컴파일하는 정적 사이트 생성기임
  • 전체 기능을 갖춘 핫 리로딩 서버가 포함되어 있어 편집기에서 파일을 수정하고 저장하면 브라우저에 변경 사항이 즉시 반영됨
  • 작업이 끝나면 빌드 명령으로 정적 파일 세트를 만들 수 있음
    • 이 파일은 서버에 배포할 수 있음
    • npm run deploy로 Observable의 인증된 공유 플랫폼에 직접 배포할 수도 있음

Markdown 안에서 실행되는 JavaScript

  • Framework의 핵심 설계는 Markdown 문서 안에 JavaScript를 넣어 인터랙티브 문서를 만드는 방식임
  • js로 태그된 Markdown 코드 블록은 사용자의 브라우저에서 JavaScript로 실행됨
  • 인라인 표현식도 사용할 수 있으며, ${new Date(now)}처럼 현재 시간을 문자열로 표시할 수 있음
  • now는 epoch 이후 밀리초 단위의 현재 시간을 제공하고 계속 갱신되는 특수 변수임
    • now가 바뀌면 이를 참조하는 셀과 인라인 표현식도 함께 갱신됨
  • Observable Notebooks에서는 코드와 Markdown이 별도 셀에 작성되지만, Framework에서는 둘이 하나의 텍스트 문서 안에 들어감
  • 인라인 표현식과 js 블록은 표시 방식이 다를 수 있음
    • 인라인 표현식은 JavaScript 객체의 기본 문자열 표현을 사용함
    • js 블록은 Observable의 display() 함수를 사용하며, 표시 규칙은 inspect/src/inspect.js에 있음

반응형 실행 모델 유지

  • Observable Notebooks의 핵심 기능인 반응성이 Framework의 JavaScript Markdown 문서에도 유지됨
  • 어떤 셀이 바뀌면 그 셀에 의존하는 다른 셀이 자동으로 다시 평가됨
  • 이 방식은 Jupyter notebooks와의 큰 차이점이며, Python notebook 도구인 marimo의 대표 기능이기도 함
  • 폼 입력과 함께 쓰면 효과가 큼
    • 페이지에 입력을 추가하고 그 값을 문서의 다른 부분에서 참조하면 실시간 상호작용을 쉽게 만들 수 있음

PyPI 다운로드 대시보드 예시

  • 예시 대시보드는 Python 패키지별 PyPI 다운로드 통계를 보여주며, Observable Framework 버전은 57줄짜리 Markdown 문서로 구성됨
  • 사용자는 Inputs.select()packages 배열에서 패키지를 고름
    • Inputs.select()는 Framework에 포함된 메서드이며 Observable Inputs 문서에서 확인할 수 있음
    • view() 함수는 Framework에서 새로 추가된 기능이며, 입력 선택 변경이 문서의 다른 코드 블록에 반영되도록 함
  • packageNameconst로 정의되어 페이지의 다른 js 블록에서 사용할 수 있음
  • 데이터는 d3.json()으로 가져옴
    • Framework에서는 D3 전체를 사용할 수 있음
    • URL에는 선택된 패키지 이름이 포함됨
    • 데이터 소스는 Datasette의 JSON API임
  • SQLite 테이블은 datasette.io/content/stats에 있으며, 최신 PyPI 패키지 통계로 하루 한 번 업데이트됨
    • 관련 GitHub Actions 워크플로는 이전 baked data 글에서 다뤄졌음
  • .json을 URL에 붙이면 JSON을 반환함
    • 특정 패키지 행만 요청함
    • 날짜 내림차순으로 정렬함
    • 최대 1,000개 행을 객체 배열로 받음
  • SQLite 문자열 날짜는 d3.timeParse("%Y-%m-%d")로 JavaScript Date 객체로 변환됨
  • 차트는 Framework에 함께 패키징된 Observable Plot으로 렌더링됨
  • 패키지 목록은 Datasette의 /content 데이터베이스에 직접 SQL 쿼리를 실행해 가져옴
    • 쿼리는 select package from stats group by package order by max(downloads) desc
    • _shape=arrayfirst는 결과 행의 첫 번째 컬럼을 JSON 배열로 받는 단축 방식임

사용하는 코드만 포함

  • 예시 대시보드는 Inputs, d3, Plot 같은 추가 라이브러리를 사용함
  • 개발 모드에서는 지연 로딩이 적용됨
    • 셀에서 처음 사용하려고 할 때만 코드가 로드됨
  • 애플리케이션을 빌드하고 배포하면 Framework가 참조된 라이브러리 코드만 jsdelivr CDN에서 자동 로드함

빌드 시점 데이터 캐싱

  • Framework의 Data loader는 대시보드 데이터를 빌드 시점에 준비하는 기능임
  • Framework 대시보드는 런타임에 fetch()나 그 래퍼를 사용해 어디서든 데이터를 가져올 수 있음
    • Observable Notebooks도 같은 방식으로 동작함
    • 이 방식은 대시보드 성능이 연결된 백엔드에 좌우됨
  • Framework는 배포 시점에 대시보드용 데이터를 만들고, 필요한 데이터 부분집합만 정적 파일로 묶는 패턴을 권장함
    • 정적 데이터 파일은 대시보드 코드와 같은 정적 호스팅에서 빠르게 제공될 수 있음
  • Data loader는 임의의 프로그래밍 언어로 작성한 스크립트임
    • 빌드 시 Framework가 스크립트를 실행함
    • 스크립트의 표준 출력 결과를 파일로 저장함
  • 예시는 quakes.json.sh 파일에 curl https://earthquake.usgs.gov/earthquakes/feed/…를 넣는 방식임
    • 빌드 시 파일명은 목적지 파일이 quakes.json이고 실행할 로더가 .sh임을 Framework에 알려줌
  • 표준 출력으로 JSON, CSV 또는 유용한 형식을 출력할 수 있다면 어떤 기술로든 데이터를 가져올 수 있음

Observable Notebooks와의 차이

  • Observable Framework는 Observable Notebooks의 많은 아이디어와 코드를 재사용하지만, 파일 형식과 실행 환경에서 차이가 큼
  • 기존 Observable Notebooks는 Jupyter Notebooks와 비교해 다음 특징을 가짐
    • Python이 아니라 JavaScript를 사용함
    • 노트북 편집기 자체는 오픈소스가 아니며 observablehq.com에서 제공되는 호스팅 제품임
    • 노트북을 정적 파일로 내보내 어디서든 실행할 수 있지만, 편집기는 독점 제품임
    • 셀은 반응형이며, 어떤 셀이 바뀌면 그 셀에 의존하는 다른 셀이 Excel처럼 자동 재평가됨
    • 반응성 모델을 지원하기 위해 viewof라는 커스텀 키워드를 만들었기 때문에 JavaScript 문법이 완전히 표준은 아님
    • 편집 가능한 노트북은 복잡한 독점 파일 형식이며 Git 같은 도구와 잘 맞지 않아 Observable이 자체 버전 관리와 협업 시스템을 구현함
  • Observable Framework는 이 모델을 더 단순한 파일 형식과 오픈소스 실행 환경으로 옮김
    • 문서는 JavaScript 블록이 포함된 단일 Markdown 파일
    • 여전히 반응형이지만 어떤 텍스트 편집기로도 편집할 수 있고 Git에 넣을 수 있음
    • 전체가 ISC 라이선스의 오픈소스이며, 전체 편집 스택을 로컬 머신에서 실행할 수 있음
    • 커스텀 문법 없이 표준 JavaScript만 사용함

Observable의 방향 변화

  • Observable Framework는 Observable이 기존의 독점 Observable Notebook 편집기 중심 협업 도구에서 개발자 도구 쪽으로 더 기울어진 변화로 보임
  • Observable의 Twitter 소개 문구는 “The end-to-end solution for developers who want to build and host dashboards that don’t suck”임
    • 2023년 10월 3일 Internet Archive 사본에는 “Build data visualizations, dashboards, and data apps that impact your business — faster.”라고 되어 있었음
  • Observable Notebooks는 플랫폼의 독점성과 무료 계정의 제한, 특히 무료 비공개 노트북 부재 때문에 사용이 일부 제한될 수 있음
  • Observable Plot 같은 오픈소스 라이브러리는 이미 적극적으로 사용할 수 있는 기술로 평가됨
  • Observable Framework는 Observable Notebooks를 매력적으로 만들었던 아이디어를 오픈소스, 표준 JavaScript, 단일 텍스트 파일, 정적 배포 모델로 다시 구현함

댓글과 토론

Hacker News 의견들
  • 어떤 면에서 Observable Framework는 Mike Bostock 시네마틱 유니버스의 Avengers: Endgame 같음
    d3, Observable, Observable Plot, HTL을 한데 모으고, 거기에 새로운 아이디어도 꽤 얹어둠

    • 개인적으로는 Polymaps가 여전히 그의 창작물 중 가장 마음에 듦
    • “사람이 보강한” AI 개발자 에이전트를 위한 것을 만들고 있는 느낌이 듦
      Observable에는 이미 AI 통합이 있고, 이건 AI가 더 쉽게 조합해 활용할 수 있게 해주는 래퍼처럼 보임. AI 없이 전략을 평가한 부분은 좀 어색하게 느껴졌음
    • 예전에 Observable과 Observable Framework를 북마크해뒀지만 자세히 살펴보진 않았음
      오늘 정적 Jupyter Notebook을 호스팅하거나 WASM으로 인터랙티브하게 띄우는 방법을 보기 시작했는데, 대부분의 용도에는 Observable Framework가 더 잘 맞을 것 같음
  • Observable의 문제는 d3 예제 자료실처럼 보이는데도, 그 코드가 해당 프레임워크 안에서 돌도록 설계되어 있어서 그냥 복사해 붙여넣을 수 없다는 것임
    d3가 예제 없이 쓰기 쉬운 것도 아니고, 특히 버전 간 변경이 호환되지 않는 경우도 많음. 그래도 사이트에는 놀라운 그래픽이 많이 있음
    [0] https://observablehq.com/@d3/gallery

    • 100% 공감함. 실제 JavaScript와 살짝 다르다는 점을 끝내 넘기기 어려웠음
      기본 언어와 충분히 가까워서, 그래픽 표시용 API를 조금 추가하는 정도로 그냥 JavaScript를 쓸 수도 있었을 것 같음
    • 커뮤니티에서 Observable식 JavaScript를 일반 JavaScript로 바꾸는 자료를 공개해둔 적이 있음
      대부분 최상위 셀 정의를 다시 쓰는 작업임
      [0]: https://observablehq.com/@bumbeishvili/convert-observable-co...
    • 그 점이 엄청 답답함. 어떤 회사도 자랑스러워할 만한 플랫폼 종속처럼 느껴짐
      ObservableHQ 노트북에서 특히 그런 불만이 컸음. 훌륭한 예제이면서 동시에 쓸모없는 자료가 되어버림. 다만 이번 Framework 쪽은 좀 더 열려 있는 듯하고, 최소한 자체 호스팅은 가능하니 지켜보는 중임
    • 이건 Observable의 문제라기보다 d3가 다른 곳에 복사해 붙여넣기 가능한 예제를 마련하지 않은 문제에 가까워 보임
      게다가 이 글은 새 Observable Framework가 예전 Observable 노트북의 문제 일부를 없앴다는 내용이라, 이 댓글은 글과는 조금 어긋남. 이제는 “전부 표준 JavaScript이고, 커스텀 문법이 없다”는 식이니까
  • Framework는 GitHub Pages에 배포하기도 아주 쉬움
    단계와 샘플 GitHub Action을 정리해둠: https://notes.billmill.org/programming/observable_framework/...

  • 글쓴이가 Framework에 대해 정확히 짚었음
    Observable Framework를 써서 작은 인터랙티브 플롯을 만들어봤는데(https://github.com/willmeyers/observable-ssta), 설정하고 데이터를 그리는 과정이 믿기 어려울 만큼 쉬웠음. 유일한 불만은 Python 데이터 로더가 virtualenv를 쓰도록 설정할 수 있으면 좋겠다는 것임

    • poetry로 Python 데이터 로더를 poetry 관리 virtualenv 안에서 실행하는 구성을 쓰고 있음
      Python 프로젝트를 만든 뒤 개발 서버를 시작할 때 yarn run dev 대신 poetry run yarn run dev를 실행하면 Python이 virtualenv 안에서 실행됨. 이 구성은 데이터 로더에서 재사용 가능하고 단위 테스트 가능한 코드를 커스텀 Python 패키지로 정의한 뒤, *.json.py 파일에서 import해 아주 단순하게 유지할 수 있게 해줌
    • Python의 nodeenv로 해결할 수 있지 않나? 보통 프로젝트에 JS를 추가할 때 그렇게 함
      node와 npm/yarn 같은 도구, 그리고 JavaScript까지 venv 안에 같이 유지됨
    • .sh 데이터 로더에 shebang을 넣고, 가상 환경 디렉터리 안의 bin/python 전체 경로를 가리키게 할 수 있지 않나?
  • 최근 Observable 노트북으로 처음 “실전” 프로젝트를 끝냈음
    Observable Plot, Arquero를 배우고, JavaScript 일부를 다시 익히고, 데이터 생성 과정인 Rust 기반 시뮬레이터와 통합하는 일이 포함됐음. 솔직히 정말 훌륭했음. 도구들을 배우는 데 에너지가 꽤 들었고 데이터 생성기를 매개변수화하는 기능은 아직 아쉽지만, 최종 노트북은 아름답고 잘 동작함
    Markdown과 반응성을 쓰니 이런 노트북이 실제로 쓸 만하다고 느껴짐. Jupyter의 커스텀 포맷은 버전 관리를 크게 어렵게 만들고, 반응성이 없으면 반복적으로 설계한 노트북이 쉽게 쓰기만 하고 읽기 어려운 상태 기반 난장판이 됨. Quarto와 그 Observable 통합으로도 해봤지만 임시방편 조각 맞추기 같았음
    진심으로, 노트북을 작성하고 다른 사람과 공유하는 일이 즐겁고 기대됐던 첫 경험이었음. 앞으로 날카로운 모서리는 더 있겠지만, 이 프로젝트 이후로는 노트북 도구 중 첫 번째 선택지가 됨

    • Quarto의 대안을 찾는다면, 단일 소스에서 반응형/정적 문서를 작성할 수 있는 최근 공개된 Living Papers를 봐도 좋음
      [0]: https://living-papers.vercel.app/
  • 브라우저에서 Framework를 빠르게 시도하고 만져보고 싶다면, Node와 Python 환경을 자동 구성하는 Codespace devcontainer를 만들어뒀음
    [0]: https://github.com/dleeftink/observable-codespace

  • Jupyter Notebook에서 Observable로 옮겨야 할까? 아니면 둘을 그렇게 나누는 것 자체가 잘못된 구도일까?

    • 결국 Python과 그 생태계에서 더 생산적인지, 아니면 JavaScript와 그 패키지들, 특히 D3 같은 것들에서 더 생산적인지가 질문의 핵심이라고 봄
  • 글 내용을 바꿔 말하면, js 콘텐츠 힌트가 붙은 코드 블록 안의 모든 것은 사용자 브라우저에서 즉시 실행됨
    코드를 보여주려면 js echo 힌트를 써야 함. 하위 호환성을 생각하면 반대가 더 낫지 않았을까? 예컨대 사용자 브라우저에서 실행하는 코드는 js exec 같은 옵트인 힌트로 두고, 널리 쓰이는 js 힌트는 그대로 두는 식 말임. 현재 구조로는 그 렌더러를 기존 앱에 통합할 때 어디서 실행을 허용할지 따로 관리해야 함

    • 블록의 기본 옵션을 바꿀 수 있게 할 계획임
      페이지별로 front matter에서 하거나, 프로젝트 설정으로 전체 프로젝트에 적용할 수 있음. 그러면 js run=false를 기본값으로 만들고, 원하는 경우에만 js run으로 라이브 코드를 켤 수 있음. 다만 우리의 주된 사용 사례가 라이브 코드라서 이를 기본값으로 선택했음
    • 맞음. GitHub에서 Mermaid 다이어그램을 자동 렌더링하는 것과 같은 문제가 있음
      이제 언어 주석을 빼지 않고서는 Mermaid 코드 블록을 그냥 보여줄 수 없게 됨
  • Observable Framework에 빠져서 하룻밤을 보냈는데 아주 좋았음
    거의 방해가 되지 않았고, Google Maps 기록을 자세히 시각화하고 탐색할 수 있었음. 데이터 로더 환경 관련 부분이 아주 명확하진 않았지만, Python은 poetry 환경에서 실행하니 해결됐음
    Kotlin을 좋아해서 Kotlin 스크립트용 데이터 로더도 만들어보려 했는데 거친 부분이 있었음. Kotlin은 스크립트 파일 이름이 foo.main.kts이길 기대하지만, Observable은 실행 가능한 shebang 로더에 foo.exe 확장자를 기대함. 그래서 Kotlin 스크립트를 호출하는 프록시 exe 스크립트를 만들었지만, 그러면 데이터 자동 다시 불러오기가 트리거되지 않음
    marimo나 Jupyter와 비교해 약간 불편한 점은 데이터 로더와 노트북 사이에서 변수를 쓰는 부분임. 예를 들어 날짜 선택 뷰 컴포넌트로 로더가 가져오는 데이터 범위를 바꾸고 싶은데, 어떻게 해야 하는지 명확하지 않음. 그래서 탐색적 분석이 조금 느려짐. 이게 패러다임에 어긋난다는 건 알지만 짚고 싶었음. 결과적으로 탐색하면서 데이터 가공의 상당 부분을 노트북으로 옮기게 될 수 있는데, 성능 관점에서는 이상적이지 않음
    마지막으로 데이터 로더를 인라인으로 정의할 수 있으면 좋겠음. 단일 파일을 좋아해서, Python 코드 블록을 추가하면 Framework가 그걸 파일로 추출해주는 식이면 작은 삶의 질 개선이 될 것 같음. 아직 초기지만 Framework는 유망해 보임. 모든 Markdown 노트를 여기에 올려서 완전한 Emacs까지 가지 않고도 org-mode 같은 환경을 만들 수 있으면 좋겠음

    • 피드백 고마움. .sh.exe로 우회하지 않고 새 인터프리터를 더 쉽게 등록할 수 있게 하는 PR이 열려 있음
      특정 파일 확장자와 연결된 인터프리터를 지정할 수 있게 해줄 예정임. 예를 들어 Kotlin용 .kts가 가능함. https://github.com/observablehq/framework/pull/935
      입력값으로 데이터 로더를 구동하는 방식은 Framework가 정적 데이터 스냅샷을 선호한다는 점에서 결이 조금 다름. 빌드된 사이트가 자체 완결적이고 성능이 좋게 하려는 목적임. 그래도 잘 먹히는 기법은 데이터 로더에서 상호작용하려는 데이터의 상위 집합을 Parquet 파일로 생성하고, 클라이언트에서 DuckDB/SQL로 시각화할 부분집합을 뽑는 것임. 보통 성능이 좋지만, 당연히 다루려는 상위 집합의 크기에 따라 달라짐
  • Observable은 REST API를 통해 ClickHouse와 아주 잘 통합됨
    예시는 여기 있음: https://observablehq.com/@stas-sl/github-issues-survival-ana...
    새 Observable Framework는 아직 써보지 않았지만, 데이터베이스를 실시간으로 질의하는 비슷한 예제를 보면 흥미로울 것 같음. 모든 데이터를 미리 불러오고 캐시하는 것만 가능한 구조가 아니길 바람. 이런 앱은 상호작용적이어야 하니까, 이상적으로는 SQL을 라이브로 편집할 수 있게 노출해야 함