0Pricing
R Academy · 강의

renderUI와 insertUI를 활용한 동적 UI

사용자 입력과 서버 상태에 따라 UI 요소를 즉시 생성합니다.

renderUI와 insertUI를 활용한 동적 UI은(는) CoddyKit의 무료 R Academy 강의입니다. 이것은 4개 중 3번째 강의입니다. 아래에서 전체 강의를 무료로 읽을 수 있으며, 내장 코드 에디터와 24/7 AI 튜터와 함께 브라우저에서 직접 실습할 수 있습니다. 이 강의는 R Academy 학습 경로의 일부이며, 진행 상황이 웹과 CoddyKit 앱에 동기화됩니다. R Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

동적 UI — 이유와 시점

때로는 앱 시작 시 필요한 UI를 완전히 결정할 수 없습니다. UI가 사용자의 선택, 로드된 데이터 또는 실행 중 조건에 따라 달라지기 때문입니다. Shiny는 두 가지 방법을 제공합니다. renderUI()는 자리 표시자의 전체 내용을 교체하고, insertUI()/removeUI()는 모든 내용을 교체하지 않고 요소를 추가하거나 제거합니다.

library(shiny)

# Static UI: columns always shown
# ui <- fluidPage(selectInput('x', ...), selectInput('y', ...))

# Dynamic UI: columns depend on which dataset was loaded
# ui <- fluidPage(fileInput('upload', ...), uiOutput('var_selectors'))

renderUI()와 uiOutput()

서버에서 renderUI({...})는 UI 요소를 반응형으로 만들어 반환합니다. UI에서 uiOutput('id')는 renderUI가 생성하는 내용을 표시하는 자리 표시자를 만듭니다. 반응형 코드가 다시 실행될 때마다 자리 표시자의 전체 내용이 교체됩니다.

ui <- fluidPage(
  selectInput('dataset', 'Dataset:', choices = c('iris', 'mtcars')),
  uiOutput('column_selector')   # placeholder
)

server <- function(input, output, session) {
  output$column_selector <- renderUI({
    df <- get(input$dataset)  # reactive dependency on dataset
    selectInput('col', 'Choose Column:',
                choices = names(df))
  })
}

shinyApp(ui, server)

여러 컨트롤과 함께 renderUI() 사용하기

한 번에 여러 UI 요소를 렌더링하려면 renderUI()에서 tagList()를 반환하십시오. 컨트롤 수가 데이터에 따라 달라질 때 사용하는 표준 패턴입니다. 예를 들어 데이터셋의 각 열마다 필터 하나를 생성할 수 있습니다.

server <- function(input, output, session) {
  output$filters <- renderUI({
    df    <- get(input$dataset)
    nums  <- names(df)[sapply(df, is.numeric)]

    # One slider per numeric column
    sliders <- lapply(nums, function(col) {
      rng <- range(df[[col]], na.rm = TRUE)
      sliderInput(
        inputId = paste0('filter_', col),
        label   = col,
        min     = rng[1], max = rng[2],
        value   = rng
      )
    })
    tagList(sliders)   # render all sliders
  })
}

insertUI() — 요소를 동적으로 추가하기

insertUI(selector, where, ui)는 전체 페이지를 다시 만들지 않고 DOM에 UI 요소를 삽입합니다. 삽입 위치를 지정하려면 CSS 선택자를 사용하십시오. where에는 'afterEnd', 'beforeEnd', 'afterBegin' 또는 'beforeBegin'을 사용할 수 있습니다.

server <- function(input, output, session) {
  item_count <- reactiveVal(0)

  observeEvent(input$add_item, {
    item_count(item_count() + 1)
    id <- paste0('item_', item_count())

    insertUI(
      selector = '#item_container',  # CSS selector
      where    = 'beforeEnd',
      ui       = div(
        id    = id,
        style = 'padding: 5px; border: 1px solid #ccc; margin: 3px;',
        textInput(id, paste('Item', item_count()), value = '')
      )
    )
  })
}

removeUI() — 요소 삭제하기

removeUI(selector)는 CSS 선택자와 일치하는 첫 번째 DOM 요소를 제거합니다. 일치하는 모든 요소를 제거하려면 multiple = TRUE를 사용하십시오. 사용자가 항목을 추가하고 제거할 수 있는 목록 기반 UI에서 insertUI()와 함께 사용합니다.

server <- function(input, output, session) {
  observeEvent(input$remove_last, {
    # Remove the last item div
    n <- isolate(item_count())
    if (n > 0) {
      removeUI(selector = paste0('#item_', n))
      item_count(n - 1)
    }
  })

  observeEvent(input$clear_all, {
    # Remove all items matching a class
    removeUI(selector = '.dynamic-item', multiple = TRUE)
    item_count(0)
  })
}

conditionalPanel() — 클라이언트 측 표시 여부

