GN⁺: Svelte 5는 Javascript가 아님
(hodlbod.npub.pro)- 최근 Svelte 5 버전으로 웹 애플리케이션을 업그레이드한 후 겪은 이슈들 정리
- 딥 리액티비티 기능과 변경된 라이프사이클로 인한 예기치 못한 동작이 발생함
- Svelte 3/4를 오래 즐겼지만, 앞으로 새로운 프로젝트에 Svelte를 선택하지는 않을 것 같음
빠른 속도의 필요성
- Svelte 팀은 deep reactivity를 통해 성능 최적화를 시도했고, 더 나은 성능을 이끌어냄
- 기존에도 컴파일 과정을 통해 빠른 성능을 제공했으며, 이는 다른 프레임워크와 차별화되는 강점이었음
- 이는 불투명한 프레임워크를 만들어 디버깅이 어려웠으나, 성능과 생산성 면에서 타협 가능한 트레이드 오프라고 느꼈음
빠른 속도의 필요성
- Svelte 팀이 Svelte 5에서 주력하는 주요 변화는 “딥 리액티비티(deep reactivity)”로, 더 세밀한 반응성을 통해 성능을 높이려는 것임
- 이전 Svelte 버전들에서는 주로 Svelte 컴파일러를 활용해 이 목표를 달성
- 개발자가 직접 새로운 개념을 배울 필요 없이 내부 로직을 재구성하기 좋았던 점이 Svelte의 독창성을 부각시켰음
- 동시에 이러한 컴파일 과정이 프레임워크를 불투명하게 만들어, 복잡한 문제 디버깅이 어려워졌음
- 컴파일러 자체 버그로 인해 원인을 파악하기 힘든 오류가 발생했으며, 때때로 문제 컴포넌트를 전면 리팩토링해야만 해결 가능했음
- 그래도 속도와 생산성 면에서 합리적인 타협이라고 느껴, 프로젝트를 주기적으로 초기화해야 하는 불편함도 감수해 왔음
Svelte는 Javascript가 아님
- Svelte 5는 이 트레이드오프를 2배로 늘림
- 핵심 차이점은 추상화·성능의 절충 지점이 컴파일 단계를 넘어 런타임 부분에까지 침투했다는 것임
- Deep reactivity를 지원하기 위한 프록시 사용
- 암묵적인(implicit) 컴포넌트 라이프사이클 상태
- 이 두가지 변경사항은 성능을 개선하고, 개발자 API가 더 멋지게(slicker) 보이게 만듦
- 싫어할게 있을까? 안타깝지만 이 2기능은 leaky abstraction의 전형적 사례로 꼽을 수 있음
- 결국 개발자가 다루기에 더 복잡한 환경을 초래함
Proxies는 객체가 아님
- Proxy 사용으로 Svelte 팀은 개발자에게 추가 작업을 요구하지 않고도 프레임워크 성능을 약간 더 끌어올릴 수 있었음
- React 같은 프레임워크에서는 여러 컴포넌트를 거치며 상태를 전달할 때 불필요한 리렌더링을 유발하기 쉬운 편인데, Svelte는 이를 줄이려는 목적으로 Proxy를 도입했음
- Svelte 컴파일러는 기존에도 가상 DOM 비교 과정에서 생길 수 있는 일부 문제를 피했지만, Proxy를 통해 성능을 좀 더 향상시킬 수 있다고 판단한 듯함
- Svelte 팀은 Proxy가 개발자 경험 향상에도 기여한다고 언급했으며, “효율성과 사용 편의성을 모두 극대화할 수 있음”이라는 주장을 내세웠음
- 문제는 Svelte 5가 겉보기에는 더 단순해 보이지만, 실제로는 더 많은 추상화를 추가한다는 점임
- 예를 들어 배열 메서드 감지를 위해 Proxy를 쓰면, Svelte 4에서
value = value
같은 코드를 작성하지 않아도 되는 이점이 생김 - Svelte 4에서는 리액티비티를 트리거하기 위해 개발자가 컴파일러의 동작 원리를 어느 정도 이해해야 했음. 반면 Svelte 5에서는 “컴파일러를 잊어도 된다”는 인상을 주지만 실제로는 그렇지 않음
- 새로운 추상화를 도입해 얻은 편의성만큼, 컴파일러가 원하는 대로 동작하도록 하기 위해 개발자가 알아야 할 규칙이 늘어났음
- 예를 들어 배열 메서드 감지를 위해 Proxy를 쓰면, Svelte 4에서
- 오랜 시간 Svelte를 사용하면서, 개인적으로는 점차 Svelte store를 더 많이 이용하고, 리액티브 선언은 덜 쓰게 되었음
- Svelte store는 기본적으로 자바스크립트 개념에 가깝고,
update
메서드를 호출하는 방식이 단순하며,$
문법은 부가적인 이점 정도였음 - Proxy는 리액티브 선언과 마찬가지로 “보기에는 하나처럼 보여도, 실제 경계 지점에서 다르게 작동한다”는 문제를 유발함
- Svelte store는 기본적으로 자바스크립트 개념에 가깝고,
- Svelte 5를 처음 쓸 때는 모든 것이 잘 돌아갔지만, Proxy 상태를 IndexedDB에 저장하려 하자
DataCloneError
가 발생했음- 더욱이 어떤 값이 Proxy인지 확실히 파악하기 위해서는 구조화된 복제를
try/catch
로 시도해야 하는데, 이는 성능 비용이 큼 - 결국, 무엇이 Proxy인지 기억하고, 외부에서 Proxy를 인식하지 못하는 문맥에는
$state.snapshot
을 매번 사용해야 하게 됨 - 이는 결과적으로 “추상화가 개발 편의를 높인다”는 당초 의도와 달리, 개발자에게 더 복잡한 규칙과 절차를 요구하게 되는 상황을 초래함
- 더욱이 어떤 값이 Proxy인지 확실히 파악하기 위해서는 구조화된 복제를
컴포넌트는 함수가 아님
- 2013년 무렵 가상 DOM이 인기를 얻은 이유는 애플리케이션을 함수 조합 형태로 모델링할 수 있었기 때문임
- Svelte는 가상 DOM 대신 컴파일러를 사용해 라이프사이클 함수를 간소화하고 성능을 높이는 방식을 유지해 왔음
- 그러나 Svelte 5에서는 라이프사이클 개념이 React Hooks와 비슷하게 다시 추가됨
- React에서 Hooks는 라이프사이클 메서드의 상태 관련 코드를 줄여주는 추상화 개념임
- 코드는 깔끔해지지만,
setTimeout
에서 상태를 참조할 때처럼 개발자가 주의해야 할 부분이 많음 - Svelte 4에서도 컴포넌트 언마운트 시점에 비동기 코드가 DOM 요소에 접근하면 문제가 생길 수 있었음
- 이제 Svelte 5에서는 상태 변경과 이펙트를 조율하기 위해 컴포넌트 라이프사이클에 암묵적인 상태가 추가된 것으로 보임
- 코드는 깔끔해지지만,
-
$effect에 대한 공식 문서에서는 다음과 같이 설명함:
“$effect는 어디든 배치할 수 있지만, 컴포넌트 초기화 중(또는 부모 이펙트가 활성화된 동안) 호출되어야 하며, 컴포넌트(또는 부모 이펙트)가 언마운트되면 사라짐”
- 이는 라이프사이클이 마운트/언마운트 두 단계만 존재한다는 설명과 달리, 상태 변화를 추적해야 하는 복잡한 이펙트 구조가 존재함을 시사함
-
공식 라이프사이클 문서에서는 “before update/after update가 없다”라고 말하지만,
$effect.pre
와tick
같은 새로운 개념이 등장함 - 이는 사실상 마운트/언마운트 외에도 상태 변화 시점에 대한 이해가 필요함을 뜻함
- 실제 사용 중 문제를 일으킨 부분은, Svelte와 무관한 함수에 넘겨진 상태 역시 컴포넌트 라이프사이클에 묶인다는 점임
- 예를 들어, 모달 창을 스토어로 관리하면서 콜백을 자식 컴포넌트에 전달하는 패턴을 사용했음
const { value } = $props() const callback = () => console.log(value) const openModal = () => pushModal(MyModal, { callback })
- 만약 이 코드가 모달 컴포넌트 내부에 있다면, 모달을 호출한 컴포넌트가 먼저 언마운트되고, 그 시점에 value가 undefined로 변경됨
- 이 리포지토리에 미니멀 재현 예시가 올라와 있음
- 즉, 컴포넌트 생명주기가 끝난 후에도 살아있는 콜백에서 참조하던 props가 갑자기 undefined가 됨
- 이는 기본 자바스크립트와 다른 동작 방식이며, Svelte가 자체적으로 가비지 컬렉션 같은 작업을 하는 것처럼 보임
- 엔지니어링적인 이유가 있겠지만, 예상치 못한 동작이라 놀라움
결론
- 쉬운 것은 분명 매력적이지만, Rich Hickey가 말했듯이 쉬움이 곧 단순함을 의미하지는 않음
- Joel Spolsky의 말처럼, 예기치 못한 동작이 일어나는 것을 달가워하지 않음
- Svelte는 그동안 많은 ‘마법’을 보여줬지만, 이번 버전에서는 그 마법을 사용하기 위해 암기해야 할 것이 많아져 이득보다 부담이 커졌음
- 이 글의 요점은 Svelte 팀을 비난하기 위함이 아니며, 오히려 Svelte 5(와 React Hooks)를 선호하는 사람들이 많다는 사실을 인지하고 있음
- 중요한 것은 사용자에게 편의를 제공하는 것과 사용자가 주도권을 가질 수 있는 것 사이의 균형임
- 진정한 좋은 소프트웨어는 ‘영리함’이 아니라 ‘이해’에 기반함
- AI 도구가 발전함에 따라, 자신이 무엇을 하고 있는지 모르게 만드는 툴보다 이미 축적한 지혜를 활용하고 깊은 이해를 돕는 툴을 선택하는 것이 중요함
- Rich Harris와 팀에게 그간의 즐거운 개발 경험에 감사함. 이 글이 부정확하지만은 않은 피드백이 되길 바람
React 와 특히 next 의 영향을 적지 않게 받으면서 이상하게 변했다고 생각합니다. +page 는 svelte 를 모르고 봐서는 이해하기 어렵고, $state, $derived 같은 rune 은 React 를 따라가는 것 같은데 차라리 변수 앞에 $: 넣던 시절이 좀더 나아보입니다. {#each a in array} {/each}
같은 올드한 문법도 참을만 한데, 여전히 귀찮습니다. 선택적 reactivity 로 인한 성능 개선이라면 solidjs 가 훨씬 좋은 방향이라고 생각합니다. jsx 를 그대로 쓰다보니 상대적으로 react 에서 넘어가기도 쉽구요. solidjs 가 상대적으로 주목을 못받는게 의아할 정도네요.
svelte같은 대안이 있었기에 React/nextjs도 큰 자극을 받을 수 있었으리라 생각합니다.
근본적으로 svelte는 language 이므로, UI를 기술하는 언어가 나아가야 할 방향도 잘 제시해줬으면 좋겠습니다.
저는 리액트 쓸겁니다
Hacker News 의견
-
처음에는 runes에 대해 별로 흥미가 없었음. 그러나 .svelte 템플릿에 반응형 외부 컴포넌트를 가져올 수 있고, 내부적으로 반응성을 캡슐화할 수 있을 때 의견이 바뀌었음. 이는 vitest 테스트를 작성할 수 있으면서도 반응성의 이점을 얻을 수 있다는 것을 의미함. 이는 정말 강력하고, AFAIK, 프론트엔드 세계에서 독특함
- 대부분의 프론트엔드 개발자들은 테스트를 전혀 하지 않음. Typescript는 정확성을 보장하기 위해 사람들이 사용하는 도구이며, 그럴 만한 이유가 있음. 그러나 svelte 사용자들은 항상 typescript에 대해 좁은 시각을 가지고 있었고, 그럴 만한 이유도 있음
- 개인적으로 테스트 가능한 프론트엔드 코드를 작성하는 것을 선호하며, Svelte 5는 그런 면에서 혁신적임. 브라우저에서 반응형이면서도 단위 테스트만큼 좋음
- 이 모든 것을 말했지만, 블로그 게시물은 진실을 말하고 있음. 프록시를 추가하는 것은 매우 불편하게 느껴짐. React와 Vue는 추상화 위에 추상화를 추가하기 시작했을 때 나를 잃었고, 프록시는 그 시작점이었음
- Svelte 5는 JavaScript가 아님이 <i>Svelte 5를 좋아하는 이유</i>임
- 프론트엔드/웹을 합리적으로 할 수 있는 두 가지 주요 방법이 있다고 생각함
-
- 정적 HTML 또는 서버 렌더링된 템플릿, 아마도 HTMX
-
- JavaScript로 컴파일되는 언어/플랫폼. 최소한 TypeScript이지만, HTML과 CSS가 실제로 꽤 좋다고 생각하기 때문에 JSX, React, Tailwind 등은 <i>제외</i>되며, Svelte 5와 몇몇 다른 프레임워크는 실제로 바닐라 TypeScript보다 개선됨
-
- Svelte 5는 카테고리 2에서 명확한 승자임
- 멋진 HTML 템플릿, 쉽고 합리적인 상태 전파, 모듈식 코드를 쉽게 작성할 수 있는 기능을 가지고 있음. CSS와 잘 작동하며, 종종 간단한 일회용 앱과 도구를 한두 파일로 만들 수 있으며, 더 크고 진지한 애플리케이션도 가능함. Svelte 4보다 덜 마법적이고 놀랍지 않으며, 솔직히 사용하기 즐거움. 다행히도 IndexedDB에 대해 신경 쓰지 않음
- 오늘날 JavaScript 한 줄을 작성할 이유를 전혀 모르겠지만, 각자 자기 방식대로 함
-
상업적으로 배포된 SvelteKit 애플리케이션을 적극적으로 개발하고 있으며, 경험에 대한 생각을 공유하고 싶음
- SvelteKit에 처음 끌린 이유는 그 단순함이었음. 프로젝트를 설정한 후, 현대적인 프레임워크의 이점을 복잡함 없이 활용하면서 한 번에 하나의 HTML/JS/CSS 파일을 작업할 수 있었음. 이는 웹 개발 초기 시절, Apache 서버에 HTML 파일을 떨어뜨리는 것만으로 모든 것이 실행되던 시절을 떠올리게 했음
- 그러나 Svelte가 그 간단한 패러다임에서 벗어나는 것을 보는 것은 실망스러움. 처음부터 Rich Harris는 Svelte의 사용 용이성과 단순함을 주요 판매 포인트로 내세웠음. 현재의 SvelteKit 버전은 나쁘지 않지만, 이전 버전을 더 선호했음. 그때는
+page
와 같은 라우팅 구조를 다룰 필요가 없었음. Svelte 파일을 원하는 곳에 배치할 수 있었고, 현대적인 프레임워크의 이점을 누리면서도 매끄럽게 렌더링되었음 - 이러한 변화는 이전에 필요하지 않았던 복잡성을 추가하며, Svelte의 매력을 처음부터 멀어지게 할 가능성이 있음. 이미 알고 있는 것을 바탕으로 선택했음
-
EmberJS가 사라진 것은 아쉬움. API는 지난 10년 동안 꽤 안정적이었음. 아이러니하게도 지난 10년 동안 EmberJS 앱을 작성한 사람은 React, Svelte, Vue 등으로 동일한 작업을 한 사람보다 마이그레이션에 덜 어려움을 겪을 것임
- 안타깝게도 Ember 팀은 초기에 몇 가지 이상한 결정을 내렸고, 일반 JavaScript에 비해 쉽게 이해할 수 없었음 (대부분은 이제 수정되었음)
- JavaScript인지 아닌지는 API의 안정성에 비하면 별로 중요하지 않음
- 개인적으로 JavaScript의 문제는 너무 자주 변화한다는 것임
-
저자가 게시물 상단에 나열한 두 개의 Github 링크는 동일한 문제를 가리키며, 그 문제에는 해결책이 있음 (use $state.raw)
- Svelte 2 또는 3 버전부터 Svelte의 팬이었음. 이는 바닐라 HTML/CSS/JS를 작성하는 것과 가장 가까우면서도 프레임워크의 이점을 누릴 수 있었기 때문임 (그리고 Sass와 TypeScript를 사용함). 환상적임
- Svelte 5는 runes가 이상해서 불안하게 느껴졌음. 프로젝트를 업그레이드하고 다른 프로젝트에서 작업한 후, 그렇게 나쁘지 않음. Svelte 5는 상태 처리의 옛 방식과 새 방식을 혼합하는 것을 허용하지 않으며, 오류 메시지는 대부분 유익함
- 이 댓글에서 htmx와 바닐라 JS가 객관적으로 더 낫다고 말하는 사람들을 보는데... 아니? 당신이 하는 일에는 그렇겠지만. 개인적으로 Svelte는 React보다 여전히 이해하기 훨씬 쉬움. 벤치마크를 신경 쓰는 사람들에게는 Svelte가 SolidJS만큼 빠름 (React 사용자들은 Solid로 쉽게 전환할 수 있을 것 같음, 문법이 비슷하게 느껴짐)
-
Svelte 5는 JavaScript가 아닌 <i>최악의</i> 방식임. js의 문제를 해결하지 않고, 일부 프론트엔드 문제에 대한 누출된 추상화를 제공함. Solid가 Svelte보다 더 나은 방향이라고 생각함. Solid는 새로운 언어에 의존하지 않기 때문임. 그러나 js가 이상적이지 않기 때문에 js가 아닌 것을 만들려는 본능이 있음. Elm은 svelte의 전신임. 그러나 더 나은 것을 만들 수 있다고 생각함...[0]
-
Svelte 4에서 스토어가 정말 싫었기 때문에 Svelte 5를 사용하기 시작했음. 새로운 프로젝트에 Sveltekit과 Svelte 5를 사용하고 있으며, 말해야겠음... React의 생산성은 여전히 무적임, Sveltekit과 Svelte 기술이 기술적으로 더 나은데도 불구하고
- 정말 짜증났던 몇 가지: 모든 페이지가
+page.server.ts
또는+page.svelte
또는 그 변형으로 이름이 지정되어 있어 코드를 쉽게 검색하기 어려움. Svelte의 도구는tsc
및 ESLint와 별도로 존재하여 CI에 통합하고 개발에서 사용하는 것이 더 어려움 - 이전 버전과의 이상한 호환성 문제도 있음. 예를 들어, 대부분의 Svelte 패키지는 여전히 스토어를 사용하므로, 두 가지 버전의 세계와 싸워야 하며, 코드 작성이 때때로 정말 혼란스러움. 또한, Svelte HMR은 여전히 초기 단계인 것 같음, 그래서 Svelte 모듈이 다시 로드될 때 상태를 망칠 것임
- Svelte를 정말 좋아하고 싶음. 렌더링 속도가 상당히 빠르고 그 뒤에 있는 아이디어가 마음에 듦. 그러나 React의 생산성은 무적임
- 정말 짜증났던 몇 가지: 모든 페이지가
-
콜백으로 클로저를 전달할 때 예상치 못한 동작 때문에 Svelte가 JavaScript가 아니라고 말하는 것은 이상하게 느껴짐. 더 나은 제목은 "Svelte 5가 나를 놀라게 해서 싫다"일 것임
-
순수 JavaScript를 사용할 수 있는 컴포넌트와 앱을 빌드할 수 있는 라이브러리를 찾고 있다면 Lit를 확인해 보세요: Lit
- 깊은 반응성을 위해 신호에 대한 추가 패키지가 있으며, 다가오는 Signals TC39 제안과의 통합을 목표로 하고 있음: Signals
- Lit는 Photoshop, Reddit, Home Assistant, The Internet Archive와 같은 주요 앱에서 사용됨
-
합리적인 프론트엔드 경험을 원한다면? 바닐라 Javascript, 웹 컴포넌트, htmx, Blazor를 사용하세요
- JS 프레임워크는 어떤 이유에서든 광기임