shiny를 사용하다 보면 하나의 *Output 과 하나의 render* 함수가 짝을 이뤄 동작하는 것을 볼 수 있습니다. 대부분 한 짝의 함수당 한개의 plot을 뱉는데 경우에 따라 여러개의 plot을 한꺼번에 표시하고 싶을 때가 있습니다. 콘솔에서 분석을 할 경우 plot을 그리는 함수를 작성하고 lapply() 또는 purrr::map() 함수를 이용하면 간단한(?) 일이지만 샤이니에서는 하나하나 *Outputrender* 함수의 짝을 맞줘 줄 수 있는 노릇도 아닙니다. 이번 글에서는 이것을 가능하게 하는 팁을 공유하고자 합니다.

TL;DR

  1. 샤이니에서 여러개의 plot을 한번에 내보내고 싶으면
  2. htmlOutput("container")
  3. output$container <- renderUI()
  4. do.call(tagList, map(data, function))

ui

일단 패키지 불러오기와 함께 가장 간단한 ui를 만들어 보겠습니다.

library(shiny)
library(highcharter)
library(tidyr)
library(dplyr)
library(purrr)

ui <- fluidPage(
    htmlOutput("container")
)

server

  1. 데이터는 어벤져스 주인공들의 능력치입니다. 지극히 주관적인 마음으로 능력을 매겼으니 혹시 팬분들이 컴플레인을 제시하신다면 수정하도록 하겠습니다. 하지만 블랙위도우의 sexy 능력치가 100임은 양보할 수 없습니다.

  2. 먼저 plot을 그리는 함수를 작성합니다. highcharter 패키지를 이용해 radar plot을 그려보았습니다.

  3. 데이터와 plot을 그리는 함수를 lapply() 또는 purrr::map()을 이용해 여러개의 plot을 생성합니다.

  4. 이를 do.call()함수를 이용해 tagList를 생성합니다.

  5. 요것들을 uihtmlOutput으로 보냅니다.

server <- function(input, output, session) {

  output$container <- renderUI({
    
    # 1. 데이터 ----
    hero <- data.frame(
      name = c("Iron man", "Thor", "Hulk", "Captain America", "Hawkeye", "Quicksilver", "Black Widow", "Dr. Strange"),
      power = c(50, 80, 100, 50, 40, 30, 30, 30),
      speed = c(60, 70, 50, 50, 40, 100, 30, 10),
      magic = c(10, 80, 10, 10, 10, 10, 10, 100),
      mental = c(90, 50, 10, 100, 90, 30, 70, 100),
      sexy= c(90, 10, 10, 30, 10, 10, 100, 20)
    )
    
    # 2. 함수 ----
    creat_hc <- function(hero_name) {
      
      hero %>%
        filter(name == hero_name) %>%
        tidyr::gather(key = "Statics", value = "Value", power:sexy) %>%
        hchart("line", hcaes(x = "Statics", y = "Value"), height = 30) %>%
        hc_chart(polar = TRUE) %>%
        hc_xAxis(title = "", categories = c("power", "speed", "magic", "mental", "sexy")) %>%
        hc_yAxis(title = "", max = 100, min = 0) %>%
        hc_plotOptions(
          line = list(
            color = "white"
          )
        ) %>%
        hc_title(text = hero_name) %>%
        hc_add_theme(hc_theme_superheroes())
    }
    
    # 3. lapply OR map ----
    hc_result <- purrr::map(hero$name, creat_hc)
    
    # 4. do.call ----
    do.call(tagList, hc_result)
  })
  
}

shinyApp(ui, server)