리액티브 HTML 노트북
(maxbo.me)- 브라우저가 실행하는 단일 HTML 파일만으로 데이터 탐색, 분석, 시각화, 조판, 배포를 한 문서 안에 묶어 기존 노트북→출판 도구→PDF 흐름을 줄일 수 있음
- 핵심 구현은
style·script를 화면에 드러내는 echo 셀,contenteditable, Observable Runtime을 결합해 편집 가능한 반응형 셀을 만드는 방식임 cell(name, inputs, definition)은 Observable의 의존성 모델을 따라counter, FizzBuzz 렌더링, 숨김 셀, 출력 없는 중간값 셀처럼 서로 연결된 실행 단위를 구성함- 같은 HTML 문서 안에서 Observable Plot, TeX, Markdown, Graphviz, WASM 기반 SQLite, Pyodide 기반 Python, WebR 기반 R, Observable Inputs, Mutable 상태까지 시연함
- 결과물은 별도 플랫폼 없이 브라우저에서 실행되는 과학·기술 문서가 되며, 구현은 이후
@celine/celine라이브러리로 공개됨
단일 HTML 파일로 줄이는 노트북-출판 분리
- HTML은 조판에는 널리 쓰이지만, 데이터 탐색·분석·시각화 플랫폼으로는 상대적으로 덜 활용돼 왔음
- 일반적인 작업 흐름은 여러 도구를 거침
- HTML 파일 하나가 이 세 단계를 맡으면 수동 절차, CLI 도구, CI 단계, 제3자 플랫폼 의존을 줄일 수 있음
- 구현은 Anton Zhiyanov의 In-browser code playgrounds, Cristóbal Sciutto의 Self-modifying HTML notes, Quarto, Observable Framework에서 강한 영향을 받음
편집 가능한 셀 만들기
- 첫 단계는
echoCSS 클래스로style과script요소를 문서 안에 그대로 보이게 하는 방식임 - 문법 강조가 내장된 폰트와
contenteditable속성을 더하면 기본적인 코드 편집기처럼 쓸 수 있음 contenteditable이 적용된script는 포커스를 잃을 때 다시 평가됨- 기존
script를 복제해 새script요소를 만들고 원본을 제거함 eval은import문이 들어간 코드에서 동작하지 않기 때문에 이 방식이 필요함
- 기존
- Observable 기반 실행을 위해 두 라이브러리를 불러옴
- 전역에는
library와cell두 심볼만 노출함
Observable Runtime 기반 반응형 데이터 흐름
cell(name, inputs, definition, observerVisibility)함수는 Observable Runtime의 모듈 스코프를 사용해 셀을 정의함- 같은 이름의 변수가 이미 있으면 기존 변수를 가져오고, 없으면 새 변수를 만들어 정의함
- 이 구조 덕분에
contenteditable셀을 다시 평가하면 새 정의가 연결된 셀로 전파됨
- 이 구조 덕분에
- Observable
Inspector는 셀 출력물을script블록 위에 표시하는 데 사용됨 counter셀은 1초마다 숫자를 내보내는 비동기 제너레이터로 구성됨script의id속성과cell의name인자가 같음- 초기값
i를 바꾼 뒤 포커스를 해제하면 새 값이 반영됨
counter에 의존하는 셀도 만들 수 있음- Hypertext Literal을 불러와
counter값을 FizzBuzz 형태로 렌더링함 htl은 HTML5 파서를 구현하며 자동 이스케이프와 이벤트 리스너, 스타일 객체, DOM 노드 같은 비직렬화 값의 보간을 처리함
- Hypertext Literal을 불러와
보이는 셀, 숨은 셀, 출력 없는 셀
echo클래스를 붙이지 않으면 셀 정의를 숨기면서도 출력은 관찰할 수 있음- 이런 셀은 렌더링 프리미티브로 쓸 수 있음
- 출력 자체를 표시하지 않는 셀 타입도 만들 수 있음
silent(name, inputs, definition)은observerVisibility를 숨김으로 설정해 중간값이나 자료구조 저장에 사용할 수 있음
- 셀은 선언 순서와 무관하게 동작할 수 있음
- 예시에서는
reallyNegative셀이 나중에 선언되는negative셀에 의존함
- 예시에서는
- 같은 셀 값은 더 복잡한 출력에도 활용됨
- Observable Plot을 불러와 숫자 배열을 선 그래프로 그리고,
counter % 40위치에 세로 규칙선을 표시함
- Observable Plot을 불러와 숫자 배열을 선 그래프로 그리고,
문서·시각화 출력의 확장
- 셀은 어떤 DOM 요소든 반환할 수 있음
- TeX, Markdown, Graphviz 데모는 각각 다른 형태의 요소를 반환함
tex셀은span요소를 반환함md셀은table요소를 반환함dot셀은svg요소를 반환함
- 셀은
Promise를 반환하거나Error를 던질 수 있음- Observable
Inspector는 실행 중인 셀 외부div에observablehq--running클래스를 적용함 - 오류 셀에는
observablehq--error클래스를 적용함 - 문서는 이 상태에 맞춰 스타일을 지정함
- Observable
브라우저 안의 SQLite, Python, R
- SQLite 데모는 Chinook sample database를
https://maxbo.me/chinook.db에 올려두고, WASM 기반 SQLite 클라이언트로 쿼리함- SQL 쿼리에
WHERE Milliseconds < 1000000을 추가해 볼 수 있음
- SQL 쿼리에
- Python 데모는 Pyodide CPython WASM 배포판을 사용함
- Pyodide에는 NumPy, Pandas, Matplotlib, scikit-learn, Scipy가 포함됨
- 앞서 만든 플롯을 Matplotlib과 Python
sqlite3모듈로 다시 만듦
- R 데모는 WebR을 사용함
입력과 가변 상태
viewof셀 타입은 Observable Inputs와 함께 쓰기 위해 만듦viewof는 두 개의 반응형 셀을 선언함NAME: 입력의 값viewof NAME: DOM 요소 자체
- 입력을 셀 위에 표시하려면 셀
id를viewof NAME으로 설정함 - 예시에서는
Inputs.range([0, 100], { step: 1 })로 범위 입력을 만들고,rangePlot셀이 해당 값에 따라 갱신됨 - Observable Inputs의 동작 방식은 다소 난해하며, Synchronized Inputs 데모가 도움이 될 수 있음
- 순수 함수형 데이터 흐름만으로 부족한 경우를 위해
mutable헬퍼도 만듦mutable은 값이 변경될 때 새 값을 내보내는Mutable객체를 등록함
라이브러리 공개와 발표용 슬라이드
- 이 구현은 처음에
incel이라는 이름을 생각했지만, 이후celine으로 부르려 했음 - 라이브러리는 @celine/celine으로 공개됨
- 이 문서는 SydJS에서 데모됨
- 문서를 슬라이드쇼로 바꾸기 위한 코드도 포함됨
Shift+N: 슬라이드쇼 시작 또는 다음 슬라이드Shift+B: 이전 슬라이드Shift+E: 슬라이드쇼 종료
- 구현 소스는 source code로 공개돼 있음
댓글과 토론
Hacker News 의견들
-
글의 전제에는 강하게 동의함. HTML은 계산형 노트북의 훌륭한 기반이 될 수 있음
다만 여기서의 구현 선택은 마음에 들지 않음. 동적·반응형 HTML은 이보다 훨씬 더 선언적으로 만들 수 있고, Observable은 멋지지만 표준 JavaScript에서 벗어나는 면이 있음
Heximal이라는 반응형 HTML 시스템을 만들기 시작했는데, 나중에는 노트북 지원도 넣을 예정임. HTML 템플릿과 사용자 정의 요소 기반의 선언형 시스템이고, 그 위에 TC39 Signals 제안 기반의 표현식/반응성 시스템을 얹는 구조임
https://github.com/elematic/heximal
HTMX, Tangle, Curvenote, Polymer를 섞은 느낌에 가깝고, HTML이 원래부터 반응형이었다면 이런 모습일 것 같음. 그래픽 편집과 노트북 사용 사례에 꽤 잘 맞을 것으로 봄- “Observable은 멋지지만 표준 JS에서 벗어난다”는 건 프런트엔드에는 맞지만, 밑단의 런타임은 그냥 JavaScript를 실행함. 소스 코드도 MIT 라이선스의 acorn 파서에 완전히 담긴 사소한 매크로가 붙은 JavaScript에 가까움
그래서 Observablehq에서 일반 JavaScript 디버깅 표현식이 제대로 동작함
https://github.com/observablehq/parser
Observable 런타임을 좋아해서, 프런트엔드 소스와 컴파일된 순수 JS 표현 사이를 양방향 변환할 수 있는 디컴파일러도 만들었음
https://observablehq.com/@tomlarkworthy/observablejs-toolcha... - 여기서 Observable Framework까지 고려한 건지 궁금함. 3월에 나왔고, 주요 특징 중 하나가 Observable Notebooks에서 만든 문법 해킹이 아니라 표준 JavaScript를 쓴다는 점이었음: https://observablehq.com/blog/observable-2-0#a-better-develo...
- “Observable은 멋지지만 표준 JS에서 벗어난다”는 건 프런트엔드에는 맞지만, 밑단의 런타임은 그냥 JavaScript를 실행함. 소스 코드도 MIT 라이선스의 acorn 파서에 완전히 담긴 사소한 매크로가 붙은 JavaScript에 가까움
-
정말 멋짐. 특히 Python과 SQLite 데모가 좋았음. 웹을 통한 편집 루프(TTW)에서 시작해 이를 quine이라고 부른 점이 흥미로운데, 타당하긴 하지만 핵심 약점은 지속성임
TiddlyWiki도 사실상 같은 일을 하지만, 작업 저장이 골칫거리임. 뭔가를 지속하려면 결국 별도의 헤드리스 서버 프로세스가 필요하기 때문임
비슷한 걸 만들고 싶었을 때는[0] 개인적으로 지속성부터 시작했고, 그 말은 당연히 파일을 뜻했음. 프로그래머용 편집기로 편집하고 파일 시스템으로 저장·배포할 수 있는 평문 파일이었음. 이렇게 하면 브라우저 안에 프로그래머용 편집기를 다시 구현해야 하는 문제를 피할 수 있는데, 그건 만만치 않음
대신 빠른 서버를 작성해야 하는 문제가 생기지만, 그건 꽤 재미있는 문제이기도 함[1]. 여기서는 기본 contenteditable 섹션을 써서 그 문제를 피해 갔지만, 지속성 다음으로 큰 문제임
임베디드 스크립트 안에서 export를 할 수 없다는 점도 같은 벽으로 보임. 전역 window 객체를 어지럽히지 않으려면 이런 기능이 있으면 좋겠고, 내 사용 사례에서는 코드 생성도 더 쉬워질 것임
0 - Literate Markdown: https://simpatico.io/lit.md
1 - Reflector, 마크다운을 변환·압축·캐시하고 파일 감시 무효화를 하는 작은 node 서버: https://simpatico.io/reflector -
쏟은 생각과 노력은 존중하지만, 이 접근의 사용성은 끔찍함. 탐색적 데이터 분석을 하려는 중에 왜 스타일링 요소까지 신경 써야 하는지 모르겠음
Jupyter 노트북 같은 도구가 잘 먹히는 이유가 바로 여기에 있음. 그래도 호기심을 갖고 대안적 아이디어를 구현한 점은 높이 평가함- 작성자: 나도 안타깝지만 이 방식의 사용성이 끔찍하다는 데 동의하고, 그래서 @celine/celine을 만든 동기 중 상당 부분이 여기에 있었음 (https://maxbo.me/celine, 즉 글을 라이브러리로 패키징한 것)
탐색적 데이터 분석 플랫폼으로는 아직 충분하지 않음. Observable Notebooks 같은 완전한 웹 코드 편집기의 즉각적 반응성도 없고, 파일 감시 기반 핫 리로딩을 제공하는 Observable Framework도 아님. Deno + VSCode용 새 Jupyter 커널도 꽤 매끄러운 경험을 줌
그래서 탐색 분석용 사용성은 음… 나쁘다는 데 동의하지만, 배포 사용성은 그렇게 나쁘지 않다고 봄. 사실 좋음. 그냥 단일 파일이기 때문임. 누군가에게 그래프와 데이터 가공 결과를 보내려고 거대한 도구 체인을 유지하거나 제3자 서비스에 돈을 낼 필요 없이, HTML 파일 하나를 Slack으로 던지거나 어딘가에 호스팅하면 됨
분석 결과를 스타일링할 수 있는 유연성 덕분에 블로그나 연구 논문처럼 스타일이 중요한 환경에도 게시할 수 있음 - 얼핏 보기에는 이걸 “탐색적 DOM 분석” 쪽으로 포지셔닝하려는 것 같음. 소개 데모는 구조와 기본 요소를 만들어가며 작업한다는 점에서 Scheme/Smalltalk 느낌이 나고,
ivory를red로 바꾸자 구문 강조가 실시간으로 바뀌는 장면이 그걸 보여줌
여기까지 오게 된 배경은 이해하지만, JavaScript 프레임워크와 라이브러리를 브라우저에서 더 쉽게 가지고 놀 수 없다는 건 아쉬움. 결국 그냥 JS인데, 가벼운 환경에서 빠르게 실험할 수 있어야 함. 이 접근은 그 점에서 강점이 있음
HTML/JS의 장난스럽고 실험적인 가능성을 다시 가져오는 방식임. TS, rollup, webpack 같은 것보다 이런 시도가 더 많았으면 함
추가: 조금 더 읽어 보니 이걸 데이터 분석용으로 제안하고 있음. 그 용도에는 잘 맞지 않는다고 봄 - “탐색적 데이터 분석만 하려는 중”이라지만, 핵심은 탐색적 데이터 분석을 하고 그 결과를 쉽게 게시할 수 있는 통합 플랫폼을 갖자는 것 아닌가 싶음. 버릴 Jupyter 노트북용은 아님
버리고 끝나지 않는 Jupyter 노트북 대안에는 큰 가치가 있다고 봄. 지금 사용자 경험이 정말 형편없는 건 개선할 수 있음. 문제는 이 구성이 Jupyter 노트북의 JSON 혼돈보다 나은가인데, 개인적으로는 그렇다고 보는 쪽임
- 작성자: 나도 안타깝지만 이 방식의 사용성이 끔찍하다는 데 동의하고, 그래서 @celine/celine을 만든 동기 중 상당 부분이 여기에 있었음 (https://maxbo.me/celine, 즉 글을 라이브러리로 패키징한 것)
-
Lit의 원 작성자가 최근 Google을 떠났고, 알기로는 이와 매우 비슷한 것을 작업 중임
https://github.com/elematic/heximal- 그가 이후 작업 중인 걸 공개한 줄 몰랐음. 알려줘서 고마움
-
이 글의 형식이 마음에 듦. 밑바닥부터 시작해서 흥미로운 무언가까지 쌓아 올림. 의존성 덩어리나 여기저기 프레임워크를 붙이는 과장된 흐름이 아님
따라가려면 제시된 조각들을 복사해 붙여넣고 단계별로 어떻게 동작하는지 이해하면 될 것 같음. 내가 이런 걸 쓴다면 org-mode의 리터레이트 프로그래밍으로 작성한 뒤 HTML로 내보내 블로그 글로 만들었을 것 같음
다만 내장 구문 강조가 있는 글꼴은 여전히 의문임 -
반응형 HTML이라고 하지만 거의 전부 JavaScript 아닌가, 아니면 뭔가를 오해한 건가?
- 상호작용/반응형 부분은 JavaScript지만, 노트북은 자기완결 HTML 파일 안에 구현되어 있음. 문서 구조부터 편집과 표시까지 전부 처리함
브라우저를 제외하면 제3자 소프트웨어에 의존하지 않고, HTML이 그렇듯 오래가고 이식 가능하며 쓰기 쉬움 - “왜 다들 브라우저에서 노트북을 돌리려고 Colab이나 Observable을 쓰지? 아무튼 어려운 부분에는 Observable을 쓰는 대안을 내놓겠습니다”라는 느낌임
- 상호작용/반응형 부분은 JavaScript지만, 노트북은 자기완결 HTML 파일 안에 구현되어 있음. 문서 구조부터 편집과 표시까지 전부 처리함
-
곧 이런 종류의 HTML 노트북을 Raku에서, 또는 Raku용으로 지원해 보려 함
현재 Raku의 “노트북 솔루션”은 Jupyter 기반이거나 Mathematica 기반임 -
이 과정에 웹 기술을 써서 복잡하게 만드는 논리를 잘 모르겠음. 추가된 복잡성이 무엇을 해결하는가? 오히려 과정을 느리게 만들고, 자동 완성·스니펫·확장 같은 필요한 기능을 없애는 쪽에 가까움
게다가 다른 사람들이 이 설정에 익숙하지 않을 가능성이 높아서 협업도 더 어려워짐
환경은 한 번만 설정해 두고, 기술이 변함에 따라 작은 개선만 더하면서 계속 쓰면 됨
n/vim 사용자라면 다음으로 충분히 할 수 있음. 1- 데이터 탐색: https://github.com/untitled-ai/jupyter_ascending를 써서 텍스트를 Jupyter 노트북으로 연결함. jupytext를 사용하므로 텍스트 편집기에서 효율적으로 코드를 편집하고 실행할 수 있음
2- 글쓰기: LaTeX용으로 https://github.com/lervag/vimtex를 쓸 수 있음
여기에 tmux와 tmuxp를 얹으면 프로젝트를 즉시 열 수 있음. Emacs에서는 org mode[1][2] 및/또는 Auctex로 매끄러운 프로세스를 만들 수 있음
[1] https://sqrtminusone.xyz/posts/2021-05-01-org-python/
[2] https://martibosch.github.io/jupyter-emacs-universe/- 브라우저에서 Jupyter 노트북을 켜면 어떤 기술을 쓰고 있는 건가? OP의 웹사이트를 열면 어떤 기술을 쓰고 있는 건가?
- 대체로 좋은 지적임
다만 Python을 그다지 좋아하지 않아서, Python 중심이 아닌 도구를 쓰고 싶음. 그래서 Raku와 Mathematica를 이야기한 것임
참고로 https://github.com/imbue-ai/jupyter_ascending를 알려줘서 고마움 - 동의함. 반응형 노트북을 원하는 사람은 Emacs를 진지하게 고려해야 함. Emacs를 몇 가지 용도로만 쓰는 사용자도 많은데, 반응형 노트북이 그중 하나인 경우가 자주 있음
-
이 글의 전제, 즉 HTML이 과학 글쓰기 배포 매체로 충분히 활용되지 못하고 있다는 데 대체로 동의함
이와 관련된 Will Crichton의 최근 작업이 있음
https://willcrichton.net/nota/
https://willcrichton.net/notes/portable-epubs/ -
요즘 노트북이 뜨겁긴 함. 우리도 TypeScript 노트북 버전을 출시했지만[1], 트레이드오프에서 꽤 다른 쪽을 택했음
우리는 백엔드 node 코드를 실행하고 싶어서, 이 글의 방식이나 Observable과 달리 브라우저 환경에서 실행하려 하지 않음. 그래도 많은 애플리케이션에는 이 아이디어가 더 나은 해법일 수 있음
작성자에게 박수를 보냄
https://github.com/srcbookdev/srcbook