왜 Svelte인가?

개요

요즘 Svelte라는 게 인기를 얻고 있다는 말을 들었다. 공식 홈페이지에서 개발자인 Rich Harris의 글과 튜토리얼을 읽어보니 개발자의 파격적인 철학과 코드 작성 방식이 꽤 마음에 들어서 개발자의 포스트를 바탕으로 그 논지를 정리해본다.

"날씬한" 정도의 뜻을 가지며 "스벨트" 정도로 발음하는 것 같다.

가상 DOM은 순전히 오버헤드다

Svelte의 근간은 가상 DOM에 대해 부정하는 것부터 시작한다.

React나 Vue 등 유명 프레임워크는 페이지의 차이를 비교하고 업데이트할 때 가상 DOM 방식을 사용한다.

React를 기준으로 설명하면 페이지가 새로운 상태에 돌입했을 때 새로운 UI의 DOM을 바로 렌더링하지 않고 메모리에 유지한다. 이후 ReactDOM 라이브러리에 의해 재조정(reconcile) 과정이 이루어진다. 이 과정에서 새로운 상태의 가상 DOM과 실제 화면에 존재하는 DOM을 비교한다. 루트부터 비교해 엘레멘트 타입 다르면(<a><-><div>) 통째로 교체한다. 같다면 어트리뷰트 등만 확인하고 수정해준다. 만약 같은 타입의 컴포넌트 엘레멘트라면 인스턴스는 유지되지만 props를 갱신해준다. 자식들은 재귀적으로 처리된다.

DOM 연산 자체는 느리지 않다(트리에서 노드 이동하고 삭제하는 것쯤이야 그냥 포인터 몇 개 갈아 끼우는 수준에 불과하다). 하지만 이것을 렌더링하는 과정은 무척이나 복잡하고 느리다. DOM이 수정되고 렌더링 되면 이 과정에서 흔히 리플로우나 리페인트라고 불리는 동작이 수행되는데 가상 DOM은 기존 DOM의 변경된 부분만 확인하고 적용해 이런 과정들을 줄여준다.

근데 잘 생각해보면 이런 가상 DOM을 만들고 비교하는 건 기존 동작에 대한 오버헤드다. 즉, 안 해도 될 동작을 굳이 앱의 모든 동작에서 한다는 것이다. 하지만 이런 오버헤드가 대부분의 상황에서 도움이 된다는 게 React의 생각으로 보인다(React 문서에서 보면 휴리스틱이라는 말이 종종 나온다).

즉, 가상 DOM은 충분히 빠르다는 것이지, 결코 원래의 동작과 비교 했을 때 빠른 것은 아니다.

Rich Harris가 근거를 드는 또 다른 이유는 React에 shouldComponentUpdateuseMemo 같은 최적화 방법이 존재한다는 것이다. 정말 React가 뭐든 신경 안 써도 될 정도로 빠르다면 이런 사이드 이펙트에 가까운 방법이 왜 존재하냐는 것이다.

그러면서 가상 DOM 자체가 기능이 아니며 단지 선언적(declarative)이고 상태에 의해 주도되는(state-driven) UI를 만들기 위한 수단이라고 한다. Svelte는 이런 수단으로 가상 DOM을 쓰지 않는다고 한다.

더 적은 코드를 작성하라

근래에는 웹 개발에 컴포넌트 방식의 개발을 지원하는 프레임워크나 라이브러리가 적극적으로 활용된다. 그러면서 자연스레 브라우저에서 로드하는 코드의 크기도 커졌다. React는 아무것도 안 하는 Hello World 앱을 빌드해도 수십 kb의 소스 파일이 결과물로 나온다고 한다.

궁금해서 create-react-app으로 한 번 해보았다.

gzip으로 압축한 결과물이 대충 45kb정도 된다. 현대의 네트워크 속도를 봤을 때 그렇게 대단한 크기는 아니다. .jpg 하나 빼면 되지 정도로 해결할 수 있다. 하지만 이 건 트래픽에 관한 문제지, 실제 js 엔진이 js 파일을 평가하는 것에 대해서는 전혀 고려하지 않고 있다. 많이들 착각하지만 js는 인터프리터 언어가 아니라 컴파일 언어다. 즉, 모든 소스 코드를 한 번씩 훑어본다는 의미다.

이는 코드가 길면 길수록 엔진의 컴파일 과정이 길어진다는 의미다. 즉, 소스 파일이 커서 좋은 건 없다.

또한 코드가 길어지면 길어질수록 버그가 유발된다. Rich Harris는 React는 Svelte에 비해 40% 긴 코드를 작성한다고 한다.

프레임워크 없는 프레임워크

