Ch 02. DT로 인터랙티브 테이블
renderTable()로 만든 표는 보기엔 깔끔하지만 정렬이나 검색이 안 됩니다. DT 패키지의 datatable()은 클릭 한 번으로 정렬하고, 검색창에 입력하면 실시간으로 필터링되며, 페이지를 넘길 수 있는 완성도 높은 테이블을 제공합니다.
DT 설치와 기본 사용
install.packages("DT")
renderTable/tableOutput 대신 renderDT/DTOutput을 씁니다.
library(shiny)
library(DT)
ui <- fluidPage(
titlePanel("DT 기본 예제"),
DTOutput("my_table") # tableOutput 대신 DTOutput
)
server <- function(input, output, session) {
output$my_table <- renderDT({ # renderTable 대신 renderDT
datatable(mtcars)
})
}
shinyApp(ui, server)
이것만으로 정렬, 검색, 페이징이 모두 작동하는 테이블이 만들어집니다.
datatable 주요 옵션
datatable()의 options 인수로 동작을 세밀하게 제어합니다.
library(shiny)
library(DT)
ui <- fluidPage(
DTOutput("options_table")
)
server <- function(input, output, session) {
output$options_table <- renderDT({
datatable(
data = iris,
rownames = FALSE, # 행 번호 숨기기
filter = "top", # 각 열 위에 필터 입력란 표시
selection = "multiple", # 다중 행 선택 허용
options = list(
pageLength = 10, # 페이지당 행 수
lengthMenu = c(5, 10, 25, 50), # 페이지 크기 선택 옵션
scrollX = TRUE, # 가로 스크롤 활성화
searching = TRUE, # 검색창 표시
ordering = TRUE, # 열 정렬 허용
info = TRUE, # "1~10 of 150 rows" 표시
language = list( # 한국어 UI
search = "검색:",
info = "_START_~_END_ / 전체 _TOTAL_건",
paginate = list(
previous = "이전",
`next` = "다음"
),
lengthMenu = "_MENU_ 행씩 보기"
)
)
)
})
}
shinyApp(ui, server)
열 스타일링
formatStyle(), formatCurrency(), formatRound(), formatPercentage() 등으로 특정 열의 표시 형식과 스타일을 지정합니다.
server <- function(input, output, session) {
output$styled_table <- renderDT({
datatable(mtcars[, c("mpg", "cyl", "hp", "wt")],
rownames = TRUE,
options = list(pageLength = 10)) %>%
# 숫자 반올림
formatRound(columns = c("mpg", "wt"), digits = 1) %>%
# 색상 강조: 값에 따라 배경색 변경
formatStyle(
columns = "mpg",
background = styleColorBar(mtcars$mpg, "#3498db"),
backgroundSize = "100% 80%",
backgroundRepeat = "no-repeat",
backgroundPosition = "center"
) %>%
# 조건부 스타일: hp가 200 이상이면 빨간색 텍스트
formatStyle(
columns = "hp",
color = styleInterval(200, c("black", "red")),
fontWeight = styleInterval(200, c("normal", "bold"))
)
})
}
styleColorBar()는 셀 안에 막대 그래프를 그려 값의 크기를 시각적으로 보여줍니다.
편집 가능한 테이블
editable = TRUE로 테이블 셀을 직접 편집할 수 있게 만들 수 있습니다.
library(shiny)
library(DT)
ui <- fluidPage(
DTOutput("edit_table"),
hr(),
h4("수정된 데이터"),
verbatimTextOutput("changed_cells")
)
server <- function(input, output, session) {
# 반응형 데이터프레임
df <- reactiveVal(
data.frame(
이름 = c("홍길동", "이순신", "강감찬"),
점수 = c(85, 92, 78),
등급 = c("B", "A", "C"),
stringsAsFactors = FALSE
)
)
output$edit_table <- renderDT({
datatable(
df(),
editable = list(
target = "cell", # 셀 단위 편집
disable = list(columns = 0) # 첫 번째 열(행 번호) 편집 비활성화
),
rownames = TRUE,
selection = "none"
)
})
# 셀 편집 이벤트 처리
observeEvent(input$edit_table_cell_edit, {
info <- input$edit_table_cell_edit
# info$row: 행 번호, info$col: 열 번호, info$value: 새 값
updated <- df()
updated[info$row, info$col] <- DT::coerceValue(info$value,
updated[info$row, info$col])
df(updated)
})
output$changed_cells <- renderPrint({
print(df())
})
}
shinyApp(ui, server)
편집 이벤트는 input${outputId}_cell_edit으로 받습니다. output$edit_table이면 input$edit_table_cell_edit입니다.
선택된 행 활용
사용자가 테이블에서 행을 선택하면 서버에서 그 정보를 활용할 수 있습니다.
library(shiny)
library(DT)
library(ggplot2)
ui <- fluidPage(
fluidRow(
column(6,
DTOutput("car_table")
),
column(6,
plotOutput("detail_plot")
)
),
p("테이블에서 행을 클릭하면 해당 차량의 정보가 차트에 강조됩니다.")
)
server <- function(input, output, session) {
output$car_table <- renderDT({
datatable(
mtcars[, c("mpg", "cyl", "hp", "wt")],
selection = "single", # 단일 행 선택
options = list(pageLength = 8)
)
})
output$detail_plot <- renderPlot({
selected_row <- input$car_table_rows_selected
df <- data.frame(
항목 = c("연비(mpg)", "실린더", "마력(hp)", "무게(wt)"),
값 = c(0, 0, 0, 0)
)
if (length(selected_row) > 0) {
row_data <- mtcars[selected_row, c("mpg", "cyl", "hp", "wt")]
df$값 <- c(row_data$mpg, row_data$cyl, row_data$hp, row_data$wt)
title_text <- paste("선택된 차량:", rownames(mtcars)[selected_row])
} else {
df$값 <- colMeans(mtcars[, c("mpg", "cyl", "hp", "wt")])
title_text <- "전체 평균"
}
ggplot(df, aes(x = 항목, y = 값, fill = 항목)) +
geom_col(show.legend = FALSE) +
geom_text(aes(label = round(값, 1)),
vjust = -0.3, size = 5) +
labs(title = title_text, x = NULL, y = NULL) +
theme_minimal() +
theme(axis.text.y = element_blank())
})
}
shinyApp(ui, server)
선택된 행 인덱스는 input${outputId}_rows_selected로 받습니다. 필터링 후 현재 보이는 행은 input${outputId}_rows_current, 전체 행 순서는 input${outputId}_rows_all로 접근합니다.
버튼 확장 기능
DT는 Excel 내보내기, CSV 다운로드, 프린트 버튼을 버튼 확장으로 추가할 수 있습니다.
output$table_with_buttons <- renderDT({
datatable(
mtcars,
extensions = "Buttons",
options = list(
dom = "Bfrtip", # B: Buttons, f: 검색, r: 처리 중 표시, t: 테이블, i: 정보, p: 페이징
buttons = list(
list(extend = "csv", text = "CSV 저장"),
list(extend = "excel", text = "Excel 저장"),
list(extend = "print", text = "인쇄")
)
)
)
})
extensions = "Buttons"와 dom = "Bfrtip"을 함께 설정해야 버튼이 나타납니다.