Hike News
Hike News

[2018-01-08] 2018년 1월 깃헙 트렌드 - Rust와 웹 어셈블리의 혁신적인 도발

2018년 새해가 밝았다. 올해는 어떤 포스팅을 더 할 수 있을지 고민해 보았다. 물론 언제나 나의 계획과 이룰 수 있는데에는 한계가 있다.

하고 싶은 포스팅

  1. 가방끈 짧은 개발자의 딥러닝 시리즈
  2. 프론트엔드 개발자가 배워야 하는 기반 기술 총망라. ( HTTP1.0 부터 ES2018까지. 아 이건 위키가 더 좋을까. 총망라 라니.. 이런 아재 감각.)
  3. 머리속에 그리는 리액트 심화 가즈아~
  4. 깃헙 트렌드 격주 발행 ( 가능할 것인가?!! )

1월의 깃헙 트렌드

1월에는 다음과 같은 프로젝트들이 각광을 받고 있다.
이 중에서 yew라는 rust 기반의 프로젝트를 살펴보자.

yew

yew는 rust 기반의 웹 프레임 워크를 표방하고 있다.

rust

rust page
rust 라고 하면 기본적으로 어려운 언어. 파이어폭스의 다음버전 퀀텀의 기반. 혁신적인 속도 등의 키워드로 개발자들에게 알려져 있다. 물론 비슷한 시기에 나와서 더 성공적으로 론칭된 go의 경우는 구글의 버프를 받고 무럭 무럭 개발자 커뮤니티가 성장하고 있다. 그에 비해 rust 는 많이 외면 받아 왔는데 그 이유로는 어려운 문맥과 더불어 타게팅된 어플리케이션을 찾기가 힘들었다.

servo page
그 와중에 우리가 들어 봤던 가장 좋은 소식중에 하나는 아마도 servo 일 것이고 그 servo가 차기 파이어 폭스 퀀텀의 핵심이 될 것이다.(아니 이미 서브 프로젝트들은 퀀텀에 장착되기 시작했다.) 그런데 퀀텀은 이미 개발자들 사이에 굉장히 화제가 되었고 속도가 많이 개선되었다는 이야기들이 오가기 시작했다. 다만 이전 플러그인들 호환 문제 때문에 파이어 폭스의 미래는 여전히 밝지 않지만 모두에게 rust === ‘고성능이였지’ 공식의 향수를 일으키기에 충분했다.

그러던 와중에 1월의 프로젝트중에 yew가 눈에 띄었다. 그래서 한 번 살펴 보았는데, 프론트엔트 개발자들에게는 개발에 대한 패러다임을 바꿀 수 있는 어떤 무언가가 보여서 한 번 나눠볼까 한다.

React like Virtural DOM

Rust 내부에 DOM 코드를 작성하는 형식은 리액트의 Virtual DOM을 따라 했는데, 컴파일을 하고 서비스가 로딩이 된 후 사이트에 접속을 하면 react 처럼 virtual dom을 만들고 렌더링을 한다.
네트워크를 통해 전송되는 html은 다음과 같다.

  • 소스코드 - To Do MVC HTML 코드
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <!doctype html>
    <html lang="en">
    <head>
    <meta charset="utf-8">
    <title>Yew • TodoMVC</title>
    <link rel="stylesheet" href="styles.css">
    </head>
    <body>
    <script src="js/app.js"></script>
    </body>
    </html>

하지만 DOM의 경우는 다음 그림처럼 렌더링이 되어 있다.
To Do MVC HTML DOM

Web Assembly

자바 스크립트는 웹 어셈블리(혹은 Asm.js)로 작성되어서 떨어진다. Asm.js는 브라우저가 이해하기 쉬운 기계어 포맷에 가까운 자바스크립트이고, 웹 어셈블리는 이전에 블로그에서 언급한 적이 있다.
WebAssembly - hello world 어셈블리를 브라우저에 올려보자
링크 : WebAssembly - hello world 어셈블리를 브라우저에 올려보자
말 그대로 브라우저에서 돌아가는 기계어다.
아무래도 ???! 이게 뭐지? 하는 느낌으로 지금쯤 읽고 있는 독자들이 많으리라.
코드를 보고 이야기 하자.

백타가 불여일견

