본문으로 건너뛰기

© 2026 Molayo

Simon헤드라인2026. 06. 19. 09:03

Datasette Apps: Datasette 내부에서 커스텀 HTML 애플리케이션 호스팅하기

요약

Datasette 프로젝트에서 커스텀 HTML 애플리케이션을 호스팅할 수 있는 새로운 플러그인인 datasette-apps를 출시했습니다. 이 플러그인은 샌드박스된 iframe 환경에서 JavaScript 앱이 Datasette 데이터에 안전하게 접근하여 읽기 및 쓰기 쿼리를 실행할 수 있도록 지원합니다.

핵심 포인트

  • datasette-apps 플러그인을 통한 커스텀 HTML/JS 앱 호스팅 지원
  • iframe sandbox를 활용하여 쿠키 및 localStorage 접근을 차단하는 보안 강화
  • CSP 헤더를 통해 외부 호스트로의 데이터 유출 방지
  • JavaScript를 이용한 Datasette 데이터의 읽기 전용 및 저장된 쿼리 기반 쓰기 기능 제공

Datasette Apps: Datasette 내부에서 커스텀 HTML 애플리케이션 호스팅하기

2026년 6월 18일

오늘 우리는 Datasette 프로젝트 블로그의 출시 발표 포스트와 함께, Datasette을 위한 새로운 플러그인인 datasette-apps를 출시했습니다. 해당 포스트에는 '무엇(what)'에 대한 내용이 담겨 있지만, 저는 여기서 '왜(why)'에 대해 조금 더 자세히 설명하고자 합니다.

요약 (TL;DR)

Datasette Apps는 여러분의 Datasette 애플리케이션에서 호스팅되는, 엄격하게 제한된 <iframe sandbox> 내에서 실행되는 독립적인 HTML+JavaScript 애플리케이션입니다. 이 앱들은 JavaScript를 사용하여 Datasette의 데이터에 대해 읽기 전용 SQL 쿼리를 실행할 수 있으며, 저장된 쿼리(stored queries)를 설정하면 쓰기 쿼리도 실행할 수 있습니다.

다음은 매우 간단한 예시와 더 복잡한 커스텀 타임라인 예시입니다. 후자는 다음과 같이 보입니다:

Screenshot of a web app titled "Datasette timeline" with "All apps", "Edit app", and "Pin" buttons top-right and a "Full screen" button below them. Inside a bordered panel, the heading "Datasette timeline" sits above a search box reading "Search news, blog posts and releases…" with three checked checkboxes labeled News, Blog, and Releases. Below, text reads "Showing 200 of 1,953 items", followed by a scrollable list of timeline entries. Each entry has a colored tag (blue "BLOG" or green "RELEASE"), a date, a blue linked title, and a paragraph of description. The visible entries are a "BLOG" post dated 2026-06-11 titled "Datasette 1.0a33 with JSON extras in the API", a "RELEASE" dated 2026-06-11 titled "datasette 1.0a33", and a "RELEASE" dated 2026-06-09 titled "llm 0.32a3", each with body text and a "▶ Show more" toggle. A separate panel at the bottom shows a collapsed "▶ 2 log entries" toggle.

앱은 JavaScript를 실행하고 HTML 및 CSS를 렌더링할 수 있는 권한이 있습니다. 하지만 접근 권한 측면에서는 제한됩니다. 앱이 실행되는 <iframe sandbox="allow-scripts allow-forms">는 앱이 쿠키나 localStorage에 접근하는 것을 방지하며, 또한 (이 연구 덕분에) 주입된 CSP 헤더가 있어 외부 호스트로 HTTP 요청을 보내는 것을 차단합니다. 이를 통해 악성 또는 버그가 있는 앱이 개인 데이터를 유출하는 것을 방지합니다.

Datasette Apps는 처음에 Datasette Agent를 위한 Claude Artifacts 메커니즘을 구축하려는 저의 시도로 시작되었지만, 샌드박스(sandboxed) 패턴이 단순히 인터페이스 표면에 커스텀 앱을 추가하는 것 이상의 훨씬 더 흥미로운 용도가 있다는 것을 빠르게 깨달았고, 이를 Datasette 생태계 내의 독자적인 최상위 개념으로 격상시켰습니다.

