web assembly를 이용하여 웹페이지에서 Shiny App 활용하기

webR이라는 기술을 활용하여 별도의 웹 서버를 사용하지 않고도 유저의 웹 브라우저(크롬)에서 Shiny App을 사용할 수 있게 하는 방법을 소개합니다.

R
webR
shiny
wasm
github page
quarto
Author
Published

September 10, 2023

개요

이번 글은 이전 글 에이어 정적 페이지에서 shiny application을 webR로 제공하는 방법에 대해 소개합니다.

webR (R)과 shiny의 차이

webR을 사용하기 위해 R과 Shiny의 차이점중 한가지를 짚고 넘어가는 것이 좋습니다.

R은 한번에 하나의 코드만 실행 가능합니다. 즉, 새로운 코드를 실행하기 위해서는 이전의 연산이 종료된 상태여야 합니다.

그런데 Shiny는 (app.R이라는) 코드를 실행한 상태에서 사용자로부터 입력을 받고, 이를 통해 새로운 계산을 진행합니다.

이는 webR과 유사하게 shiny에서 ( 로컬에 ) 별도의 server를 만들고, 브라우저의 입력값과 계산된 출력 값을 서버와 주고 받으면서 ui로 보내주는 과정을 거칩니다.

그래서 webR과 Shiny를 이어주려면 아래 그림처럼 또 하나의 브라우저 (service worker)를 만든다고 이해하는 것이 편합니다.

Shiny를 사용자의 브라우저에서 실행시키면 얻을 수 있는 장점과 단점은 webR의 장단점과 대체로 유사합니다.

그러나 service worker라는 추가 개념이 등장했고, 이로 인한 Cross-Origin Isolation이라는 새로운 문제점이 발생합니다.

Cross-Origin Isolation

Shiny를 정적 페이지에서 제공하기 위해 Cross-Origin Isolation의 정확한 개념을 이해할 필요는 없지만, 설명을 하면 다음과 같습니다.

service worker는 별도의 프로세스를 위해 외부에서 (자체 실행을 위한) 파일을 다운로드 받고 이를 사용합니다.

그런데 최근의 웹페이지에서는 보안 이슈로 인해 (검증되지 않은) 외부의 파일을 실행하는 것을 정책적으로 막고 있습니다.

이를 위해 정적 페이지를 제공하는 서비스 (github, netlify, wordpress 등)에서 외부 파일의 실행을 허용하는 설정을 해야합니다.

차라투 블로그가 사용하는 github page를 위해서는 enable-thread.js 라는 스크립트를 실행해야합니다.

마찬가지로 Quarto를 활용해 웹페이지를 만들고, Shiny에서 기본으로 제공하는 01_hello 심도록 하겠습니다.

Quarto 페이지의 구성

Shiny를 정적페이지로 제공하는 quarto page에는 3가지가 필요합니다.

  1. Javascript 파일:
  • service worker를 만들기 위한 httpuv-serviceworker.js (from Inspired from Here)
  • (github blog 기준) Cross origin isolation을 풀기 위한 enable-threads.js
  1. Button & iframe: shiny는 webR에 비해 조금 더 로딩이 필요하기 때문에 진행 상태를 보여줄 버튼과, shiny app을 심을 iframe이 필요합니다.

  2. Javascript 파일: webR을 준비하고, iframe에 Shiny를 심는 loadshiny.js

<!-- 1. scripts -->
<script src='httpuv-serviceworker.js'></script>
<script src='enable-threads.js'></script>

<!-- 2. button & iframe -->
<button class="btn btn-success btn-sm" type="button" style="background-color: dodgerblue" id="statusButton">
  <i class="fas fa-spinner fa-spin"></i>Loading webR...
</button>
<div id="iframeContainer"></div>

<!-- 3. webR & shiny-->
<script type="module" src='loadshiny.js'></script>

추가적으로 loadshiny.js의 두 부분을 커스텀 해야합니다. (TODO로 표기)

  1. 임베드할 shiny app을 담고 있는 url 코드 (app.R): 이 글의 경우 차라투 블로그의 github에 (공개 된) 있는 코드를 사용합니다.

  2. service worker를 적용할 범위: httpuv-serviceworker.js를 등록하고, index.html을 담고 있는 디렉토리를 scope에 저장합니다.

마지막으로 디렉토리 구조는 다음과 같습니다.

  • 2023-09-10-wasm2
    • enable-threads.js
    • index.qmd
    • index.html
    • loadshiny.js
    • httpuv-serviceworker.js

실제 webR + shiny 결과

정리

단순히 webR을 심는 것보다 shiny를 심는 것은 3가지 이유에서 조금 더 복잡합니다.

  1. service worker 의 구성 및 연결
  2. shiny app의 실행을 위한 shiny 패키지를 webR에 설치
  3. 실제 정적 페이지에 배포하기 전까지 로컬 PC 에서는 shiny 결과를 확인할 수 없음

또한 이전의 webR과 마찬가지로 단순한 shiny app만 webR로 구현이 가능합니다.

가벼운 (특히 shiny 교육) 용도로는 shiny app을 별도의 서버를 구성하지 않고도 정적 페이지로 사용자에게 제공할 수 있다는 점은 꽤 흥미롭고, (수초의 시간이 더 필요하긴 하지만) 여전히 개선할 수 있는 부분이 존재합니다.

다만 R과 quarto를 넘어서 javascript를 포함한 웹 개발 지식과 service worker 구성을 위한 네트워크와 인프라 구성에 대한 경험이 없다면 많은 시행착오가 필요하기도 합니다.

이 webR에 대해 appsilon은 초창기 기술인 만큼 아쉬운 점도 있지만 동적 페이지에서 제공되는 shiny app을 대체할 기술이라기보단, 상호간에 보조할 수 있는 방법이라고 평가했습니다.


이전 글과 이번 글을 통해서 webR과 shiny를 정적페이지로 사용자에게 제공하는 방법을 알아보았습니다.

배경 지식이 없다면 한번에 이해하긴 어렵고, 이렇게 사용할 수 도 있다는 것만 기억하셔도 충분합니다.

🤗 Let’s talk

차라투에서는 R과 Shiny에 대한 컨설팅을 제공합니다. 진행중인 프로젝트 관련하여 도움이 필요하시다면 jinhwan@zarathu.com 으로 알려주세요!

Reuse

Citation

BibTeX citation:
@online{kim2023,
  author = {Kim, Jinhwan},
  title = {Web {assembly를} {이용하여} {웹페이지에서} {Shiny} {App}
    {활용하기}},
  date = {2023-09-10},
  url = {https://blog.zarathu.com/posts/2023-09-10-wasm2},
  langid = {en}
}
For attribution, please cite this work as:
Kim, Jinhwan. 2023. “Web Assembly를 이용하여 웹페이지에서 Shiny App 활용하기.” September 10, 2023. https://blog.zarathu.com/posts/2023-09-10-wasm2.