Shiny 반응형

개요

앞에서 ojs_define() 함수를 사용해 Python과 R의 데이터를 OJS 셀에서 사용할 수 있게 만드는 방법을 설명했습니다. 이 경우 데이터 전처리는 렌더링 시 한 번 수행되고, 이후의 모든 상호작용은 클라이언트에서 처리됩니다.

하지만 사용자 입력에 따라 동적으로 데이터를 변환하고 싶다면 어떨까요? ojs_define()은 정적 값뿐 아니라 Shiny 반응형도 전달할 수 있으므로 이 역시 가능합니다(Shiny 인터랙티브 문서 안에서 실행되는 경우).

안녕하세요, Shiny

다음은 Shiny Gallery의 K-Means Clustering 예제를 OJS 클라이언트와 Shiny Server로 구현한 것입니다.

https://jjallaire.shinyapps.io/kmeans-shiny-ojs/에서 배포된 문서를 확인할 수 있습니다.

소스 코드

소스 코드를 살펴보겠습니다. 클라이언트에는 익숙한 OJS 입력과 panel: sidebar, panel: fill로 배치한 플롯이 있습니다.

```{ojs}
//| panel: sidebar
vars = ["Sepal.Length", "Sepal.Width", "Petal.Length", "Petal.Width"]
viewof xcol = Inputs.select(vars, {label: "X Variable"})
viewof ycol = Inputs.select(vars, {label: "Y Variable", value: vars[1]})
viewof count = Inputs.range([1, 9], {label: "Cluster Count", step: 1, value: 3})
```

```{ojs}
//| panel: fill
Plot.plot({
  color: {
    type: "ordinal",
    scheme: "category10"
  },
  marks: [
    Plot.dot(transpose(selectedData), {
      x: xcol,
      y: ycol,
      fill: (d, i) => clusters.cluster[i],
    }),
    Plot.dot(clusters.centers, { 
      x: d => d[0],
      y: d => d[1],
      r: 10,
      stroke: "black",
      fill: (d, i) => i + 1
    }),
  ]
})
```

플로팅 코드는 selectedDataclusters 변수를 참조합니다. 이는 Shiny 서버 코드의 반응형 표현식에서 제공합니다. 또한 Plot 라이브러리가 기대하는 행 지향 형식으로 데이터를 변환하기 위해 transpose() 함수를 사용합니다.

다음은 서버 코드입니다:

```{r}
#| context: server

selectedData <- reactive({
  iris[, c(input$xcol, input$ycol)]
})

clusters <- reactive({
  kmeans(selectedData(), input$count)
})

ojs_define(selectedData, clusters)
```

이 코드는 context: server 옵션을 통해 서버에서 실행되도록 지정합니다.

클라이언트의 viewof 표현식에서 정의한 여러 입력(예: input$xcol)을 참조합니다. 이 입력이 바뀌면 해당 서버 측 반응형이 다시 실행됩니다.

selectedDataclusters 두 반응형 값을 만들고 ojs_define()으로 클라이언트에 전달합니다. 이 값이 바뀌면 플롯은 클라이언트 측에서 자동으로 다시 그려집니다.

예제

다음은 Shiny와 함께 OJS를 사용하는 다양한 방법을 보여주는 예제입니다.

예제 소스 설명
K-평균 코드 OJS 입력을 Shiny 입력에, Shiny 반응형을 OJS 플롯에 바인딩하는 간단한 예제입니다.
구간 나누기 코드 서버에서 중간 크기(32mb) 데이터셋을 빠르게 구간 나누기하는 예제를 보여줍니다.
데이터 바인딩 코드 https://observablehq.com에서 노트북을 가져와 데이터 필드를 Shiny 반응형에 바인딩하는 예제를 보여줍니다.

바인딩

OJS에서 Shiny로

위의 예제에서는 기본적으로 OJS viewof 표현식이 Shiny 입력(예: input$xcol)으로 자동 전파된다는 점을 활용했습니다. 이는 관심사의 분리를 적절히 유지하고, OJS 변수가 큰 경우 과도한 네트워크 트래픽을 방지합니다.

하지만 다른 OJS 변수를 Shiny 입력으로 사용하고 싶다면 ojs-export 옵션으로 이를 설정할 수 있습니다. 기본 동작은 다음 구성에 대응합니다.

---
server:
  type: shiny
  ojs-export: viewof
---

ojs-export: all을 지정하면 모든 OJS 반응형이 Shiny 입력에 바인딩됩니다.

---
server:
  type: shiny
  ojs-export: all
---

또는 이름으로 OJS 반응형 목록을 지정할 수 있으며(~로 제외할 반응형을 필터링 가능), viewof 및/또는 all 옵션과 조합할 수도 있습니다. 예를 들면 다음과 같습니다.

---
server:
  type: shiny
  ojs-export: [all, ~large_dataset]
---

Shiny에서 OJS로

덜 흔하지만 때때로 유용한 기능으로 Shiny 입력을 OJS에 바인딩할 수 있습니다. 기본적으로는 이런 바인딩이 발생하지 않지만, ojs-import 옵션으로 특정 Shiny 입력을 선택적으로 가져올 수 있습니다. 예를 들면 다음과 같습니다.

---
server: 
  type: shiny
  ojs-import: [minimum, maximum]
---