Streamlitで簡単なアプリをデプロイする


以前から気になっていたStreamlitを勉強がてら触ってみました。
初心者の学習や簡単なモック作成をしたいような場面では、結構使えると思ったのでまとめてみました。

**やること**

- Streamlitでの簡単なアプリの作成
- Streamlit Cloudで、無料でアプリを公開する
- テスト駆動開発

## 今回作るもの

今回作成したものはGithubで公開しています。
READMEに概要が記載されているので、そちらを参考にしてください。
(この記事以降も機能を追加しています)

https://github.com/boss-kasai/streamlit-mini-apps

今回はボタンを押したら、ランダムにUUIDが作成されるWEBアプリケーションを作成します。Streamlitを使ってみて、デプロイまでやってみることが目的なのでまずは難しいことはしません。

流れは下記の通りです

1. GitHubのPublicリポジトリを作成する
2. コードを書く
3. GitHubにプッシュ
4. Streamlitに公開する

さて気楽にやっていきましょう！

## GitHubのpublicリポジトリを作成する

GitHubの使い方などは他の記事にお任せします。
今回は、Streamlit Cloudの無料枠でデプロイします。
そのためには、ソースコードもデプロイしたアプリも一般公開されている必要があるそうです。
有料版を使えば、もっと自由にできるらしいです。

今回私はstreamlit-mini-appsというリポジトリを作成しました。

## コードを書く

現在のディレクトリはこんな感じです。

```bash
$ tree
.
├── LICENSE
└── README.md
```

開発に必要なディレクトリを作成します。

```bash
mkdir app test development
```

現状はこんな感じです。

```bash
$ tree
.
├── LICENSE
├── README.md
├── app # サービスの本体のコードを格納
├── development # 開発用のPythonの仮想環境を格納
└── test # pytestのテストコードの格納
```

### 仮想環境の作成と開発の準備

開発環境はuv推しなんですが、今回は初学者の方にも参考にさればと思いますので、Pythonに標準で搭載されているvenvを使用します。

```bash
# 仮想環境の作成
python3.13 -m venv development/venv
```

developmentフォルダ内にvenvというフォルダが作成されて、仮想環境関連のたくさんのファイルが作成されます。

あとは必要そうなライブラリをインストールします。

```bash
# 仮想環境の有効化する
source development/venv/bin/activate

# とりあえず、streamlitをインストールする
pip install streamlit
```

今後のために、ライブラリの管理もしておきましょう。

```bash
# 依存関係の保存
pip freeze > ./app/requirements.txt # appフォルダー下にrequirements.txtファイルを作成され、依存関係が記載されます。
```

これで、他の人も同じ環境で開発できるようになります。
個人でやっているとあまり意識しませんが、個人開発でも長期間になってくると、バージョンがズレたりPC買い替えて環境を作り直したりする必要が出てきたりするので、習慣化した方がいいです。

ちなみに、requirements.txtからライブラリをインストールする方法は下記です。

```bash
# 依存関係のインストール
pip install -r ./app/requirements.txt
```

### Streamlitで画面を作ってみる

さて早速ですが、streamlitの画面を作成してみましょう。
まずは、画面から作成していきます。

```bash
# ファイルの作成
touch app/main.py
```

まずは難しいものを作るべきではないので、
ボタンを押すと"Hello World"と表示されるだけのものを作成してみます。

```bash:main.py
import streamlit as st # streamlitのインポート

# タイトルを設定
st.title("Hello, World! ボタンアプリ")

# ボタンを作成
if st.button("クリックしてHello, World!を表示"):
    # ボタンが押されたときに表示するメッセージ
    st.write("Hello, World!")
```

このコードで一旦、streamlitをサーバーを起動してみましょう。
起動コマンドは下記です(仮想環境をアクティブにしてください)。

```bash
streamlit run app/main.py
```

下記画像のように機能すればstreamlitの第一歩が完了です。
意外と簡単ですよね！

![スクリーンショット 2025-01-18 13.32.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570453/03abc6af-0651-6635-660d-10070cf31a27.png)


### 関数の作成

ここからは、UUIDを作成する機能の作成に移行します。
テスト駆動開発で進めたいので、一旦ダミーの関数を作成します。

```bash
touch app/func.py # 関数作成用のファイルを作成

pip install pytest # pytestのインストール
```

まずは固定値を返す適当な関数を作成します。

```python:func.py
# UUID生成アプリ
def uuid_generator(create_num):
    return "00000000-0000-0000-0000-000000000000"
```

次はテスト関数の作成です。

```bash
touch test/test_func.py # テスト関数作成用のファイルを作成
```

テストを作成します。
テストケースは下記
- [ ] UUIDが作成されている 

```python:test_func.py
import uuid

from app.func import uuid_generator


def test_uuid_generator():
    # 関数の戻り値を取得
    result = uuid_generator()

    # 戻り値がUUIDのインスタンスであることを確認
    assert isinstance(result, uuid.UUID)
```

