iBetter Books
수정

Shiny 개발 정리

이 교재에서 다룬 내용을 한 장으로 모았습니다. 앱을 만들다가 막힐 때 여기를 먼저 펼쳐보세요.

핵심 구조 복습

모든 Shiny 앱은 UI와 Server, 두 부분으로 이루어집니다.

# Shiny 앱의 기본 뼈대
library(shiny)

ui <- fluidPage(
  # 화면 레이아웃과 위젯 정의
)

server <- function(input, output, session) {
  # 반응형 로직과 출력 정의
}

shinyApp(ui, server)

UI 함수 치트시트

레이아웃

함수 역할
page_fluid() 반응형 유동 레이아웃 (bslib)
page_sidebar() 사이드바 + 메인 레이아웃 (bslib)
page_navbar() 상단 네비게이션 바 (bslib)
layout_columns() 컬럼 기반 배치 (bslib)
card() 컨텐츠 카드 박스 (bslib)
navset_card_tab() 탭 패널 (bslib)
sidebar() 사이드바 정의 (bslib)

입력 위젯

함수 입력 유형 반환값
textInput() 텍스트 character
numericInput() 숫자 numeric
sliderInput() 슬라이더 numeric 또는 c(min, max)
selectInput() 드롭다운 character
radioButtons() 라디오 버튼 character
checkboxGroupInput() 체크박스 character vector
dateRangeInput() 날짜 범위 c(Date, Date)
fileInput() 파일 업로드 data.frame (name, size, type, datapath)
actionButton() 버튼 integer (클릭 횟수)

출력 함수 짝

Server 함수 UI 함수 출력 내용
renderText() textOutput() 텍스트
renderPrint() verbatimTextOutput() 콘솔 출력
renderPlot() plotOutput() ggplot2 차트
renderPlotly() plotlyOutput() 인터랙티브 차트
renderTable() tableOutput() 기본 테이블
renderDT() DTOutput() 인터랙티브 테이블
renderUI() uiOutput() 동적 UI
renderLeaflet() leafletOutput() 지도
downloadHandler() downloadButton() 파일 다운로드

반응형 계열 함수 정리

함수 역할 특징
reactive() 반응형 표현식 여러 출력에서 공유 가능
reactiveVal() 단일 반응형 값 val(), val(새값) 사용
reactiveValues() 반응형 리스트 rv$x, rv$x <- 새값 사용
observe() 반응형 부작용 값 반환 없음, 항상 실행
observeEvent() 이벤트 기반 실행 특정 입력 변화에만 반응
eventReactive() 이벤트 기반 반응식 버튼 클릭 등 명시적 트리거
isolate() 반응성 격리 읽기는 하되 의존성 미등록
req() 실행 조건 검사 NULL·NA 등일 때 실행 중단
invalidateLater() 타이머 무효화 자동 갱신에 사용
reactivePoll() 폴링 반응식 변경 감지 후 데이터 읽기
reactiveFileReader() 파일 모니터링 파일 변경 시 자동 재읽기

모듈 패턴 요약

# 모듈 UI
myModuleUI <- function(id) {
  ns <- NS(id)
  tagList(
    selectInput(ns("var"), "선택", choices = letters)
  )
}

# 모듈 Server
myModuleServer <- function(id) {
  moduleServer(id, function(input, output, session) {
    reactive({ input$var })   # 반응형 반환
  })
}

# 사용
ui <- page_fluid(myModuleUI("mod1"), myModuleUI("mod2"))

server <- function(input, output, session) {
  val1 <- myModuleServer("mod1")
  val2 <- myModuleServer("mod2")
}

개발 워크플로우

1. 데이터 준비        — global.R, parquet 변환, 사전 집계
2. UI 스케치          — 손으로 그려보기, layout_columns 배치
3. 하드코딩 출력      — reactive 없이 정적 데이터로 차트 먼저 완성
4. 반응형 연결        — input → reactive → render 연결
5. 모듈 추출          — 반복되는 패턴 발견 시 모듈로 분리
6. 성능 최적화        — bindCache, memoise 적용
7. 테스트             — shinytest2로 핵심 경로 자동화
8. 배포               — shinyapps.io 또는 Docker

3번 "하드코딩 출력"이 중요합니다. 반응형을 너무 일찍 넣으면 UI 버그와 로직 버그가 섞여서 디버깅이 어려워집니다. 차트와 테이블이 정적 데이터로 잘 보이는 것을 먼저 확인한 다음 반응형을 붙이세요.

자주 쓰는 디버깅 방법

# 1. 반응형 값 콘솔 출력
observe({ message("input$region: ", paste(input$region, collapse = ", ")) })

# 2. 브라우저 일시정지
server <- function(input, output, session) {
  browser()   # R 인터랙티브 디버거 진입
  ...
}

# 3. reactive 값 UI에 표시
output$debug <- renderPrint({ reactiveValuesToList(input) })
# ui에: verbatimTextOutput("debug")

# 4. 오류 메시지 상세화
options(shiny.error = browser)
options(shiny.trace = TRUE)