티스토리 뷰

728x90
반응형

WebAssembly

아직까지 많은 활용은 이뤄지지 못하고 있지만 모든 모던 웹브라우저가 지원을 하기 시작했고,
많은 양의 데이터의 처리가 항상 문제가 되는 웹 특성상 이 속도를 획기적으로 줄일 수 있는 웹 어셈블리는 앞으로 확실히 그 사용용도나 가치는 증가할 것으로 보인다.
c, c++, golang, rust 등 다양한 언어 등으로 개발 할 수 있다.
개인적으로 설명을 더 하고 싶지만 설명보다는 직접 해보는것이 빠르고
이미 웹어셈블리가 가진 장점이나 단점 가능성 등은 구글에서 검색만 해봐도 나오기에 굳이 쓰지 않겠다.

Rich Text Editor

외국웹에선 위와 같은 명칭으로 불리지만 쉽게 말하자면 말하는 웹에디터이다.
스마트에디터, 다음에디터 등 웹에서 조금 더 서식을 꾸며 작성할 수 있게 하는 에디터를 말한다.

이 블로그도 vue3를 연습하기 위해 만든 블로그이기도 하고,
웹 어셈블리도 연습으로 어떤걸 제작하면 잘 연습할 수 있을까 고민하던 중 떠오른 것이 바로 에디터 아이디어이다.

물론 실사용이 가능할정도로 고퀄리티로 개발할 계획은 없다.
능력도 없지만

Rust 기반의 개발

현업 사용자들이 사용 후 만족도가 가장 높다는 언어 중 하나인 Rust도 배워볼겸, wasm 언어는 Rust로 선택했다.
러스트를 웹어셈블리로 컴파일하기
위의 링크에서 간단한 설치, 구동을 참조하기 바란다.

**라이브러리 설치

**

rust 언어 자체도 아직까진 마이너한 언어이며 외국 레퍼런스도 찾기 힘들다.
더불어 웹어셈블리도 마찬가지라 자료를 찾고, 에러를 디버깅하는데 상당히 고생했다.
우선 아래의 명령어로 필요한 wasm폴더를 생성하자.

$ cargo new --lib webeditor

vue의 create vue와 비슷한 명령어로 기본적인 웹어셈블리에 필요한 부분들이 생성된다.
Cargo.toml

[lib]
crate-type = ["cdylib"]

[dependencies]
wasm-bindgen = "0.2.68"
js-sys = "0.3.45"
gloo = "0.2.1"

[dependencies.web-sys]
version = "0.3.45"
features = [
  'Document',
  'HtmlDocument',
  'Element',
  'EventTarget',
  'HtmlElement',
  'MouseEvent',
  'HtmlDivElement',
  'Node',
  'Window',
  'console',
  'Blob',
  'Url',
  'KeyboardEvent'
]

Rust는 node의 pakage.json과 비슷한 역할을 하는 Cargo.toml란 파일이 있다.
해당 파일에 필요한 라이브러리를 선언해주면 알아서 설치가 된다.
파이썬의 pip, php의 composer, 앞서 말한 node의 npm 최근 언어들은 당연하다는듯이 지원하는 라이브러리지만
시스템 프로그래밍 언어쪽에서는 상당히 드문 기능이며 반가운 기능이 아닐 수 없다.

우선 웹어셈블리를 rust에서 활용하기 필수적으로 들어가야 하는 라이브러리가 세가지 있는데,
wasm-bindgen, js-sys, web-sys이다
이 3개는 rust로 개발시 무조건 필수로 들어가게 되며
gloo의 경우 개발할때 이벤트리스너의 관리를 편하게 하기 위해 추가 한 라이브러리이다.
이벤트 리스너의 관리는 web-sys에서도 할 수 있지만 번거로운 부분이 많다.
그 외의 yew 등 직접 rust에서 dom을 관리할 수 있는 여러가지 라이브러리가 있으니 rust로 좀 더 큰 프로젝트를 다루거나, 개발할 예정이라면 관련 프레임워크를 찾아보는것도 나쁘지 않다.

$ wasm-pack build --target web

cargo.toml 수정 후
해당 폴더에서 아래와 같이 빌드 하게 되면 해당 라이브러리 설치도 동시에 진행되므로 수정했다면 한번 빌드를 해주자.

index.html에 아래와 같이 입력하자.

<!DOCTYPE html>
<html>
  <head>
    <meta content="text/html;charset=utf-8" http-equiv="Content-Type"/>
    <title>Hello wasm-pack! WebEditor</title>
    <link ref="stylesheet" type="text/css" href="wasm-rte.css" />
  </head>
  <body>
    <div id="WebEditor"></div>
    <script type="module">
      import init, * as Editor from './pkg/webeditor.js';
      async function execute() {
        console.log(Editor);
        await init();
        var test = Editor.WebEditor("#WebEditor");
      }
      execute();
    </script>
  </body>
