I spent three years building Nanoviews, a tiny UI library on signals. It just hit 1.0
3년간 만든 시그널 기반 UI 라이브러리 Nanoviews, 1.0 출시
Nanoviews는 시그널로 실제 DOM을 직접 갱신하는 소형 UI 라이브러리입니다. 작성자는 번들 크기와 js-framework-benchmark 결과를 다른 프레임워크와 비교하고 Nano Kit 생태계와 서버 사이드 렌더링 미지원 상태를 소개합니다.
- 주제
에디터 노트
Nanoviews의 성능 주장은 DOM을 처음 한 번 만들고 변경된 노드만 갱신한다는 구조와 연결되며, 제시된 벤치마크에서도 Solid와 Svelte에 가까운 결과를 보입니다. 다만 벤치마크 점수와 번들 크기는 작성자가 특정 시점에 만든 앱과 테스트의 결과입니다. 댓글에서 지적했듯 목록 전체를 다시 도는지 같은 갱신 비용은 크기 수치만으로 드러나지 않습니다. 도입 판단에서는 문법 취향도 실질적인 조건입니다. 작성자는 JSX 대신 함수 호출과 if_, for_를 선택했고, 댓글에는 중첩 괄호가 읽기 어렵다는 반응과 HTML 템플릿을 선호한다는 의견이 나왔습니다. 빌드 단계가 없다는 점은 필수 목적이 아니라 부수 효과라고 작성자가 밝혔습니다. 따라서 비교의 중심은 빌드 도구 유무보다 작은 번들, 시그널 기반 갱신, Nano Kit 연동을 원하는지와 함수 호출형 문법을 팀이 받아들일 수 있는지에 있습니다. 서버 사이드 렌더링은 아직 지원하지 않습니다.
AI 요약
Nanoviews 1.0은 Nano Kit과 같은 시그널 코어를 사용하는 UI 라이브러리입니다. 컴포넌트는 일반 함수 호출로 뷰를 만들고 실제 DOM 노드를 한 번 생성합니다. 이후 시그널 값이 바뀌면 해당 값에 연결된 노드만 갱신합니다. JSX나 가상 DOM을 쓰지 않으며 컴포넌트 렌더 함수도 다시 실행하지 않습니다.
함수 호출로 만드는 UI
HTML 태그마다 팩토리 함수가 있습니다. 첫 호출에 속성을 전달하고 두 번째 호출에 자식을 전달합니다. 컴포넌트는 component$로 감싸며 렌더 함수는 한 번만 실행합니다. 예를 들어 할 일 목록에서는 입력값과 목록을 시그널에 기록하고 for_가 새 li를 추가합니다. 카운터는 텍스트 노드만 바뀝니다. 조건과 목록에는 if_와 for_ 같은 반응형 래퍼를 사용합니다. JSX 변환이나 템플릿 컴파일은 없지만 TypeScript를 쓰면 요소와 속성, 이벤트, 자식의 타입을 검사합니다.
번들 크기 비교
작성자 측정에서 일반 앱이 가져오는 Nanoviews 크기는 gzip 기준 약 4 kB이며 전체 export와 시그널을 포함하면 7 kB를 조금 넘습니다. Vite 8로 만든 날씨 앱의 브라우저용 축소 JS 합계는 Nanoviews와 Nano Kit 조합이 10.36 kB, Solid와 Nano Stores가 13.55 kB, Svelte와 Nano Kit이 23.12 kB, React와 Nano Kit이 66.49 kB입니다. React와 TanStack Query 조합은 71.56 kB입니다. 라우팅과 데이터 요청을 포함한 Rick and Morty 백과사전 예제에서는 Nanoviews가 gzip 기준 17.75 kB, Svelte가 36.20 kB, React와 Nano Kit이 73.70 kB, React와 TanStack 제품 조합이 102.34 kB입니다.
성능과 생태계
2026년 10월 js-framework-benchmark에서 기하평균 감속도는 Vue Vapor 1.02, Solid 1.02, Svelte 1.07, Nanoviews 1.11입니다. Vue는 1.18, Preact와 Signals 및 Angular는 각각 1.27, React는 1.36입니다. 값이 낮을수록 빠르며 메모리 항목에서도 Nanoviews는 표에 든 라이브러리 가운데 가장 낮은 값을 기록했다고 작성자는 설명합니다. Nano Kit의 저장소, 데이터 요청, 라우터, 국제화, 의존성 주입 기능은 별도 어댑터 없이 뷰에 연결됩니다. 라우터 패키지와 Testing Library, Storybook 연동, 코딩 에이전트용 안내도 제공합니다. 서버 사이드 렌더링은 아직 없으며 현재 브라우저 렌더링만 지원합니다.
Reddit 반응
- @speechlessnpc — 뷰 라이브러리가 반응형 UI 렌더링의 새 접근법을 찾는 점은 좋지만 HTML 템플릿이 없는 점은 큰 단점입니다.
- @dangreen58 — 동의합니다. 템플릿은 읽기 쉽지만 런타임 파싱 비용이나 빌드 단계가 필요합니다.
- @speechlessnpc — 그렇더라도 크기가 4 kB 더 늘어나는 정도라면 괜찮을 수도 있습니다.
- @MediocreAnalyst2121 — 이 라이브러리가 겨냥한 바는 아니겠지만 TypeScript, Sass, 압축 등 웹에서는 거의 모든 작업에 빌드 단계가 들어갑니다.
- @dangreen58 — 빌드 단계가 없는 점은 목표라기보다 일반 함수 호출로 뷰를 만들면서 따라온 결과입니다. 주된 장점은 크기와 Solid, Svelte에 가까운 성능, Nano Kit 생태계입니다.
- @eracodes — 작은 라이브러리는 좋지만 Todo 예제의 깊게 중첩된 괄호가 읽기 편하지 않습니다. 의미 있는 이름을 가진 객체를 받는 생성자 계층이 있으면 좋겠습니다.
- @_Alpha-Ceph_ — VanJS에서 영감을 받았는지, Nanoviews는 어떤 점이 다른가요?
- @dangreen58 — Nanoviews를 만든 뒤 VanJS를 알게 됐습니다. 둘 다 일반 함수 호출을 쓰지만 Nanoviews는 alien-signals 포크를 사용하고 Nano Kit의 저장소, 데이터 요청, 라우터, 국제화, 의존성 주입을 바로 연결합니다. VanJS가 더 작고 약 1 kB이며 추가된 약 3 kB가 기능과 생태계에 쓰입니다.
- @Disastrous-Town-4820 — 벤치마크 순위만 보고 실제 DOM을 한 번 만들고 시그널이 바뀐 노드만 건드리는 게 맞다고 판단할 수는 없습니다. 전체 목록을 순회하는 시그널은 클릭 시간을 잡아먹으며 10 kB라는 수치로는 그 차이를 알 수 없습니다.
- @svish — 조건문을 왜 컴포넌트로 만들어야 하나요? React에서는 일반 JavaScript를 쓰지 않나요?
- @dangreen58 — React는 상태가 바뀔 때 컴포넌트를 다시 실행하므로 삼항 연산자나 map 결과를 다시 계산합니다. Nanoviews는 컴포넌트를 다시 실행하지 않으므로 조건과 목록에 if_와 for_ 같은 반응형 래퍼가 필요합니다.
- @m0rpheus23 — SwiftUI나 Jetpack Compose는 그런 방식을 처음부터 지원하도록 만든 언어와 환경에서 작동합니다. 웹 템플릿은 다르게 발전했고 JSX 전 React 방식은 중첩 UI에서 지저분했습니다. 작업은 응원하지만 템플릿 방식도 고려해 주길 바랍니다.
- @barake — 재미있는 라이브러리입니다. F# 연결 계층을 만들고 싶어지는 API네요. 게시물에 질문에 답하고 생각을 설명해 줘서 고맙습니다.
원문: dangreen.blog / 번역·요약: Trawling