Shiny 웹앱에 정적 HTML 문서 넣기

R-Programming

“Shiny 웹앱을 개발할 때, 기능에 대한 설명을 위한 도움말 기능의 구현이 필요할 수 있다. 아마도 Rmarkdown으로 HTML 기반의 웹 문서를 떠올릴 것이다. 이 글에서 해당 솔루션을 제시한다.”

유충현
2021-10-19

프롤로그

통계 기반의 데이터 분석을 UI/UX로 지원하는 Shiny 웹앱 기반으로 작성하고 있다. BitStat라고 이름을 명명한 프로젝트는 어느 정도 가시적인 앱의 형상이 구현되었고, 이제는 필요한 기능을 추가하는 일을 본격적으로 진행할 때다. 그러기에 앞서서 초보자가 BitStat의 기능을 쉽게 익힐 수 있도록 도움말 기능을 추가하였다.

도움말 기능을 추가하면서 겪은 몇 가지 문제점에 대해서 정리하였다. Shiny 웹앱에 정적 HTML 문서를 넣고자하는 R 사용자에게 도움이 되었으면하는 바램이다.

htmlOutput

아시다시피 Shiny에서 웹 페이지를 출력하는 출력 위젯은 htmlOutput이다. Shiny 웹앱에 정적 HTML 문서를 넣고자하는 R 사용자는 BitStat에서 정적 웹 문서를 만들고, 그 문서를 htmlOutput에 넣어주는 것을 제`일 먼저 떠올렸을 것이다.

어느 누구도 이의를 제기하지 않을 명확한 방법이다.

다음은 “데이터 준비”라는 도움말을 만들어 놓은 HTML 문서를 삽입하기 위해서 출력 위젯인 htmlOutput를 “date_prepare”라는 아이디로 정의한 예다.

output$ui_help <- renderUI({
  tagList(
    tabBox(
      width = 12,
      tabPanel(
        title = translate("데이터"),
        tabsetPanel(
          tabPanel(
            title = translate("데이터 준비"),
            htmlOutput("date_prepare", style = "height: 700px;")
          ),
          tabPanel(
            title = translate("데이터 진단")
          ),
          tabPanel(
            title = translate("데이터 변환")
          )          
        )
      ),
      
      tabPanel(
        title = translate("기술통계"),
        tabsetPanel(
          tabPanel(
            title = translate("탐색적 데이터분석")
          )
        )
      )
    ) 
  )  
})

웹 문서 랜더링하기

이 작업을 수행하면서 웹 문서를 랜더링하여 htmlOutput에 넣는 방법에는 두 가지 솔루션이 있음을 알았다.

includeHTML

includeHTML는 HTML 파일을 읽어 HTML 컨텐츠를 삽입한다. 함수 이름이 직관적이라 쉽게 그 기능을 유추할 수 있을 것이다.

help 디렉토리의 “date_prepare.html” HTML 파일을 로드하기 위해서 렌더링 파트를 완성했다.

output$date_prepare <- renderUI({
  includeHTML("help/date_prepare.html")
})

렌더링된 결과는 다음 그림과 같다. 원하는대로 웹 문서가 잘 삽입되었다.

htmltools 패키지의 includeHTML 함수는 유용한 함수임에는 틀림없다. 이 함수는 정적 HTML 페이지를 Rmarkdown 문서에 포함시키는 것을 쉽게 준다. 그러나 Shiny에서 웹 페이지를 추가할 때는 예기치 못한 부작용(side effect)이 발생했다.

사이드바의 도움말 메뉴를 실행해서 도움말을 출력한 이후에, 다른 메뉴의 기능이 작동하지 않았다. 메뉴를 클릭해도 어떠한 이벤트가 발생하지 않았다. 사이드바의 메뉴 모양도 미묘하게 바뀌어 있었다. 화면처럼 선택한 사이드바인 도움말에 사각형의 보더가 표현되었다.

Shiny의 근간이 되는 Bootstrap의 CSS, Javascript와 삽입된 HTML 페이지의 CSS, Javascript가 충돌한 것으로 여겨진다. 그러므로 includeHTML 함수로 Shiny 웹앱에 정적 HTML을 삽입할 경우에는 간단한 페이지로 제한할 필요가 있다.

iframe에 HTML 삽입하기

충돌을 피하기 위해서 iframe에 HTML을 삽입하였다.

addResourcePath("tmpuser", getwd())

output$date_prepare <- renderUI({
  tags$iframe(
    seamless = "seamless",
    src = "tmpuser/help/date_prepare.html",
    width = "100%",
    height = "100%"
  )
})

여기서 중요한 것은 addResourcePath 함수로 워킹 디렉토리를 명시적으로 정의하지 않으면, iframe에서 HTML 파일을 찾을 수 없다.1

랜더링된 결과는 includeHTML로 출력한 HTML의 화면과 미미한 차이가 있다. iframe 영역이 명확하게 구분된다는 점이다. 그러나 부작용없이 정상적으로 다른 모든 기능이 구동되었다.

에필로그

정적 웹 페이지를 Shiny 웹앱에 포함시킬 경우에는 iframe을 확보한 후 iframe에 포함시키는 것이 부작용 없이, 기 정의한 다른 기능과 문제없이 동작하게 된다.


  1. https://stackoverflow.com/questions/24875943/display-html-file-in-shiny-app을 참고하였음↩︎

Citation

For attribution, please cite this work as

유충현 (2021, Oct. 19). Dataholic: Shiny 웹앱에 정적 HTML 문서 넣기. Retrieved from https://choonghyunryu.github.io/posts/2021-10-19-includehtml/

BibTeX citation

@misc{유충현2021shiny,
  author = {유충현, },
  title = {Dataholic: Shiny 웹앱에 정적 HTML 문서 넣기},
  url = {https://choonghyunryu.github.io/posts/2021-10-19-includehtml/},
  year = {2021}
}