Rust와 Yew로 프론트엔드 개발하기

Rust로 프론트엔드를 개발할 수 있는 프레임워크인 Yew를 체험해봤는데, 그 후기를 써보려고 합니다.

WASM & Rust

웹 프론트엔드라 하면 보통 JavaScript나 JS로 컴파일되는 TypeScript 등의 언어를 사용해서 개발하는데요, 웹 어셈블리(WASM)라는 기술을 이용해서 JS 외의 다른 언어로 프론트엔드를 만들 수 있습니다.

웹 어셈블리는 C#, Rust 등 다양한 언어로 사용할 수 있고, 각 언어별로 Blazor 등 다양한 프레임워크도 존재하는데요, Rust에서는 Yew라는 프레임워크가 가장 잘 만들어져 있습니다.

%EC%8A%A4%ED%81%AC%EB%A6%B0%EC%83%B7%202024-08-21%20045153

Yew

Yew는 React나 Vue 처럼 컴포넌트 기반으로 SSR 웹사이트를 만들 수 있게 해주는 Rust 프레임워크입니다.

2017년에 처음 발표되어서 이제 6~7년 정도 되었는데요, WASM이 2015년에 처음 발표되었으니 거의 웹 어셈블리와 역사를 같이 했다고 봐도 과언이 아닙니다...만 아직도 1.0이 정식으로 나오지 않은 상태입니다.

처음 써보면 리액트와 상당히 유사하다는 느낌을 받을 수 있고, 실제로도 use_state 등 대놓고 리액트 같은 JS 프레임워크로부터 가져온 기능이 상당히 많은 걸 볼 수 있습니다.

기본적인 Yew의 기능들을 알아보겠습니다.

컴포넌트

컴포넌트는 함수에 #[function_component] 어트리뷰트를 붙혀서 만들 수 있습니다.

#[function_component]
pub fn Component() -> Html {
    html! {
        <p>Hello, Vue!</p>
    }
}

html!은 Yew에서 사용하는 매크로입니다. IntelliJ (RustRover), VSCode, Neovim에 이 매크로에 대응하는 플러그인이 있으니 그걸 사용하면 문법 하이라이팅 지원을 받을 수 있습니다.

프로퍼티

하위 컴포넌트에 값을 전달할 때 사용합니다. 타입스크립트에서 미리 프로퍼티의 인터페이스를 만들고 사용하듯이, Yew에서도 먼저 구조체를 선언한 다음 해당 형식대로 자식 컴포넌트에 값을 넘깁니다.

#[derive(Properties, PartialEq)]
struct Probs {
    #[prop_or(true)]
    pub is_loading: bool,
}

#[function_component]
fn HelloWorld(probs: &Probs) -> Html {
    html! { <>{"Am I loading? - "}{probs.is_loading.clone()}</> }
}

제네릭 컴포넌트

컴포넌트와 프로퍼티를 제네릭을 통해 선언하는 것도 가능합니다.

#[derive(Properties, PartialEq)]
struct GenericProbs<T>
where
    T: PartialEq,
{
    data: T,
}

#[function_component]
fn HelloGeneric<T>(probs: &GenericProbs<T>) -> Html
where
    T: PartialEq + ToHtml,
{
    html!{
        <p>
            {&probs.data}
        </p>
     }
}

스테이트

스테이트는 use_state라는 훅을 통해 할 수 있습니다. 리액트와 달리 값이 아닌 클로저를 넘깁니다.

pub fn TestComponent() -> Html {
    let counter = use_state(|| 0);

    let onclick = {
        let counter = counter.clone();

        move |_| {
            let value = *counter + 1;
            counter.set(value);
        }
    };

    html! {
        <div>
            <button {onclick}>{ "+1" }</button>
            <p>{ *counter }</p>
        </div>
    }
}

onclick 클로저에서 counter.clone()을 통해 counter를 복사하는 걸 볼 수 있는데, Rust의 소유권 규칙을 지키기 위함입니다.

web_sys

원래 React에서 JS, TS 코드를 써서 하던 일은 wasm_bindgenweb_sys 크레이트를 통해 할 수 있습니다. 아래와 같이 코드를 짜면 console.log를 Rust 측에서 사용할 수 있습니다.

#[wasm_bindgen]
extern "C" {
    #[wasm_bindgen(js_namespace = console)]
    fn log(s: &str);
}

CSS Framework

Yew에서 CSS 프레임워크를 쓰기 위해서는 Pico CSS 같은 CSS only가 아닌 이상 node 환경도 함께 설정해주어야 합니다. 예를 들어 Yew와 UnoCSS를 함께 쓰기 위해서는 아래와 같이 합니다. (pnpm을 사용하였으며, npm이나 yarn을 쓸 때는 다소 차이가 있을 수 있습니다)

  1. pnpm init 을 통해 pnpm 환경을 설정합니다.
  2. package.json@unocss/cli를 추가합니다. 이러면 UnoCSS를 커맨드라인에서 사용할 수 있습니다.
  3. package.json의 scripts에
    "uno": "unocss src/**/*.rs index.html --out-file static/uno.css",

    를 추가합니다.

  4. Yew의 index.html의 head에
    <link data-trunk rel="css" href="./static/uno.css" />

    를 추가합니다.

이 과정을 통해 UnoCSS가 Rust 파일을 기반으로 CSS 파일을 생성할 수 있게 하고, Yew가 UnoCSS의 CSS 파일을 가져다 쓰게 합니다.

Previous Post Next Post