-
R과 대시보드 : ShinyR 이모저모 2019. 4. 9. 19:54
R과 Shiny
최근 R이 이슈가 되고 있는 이유 중 하나는 사용의 편리함 때문입니다. R에서는 보다 편한 스크립트 실행 환경인 RStudio를 제공하지만, 여기서 더 나아가서 배표를 위한 R Markdown, 그리고 동적 대시보드/앱 등을 구현하기 위한 Shiny를 제공합니다. Shiny는 분석 업무의 결과를 R을 잘 모르는 현업에 배포할 때 매우 효율적인 수단이지만, 기존 R 스크립트와는 다른 이질적인 분위기와 제대로 된 가이드라인이 별로 없어서 접하기가 어려운 단점이 있습니다. 그래서 이번 포스팅에선 이 Shiny의 기초에 대해서 다뤄보고자 합니다.
[예시] https://youtu.be/wKG3LZXG_8g
1. UI와 서버
Shiny는 크게 UI와 서버라는 두 가지 요소로 구성되어 있습니다. 이름 그대로 UI는 유저 인터페이스를 위해 보여질 프론트 엔드, 서버는 백엔드를 생각하면 됩니다. 이 두 개체들은 input이랑 output이라는 곳에 R에서 돌아가는 객체들을 담아 상호작용을 하게 되며, render~로 시작하는 함수들을 활용해서 Shiny로 만들어진 html 구조에 집어넣게 됩니다. 이 rendering된 객체들은 ~Output이라는 함수를 이용해서 나오게 되며, 이러한 객체간 연결고리들로 Shiny는 하나의 앱으로 완성되게 됩니다. 말로는 설명이 모호하니 그림으로 표현하자면, 아래와 같은 구조가 됩니다.
UI는 기본적인 fluidPage(동적 페이지), shinydashboard 패키지에서 제공하는 dashboardPage 등 다양한 함수로 정의할 수 있으며, 깔끔한 상태에서 본인이 직접 디자인을 하고 싶다면 동적 페이지를, 아니면 대시보드 페이지를 활용하는 것이 좋습니다.
서버는 function(input, output) 형태로 정의하며, 보다시피 input을 받아서 output으로 내뱉는 역할을 해줍니다. 그리고 shinyApp함수는 이 두 객체를 받아서 앱으로 구성해주는 역할을 하게 됩니다.
2. Input과 Output 정의
이제 UI와 서버를 알았으니, 다음 단계인 Input과 Output을 정의해볼 차례입니다. 위에서 언급 했다시피 두 객체에 원하는 정보를 할당하는 법은 list와 동일하게 input$id, output$id 같은 식으로 할당하게 됩니다. 함수도 직관적으로 ~Input 형식으로 이름이 되어있으면 Input을 받아주는 함수, ~Output이면 Output을 받아주는 함수입니다. 예를 들어 봅시다.
이 코드는 예제에서 첫번째 탭에 보여지는 요소들을 구성하기 위한 코드입니다. Input에 해당하는 fileInput, selectInput과 Output에 해당하는 dataTableOutput, uiOutput이 보입니다. 이 함수들은 말 그대로 파일이나 유저가 고른 것을 input요소로 만들고, data.table형식 객체와 ui를 Output으로 만들어주는 역할을 하게 됩니다.
첫번째 함수인 fileInput을 예로 들면, 함수의 처음 선언되는 data는 이 객체가 input에 어떤 이름으로 저장될 지입니다. 즉, 나중에 서버에서 input$data로 호출하게 되면 해당 함수가 만들어낸 객체를 불러오게 되는 것이죠. 다음으로는 UI에 어떻게 보일 지입니다. 이 함수에서 정의한대로 파일을 선택하는 곳에 Load Data(CSV Only!)라고 쓰여지는 것을 볼 수 있습니다
다음으로 multiple = FALSE는 선택형 input에서 자주 보이는 옵션으로, 여러 개를 선택할 수 있는지에 대해 설정하는 것입니다. 마지막으로 accept는 어떤 파일을 받아줄지 선택하는 것이며, 저는 csv, 콤마로 나눠진 파일만 받도록 설정하였습니다.
Output 관련 예시로 dataTableOutput을 보면 위의 input과는 다르게 굉장히 간단히 설정되있는 것을 볼 수 있습니다. 이는 함수가 후술할 서버에서 정의한 dt라는 객체를 내보내서 ui에 띄우는 역할만을 하기 때문입니다.
3. Interactive 객체 : reactive, event, action
본래 Shiny는 상호작용을 위한 대시보드를 R 환경을 이용하여 쉽게 만들기 위해 디자인되었기 때문에, 당연히 이 상호작용을 하지 못하고 스크립트 리더로서의 기능만 한다면 별 쓸모가 없을 것입니다. 물론 패키지 내의 여러 데이터나 기능들을 활용하기야 하겠지만, 사용자가 로컬에 있는 파일을 불러오거나 위의 도식에서 보듯이 내 입맛에 맞는 input을 넣으려면 이런 상호작용을 할 수 있는 형식이 필요합니다. 여기서 가장 기초는 reactive 객체입니다.
3-1 reactive
Reactive는 reactive, reactiveplot 등 여러 종류의 함수로 선언할 수 있으나, 제일 일반적인 것은 이 reactive()함수로 정의하는 객체입니다. 이 함수에 아까 fileInput에서 받아온 data$datapath(데이터 경로)를 이용해서 데이터를 불러올 수 있습니다.
이러면 이제 df라는 reactive 객체가 정의되었습니다. Reactive 객체는 함수처럼 ()를 뒤에 붙여야만 사용할 수 있습니다. 예제에서 df 바로 다음 코드를 보면 df객체를 이용해서 아까 2번 항목에서 보였던 dt output을 만드는 것을 볼 수 있습니다.
Rendering에 대해서는 제가 따로 후술하지 않겠습니다. 간단히 말하자면 renderDataTable은 데이터 테이블 형식의 객체를 렌더링 해주는 것이며, 데이터 프레임/tibble도 호환됩니다. renderPlot은 plot을 호환해주고, 비슷한 식으로 다른 객체들도 rendering이 가능하며 일부 js기반 뷰어 환경을 따로 구축하는 패키지들 또한 shiny를 지원해주면 해당 형식을 rendering 하는 함수를 제공하는 경우가 많습니다.
상기 코드에서 굳이 req()를 쓰는 이유는 data라는 인풋이 없을 때 렌더링을 하도록 정의하면 df라는 객체는 data 인풋에 reactive란 객체라 존재하지 않아 오류가 발생하기 때문입니다.
3-2 event와 action
이벤트, 말 그대로 특정 행동이 감지되면 실행하는 코드입니다. 주로 actionButton에서 나오는 action을 감지해서 하게 되며, actionButton은 말 그대로 UI에 버튼을 만들어서 그것을 사용자가 누르면 input에 입력을 해주는 역할을 합니다. 이는 위 첫번째 그림에서 나오는 actionButton 함수를 보면 알 수 있습니다.
이러한 액션이 감지되면 observeEvent같은 함수로 이벤트에 따른 행동을 진행하도록 코드를 짜게 되며, 예를 들어 두번째 탭 액션버튼인 Draw Plot(input id : draw)를 누르게 되면 plot을 렌더링 하는 아래와 같은 코드가 실행되게 됩니다.
4. Dashboard : page, header, sidebar, body, tab
이제 대시보드 안에 들어갈 요소들이 어떤 의미를 가지는지는 알 수 있게 되었습니다. 이제 대시보드의 제목부터 탭까지 어떻게 구성할 지를 살펴봐야 합니다.
제목에 써진 바와 같이 예제에서도 page부터 tab까지 단계별로 정의하고 있으며, Page는 가장 큰 범위에서 dashboardPage함수로 정의하게 됩니다. 다음으로 header는 제목칸을 의미하며, title옵션을 이용하여 텍스트를 넣게 되며 dropdownMenu등의 Shiny에서 제공되는 옵션을 활용하여 더 많은 정보를 넣어줄 수 있습니다.
다음으로 sidebar입니다. dashboardSidebar 함수로 정의하는 sidebar는 말 그대로 대시보드 옆에 붙을 바에 어떤 내용을 넣을지를 결정합니다. 예제에서는 sidebarMenu 함수로 Load and View Data라는 설명이 UI에 보이고 ID는 data인 탭과, Visualizing data라는 설명을 가진 visual이란 ID를 가진 탭을 만들어보도록 하겠습니다.
함수 설명 문서의 예제에도 나와있든 sidebar~가 붙은 함수를 이용하여 더 많은 기능을 추가할 수 있습니다.
마지막은 body입니다. 이 곳에는 아까 sidebar에서 정의했던 탭들을 넣어보겠습니다. 먼저 tabItems라는 함수로 큰 집합을 정의한 후, 안에 tabItem이라는 함수로 탭 각각을 위한 공간을 정의해줍니다. 탭 아이템은 tabName이라는 id로 구분되며, 위에 sidebar에서 정의했던 id들이 사용되게 됩니다.
5. Shinyapp
이제 완성된 예제를 직접 실행해보겠습니다. shinyApp이라는 함수를 이용해서 ui와 서버 객체를 넣고 실행해도 되지만, 스크립트 pane 우측 상단의 Run App이라는 버튼을 이용해서도 할 수 있습니다.
실행하면 콘솔에 runApp이 뜨면서 팝업창에 디자인한 앱이 뜨게 됩니다. 좀 더 큰 화면으로 보고 싶다면 확대를 하거나 Open in Browser 버튼을 눌러 브라우저에 직접 띄우면 편하게 볼 수 있습니다.
이제 앱 화면에서 아까 디자인 한 부분이 어떻게 적용되었는지에 대해서 그림으로 정리해보도록 하겠습니다.
위 사진에서 보듯 앞서 정의한 input, output, ui, server가 모여 하나의 앱으로 작동하고 있음을 볼 수 있습니다.
*convert 기능은 구현되어 있지 않습니다.
6. 마치며
Shiny는 R 마크다운과 더불어 쉽게 R 결과물을 전달해줄 수 있으며, 특히 사용자의 행동에 반응하는 결과물을 R 문법으로 쉽게 작성할 수 있다는 매력이 있는 기능입니다. 예제가 부족하고 제대로 된 설명서들이 잘 없어 독학으로 배우기 힘들지만, 이 글을 보고 shiny 공식 홈페이지의 갤러리(https://shiny.rstudio.com/gallery/)에서 예제 코드들을 본다면 간단한 분석 리포트 정도는 어렵지 않게 할 수 있을 것입니다.
[ 컨텐츠 코드 ]
'R 이모저모' 카테고리의 다른 글
Shiny : 대시보드 배포하기 (7) 2019.04.24 R과 워드 (0) 2019.04.19 R과 병렬처리 (4) 2019.04.03 알고리즘 소개 : XGBoost (0) 2019.03.30 R과 데이터프레임(3) (0) 2019.03.24