</html>

우리는

<div id="WebEditor"></div>

에 해당 에디터를 자동생성하게 할 것이며
아래 script 모듈로 선언된 부분에 해당 태그들을 불러 올 수 있게 할 것이다.

이제 src/lib.rs로 가서
아래와 같이 입력한다

extern crate wasm_bindgen; 
use gloo::{events::EventListener};
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsValue;
use wasm_bindgen::JsCast;

#[wasm_bindgen(js_name = WebEditor)]
pub fn web_editor(dom: &str) -> Result<(), JsValue> {
    let window = web_sys::window().expect("no global `window` exists"); //window 객체를 러스트에서 다룰 수 있도록 가져옴
    let document = window.document().expect("should have a document on window"); //마찬가지로 document 객체를 가져올 수 있도록 가져옴
    let ele = &format!("{}", dom);
    let wrapper = document.query_selector(&ele).unwrap().unwrap().dyn_into::<web_sys::HtmlElement>().unwrap(); //javascript(index.html)에서 입력한 id를 가져옴
    wrapper.set_class_name("wasm-editor"); //해당 div의 클래스를 가져온다.
    let editor = document.create_element("div")?; //div를 하나 생성
    editor.set_class_name("wasm-content");
    editor.set_attribute("contenteditable", "true"); //편집 가능한 div로 속성을 정함
    editor.set_attribute("placeholder", "내용을 입력하세요."); //placeholder 속성이나 div에는 동작하지 않음. 추후 기능을 넣기 위해 넣은 요소
    editor.set_inner_html("<p><br></p>"); //개행(엔터) 처리가 될 때 p태그가 추가되게 하기 위하여 초기값으로 <p> 태그를 넣어준다.

    wrapper.append_child(&editor); // 추가
    Ok(()) //리턴
}

대부분이 러스트나 웹어셈블리나 어색하기에 설명을 최대한 자세하게 해보자면

#[wasm_bindgen]가 선언되어야만 javascript내에서 접근할 수 있는 함수가 된다.
그리고 (js_name = WebEditor)처럼 해당 부분이 붙으면 함수명을 러스트에서 활용하는 함수와 별개로 정할 수 있으며, 따로 붙이지 않는다면 러스트에서 선언한 함수가 외부에서 접근할 수 있는 함수명이 된다.
위 소스의 경우 선언하지 않은 경우 web_editor가 외부함수명이 된다.

pub fn web_editor(dom: &str) -> Result<(), JsValue> 

시스템 프로그래밍 언어답게 리턴값과 입력값은 무조건 자료형을 입력해줘야하는데
dom: $str은 dom이라는 변수명을 문자형으로 받겠다는 뜻이다
그리고 화살표함수(->)가 선언되면 해당 함수에 리턴값을 선언하겠다는 뜻이며
Result<(), JsValue>가 리턴값이다.
러스트내의 고유 자료형은 아니고 웹어셈블리때문에 존재하는 특수한 자료형이다.
러스트의 경우 retrun을 따로 언급하지 않으면 가장 마지막 행에 해당하는 값이 리턴이 되는데
Ok(())로 리턴하고 있으며 사실상 아무값도 리턴하지 않겠다는 뜻이다.
JsValue는 러스트에서 처리한 값을 웹브라우저에서 사용하는 자바스크립트 값 형태를 말하는데,
Result<JsValue, JsValue>식으로 리턴값을 선언하고
Ok((JsValue))식으로 리턴할 수 있지만 개발해본 바로는 이러한 리턴값은 실제로 잘 쓰이지 않게 된다.
왜냐면 어셈블리내에서 다 처리하고 돌아오는 결과값이 Js형태의 값보다는 특정한 처리값인 문자열이나 숫자, 복잡해봐야 json형태이기 때문이다.
하지만 해당 웹어셈블리를 활용할때의 리턴값이 해당 형태를 취해야만 빌드가 되므로 위와 같이 선언해주어야 한다.

러스트 웹 어셈블리 둘 다 워낙 생소하기에 설명이 길었는데,
위와 같이 소스 수정후

$ wasm-pack build --target web

로 빌드 후 index.html에서 보게되면 해당 div요소가 편집가능한 속성이 추가되어 있을 것이다.
다음 글에는 간단한 위지윅에디터의 기본인 속성 추가와 외부 js파일을 러스트 내에서 직접 접근할 수 있는 방법을 에디터 제작과 함께 알아보려 한다.

728x90
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
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
글 보관함