또한, 이는 제가 수년간 진행해 온 '바이브 코딩(vibe-coded)' HTML 도구 실험을 제 메인 프로젝트의 핵심 기능으로 전환하는 즐거운 방법이기도 합니다!

agent.datasette.io 데모 인스턴스에 GitHub로 로그인하여 Datasette Apps를 직접 체험해 보실 수 있습니다.

이것을 왜 만드는가?

첫 번째 릴리스 이후로, Datasette은 JSON API를 통해 커스텀 HTML 앱을 생성할 수 있는 유연한 백엔드를 제공해 왔습니다.

제가 Eventbrite에서 근무할 때 진행했던 초기 Datasette 프로젝트 중 하나는 문서용 내부 검색 엔진이었습니다. 이 프로젝트는 cron에 따라 서로 다른 시스템의 문서들을 SQLite로 가져온 다음, Datasette API를 직접 쿼리하는 커스텀 HTML+JavaScript 검색 인터페이스를 갖춘 Datasette 인스턴스를 통해 서비스를 제공하는 방식으로 작동했습니다.

저는 클라이언트 측 JavaScript가 SQL 쿼리를 생성하도록 만들었는데, 이는 원래 엔지니어링 유머(engineering joke)로 의도한 것이었으나 앱을 반복적으로 개선하는 데 정말 생산적인 방법임이 밝혀졌습니다!

이 프로젝트는 제가 HTML 도구 컬렉션을 구축한 경험 및 Claude Artifacts를 활용한 실험들과 결합되어, 독립적인 HTML 프론트엔드에 Datasette 스타일의 백엔드를 추가하는 것이 놀라울 정도로 강력한 조합이라는 확신을 주었습니다.

Claude Artifacts가 영구적인 관계형 데이터베이스 (relational database)에 접근할 수 있다면 얼마나 더 유용해질지 상상해 보세요. 그것이 바로 제가 Datasette Apps를 통해 만들고 있는 것입니다!

Datasette Apps의 멋진 아이디어들

이것을 구축하면서 제가 파악한 아이디어와 패턴 중 지속 가능성이 있다고 생각되는 몇 가지를 소개합니다.

<iframe sandbox="allow-scripts" srcdoc="...">

  • <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data: blob:;">

이것은 애초에 Datasette Apps를 가능하게 만드는 마법 같은 조합입니다. 저는 매우 민감한 도메인—인증된 Datasette 인스턴스는 온갖 종류의 개인 데이터를 포함할 수 있습니다—에서 신뢰할 수 없는 HTML과 JavaScript를 실행해야 합니다. sandbox= 속성을 사용하면 해당 신뢰할 수 없는 코드를 부모 애플리케이션과 상호 작용할 수 없는 방식으로 실행할 수 있습니다. 즉, DOM을 읽거나, 쿠키에 접근하거나, localStorage에서 비밀 정보를 훔칠 수 없습니다. 하지만 fetch()와 그 친구들을 사용하여 다른 도메인에서 콘텐츠를 로드하거나 (또는 데이터를 유출할) 수는 있습니다. 하지만... HTML 페이지를 <meta http-equiv="Content-Security-Policy">시작하면...

헤더를 설정하면 다른 도메인에 대한 액세스를 차단하는 추가 정책을 설정할 수 있습니다. 악성 JavaScript가 해당 헤더를 업데이트하거나 제거할 수 있을까 봐 걱정했지만, 결과적으로 그것은 불가능하다는 것이 밝혀졌습니다. 한 번 설정되면, 해당 프레임의 콘텐츠에 대한 CSP (Content-Security-Policy) 정책은 불변(immutable)입니다.

postMessage()를 통한 API 제한

MessageChannel()

해당 iframe들을 아무런 흥미로운 작업도 할 수 없을 정도로 강력하게 제한하고 나니, 다음 과제는 지정된 데이터베이스에 대한 읽기 전용 SQL 쿼리를 시작으로, 허용 목록(allow-list)에 있는 작업들만 실행할 수 있도록 다시 통로를 열어주는 것이었습니다.

저는 이 기능의 첫 번째 버전을 postMessage()를 사용하여 구축했습니다. 이는 자식 iframe이 부모 창(parent window)으로 메시지를 보낼 수 있게 해줍니다. 저는 부모가 SQL 쿼리를 실행하도록 요청하는 간단한 프로토콜을 만들었습니다. 그러면 부모는 쿼리를 실행하기 전에 그것이 허용 목록에 있는 데이터베이스에 대한 것인지 확인할 수 있습니다.