그렇다면 Svelte는 이런 불만들을 어떻게 해결하고 가용한 수준의 프레임워크로 거듭난 것일까? 바로 Svelte 자체가 컴파일러가 됨으로써 이 문제를 해결했다. Svelte는 작성한 코드를 자체 컴파일러를 통해 바닐라 js로 바꾼다. 이는 많은 걸 해결한다.

가상 DOM을 쓰지 않는다고 했는데 이 변화에 대한 직접적인 코드를 컴파일러가 바닐라 js로 작성해준다. 즉, 불필요한 가상 DOM 비교 연산을 거치지 않고 직접적으로 관련 있는 DOM 연산만 진행한다. Rich Harris는 이런 업데이트에 대해 외과적(surgically)이라고 표현했다. 아마 외과수술처럼 정밀한 업데이트 로직을 의미하는 게 아닐까 싶다. 이런 업데이트도 그냥 바닐라 js로 다루니 React처럼 별도의 라이브러리 코드가 들어갈 필요가 없다. 즉, 가상 DOM 연산도 빠지고 추가적인 라이브러리 코드도 필요 없는 것이다. 코드가 별도의 로직 없이 엄청나게 가벼워지니 임베디드 장치처럼 낮은 수준의 연산력을 가지는 머신에서도 문제가 없다.

Svelte 코드 샘플

<script>
	let count = 0;

	function handleClick() {
		count += 1;
	}
</script>

<button on:click={handleClick}>
	Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

<style>
	button {
		color : red;
	}
</style>

여기서 부가되는 장점이 하나 더 있는데 바로 바닐라 js이므로 다른 코드와의 상호운용(interoperability)도 문제없다. 브라우저에서 돌아가는 코드라면 뭐든 결국 js니까 말이다.

리액티브 프로그래밍(Reactive Programming)

Rich Harris는 Svelte를 통해 훌륭한 리액티브 프로그래밍을 구현할 수 있다고 한다. 리액티브 프로그래밍은 말하자면 스프레드시트 같은 선언적이고 데이터의 변화에 따라 프로그램이 자연스레 영향을 받는 프로그래밍 패러다임이다.

가령, 아래 같은 코드가 있다고 생각해 보자. 위키 백과에서 가져왔다.

var b = 1;
var c = 2;
var a = b + c;
b = 10;
console.log(a); // 당연한 결과

// "$=" 연산자가 있다고 가정해보자. 
//이 연산자는 좌측에 선언된 변수가 우측 변수의 값들이 바뀔 때마다 재평가된다.
var b = 1
var c = 2
var a $= b + c
b = 10
console.log(a) // 12

스프레드 시트의 동작과 동일하다.

Svelte는 아래와 같은 $, 리액티브 선언을 도입해 이를 실현했다.

<script>
	let count = 0;
    	//count가 바뀌면 $ 이후 내용이 재평가 된다.
	$: doubled = count * 2;

	function handleClick() {
		count += 1;
	}
</script>

<button on:click={handleClick}>
	Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

<p>{count} doubled is {doubled}</p>

개인적으로 이런 프로그래밍 방식에 익숙하지 않지만 좀 더 선언적(declarative)이고 상태에 의해 주도되는(state-driven) 앱에 확실히 잘 어울리는 것 같다.

소고 & 마무리

<script>
  let name = 'world';
</script>

<h1>Hello {name}!</h1>

<style>
  h1{
    color : red;
  }
</style>

Svelte의 컴포넌트 코드를 보면 알겠지만 <script><style>이 함께 존재한다. 그리고 번거로운 탑레벨 엘레멘트도 없다. 당연히 style은 CSS Modules처럼 고유 id와 함께 컴포넌트에 종속된다. 그리고 구조도 상당히 간결하다는 생각이 든다. 데이터의 양방향 바인딩은 물론, 리액티브 프로그래밍적인 요소들 굉장히 마음에 든다.

튜토리얼을 보면 알겠지만 트랜지션과 애니메이션 API에 굉장히 공을 많이 들였다. 멋진 효과들을 네이티브하게 잔뜩 사용할 수 있다.

외에도 React의 Context에 해당하는 store도 존재한다. 후발주자인 만큼 여러 기능들이 세심하게 구현 되어있다.

Svelte는 이제 3 버전을 릴리즈하여 여러 개발 키트, 앱 프레임워크(Next.js나 Gatsby 같은), 네이티브 지원(모바일) 등 여러가지를 준비중이라고 한다. 대부분 거의 알파버전에 진입한 것으로 보아 곧 프로덕션 환경에서도 많이 볼 수 있을 것 같다.

무엇보다 나는 SVELTE가 페이지에 내건 슬로건이 마음에 든다. 무언가를 표현할 때 CYBERNETICALLY 같은 표현은 처음 들어본다.

CYBERNETICALLY ENHANCED WEB APPS

바로 사용해보고 싶은 욕심이 든다.

references

좋은 웹페이지 즐겨찾기