conditionalPanel(condition, ...)은 브라우저에서 평가되는 JavaScript 조건에 따라 UI 요소를 표시하거나 숨깁니다. renderUI()와 달리 요소는 항상 DOM에 존재하며 표시되거나 숨겨지기만 합니다. 서버와 통신할 필요가 없으므로 간단한 표시 여부 로직에서는 더 빠릅니다.

ui <- fluidPage(
  selectInput('plot_type', 'Plot Type:',
              choices = c('scatter', 'histogram', 'boxplot')),

  # Only shown when scatter is selected
  conditionalPanel(
    condition = "input.plot_type == 'scatter'",
    selectInput('x_var', 'X Variable:', choices = names(mtcars)),
    selectInput('y_var', 'Y Variable:', choices = names(mtcars))
  ),

  # Only shown when histogram is selected
  conditionalPanel(
    condition = "input.plot_type == 'histogram'",
    sliderInput('bins', 'Number of bins:', 5, 50, 20)
  ),

  plotOutput('main_plot')
)

updateSelectInput() — 기존 입력 수정하기

전체 입력 컨트롤을 교체하는 대신 updateSelectInput(session, id, choices, selected)를 사용하여 기존 selectInput의 선택 항목이나 선택된 값만 업데이트할 수 있습니다. 모든 입력 유형에 updateSliderInput(), updateTextInput() 등과 같은 유사한 업데이트 함수가 있습니다.

server <- function(input, output, session) {
  # When dataset changes, update the column selector
  observeEvent(input$dataset, {
    df <- get(input$dataset)
    updateSelectInput(
      session  = session,
      inputId  = 'column',
      choices  = names(df),
      selected = names(df)[1]
    )
  })

  # Reset slider when reset button pressed
  observeEvent(input$reset_btn, {
    updateSliderInput(session, 'n_items',
                     value = 20, min = 1, max = 100)
  })
}

updateSliderInput()와 updateNumericInput()

업데이트 함수는 브라우저에 메시지를 보내 기존 입력의 현재 값, 최솟값, 최댓값 또는 레이블을 수정합니다. renderUI()로 컨트롤을 삭제하고 다시 만드는 것보다 효율적이며, 페이지 레이아웃에서 컨트롤의 위치도 유지합니다.

server <- function(input, output, session) {
  # When data loads, set slider max to actual data length
  observeEvent(input$load_data, {
    data <- readRDS(input$upload$datapath)

    updateSliderInput(session, 'row_slider',
      min   = 1,
      max   = nrow(data),
      value = c(1, min(50, nrow(data)))
    )

    updateNumericInput(session, 'page_size',
      max   = nrow(data),
      value = min(10, nrow(data))
    )
  })
}

req()로 renderUI 입력에 액세스하기

renderUI()로 동적으로 생성된 입력은 렌더링될 때까지 NULL입니다. 동적으로 생성된 입력에 의존하는 모든 서버 코드에서 req(input$dynamic_input)을 사용하여 초기화 중 오류가 발생하지 않도록 하십시오.

server <- function(input, output, session) {
  # dynamic input: 'col' is created by renderUI
  output$column_selector <- renderUI({
    selectInput('col', 'Column:', choices = names(get(input$dataset)))
  })

  output$summary_stats <- renderPrint({
    req(input$col)           # wait until 'col' exists
    req(input$dataset)       # and dataset is chosen
    df <- get(input$dataset)
    summary(df[[input$col]])
  })
}

appendTab()으로 동적 탭 만들기

appendTab(inputId, tab, select)은 실행 중에 tabsetPanel()에 새 탭을 추가합니다. removeTab(inputId, target)은 탭 하나를 제거합니다. 이 패턴을 사용하면 사용자가 동적으로 생성된 분석 화면을 열고 닫고 전환할 수 있는 작업 흐름을 만들 수 있습니다.

ui <- fluidPage(
  actionButton('add_tab', 'Open New Analysis Tab'),
  tabsetPanel(id = 'analysis_tabs', type = 'tabs')
)

server <- function(input, output, session) {
  tab_count <- reactiveVal(0)

  observeEvent(input$add_tab, {
    tab_count(tab_count() + 1)
    n <- tab_count()
    appendTab(
      inputId = 'analysis_tabs',
      select  = TRUE,
      tab = tabPanel(
        title = paste('Analysis', n),
        value = paste0('tab_', n),
        plotOutput(paste0('plot_', n)),
        actionButton(paste0('close_', n), 'Close')
      )
    )
  })
}

renderUI와 insertUI 비교 — 각각 언제 사용할까요