먼저 TO DO MVC 코드는 다음과 같다. 맞다 . 여러분이 아는 그 To Do MVC 말이다. ToDoMVC

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
// 중략
// 모델을 정의한다.
#[derive(Serialize, Deserialize)]
struct Model {
entries: Vec<Entry>,
filter: Filter,
value: String,
edit_value: String,
}
#[derive(Serialize, Deserialize)]
struct Entry {
description: String,
completed: bool,
editing: bool,
}
//중략
// update 함수는 Add,Edit 등등의 명령어를 받으면 모델을 변경한다.
fn update(context: &mut Context, model: &mut Model, msg: Msg) {
match msg {
Msg::Add => {
let entry = Entry {
description: model.value.clone(),
completed: false,
editing: false,
};
model.entries.push(entry);
model.value = "".to_string();
}
Msg::Edit(idx) => {
let edit_value = model.edit_value.clone();
model.complete_edit(idx, edit_value);
model.edit_value = "".to_string();
}
Msg::Update(val) => {
println!("Input: {}", val);
model.value = val;
}
Msg::UpdateEdit(val) => {
println!("Input: {}", val);
model.edit_value = val;
}
Msg::Remove(idx) => {
model.remove(idx);
}
Msg::SetFilter(filter) => {
model.filter = filter;
}
Msg::ToggleEdit(idx) => {
model.toggle_edit(idx);
}
Msg::ToggleAll => {
let status = !model.is_all_completed();
model.toggle_all(status);
}
Msg::Toggle(idx) => {
model.toggle(idx);
}
Msg::ClearCompleted => {
model.clear_completed();
}
Msg::Nope => {}
}
context.storage.store(KEY, Json(&model));
}
//중략
// view 는 브라우저에 렌더링한다. 변경에 따른 바인딩과 이벤트의 바인등을 정의한다.
fn view(model: &Model) -> Html<Msg> {
html! {
<div class="todomvc-wrapper",>
<section class="todoapp",>
<header class="header",>
<h1>{ "todos" }</h1>
{ view_input(&model) }
</header>
<section class="main",>
<input class="toggle-all", type="checkbox", checked=model.is_all_completed(), onclick=|_| Msg::ToggleAll, />
<ul class="todo-list",>
{ for model.entries.iter().filter(|e| model.filter.fit(e)).enumerate().map(view_entry) }
</ul>
</section>
<footer class="footer",>
<span class="todo-count",>
<strong>{ model.total() }</strong>
{ " item(s) left" }
</span>
<ul class="filters",>
{ for Filter::iter().map(|flt| view_filter(&model, flt)) }
</ul>
<button class="clear-completed", onclick=|_| Msg::ClearCompleted,>
{ format!("Clear completed ({})", model.total_completed()) }
</button>
</footer>
</section>
<footer class="info",>
<p>{ "Double-click to edit a todo" }</p>
<p>{ "Written by " }<a href="https://github.com/DenisKolodin/", target="_blank",>{ "Denis Kolodin" }</a></p>
<p>{ "Part of " }<a href="http://todomvc.com/", target="_blank",>{ "TodoMVC" }</a></p>
</footer>
</div>
}
}

코드는 생각보다 어렵지 않다. Gorilla 같은 Go의 웹 프레임워크를 처음 볼때와 흡사하다. 다만 다른 부분은 view안에 JSX 처럼 코드 스니펫을 사용한다는 것이다. JSX 처럼 컴포넌트화 하지는 않는 것 같다.
struct는 모델을 정의하고 이벤트를 처리할 함수를 update로 정의한 후에 view 에서 렌더링을 하고 있다. 이렇게 코드를 작성하고 실행을 시키고 나면, 다음과 같이 ToDoMVC 앱이 뜨는 것이다.
진리의 asdf

이것이 정확히 어떻게 작동하게 되는지 app.js를 알아보자. toggleEdit이라는 이벤트의 아래 코드는 다음과 같이 바뀐다.
그만 알아 보자

cargo-web

이런 극적인 코드가 가능해 지는데에는 cargo-web 이라는 프로젝트가 뒤에 있다.
카르고 웹
링크 : cargo web

cargo는 rust 에서 사용되는 패키지 매니저인데 subcommand라는 개념으로 패키지들을 관리한다.
그 중에 이 cargo web이라는 프로젝트가 rust 코드를 asm.js 록은 웹어셈블리로 변환해 주는 역할을 하고 있다.

설치스크립트

Mac OS 기준으로 아래와 같은 명령어를 사용해서 설치를 했다.

  1. emscripten 설치

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    $curl -O https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-portable.tar.gz
    $tar -xzf emsdk-portable.tar.gz
    $source ./emsdk_portable/emsdk_env.sh
    $emsdk install sdk-incoming-64bit
    $emsdk activate sdk-incoming-64bit
    ```
    설치하다 openssl 관련해서 에러가 날 수가 있는데, 그럴 경우에는 OPENSSL_DIR을 제대로 지정해 주면 에러가 사라진다.
    2. rust 설치
    ```cmd
    $curl https://sh.rustup.rs -sSf | sh
  2. cargo 설치

    1
    $curl -sSf https://static.rust-lang.org/rustup.sh | sh
  3. cargo web 설치

    1
    $cargo install cargo-web
  4. yew 설치

    1
    2
    $ cargo install cargo-web
    $ rustup target add asmjs-unknown-emscripten

실행

프로젝트에 데모가 존재한다.
깃으로 클론을 받자

1
$ git clone https://github.com/DenisKolodin/yew

프로젝트에 들어가서 해당 샘플폴더(Cargo.toml이 있는 곳)에서 cargo web start를 실행하면 프로젝트가 시작이 된다. 기본포트는 8000번을 사용한다.

1
$cargo web start

카르고 웹 스타트

링크 모음

관련 링크는 다음과 같다.