목차
공부를 위한 자료
shiny
shiny-ext
shiny는 R로 웹 어플리케이션을 만들 수 있게 해주는 프레임워크입니다. shiny는 ui
라고 불리는 화면, server
라고 불리는 데이터 처리 및 관리, 그 안에 입출력과 render
와 배포로 구성되어 있습니다.
패키지를 설치하는 방법은 다음과 같습니다.
## cran에서 설치install.packages("shiny")## github에서 설치if (!requireNamespace("devtools")) install.packages("devtools")devtools::install_packages("rstudio/shiny")
shiny는 개발이 어려운 만큼 생태계 활성화를 위해 다양한 예시를 쉽게 접할 수 있게 준비되어 있습니다.
library(shiny)runExample()
## Valid examples are "01_hello", "02_text", "03_reactivity", "04_mpg", "05_sliders", "06_tabsets", "07_widgets", "08_html", "09_upload", "10_download", "11_timer"
위 코드는 실행해 볼 수 있는 예제를 보여줍니다. 아래처럼 실행하시면 바로 실행되는 shiny
앱 예제를 확인하 실 수 있습니다. shiny github에는 예제 repo
가 있고, 활용할 수 있는 예제는 2017-11-22 기준 111개 입니다.
runExample() 함수로 리스트 내의 예시들을 실행해 볼 수 있습니다.
runExample("01_hello")
웹에서 제공하는 다른 사람의 소스를 바로 실행해 볼 수 있습니다.
# 기스트의 예시 앱 실행runGist()# 깃헙의 예시 앱 실행runGitHub()
Gist란 스크립트 파일 몇 개만 공유할 때 유용한 서비스입니다. shiny 코드의 경우도 간단하게 한 두개 파일로만으로도 작성할 수 있으므로 Gist로 공유되는 경우도 있습니다.
runGist("https://gist.github.com/mrchypark/61283120fea4e392116503703547f39d")
github의 username, repo 이름, 폴더가 있다면 폴더명을 지정해주면 경로내의 shiny 앱을 가져와 실행해 줍니다. 물론 앱내 사용하는 패키지가 없다면 설치해줘야 합니다.
if (!requireNamespace("dplyr")){ install.packages("dplyr")}if (!requireNamespace("shinydashboard")){ install.packages("shinydashboard")}runGitHub(repo = "shiny-examples",username = "rstudio",subdir = "086-bus-dashboard")
ui
라고 말하는 화면은 실제로 사용자가 보는 화면을 뜻합니다. shiny에서는 크게 titlePanel
과 sidebarPanel
, mainPanal
의 세 가지로 구성되어 있습니다. 이렇게 지정함으로써 html
에 대해 잘 몰라도 사용할 수 있게 shiny
가 구성되어 있습니다.
shiny
에서의 server
는 실제 서버가 브라우저와 통신하는 과정 전체를 간단하게 만들어주는 역할을 합니다. 화면에서 사용자가 동작하는 것에 대해서 받을 수 있는 input
을 서버에서 데이터나 그림 조작에 사용을 하고 웹 기술이 이해할 수 있게 render
하여 output
으로 화면에 다시 보내주는 형태로 shiny
가 동작합니다.
shiny
는 다른 R
패키지와는 다르게 강제하는 변수가 3개 있습니다. 그것은 input
, output
, session
입니다. 각각 객체로써 존재하고 이번에는 input
과 output
으로 데이터를 ui
와 server
가 교환하는 방법을 소개하겠습니다. 각각의 객체는 *Input
함수와 *Output
함수로 데이터를 입력 받아 저장합니다. *Output
함수는 render*
함수로 선언됩니다.
shiny
는 다른 R
패키지와는 다르게 강제하는 변수가 3개 있습니다. 그것은 input
, output
, session
입니다. 각각 객체로써 존재하고 이번에는 input
과 output
으로 데이터를 ui
와 server
가 교환하는 방법을 소개하겠습니다. 각각의 객체는 *Input
함수와 *Output
함수로 데이터를 입력 받아 저장합니다. *Output
함수는 render*
함수로 선언됩니다.
shiny
는 shiny-server
를 통해서 동작합니다. shiny-server
는 shiny app
이 동작할 수 있는 서버를 의미합니다. 오픈소스와 기업용 솔루션이 모두 준비되어 있으며 실습에는 http://www.shinyapps.io/를 이용해 보겠습니다.
library(shiny)ui <- fluidPage()server <- function(input, output){ }shinyApp(ui=ui,server=server)
ui는 화면의 위치를 나누는 함수와 입력을 받는 요소, 출력을 보여주는 요소로 구성되어 있습니다. *는 여러 이름이 있다는 뜻입니다.
# *Page(), *Panel() 등의 함수로 위치를 나누고 모양을 결정ui <- fluidPage( # *Input() 함수로 입력을 받는 요소들을 배치 # *Output() 함수로 결과물을 출력하여 배치)
fluidPage()는 반응형 페이지를 만든다는 뜻입니다. fluidRow() 함수, column() 함수와 함께 사용합니다.
fluidPage( fluidRow( column(), column() ))
column() 은 총 화면 길이를 12개로 나누어서 사용합니다.
fluidPage( fixedRow( column(2), column(10) ))
offset 이란 그만큼 띄어낸다는 뜻입니다.
fluidPage( fluidRow( column(4, "4" ), column(4, offset = 4, "4 offset 4" ) ), fluidRow( column(3, offset = 3, "3 offset 3" ), column(3, offset = 3, "3 offset 3" ) ))
column()내에 column()을 추가할 수 있습니다. column()내에 column()은 다시 그 속한 column()을 통 12개로 나누어서 크기를 지정합니다. column()내에 column()을 지정하기 전에 column()을 fluidRow()로 감싸야 동작합니다.
fluidPage( fluidRow( column(12, "Fluid 12", fluidRow( column(6, "Fluid 6", fluidRow( column(6, "Fluid 6"), column(6, "Fluid 6") ) ), column(width = 6, "Fluid 6") ) ) ))
*Panel() 함수는 기본적으로 3가지(titlePanel(), sidebarPanel(), mainPanel())를 제공하며 구조는 아래와 같습니다.
fluidPage( titlePanel(), sidebarLayout( sidebarPanel(), mainPanel() ))
sidebarLayout() 은 sidebarPanel()의 위치(왼쪽이 기본, 오른쪽)를 지정하기 위해 감싸는 함수로 왼쪽으로 되어 있다면 필요하지 않습니다.
*Panel() 함수에는 navbarPage()과 navlistPanel(), tabsetPanel()도 tabPanel()과 함께 사용할 수 있습니다. 각각의 링크로 가서 예시코드로 동작을 확인하겠습니다.
fluidPage( titlePanel("test"), sidebarLayout( sidebarPanel( sliderInput( inputId = "slider1" , label = "Slider" , min = 0 , max = 100 , value = 50 ) ), mainPanel("mainPanel Area") ))
fluidPage( titlePanel("test"), sidebarLayout( sidebarPanel( sliderInput( inputId = "slider1" , label = "Slider" , min = 0 , max = 100 , value = 50 ), dateInput( inputId = "date" , label = "Date input" , value = "2014-01-01" ), fileInput( inputId = "file" , label = "File input" ) ), mainPanel("mainPanel Area") ))
input에서 입력값을 받아 서버가 처리한 결과물을 보여주는 곳으로 아래와 같은 함수들과 추가 패키지들이 제공하는 *Output() 함수가 있습니다.
Function | Inserts |
---|---|
dataTableOutput() | 인터렉티브 테이블 |
htmlOutput() | HTML 문서 |
imageOUtput() | 그림 |
plotOutput() | 차트 |
tableOutput() | 테이블 |
textOutput() | 글자 |
verbatimTextOutput() | 코드 글자 |
fluidPage( titlePanel("test"), sidebarLayout( sidebarPanel( sliderInput( inputId = "slider1" , label = "Slider" , min = 0 , max = 100 , value = 50 ) ), mainPanel( plotOutput( outputId = "plot1" ) ) ))
Input() 함수와 Output() 함수는 사용할 변수에 대한 Id를 입력받습니다. 전부 글자형(character)로 구성됩니다. inputId는 서버에서 input$inputId의 형태로 사용하며 아래의 경우 input$slider1 입니다. outputId는 서버에서 만든 결과물을 전달 받기 위해서 사용하며 서버에서 저장할 때는 output$outputId로 사용합니다. 아래의 경우 output$plot1 입니다.
# inputIdsliderInput(inputId = "slider1", label = "Slider", min = 0, max = 100, value = 50)# outputIdplotOutput(outputId = "plot1")
server는 아까 Input() 함수로 입력받은 데이터를 사용하기 위한 input 변수와 Output() 함수로 출력하기 위한 결과물을 저장하는 output 변수, 마지막으로 출력 결과물인 R객체를 web의 세상에서 사용할 수 있는 상태로 output 변수에 저장하는 render*({}) 함수로 구성됩니다.
server <- function(input, output){ output$plot1 <- renderPlot({ plot(input$slider1) })}
shiny
는 웹 기술을 R
로 사용할 수 있게 만드는 덕분에 render
라는 과정이 필요합니다. 그래서 render
환경에서 변수들이 관리되어야 합니다. 입출력은 input
변수와 output
변수로 관리합니다. 이 두 가지는 render
밖에서 관리되는 변수로 다르게 취급해야 합니다.
input
: 웹 페이지의 입력을 통해서 들어오는 데이터를 사용하기 위한 변수output
: 웹 페이지에 R 연산 결과물을 전달하기 위해 사용하는 변수output
변수는 list
라고 이해하시면 좋을 것 같습니다. list
인 output
변수는 output$
뒤에 변수명을 작성함으로써 output
객체에 필요한 결과물을 전달합니다. 만약에 화면에 보여줘야할 것의 이름을 sample
이라고 하면 output$sample
에 필요한 내용을 선언하는 것으로 진행합니다.
...output$sample <- randerPlot({ ...plot()... })...
위 코드는 server
쪽 코드에서 작성합니다. 위 예시는 plot
함수로 만들어지는 이미지를 output$sample
에 저장하는 것을 뜻합니다. 그럼 ui
쪽에서 이걸 어디에다 위치하게 하는지를 결정하는 함수에서 사용할 수 있습니다. 이때는 plotOutput
함수를 사용합니다.
...plotOutput("sample")...
함수명이 Output
이고, "
로 변수명을 감싸며, output$
문법을 사용하지 않고 컬럼명인 sample
만 사용한다는 점을 주목해 주세요. server
쪽 코드에서 output$
에 컬럼명의 형태로 저장한 R
의 결과물을 ui
쪽 코드에서 *Output
함수에서 "
로 감싼 글자의 형태로 컬럼명만 작성해서 사용합니다. 그럼 이제 위해서 output$sample
에 선언할 때 사용한 render*({})
에 대해서 알아보겠습니다.
Rmd
때 render
라는 과정을 거쳐서 Rmd
를 md
로, md
를 html
로 바꾸는 것을 알아봤었습니다. shiny
에서도 같은 과정이 필요합니다. 그래서 render*({ })
함수가 필요합니다. 예시로 보겠습니다.
...output$sample <- randerPlot({ plot(faithful)})...
render*({ })
함수 안에는 plot
함수가 R
문법으로 작성되어 있습니다. 그리고 render*({ })
의 특이한 점은 ()
안에 {}
가 또 있다는 점입니다. 여기서는 ({ })
안쪽은 R
의 세계이고, 그 바깥은 웹의 세계라고 이해하겠습니다. 그래서 이런게 가능합니다.
...output$sample <- randerPlot({ data <- faithful[faithful$eruptions >3, ] plot(data)})...
R
의 세계 내에서 처리하는 것은 계속 사용할 수 있어서 data
변수에 선언한 내용을 plot
함수가 사용할 수 있습니다. 여기서 input$
이 활용될 때 반응형으로 작성할 수 있는 것입니다.
output() 함수에 대응되는 render() 함수가 모두 있습니다.
Output.func | render.func | Inserts |
---|---|---|
dataTableOutput() | renderDataTable() | 인터렉티브 테이블 |
imageOUtput() | renderImage() | 그림 |
plotOutput() | renderPlot() | 차트 |
tableOutput() | renderTable() | 테이블 |
textOutput() | renderText() | 글자 |
verbatimTextOutput() | renderText() | 코드 글자 |
intput$
은 output$
과 같이 웹의 세계에 있는 변수입니다. 그래서 *Input()
함수들을 통해서 input$
의 컬럼 이름으로 웹 페이지 내에서 얻을 수 있는 데이터를 R
의 세계에서 사용할 수 있게 해줍니다. 우선 input$
에 웹의 세계의 데이터를 R
의 세계로 가져와 보겠습니다.
sliderInput("sdata1", "슬라이더 입력:", min=50, max=150, value=100)
위의 코드는 sliderInput()
이라는 *Input()
패밀리 함수를 통해서 input$sdata1
이라는 곳에 웹 데이터를 저장하는 것을 뜻합니다. *Input()
패밀리 함수는 같은 규칙을 가지는데 입력형태명Input()
의 함수명을 가지고, 첫번째 인자는 input$
뒤로 붙을 컬럼명, 두번째 인자는 화면에 보여줄 글자를 의미합니다. 나머지 인자는 입력형태에 따라 다양하게 달라집니다.
runExample("01_hello")
를 수정해보겠습니다.우선 서버쪽 부터 보겠습니다.
# shiny 패키지를 불러옵니다.library(shiny)# server를 function으로 선언합니다. 웹의 세계에서 사용할 변수는 input과 output 입니다.function(input, output) {# 웹의 세계의 변수인 output에 list 컬럼인 distPlot을 선언합니다.# 그 선언을 renderPlot({})으로 해서 distPlot이 plot()으로 작성한 그림이라는 것을 이해합니다.# renderPlot({ ... }) 함수 안에 R의 세계에서 hist() 함수의 결과가 나오게 작성합니다.# 이때 input$bin으로 웹에서 사용자가 주는 데이터를 활용할 것입니다.# bin은 경계라는 뜻으로 연속형 변수를 histogram을 그릴 때 얼마나 구간을 나눌 것인지를 결정합니다.# ?hist를 통해 어떤 그림을 그리는 함수인지를 확인해 보세요.# ?seq를 통해 어떤 결과물을 만드는 함수인지 확인해 보세요. output$distPlot <- renderPlot({ x <- faithful[, 2] # Old Faithful Geyser data bins <- seq(min(x), max(x), length.out = input$bins + 1) # draw the histogram with the specified number of bins hist(x, breaks = bins, col = 'darkgray', border = 'white') })}
위에 코드에서 input$bins
가 render*({})
함수 안에서 사용됬습니다. output$
은 웹의 세계에서 사용하지만 input$
은 R
의 세계에서 사용합니다. 이부분은 reactive({})
함수에서 추가적으로 다루기로 하겠습니다. 이제 ui
쪽 코드를 확인해 보겠습니다.
# shiny 패키지를 불러옵니다.ibrary(shiny)# fluidPage는 shiny의 페이지를 보여주는 핵심 기능입니다. # titlePanel, sidebarPanal, mainPanel로 구성되어 있습니다.# titlePanel은 제목을 작성하는 곳입니다.# sidebarPanel은 전체 화면을 3등분해서 왼쪽을 뜻합니다.# mainPanel은 전체 화면을 3등분해서 오른쪽을 뜻합니다.fluidPage( # Application title titlePanel("Hello Shiny!"), # Sidebar with a slider input for the number of bins sidebarLayout( sidebarPanel( sliderInput("bins", "Number of bins:", min = 1, max = 50, value = 30) ), # Show a plot of the generated distribution mainPanel( plotOutput("distPlot") ) ))
reactive는 반응성으로 번역하기도 합니다. 반응성이라고 하는 이유는 input$bins의 값이 변하는 순간을 render({ })가 감지해서 변한 값을 반영하여 output$distPlot에 저장하고, 그것을 plotOutput("distPlot")으로 전달하여 그림을 새로 그리기 때문입니다. input의 변화에 반응
하여 output이 변화하니 간단한 반응성으로 연결된 것입니다.
reactive({})
: input$
에 대한 연산을 render({ }) 외부에서 수행하기 위해서 사용합니다.isolate()
: 즉각적인 반응이 아니라 어떤 다른 사용자의 입력(ex> action 버튼)을 인지하여 그 때 input
에 해당하는 부분을 확인하여 결과물을 만듭니다. render*({})
함수 내부에서 사용합니다. observeEvent({})
, eventReactive({})
: 어떤 input
을 인지하여 동작합니다. observeEvent({})
는 서버 부분에서 실행해야 할 것들을 담당하기 때문에 선언의 형태가 아니라 독립적으로 작성됩니다. 예를 들어 서버에 파일을 저장한다거나, 로그로 남기는 글자를 출력하거나 할 때 사용합니다.eventReactive({})
: reactive({})
와 같이 웹의 세상의 변수에 선언하면서 함수화
하여 R
의 세상에서 반응형 데이터로써 사용할 수 있습니다. reactiveValues({})
: input$
이나 output$
과 같은 역할을 하는 변수를 만드는 함수입니다. observe({})
: observeEvent({})
와 eventReactive({})
의 근간이 되는 함수로, input$
의 입력을 인지하는 역할을 합니다.reactive({})
, isolate()
위에서 shiny
코드들을 소개할 때 웹의 세상과 R
의 세상으로 설명한 부분이 기억나실 겁니다. 이것은 웹의 세상의 데이터를 R
의 세상으로 데려와서 여러 처리에 사용하고, 결과를 다시 웹의 세상으로 보내는 과정을 설명하면서 사용했습니다. 이 때 input$
은 R
의 세상에 있었고, output$
은 웹의 세상에 있었습니다. render*({})
함수를 통해서 R
의 세상의 결과물을 output$
에 전달한다고도 했구요. 이 과정에서 render*({})
함수는 목적이 결과물을 전달하는데 있습니다. input$
을 이용해서 계산하는 과정이 간단한 것들은 render*({})
함수 내부에 포함되어도 큰 문제가 되지 않지만, 계산을 여러 output$
에서 사용한다면 고치는 것을 고려해 볼 수있습니다.
개발에서는 반복되는 부분을 최대한 줄이거나 합치려는 경향이 있는데 2가지 장점이 있습니다. 하나는 효율화가 가능하구요, 다른 하나는 가독성이 좋아집니다.
우선 겹쳐서 사용하는 사례를 보겠습니다.
library(shiny)ui<-fluidPage( titlePanel("test"), sidebarPanel( selectInput("dataSelect","데이터셋 선택 :", choices=list("mtcars","sleep","iris","co2")) ), mainPanel( verbatimTextOutput("out1"), verbatimTextOutput("out2") ))server<-function(input, output){ output$out1 <- renderPrint({ summary(get(input$dataSelect)) }) output$out2 <- renderPrint({ str(get(input$dataSelect)) })}shinyApp(ui,server)
?get으로 어떤 동작을 하는 함수인지 확인하세요. renderPrint({})
가 두 번 있으면서 get
함수가 각각 실행되어 같은 연산을 두 번하는 효과가 발생하였습니다. 그렇다면 그 동작을 한번만 하게끔 바꿔보겠습니다.
library(shiny)ui<-fluidPage( titlePanel("test"), sidebarPanel( selectInput("dataSelect","데이터셋 선택 :", choices=list("mtcars","sleep","iris","co2")) ), mainPanel( verbatimTextOutput("out1"), verbatimTextOutput("out2") ))server<-function(input, output){ selectedData<-get(input$dataSelect) output$out1 <- renderPrint({ summary(selectedData) }) output$out2 <- renderPrint({ str(selectedData) })}shinyApp(ui,server)
get
함수로 데이터를 부르는 부분을 앞으로 빼내고, 선언한 selectedData
를 summary
와 str
함수로 사용하였습니다. 실행해 보시면 에러가 나는 것을 확인할 수 있습니다. 웹의 세상에서 R
의 세상 문법으로 작성해서 생기는 문제인데요. 에러코드를 함께 보겠습니다.
Error in .getReactiveEnvironment()$currentContext() : Operation not allowed without an active reactive context. (You tried to do something that can only be done from inside a reactive expression or observer.)
reactive context
라는게 눈에 띄는데요. 이것이 R
의 세상 문법이 동작하는 공간입니다.
selectedData<-get(input$dataSelect)
위에 코드는 R
문법으로 이루어져 있는데, 이것을 웹의 세상에서 작성했기 때문에 생기는 것이죠. 그럼 R
의 세상 문법으로 작성할 수 있게 render*({})
와 같은 환경을 제공하는 함수를 사용해 보겠습니다.
library(shiny)ui<-fluidPage( titlePanel("test"), sidebarPanel( selectInput("dataSelect","데이터셋 선택 :", choices=list("mtcars","sleep","iris","co2")) ), mainPanel( verbatimTextOutput("out1"), verbatimTextOutput("out2") ))server<-function(input, output){ selectedData<-reactive({ get(input$dataSelect) }) output$out1 <- renderPrint({ summary(selectedData()) }) output$out2 <- renderPrint({ str(selectedData()) })}shinyApp(ui,server)
reactive({})
는 그 내부를 R
의 세상으로 만듬으로써 선언 받는 변수를 함수화
하여 반응형 데이터의 특성을 유지한채로 R
의 세상에서 사용할 수 있게 해줍니다. 함수화
라는 부분을 잘 보셔야 하는데, selectedData
가 변수가 아니라 뒤에 ()
가 붙은 함수라는 사실을 확인하세요. 웹의 세상에 있는 selectedData
는 변수를 선언한 것이 아니라 R
의 세상에서 사용할 수 있게 함수화
하여 함수로써 선언하였습니다. selectedData() 함수는 앞에서 선언한 reactive({ get(input$dataSelect) })
부분이 변하는지를 보고 있다가 변하면 결과물을 R
의 세상인 render*({}) 함수 내부에서 사용할 있게 만들어 줍니다.
reactive({})와 render*({})는 내부에 품고 있는 input$ 변수의 변화를 계속 지켜보고 있습니다. 그리고 변화에 따라 순서대로 위 동작을 수행합니다. 전달 때문에 화면에 결과물이 변경되어서 보여지는 것입니다.
지금까지는 input이 변하는 대로 일일이 반응하는 형태의 반응성을 구현했습니다. 그런데 타이핑 같이 계속 빠른 변화가 있는 경우 오히려 성능의 저하를 가져오기 때문에 일일이 반응하지 말라고 해줘야 할 때도 있습니다. isolate()은 render*({})나 reactive({}) 안쪽에서 사용해서 input의 변화가 output 쪽으로 전달되지 않고 값만 가지고 있게 할 수 있습니다. 즉, reactive({})의 동작 중에 isolate() 함수 내부의 input$은 2번 저장까지만 진행되고 3번의 전달은 진행되지 않습니다. isolate() 함수 밖의 다른 input$이 변화하여 값을 전달하는 일이 발생할 때 그 전까지 저장해둔 모든 값이 전달됩니다.
library(shiny)ui <- fluidPage( headerPanel("Click the button"), sidebarPanel( sliderInput("obs1", "Number of obs1:", min = 0, max = 1000 , value = 500), sliderInput("obs2", "Number of obs2:", min = 0, max = 1000 , value = 500), actionButton("goButton", "Go!") ), mainPanel( plotOutput("distPlot1"), plotOutput("distPlot2") ))
server<- function(input, output) { output$distPlot1 <- renderPlot({ dist <- rnorm(input$obs1) hist(dist) }) output$distPlot2 <- renderPlot({ input$goButton dist <- isolate(rnorm(input$obs1)) hist(dist) })}shinyApp(ui,server)
shinyapps로 웹앱 배포 연습
회원가입
정보 입력
로그인 화면
step1,2 진행
Copy to clipboard 클릭 후 내용 복사
publish 클릭
next
shinyapps.io 선택
입력칸에 아까 복사한 내용 붙여넣기
Connect Account 클릭
배포할 이름 결정 후 Publish 클릭
배포 진행중
배포 완료 - 이름 클릭하여 세부사항 확인
url 클릭하여 결과 확인
온라인 배포 완료
leaflet은 javascript
언어로 이루어진 지도에 대한 패키지입니다. shiny
는 htmlwidget을 이용해서 javascript
를 R
로 변환하여 사용하는 것이 매우 많습니다. leaflet
역시 마찬가지로 이곳에 상세한 코드 예시들과 함께 소개가 준비되어 있습니다.
ggiraph는 ggplot
의 geom
계 함수들에게 뒤에 _interactive
를 붙여 활용함으로써 조금더 표가 사용자와 상호작용 할 수 있는 부분을 추가해줍니다. 효과로는 click
, hover
, zoom
, data_id
등이 있습니다.
visNetwork는 네트워크 시각화를 위한 패키지로 상호작용에 대해 유연하게 작성할 수 있게 설계되었습니다. node
와 edge
, group
에 대한 개념만 이해하시면 멋진 네트워크 시각화 화면을 만들어 볼 수 있습니다.
shinydashboard는 대쉬보드를 shiny
로 제작할 수 있게 대쉬보드 제작 프레임워크인 AdminLTE을 기반으로 작성했습니다. 유료 템플릿을 판매하는 기반을 가진 오픈소스인 만큼 프레임워크의 질이 product ready
수준이라 사용하기 매우 좋습니다.
목차
공부를 위한 자료
shiny
shiny-ext
Keyboard shortcuts
↑, ←, Pg Up, k | Go to previous slide |
↓, →, Pg Dn, Space, j | Go to next slide |
Home | Go to first slide |
End | Go to last slide |
Number + Return | Go to specific slide |
b / m / f | Toggle blackout / mirrored / fullscreen mode |
c | Clone slideshow |
p | Toggle presenter mode |
t | Restart the presentation timer |
?, h | Toggle this help |
Esc | Back to slideshow |