Streamlit Community Cloud + GitHub Codespacesで爆速ダッシュボード開発してみた

### 要約
:::note
この記事では、Streamlit Community CloudとGitHub Codespacesを用いて、
Pythonベースでダッシュボードを開発した作例と、
初心者が実際に試してみて重要だと感じたポイントを3点解説しています。
:::

### 作例
こちらはStreamlitを**初めて**使った@yusa-sanが、実働**2.5日**（利用データのWebアプリ向け加工を含む）で作成できたダッシュボードです。事前の勉強無しで、丸腰で挑んでみました。

<iframe width="560" height="315" src="https://www.youtube.com/embed/vl6UA8-7zgg?si=pG2alHNpbprRnOwq" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

:::note
<details><summary>デモに登場する作品の説明</summary>

- 都知事杯オープンデータハッカソン2024のDemo Dayに向けて作成しました。

- [作品紹介ページ](https://odhackathon.metro.tokyo.lg.jp/collection/73/)

</details>
:::

ダッシュボードは①GitHub repositoryにアップロード済みのファイル、②URLでアクセスできるファイル、③ローカルからアップロードするファイル、の3種類に対応しています。
（細かく動作確認済みの機能は①と②のみです！）

デモ動画で入力しているURLは、[AWSの静的ウェブサイトホスティング](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/HostingWebsiteOnS3Setup.html)で公開しているデータのURLです。

https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/HostingWebsiteOnS3Setup.html

### Streamlit Community Cloudとは
こちらの[公式サイト](https://docs.streamlit.io/deploy/streamlit-community-cloud/get-started/quickstart "Quick Start")に書かれている通り、

1. Streamlit Community Cloudのアカウントを作る
2. GitHubアカウントを接続する
3. `Create app`からアプリ作成画面に遷移し`Deploy a public app from a template`を選択
（後からアプリを非公開（private app）に変更可能）
4. `Blank app`（空のアプリ）を選択
5. 作成するアプリのコードを格納するGitHub repositoryの名前と、アプリのURLを設定し、
`Open GitHub Codespaces to immediately edit this app in your browser`にチェック☑
6. `Deploy`をクリック

以上の1分くらいの操作で、**あなたのアプリの外枠はすでにデプロイされ、URLからアクセスもできるように**なってしまいます。手軽すぎる…

さらに感動したのは、**GitHubに新たなコミットをするとリロードしなくてもアプリの挙動が変化する**（いわゆるホットリロード）ことです。快適すぎる…

https://docs.streamlit.io/deploy/streamlit-community-cloud/get-started/quickstart

https://docs.streamlit.io/deploy/streamlit-community-cloud/get-started

どこにデプロイされているのか気になる方はこちら

https://docs.streamlit.io/deploy/streamlit-community-cloud/get-started/trust-and-security

### ポイント① コードは初めからGitHub Codespacesで編集する
あなたのGitHubアカウントには、上記5.で指定した名前のrepositoryが出来ています。
そのrepository内にある`streamlit_app.py`を編集・コミットすることで、**公開されているURL上のアプリがどんどんアップデート**されていきます。
（`streamlit_app.py`：2025年3月時点での、デフォルトのPythonファイル名）

<details><summary>私がStreamlitを使ってみようと考えた時...（体験談が気になる方向け）</summary>

私がStreamlitを使ってみようと考えた時、ローカルにStreamlitをpip installするところから始めていました。
ですが依存関係をクリアするために、[Microsoft Visual C++ Redistributable package](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170)をインストールしなければならないなど、PCによっては環境構築に手間がかかると考えられます。
（上記の依存関係はWindows固有のもので、[duckdbのKnown Python Issuesのリスト](https://duckdb.org/docs/stable/clients/python/known_issues.html#error-when-importing-the-duckdb-python-package-on-windows)でも紹介されています）

</details>

そこで！
ローカルでコードを作成してからGitHubにPublishするよりも、初めからGitHub Codespacesでコードを書いていく方がお手軽と考えます。

#### Pythonモジュールを追加したい場合
Pythonでの環境構築と聞いて気になるのが、ライブラリのインストールです。
しかしGitHub Codespacesでのコード編集ではそれも簡単でした！
5.で作成されたrepositoryには、すでに`requirements.txt`が用意されているため、

- `requirements.txt`に欲しいライブラリのモジュール名を記述していく

これだけで、欲しいライブラリをimportできるようになります。

<details><summary>なおStreamlitはアプリをビルドする際にuvを使っているようです（気になる方向け）</summary>
アプリをビルドすると以下のようなメッセージを見ることができます。

```shell-session
Using uv pip install.
Using Python 3.11.11 environment at /home/adminuser/venv
Resolved 61 packages in 681ms
Prepared 61 packages in 9.60s
Installed 61 packages in 851ms
 + affine==2.4.0
 ...
 + xyzservices==2025.1.0
Checking if Streamlit is installed
Found Streamlit version 1.43.2 in the environment
```

</details>

### ポイント② 非公開にしたい情報の設定を適切に行う

Streamlit Community Cloudでは、以下の2つの公開/非公開設定ができます。

- アプリURLの公開/非公開設定
- private repositoryの使用（ソースコードの非公開設定）

また、
- `Secrets`の設定（APIキーなど公開したくない情報の設定）

も可能です。

#### アプリURLの公開/非公開設定
Streamlit Community Cloudのマイページに行くと、`My apps`から作成したアプリの一覧が見られます。
設定したいアプリの三点リーダーから`Settings`を開き、`Sharing`タブに行くと、`Only specific people can view this app`を選択することができます。
これにより、指定のメールアドレスでサインインしたユーザーにのみ、アプリを公開できます。

なお、[上記の手順](https://qiita.com/yusa-san/items/89dda660d4faf7fbde33#streamlit-community-cloud%E3%81%A8%E3%81%AF)で設定した場合、デフォルトで`This app is public and searchable`と設定されており、アプリは誰でも閲覧でき、かつ検索も可能な状態になっているようです。

![Settings - Sharing](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629721/6e2007e0-a832-46c9-8e73-de65dafa4d16.png)

#### private repositoryの使用
アプリのソースコード自体を公開したくない場合、Streamlitに`GitHubのprivate repositoryを読み取る権限`を与える必要があります。
[Quick Start](https://docs.streamlit.io/deploy/streamlit-community-cloud/get-started/quickstart)の`Optional: Add access to private repositories`が参考になります。

実は、私はまだこちらの機能を試してみていません。今回作成したアプリ自体は何の変哲もないダッシュボードで自由にForkしていただいて構わないものだからです。
また、次に説明する`secrets`の活用により、APIキーや非公開URLなどは保護できます。

使用する場合、**private repositoryから公開できるアプリは1つまで**という制約があるようなので、注意しましょう。（[こちらのドキュメント](https://docs.streamlit.io/deploy/streamlit-community-cloud/share-your-app)に書かれています）

https://docs.streamlit.io/deploy/streamlit-community-cloud/share-your-app

自由にForkできる[今回作成したデモアプリ](https://high-resolution-wbgt-dashboard.streamlit.app/)のrepository

https://github.com/yusa-san/hits-high-resolution-wbgt-map

#### `Secrets`の設定
コードで非公開のAPIキーや非公開のURLを利用する場合、アプリの公開設定と同様に、アプリの`Settings`から`Secrets`タブに行くと、Streamlit Community Cloud独自の方法で`Secrets`の設定が可能です。
なお、他のマネジメントツールも動くそうで、2025年3月現在、dotenv files, AWS credentials files, Google Cloud Secret Manager, or Hashicorp Vault等が公式サイトで紹介されています。

Streamlit Community Cloud独自の方法では、
1. [`Secret management`で説明されている形式](https://docs.streamlit.io/develop/concepts/connections/secrets-management)に沿って`secrets.toml`に書くべき内容を決める
2. アプリの`Settings`から`Secrets`タブに行き、`secrets.toml`に書くべき内容をそのままコピーペーストする
（`secrets.toml`ファイル自体は作成しなくてよい）
3. コード内では、`st.secrets.key`または`st.secrets["key"]`で情報にアクセスする
（`key`は`Secrets`内で記述した任意のキー）

これだけでokです。

以下の2つのドキュメントを見ておけば良さそうです。

https://docs.streamlit.io/develop/concepts/connections/secrets-management

https://docs.streamlit.io/deploy/streamlit-community-cloud/deploy-your-app/secrets-management

### ポイント③ Streamlitの挙動の特徴を知る
実際にコードを書いていく上で、Streamlitのいくつかの挙動に少しくせを感じました。

特に今回作成したダッシュボードでは**ユーザーとのインタラクション**が多かったので、それにまつわるポイントを紹介します。

#### ユーザーとのインタラクションがある度にコード全体を再読み込みしている
streamlitのアプリはユーザーが何かを選択したり入力したりする度に、**コード全体を再読み込み**します。`st.rerun()`によってもコード全体が再読み込みされます。
**再読み込み後も保持しておきたい変数は`st.session_state`に保存する**ことも重要です。

[今回作成したデモアプリのコード](https://github.com/yusa-san/hits-high-resolution-wbgt-map)では、上記を利用して、ユーザーが入力したURLはすぐにマスクされるようにしています。（デモ動画を撮る際に入力されたURLを映したくなかったため）
以下のように、URL入力の一連の処理が終わった後に`st.rerun()`を入れることで実現しています。
```python
# ロード完了フラグ
st.session_state["url_entries"][i]["loaded"] = True
# URL入力欄をマスク
st.rerun()
```

#### ユーザーとのインタラクションを行うフィールドの設定
ユーザーによるテキスト入力は`st.text_input`で簡単に実現可能ですが、**必ず入力フィールドに対してユニークになるキーを設定する**ことと、**初期値を上手く設定する**ことが大切です。

下記では、for文のイテレートごとにkeyが変わるようにしています。また、`st.rerun()`で再読み込みされた後は"URL入力済"と表示されるようにしています。
```python
for i, entry in enumerate(st.session_state["url_entries"]):
    st.write(f"#### ファイル {i+1} のURL入力")
    # URL入力欄
    url_input = st.text_input(
        "URLを入力してください",
        key=f"url_input_{i}",  # キーをユニークに
        value="" if entry["url"] == "" else "URL入力済",
    )
```
#### デバッグの仕方
また、デバッグの仕方も少し特徴的でした。
- アプリのエラーはアプリ画面右下の`Manage app`から見る
<img width="200"src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629721/27afdc03-64eb-4208-b0ef-58a3ddbf44ba.png">

- アプリの再起動をしたい際には、`Manage app`を開いた後に出てくる三点リーダーから`Reboot app`を選択する
<img width="200"src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/629721/d2155655-d74e-4062-9a01-6559e9f2f9fc.png">

### 最後に
ここまででStreamlit Community Cloud + GitHub Codespacesによるダッシュボード開発が爆速でできた理由が伝わっていたら嬉しいです。

Streamlitはコミュニティによる助け合いも充実しています。
Streamlitの独自仕様で困った時は[Discuss](https://discuss.streamlit.io/)で質問されていることをチェックするのが良さそうです。

https://discuss.streamlit.io/

それでは、楽しいPythonライフを！