早速のテスト結果は下記です。

```bash
$ pytest
============================================================= test session starts =============================================================
platform darwin -- Python 3.13.1, pytest-8.3.4, pluggy-1.5.0
rootdir: your_dir/streamlit-mini-apps
collected 1 item                                                                                                                              

test/test_func.py F                                                                                                                     [100%]

================================================================== FAILURES ===================================================================
_____________________________________________________________ test_uuid_generator _____________________________________________________________

    def test_uuid_generator():
        # 関数の戻り値を取得
        result = uuid_generator()
    
        # 戻り値がUUIDのインスタンスであることを確認
>       assert isinstance(result, uuid.UUID)
E       AssertionError: assert False
E        +  where False = isinstance('00000000-0000-0000-0000-000000000000', <class 'uuid.UUID'>)
E        +    where <class 'uuid.UUID'> = uuid.UUID

test/test_func.py:11: AssertionError
=========================================================== short test summary info ===========================================================
FAILED test/test_func.py::test_uuid_generator - AssertionError: assert False
============================================================== 1 failed in 0.03s ==============================================================
```

無事、エラーになりました。
コードを改修します。

```python:func.py
import uuid


# UUID生成アプリ
def uuid_generator():
    return uuid.UUID("00000000-0000-0000-0000-000000000000")
```

さて、再度テストを実行します。

```bash
$ pytest
============================================================= test session starts =============================================================
platform darwin -- Python 3.13.1, pytest-8.3.4, pluggy-1.5.0
rootdir: your_dir/streamlit-mini-apps
collected 1 item                                                                                                                              

test/test_func.py .                                                                                                                     [100%]

============================================================== 1 passed in 0.02s ==============================================================
```

無事テストをグリーンにすることができました。
しかし、現在は固定のUUIDを返しているだけなので毎回違うUUIDを返すようにしましょう。

テストケース
- [x] ~~UUIDが作成されている~~
- [ ] 毎回違うUUIDが生成される

テストコードを作成します。

```python:test_func.py
import uuid

from app.func import uuid_generator

# UUIDが作成されているか
def test_uuid_generator():
    # 関数の戻り値を取得
    result = uuid_generator()

    # 戻り値がUUIDのインスタンスであることを確認
    assert isinstance(result, uuid.UUID)

# 毎回違うUUIDが生成されるか
def test_uuid_generator_generates_unique_uuids():
    # UUIDを複数回生成
    uuid1 = uuid_generator()
    uuid2 = uuid_generator()

    # それぞれが異なることを確認
    assert uuid1 != uuid2

```

さてテスト

```bash
$ pytest
============================================================= test session starts =============================================================
platform darwin -- Python 3.13.1, pytest-8.3.4, pluggy-1.5.0
rootdir: your_dir/streamlit-mini-apps
collected 2 items                                                                                                                             

test/test_func.py .F                                                                                                                    [100%]

================================================================== FAILURES ===================================================================
_________________________________________________ test_uuid_generator_generates_unique_uuids __________________________________________________

    def test_uuid_generator_generates_unique_uuids():
        # UUIDを複数回生成
        uuid1 = uuid_generator()
        uuid2 = uuid_generator()
    
        # それぞれが異なることを確認
>       assert uuid1 != uuid2
E       AssertionError: assert UUID('00000000-0000-0000-0000-000000000000') != UUID('00000000-0000-0000-0000-000000000000')

test/test_func.py:20: AssertionError
=========================================================== short test summary info ===========================================================
FAILED test/test_func.py::test_uuid_generator_generates_unique_uuids - AssertionError: assert UUID('00000000-0000-0000-0000-000000000000') != UUID('00000000-0000-0000-0000-000000000000')
========================================================= 1 failed, 1 passed in 0.17s =========================================================
```

新しく追加したコードがレッドになりました。
コードを修正します。

```python:func.py
import uuid


# UUID生成アプリ
def uuid_generator():
    return uuid.uuid4() # ここを修正
```

```bash
pytest
============================================================= test session starts =============================================================
platform darwin -- Python 3.13.1, pytest-8.3.4, pluggy-1.5.0
rootdir: your_dir/streamlit-mini-apps
collected 2 items                                                                                                                             

test/test_func.py ..                                                                                                                    [100%]

============================================================== 2 passed in 0.04s ==============================================================
```

一旦グリーンになったので、streamlitの表示も変更しましょう。

```python:main.py
import streamlit as st
from func import uuid_generator

# タイトルを設定
st.title("UUID生成アプリ")

# ボタンを作成
if st.button("UUIDを生成する"):
    # UUIDを生成
    uuid = uuid_generator()
    # UUIDを表示
    st.write(uuid)
```

これで作りたいものは一旦完了です。
しかし、1つだけUUIDが出てもつまらないので指定した個数出力されるように変更しましょう。