사용 사례에 따라 적절한 도구를 선택하십시오:

  • renderUI(): 자리 표시자의 전체 내용을 반응형으로 교체합니다. 전체 블록이 함께 변경될 때 적합합니다.
  • insertUI(): 기존 내용에 새 요소를 추가합니다. 모든 내용을 다시 렌더링하지 않고 목록을 확장할 때 적합합니다.
  • updateXxxInput(): 기존 입력을 제자리에서 수정합니다. 다시 렌더링하지 않고 선택 항목이나 값을 변경할 때 적합합니다.
  • conditionalPanel(): 서버와 통신하지 않고 표시하거나 숨깁니다. 간단한 표시 여부 로직에 적합합니다.
# Decision guide:
# - 'Show filter only when advanced mode' -> conditionalPanel
# - 'Populate column choices from loaded CSV' -> updateSelectInput
# - 'Show different controls per selected tab' -> renderUI
# - 'Add a row editor for each row user adds' -> insertUI
# - 'Remove a row editor' -> removeUI

# Combining strategies:
server <- function(input, output, session) {
  # Use renderUI for column selectors (replaces whole block)
  output$col_selectors <- renderUI({ ... })
  # Use insertUI for dynamically added rows
  observeEvent(input$add_row, { insertUI(...) })
  # Use conditionalPanel for show/hide (in UI, no server code)
}

빠른 확인

Shiny에서 동적 UI를 만들 때 renderUI()와 insertUI()의 핵심적인 차이점은 무엇입니까?

동적 UI 복습

renderUI와 insertUI를 활용한 동적 UI의 핵심 내용:

  • renderUI({}) + uiOutput('id'): 자리 표시자의 전체 내용을 반응형으로 교체합니다.
  • renderUI()에서 tagList()를 반환하여 여러 요소를 한 번에 렌더링합니다.
  • insertUI(selector, where, ui): DOM의 어느 위치에나 요소를 삽입합니다.
  • removeUI(selector): CSS 선택자로 요소를 제거합니다.
  • conditionalPanel(condition, ...): 서버 없이 클라이언트 측에서 표시하거나 숨깁니다.
  • updateSelectInput(), updateSliderInput(): 기존 입력을 제자리에서 수정합니다.
  • 동적 입력이 렌더링되기 전에 NULL인지 확인하려면 항상 req(input$dynamic_id)를 사용하십시오.
# Typical pattern: dynamic column selector
ui <- fluidPage(
  fileInput('upload', 'Upload CSV'),
  uiOutput('col_ui'),          # placeholder
  plotOutput('col_plot')
)

server <- function(input, output, session) {
  df <- reactive({
    req(input$upload)
    read.csv(input$upload$datapath)
  })

  output$col_ui <- renderUI({
    req(df())
    selectInput('col', 'Column:', choices = names(df()))
  })

  output$col_plot <- renderPlot({
    req(input$col)
    hist(df()[[input$col]])
  })
}

자주 묻는 질문

“renderUI와 insertUI를 활용한 동적 UI” 강의는 무료인가요?

네 — “renderUI와 insertUI를 활용한 동적 UI” 전체 내용을 이 웹사이트에서 무료로 읽을 수 있습니다. 인터랙티브하게 실습하려면(내장 코드 에디터와 24/7 AI 튜터), CoddyKit PRO로 업그레이드하면 R Academy 강의 전체를 잠금 해제할 수 있습니다. R Academy 강의에는 총 4개의 강의가 포함되어 있습니다.

“renderUI와 insertUI를 활용한 동적 UI”에서 뭘 배우나요?

사용자 입력과 서버 상태에 따라 UI 요소를 즉시 생성합니다. 브라우저에서 직접 실행하는 실습 코드로 R Academy을(를) 배우며, 24/7 AI 튜터가 강의를 진행하면서 질문에 답변해줍니다.

R Academy을(를) 시작하는 데 경험이 필요한가요?

사전 경험은 필요하지 않습니다. CoddyKit의 R Academy은(는) 초급자부터 고급 학습자까지를 위해 구성되어 있으므로, 여기서 시작하거나 처음부터 시작할 수 있으며 자신의 속도대로 진행할 수 있습니다. 이것은 4개 중 3번째 강의입니다.

“renderUI와 insertUI를 활용한 동적 UI” 강의는 얼마나 걸리나요?

대부분의 CoddyKit 강의는 약 5~10분이 소요됩니다. 각 강의는 간결하고 인터랙티브하여 꾸준한 진행이 가능하며, 웹과 앱에서 중단한 부분부터 바로 시작할 수 있습니다.

이 R Academy 강의에서 코드를 작성하고 실행할 수 있나요?

네. 모든 R Academy 강의에는 내장 코드 에디터가 포함되어 있으므로, 브라우저에서 바로 실제 코드를 작성하고 실행한 후 즉시 AI 피드백을 받을 수 있습니다 — 로컬 설정이 필요 없습니다.

이 강의의 모든 강의

  1. 반응형 프로그래밍 심화
  2. 코드 재사용을 위한 Shiny 모듈
  3. renderUI와 insertUI를 활용한 동적 UI
  4. Shiny 앱 배포
← R Academy(으)로 돌아가기