Ch 01. 입력 위젯 총정리
사용자가 앱과 소통하는 방법은 다양합니다. 텍스트를 입력하거나, 숫자를 조절하거나, 목록에서 항목을 고르거나, 달력에서 날짜를 선택하기도 합니다. Shiny는 이런 모든 상황에 맞는 위젯을 미리 준비해두었습니다. 각 위젯의 특성과 사용법을 익혀두면, 앱에 가장 잘 어울리는 인터페이스를 자연스럽게 선택할 수 있게 됩니다.
입력 위젯 한눈에 보기
Shiny의 입력 위젯은 모두 같은 패턴을 따릅니다. 첫 번째 인수는 inputId이고, 두 번째는 label입니다. inputId는 서버에서 input$inputId 형태로 값을 읽는 데 사용하므로 앱 전체에서 유일해야 합니다.
아래 표는 주요 입력 위젯을 한눈에 정리한 것입니다.
| 함수 | 반환 타입 | 주요 용도 |
|---|---|---|
textInput() |
character | 단일 줄 텍스트 입력 |
textAreaInput() |
character | 여러 줄 텍스트 입력 |
numericInput() |
numeric | 숫자 직접 입력 |
sliderInput() |
numeric / 벡터 | 범위 슬라이더로 숫자 선택 |
selectInput() |
character | 드롭다운 목록 선택 |
selectizeInput() |
character | 검색 가능한 드롭다운 |
radioButtons() |
character | 라디오 버튼 단일 선택 |
checkboxInput() |
logical | 체크박스 단일 |
checkboxGroupInput() |
character 벡터 | 체크박스 다중 선택 |
dateInput() |
Date | 날짜 단일 선택 |
dateRangeInput() |
Date 벡터(2) | 날짜 범위 선택 |
fileInput() |
데이터프레임 | 파일 업로드 |
actionButton() |
integer | 클릭 이벤트 트리거 |
passwordInput() |
character | 비밀번호 입력(마스킹) |
textInput과 textAreaInput
textInput()은 한 줄짜리 텍스트 상자입니다. 제목이나 검색어처럼 짧은 텍스트를 받을 때 사용합니다.
# 새 파일: app.R
library(shiny)
ui <- fluidPage(
textInput(
inputId = "name",
label = "이름을 입력하세요",
value = "홍길동", # 초기값
placeholder = "예: 홍길동" # 입력 전 안내 문구
),
textOutput("greeting")
)
server <- function(input, output, session) {
output$greeting <- renderText({
paste0("안녕하세요, ", input$name, "님!")
})
}
shinyApp(ui, server)
여러 줄이 필요하다면 textAreaInput()을 씁니다. rows 인수로 높이를 조절합니다.
textAreaInput(
inputId = "memo",
label = "메모",
rows = 5,
value = ""
)
numericInput
numericInput()은 숫자를 직접 타이핑하거나 화살표 버튼으로 조절하는 위젯입니다. min, max, step 인수로 허용 범위와 증감 단위를 지정합니다.
numericInput(
inputId = "n_bins",
label = "구간 수",
value = 30, # 초기값
min = 5,
max = 100,
step = 5 # 화살표 클릭 시 증감량
)
서버에서는 input$n_bins로 숫자 값을 그대로 받습니다. 사용자가 범위를 벗어난 값을 입력해도 강제로 막지는 않으므로, 중요한 앱에서는 validate()나 req() 함수로 서버측 검증을 추가하는 것이 좋습니다.
sliderInput
슬라이더는 마우스로 끌어서 숫자를 고르는 방식입니다. 정확한 값보다 대략적인 범위나 비율을 선택할 때 직관적입니다.
# 단일값 슬라이더
sliderInput(
inputId = "year",
label = "연도",
min = 2000,
max = 2024,
value = 2020,
step = 1,
sep = "" # 천 단위 구분자 제거 (2,020 → 2020)
)
# 범위 슬라이더: value에 길이 2인 벡터 지정
sliderInput(
inputId = "price_range",
label = "가격 범위",
min = 0,
max = 100000,
value = c(10000, 50000),
step = 1000,
pre = "₩" # 앞에 붙는 접두어
)
범위 슬라이더의 경우 input$price_range는 길이 2인 숫자 벡터입니다. input$price_range[1]이 최솟값, input$price_range[2]가 최댓값입니다.
selectInput과 radioButtons
목록에서 하나를 고를 때는 selectInput()과 radioButtons() 중 하나를 씁니다. 선택지가 5개 이하이고 모두 보여주고 싶을 때는 라디오 버튼이 낫고, 선택지가 많으면 드롭다운이 깔끔합니다.
# 드롭다운
selectInput(
inputId = "region",
label = "지역 선택",
choices = c("서울", "부산", "대구", "인천"),
selected = "서울" # 초기 선택값
)
# 다중 선택 허용
selectInput(
inputId = "cols",
label = "표시할 열",
choices = names(mtcars),
selected = c("mpg", "cyl"),
multiple = TRUE
)
# 라디오 버튼
radioButtons(
inputId = "plot_type",
label = "차트 종류",
choices = c("막대" = "bar", "선" = "line", "산점도" = "scatter"),
selected = "bar",
inline = TRUE # 가로로 나열
)
choices는 이름 있는 벡터로 지정할 수 있습니다. 이름은 화면에 표시되는 라벨이고, 값은 input$으로 읽히는 실제 값입니다.
checkboxInput과 checkboxGroupInput
checkboxInput()은 TRUE/FALSE를 반환하는 단일 체크박스입니다. 옵션 켜기/끄기에 알맞습니다.
checkboxInput(
inputId = "show_trend",
label = "추세선 표시",
value = FALSE # 초기 체크 여부
)
여러 항목을 동시에 고를 때는 checkboxGroupInput()을 씁니다. 선택된 항목들이 문자 벡터로 반환됩니다.
checkboxGroupInput(
inputId = "weekdays",
label = "요일 선택",
choices = c("월", "화", "수", "목", "금"),
selected = c("월", "수", "금")
)
dateInput과 dateRangeInput
날짜를 고를 때는 달력 팝업 위젯을 씁니다.
# 단일 날짜
dateInput(
inputId = "start_date",
label = "시작 날짜",
value = Sys.Date(), # 오늘
min = "2020-01-01",
max = Sys.Date(),
format = "yyyy-mm-dd",
language = "ko" # 한국어 달력
)
# 날짜 범위
dateRangeInput(
inputId = "date_range",
label = "기간 선택",
start = Sys.Date() - 30,
end = Sys.Date(),
language = "ko",
separator = " ~ "
)
dateInput()은 Date 클래스 값을 반환합니다. dateRangeInput()은 길이 2인 Date 벡터이며 input$date_range[1]과 input$date_range[2]로 접근합니다.
actionButton
버튼은 클릭 횟수를 정수로 반환합니다. 주로 observeEvent() 또는 eventReactive()와 함께 써서 버튼을 눌렀을 때만 계산을 실행하도록 합니다.
ui <- fluidPage(
actionButton("run_btn", "분석 시작", class = "btn-primary"),
tableOutput("result")
)
server <- function(input, output, session) {
data <- eventReactive(input$run_btn, {
# 버튼을 눌렀을 때만 실행
head(mtcars, 10)
})
output$result <- renderTable(data())
}
class = "btn-primary" 등 Bootstrap 버튼 스타일을 지정할 수 있습니다. btn-success, btn-danger, btn-warning도 자주 씁니다.
실전 패턴: 필터 패널 구성
여러 위젯을 조합하면 데이터 필터 패널을 만들 수 있습니다.
library(shiny)
library(dplyr)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
selectInput("cyl", "실린더 수",
choices = c("전체" = "all", "4" = 4, "6" = 6, "8" = 8)),
sliderInput("mpg_range", "연비 범위",
min = 10, max = 35, value = c(10, 35)),
checkboxInput("show_am", "수동변속기만 보기", value = FALSE),
actionButton("apply", "적용", class = "btn-primary")
),
mainPanel(
tableOutput("filtered_table")
)
)
)
server <- function(input, output, session) {
filtered <- eventReactive(input$apply, {
df <- mtcars
if (input$cyl != "all") {
df <- df %>% filter(cyl == as.numeric(input$cyl))
}
df <- df %>%
filter(mpg >= input$mpg_range[1],
mpg <= input$mpg_range[2])
if (input$show_am) {
df <- df %>% filter(am == 1)
}
df
}, ignoreNULL = FALSE)
output$filtered_table <- renderTable(filtered(), rownames = TRUE)
}
shinyApp(ui, server)
이렇게 actionButton을 중간에 두면 사용자가 모든 조건을 설정한 후 한 번에 적용할 수 있어서 불필요한 재계산을 줄일 수 있습니다.