LLM 도구 중 하나(제 생각에는 GPT-5.5였습니다)는 만약 iframe이 신뢰할 수 없는 도메인으로부터 추가 코드를 로드하게 될 경우, postMessage() 자체만으로는 취약점이 노출될 수 있다고 제안했습니다. 저는 이것이 Datasette Apps에는 해당되지 않는다고 생각하지만, 심층 방어 (defense in depth) 원칙을 믿기 때문에 GPT-5.5의 도움을 받아 MessageChannel() 기반의 전송 방식으로 포팅했습니다.

MessageChannel()은 페이지가 다른 곳으로 이동할 경우 채널이 자동으로 닫히기 때문에, 신뢰할 수 없는 외부 페이지에서 전송된 명령을 실행할 가능성을 완전히 제거한다는 장점이 있습니다.

쿼리 및 오류를 위한 가시적인 로그

타임라인 데모로 이동하여 usercontent 문자열을 검색하면, user-images.githubusercontent.com 도메인에서 이미지를 임베드하는 검색 결과가 나타납니다. 이 도메인은 CSP 허용 목록에 없으므로 오류가 발생합니다.

이러한 오류들은 캡처되어 부모 프레임으로 다시 전송되며, 유용한 오류 로그로 표시될 수 있습니다. 이는 평소에는 보이지 않는 문제들을 표면화함으로써 앱을 해킹(hacking, 개발 및 수정)하는 과정을 더욱 생산적으로 만들기 위함입니다.

저는 무엇이 차단되는지에 따라 CSP 허용 목록(allow-list)을 구축하는 '원클릭 허용(one-click-to-allow)' 메커니즘으로까지 전환할 수 있음을 보여주는 실험을 구축했지만, 아직 그 아이디어를 datasette-apps에 통합하지는 않았습니다.

SQL 쿼리 또한 눈에 보이게 기록됩니다. 타임라인 페이지의 맨 아래로 스크롤하여 실제로 작동하는 모습을 확인해 보세요.

쓰기 작업을 위한 저장된 쿼리 (Stored queries)

저는 앱이 조건부로 데이터베이스에 쓸 수 있기를 원하지만, 이는 SQL 읽기보다 훨씬 더 위험한 제안입니다!

저의 해결책은 Datasette의 저장된 쿼리(stored queries) 기능을 활용하는 것입니다. 이는 최근 Datasette 1.0a31에서

Datasette Apps는 지난 4월 datasette-agent-artifacts로 시작되었습니다. 이는 제가 이후 편집 도구만을 남겨두기 위해 datasette-agent-edit로 이름을 변경한 플러그인입니다. 저는 플러그인 훅 (plugin hooks)을 적절한 형태로 갖추는 것을 돕기 위해 Datasette Agent를 위한 첫 번째 플러그인 중 하나로 이를 제작했습니다. 그 첫 번째 프로토타입 (prototype)은 주로 Claude Code 내의 Claude Opus 4.6을 사용하여 구축되었습니다.

제가 Datasette Apps로 방향을 전환했을 때, 저는 Codex Desktop과 GPT-5.5 xhigh를 사용하여 구축한 계획을 바탕으로 시작했습니다. 이 계획은 광범위한 대화와 datasette-agent-artifacts 및 제가 구축했던 다른 프로토타입들을 입력값으로 제공한 결과였습니다.

그 이후의 작업 대부분은 Codex를 통해 진행되었지만, Claude Fable 5를 사용할 수 있었던 짧은 며칠 동안 제품의 보안 평가 (security evaluation)를 실행하도록 했습니다 (이 능력은 직후 미국 정부에 의해 금지될 능력이었습니다). 그리고 그것은 매우 실질적인 문제를 발견했습니다.

저는 사용자들이 자신의 앱을 위해 CSP 호스트 (CSP hosts)를 허용 목록 (allow-list)에 추가할 수 있도록 허용하고 있었는데, Fable은 다음과 같은 공격 가능성을 지적했습니다:

  • create-app 권한을 가진 권한이 낮은 사용자가

AI 자동 생성 콘텐츠

본 콘텐츠는 Simon Willison Blog의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0