```python:test_func.py
import uuid

from app.func import uuid_generator


# UUIDが作成されているか
def test_uuid_generator():
    # 関数の戻り値を取得
    result = uuid_generator(1)

    # 戻り値がUUIDのインスタンスであることを確認
    assert isinstance(result[0], uuid.UUID)


# 毎回違うUUIDが生成されるか
def test_uuid_generator_generates_unique_uuids():
    # UUIDを複数回生成
    uuid1 = uuid_generator(1)
    uuid2 = uuid_generator(1)

    # それぞれが異なることを確認
    assert uuid1 != uuid2


# 指定した個数のUUIDが生成されるか
def test_uuid_generator_generates_specified_number_of_uuids():
    # UUIDを生成
    uuids = uuid_generator(50)

    # 生成されたUUIDの個数が正しいことを確認
    assert len(uuids) == 50
```

コード

```python:func.py
import uuid


# UUID生成アプリ
def uuid_generator(create_num):
    uuid_list = []  # UUIDを格納するリスト
    for _ in range(create_num):
        # UUIDを生成
        uuid_list.append(uuid.uuid4())
    return uuid_list
```

最後にStreamlitの表示を変更します。
現状はこんな感じでクールではありません。

![スクリーンショット 2025-01-18 13.44.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570453/56a84f72-623e-1bcc-ec69-399c659a0822.png)


もっとクールにしましょう

```python:main.py
import streamlit as st
from func import uuid_generator

# タイトルを設定
st.title("UUID生成アプリ")

# ユーザーからの入力を受け取る
num_uuids = st.number_input(
    "生成するUUIDの数を入力してください", min_value=1, max_value=100, value=50
)

# ボタンを作成
if st.button("UUIDを生成する"):
    # UUIDを生成
    uuids = uuid_generator(num_uuids)
    # UUIDを表示
    for uuid in uuids:
        st.write(uuid)

```

![スクリーンショット 2025-01-18 14.00.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570453/f265b7f9-9261-baf1-dd99-d48a4a05c640.png)



最低限このくらいなら良いかなという感じにまとめられました。
一旦はこれでデプロイに進んでみましょう。

まずは現在作成したコードをGitHubのmain(master)にマージしておいてください。

## Streamlit cloudでデプロイ！！

ここからはStreamlit cloudでの作業になります。
下記、URLからStreamlit cloudのアカウントを作成してください。
https://streamlit.io/

今回は無料枠での公開をします。

ここからはStreamlit cloudの画面なのでスクショで解説します。
GitHubでのアカウント作成するのが良いのではないと思います。

ログイン後の画面は下記のようになります。
新規アプリ作成のために、画面右上の「Creat app」をクリックしてください。

![スクリーンショット 2025-01-18 14.43.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570453/a3f6a7ef-5fb8-ec8c-405f-65751553e01f.png)

下記のような画面に遷移すると思います。
今回はGitHub連携を使うので、「Deploy a public app from GitHub」の「Deploy now」をクリックしてください。

![スクリーンショット 2025-01-18 14.43.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570453/9290b53c-59d0-bbb5-b11e-b70f5ba38eac.png)

GitHubの自分のアカウントと連携しておくと、下記のようにpublicリポジトリから選択することができます。

![スクリーンショット 2025-01-18 14.44.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570453/827ed11e-aedf-66b8-b66c-c24862bf9470.png)

Streamlitのmainのコードが書かれているファイルを選択します。

![スクリーンショット 2025-01-18 14.53.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570453/f32ea930-f752-d155-8840-306d70b6bba3.png)

ブランチ名がmainではなくmasterの場合は適宜変更してください。
またURLはある程度自らの希望で決められるので、ご自由に決めてください。

![スクリーンショット 2025-01-18 14.56.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570453/bca6fee8-717e-83c5-7cae-0194fba4a383.png)

全ての項目が問題なくなれば、画面下部の「Deploy」をクリックしてください。
可愛い画面に変わります。

![スクリーンショット 2025-01-18 14.57.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570453/6b2f902f-5d25-00c0-bd81-6462b38524cc.png)

デプロイが無事完了しました。

![スクリーンショット 2025-01-18 14.57.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570453/c165762e-cf31-74dd-58bf-b5e44820b65e.png)


今回、アップしたアプリは下記URLから見ていただけます。
https://mini-apps-tk.streamlit.app/

## まとめ

Pythonの学習をしているとUIを作ることが大変だったりして、どうしてもサービスを作ることの壁にUIが出てきてしいます。Python学習の1つの壁になってしまいます。しかし最近はPythonである程度までフロントが作れるフレームワークなども出てきています。その1つがStreamlitです。

実際にUIがあってサービスっぽくなった方が楽しいと思うので、学習を楽しくするためにもStreamlitはおすすめかなと思います。

初学者の方の参考になればと思います！