Spring Boot・Spring Session・Herokuでオンライン研修に役立つアプリを自分で作った話

このご時世もあり、お客様の新人研修をリモートで実施することになりました。僕が担当した範囲では、講師だけでなくすべての受講者さんがほぼ全日程、自宅からオンラインで研修に参加しました(ツールとしては主にZoomとSlackを利用。そのへんは別記事にて)。

別記事↓

masatoshitada.hatenadiary.jp

4月に初めてオンライン実施した中で大きな課題が見えてきたので、5月以降の研修に向けて、ゴールデンウィーク中にその課題を解決するためのアプリを自分で作ることにしました。

4月で見えてきた課題

4月の研修が、僕にとって初めてのオンライン研修担当でした。色々と試行錯誤しましたが、「技術を身につけていただく」という点ではオフラインの研修と変わらずにできたと感じました。

しかし、課題もありました。

オフラインならば講師が、部屋を巡回して受講者さんの進捗を把握したり、PC画面を覗き込んでトラブルを見つけたりできます。

オンライン研修では、それができません。作業や演習の進捗にしても、PCトラブル等にしても、すべて受講者さん側からアクションしてもらわないと、講師は気付くことすらできません。

4月はチャットとしてSlackを使っていたので、Votum Pollという投票アプリで進捗やトラブルを報告してもらっていました(下記はイメージ図)。

f:id:MasatoshiTada:20200622110008j:plain

これはこれで良かったのですが「誰がまだ報告していないかをひと目で確認できない」という弱点がありました。

未報告の人はトラブルで困っているケースが多いため、いち早くその人を特定して講師から様子を伺う必要があります。しかし、実際の研修では受講者さんが数十名いらっしゃるので、その中から未報告の人を特定するのはとても時間がかかりました。

Slackで使える投票アプリは他にも色々試したのですが、僕が確認した限りでは、未報告の人を一覧できるアプリはありませんでした。

進捗管理アプリのコンセプト

このような課題と、色々と探しても適したツールが無さそうということから、自分で作ることにしました。コンセプトは次のとおりです。

(実際に考えてたときのメモ書きです。走り書きなので字が汚くてすいませんw) f:id:MasatoshiTada:20200622112317j:plain

(1) スマホ操作

オンライン研修では受講者さんは、Zoom・Slack・IDE・ブラウザなど、PCの1つの画面で多くのものを動かしています。全員が2つ目の画面(追加モニターやタブレットなど)を持っているとは限りません。そのような状況で、PCで動かすものを更に増やしたくありませんでした。

代わりのデバイスとして考えられるのは、全員が持っていて操作にも慣れているであろう、スマートフォンでした。

短期間でiOSアプリ・Androidアプリとして作ることは困難だったため(審査に時間がかかるし、そもそも自分に知識が無い)、ブラウザから操作できるWebアプリとして作ることにしました。

(2) ボタン一発

イメージとしては「Amazon Dash Button」です。ボタン1つで進捗やトラブルを報告できるようにしたいと考えました。

(3) トラブル報告しやすい

「ボタン一発」と重なりますが、進捗だけでなくトラブルも簡単に報告できるようにしたいと考えました。

(4) 報告していない人を含めて一目瞭然

Slackの投票アプリで大変だった「未報告の人を早く特定する」を可能にしたいと考えました。

ユースケース

作るべき機能を整理するためにユースケース図を書いてみました。

(実際に書いたユースケース図) f:id:MasatoshiTada:20200622113749j:plain

アクターとしては「講師」「受講者」の2種類です。

  • 講師
    • ユーザー(=受講者)を登録する
    • 項目(=報告する項目)を設定する
    • 進捗を見る
  • 受講者
    • 進捗を報告する
    • トラブルを報告する

画面デザイン

絵心も知識も無いので「デザイン」なんて呼べる代物では無いのですが😅

(実際に書いたデザイン) f:id:MasatoshiTada:20200622114440j:plain

受講者さんが使うスマホ画面には、大きめのボタンを配置します。特に重要なトラブル報告ボタンは一番上に配置しました。

これを書いていた当時はWebSocketを使って「講師が項目を登録したら、受講者さんの画面も自動更新される」ことを考えていたのですが、後で説明する理由から一番下に「再読み込み」ボタンを配置するだけにしました。

技術選定

ベース技術

  • サーバーサイド = Spring Boot
  • フロント = jQuery

開発を思いついたのがゴールデンウィーク中、かつゴールデンウィーク明けには次の研修が始まる予定だったため、今回の開発はプライベートの時間を使っていました。

しかし自宅に生後数ヶ月の子供がいるため、そんなに多くの時間を割くことはできませんでした。もし時間があれば、学習ついでに未知の技術(考えていたのはサーバーサイドはGo、フロントはReact)を使ってみたかったのですが。。。時間を割くことに協力してくれた妻には、本当に感謝です。

なので、「自分が新規に学習しなくても使える技術」で開発することがMUSTでした。となると僕には、サーバーサイドはSpring Boot、フロントはjQueryという選択肢しか無かったのです。

今後時間があれば、Go + Reactにしたり、Slackアプリとして作ってみたりしたいですね。

デプロイ先の選定と通信の暗号化

この進捗管理アプリは受講者さんの氏名という個人情報を扱うので、暗号化は必須要件でした。具体的には「ブラウザ↔サーバー間(HTTPS)」「サーバー↔DB間」の通信の暗号化です。

僕はインフラ知識はほとんどありません。サーバーをHTTPSで公開した経験もありませんでした。そんなインフラド素人でも簡単に通信暗号化できるようなプラットフォームが必要でした。

Pivotal Web Servicesや他のクラウドサービスなど色々と試したのですが、最終的には会社の若者に教えてもらったHerokuを使うことにしました。

Herokuは自分で証明書などを用意する必要もなく、デフォルトで通信がHTTPSになります。

SSL Endpoint | Heroku Dev Center

Spring Bootアプリの場合、 spring.datasource.url などのプロパティ設定は環境変数を使ってよろしくやってくれます。

Connecting to Relational Databases on Heroku with Java | Heroku Dev Center

Heroku Postgresを使えば、アプリをデプロイするだけでJDBC URLに sslmode=require というパラメーターが付加され、DBとの通信も暗号化されます。

Connecting to Relational Databases on Heroku with Java | Heroku Dev Center

Gitで管理したプロジェクトのルートディレクトリで、下記のコマンド3つを実行するだけでデプロイできます。

# URLが 一意なアプリ名.herokuapp.com になる
heroku create 一意なアプリ名

# Heroku Postgresを作成
heroku addons:create heroku-postgresql:hobby-dev

# Herokuにデプロイ
git push heroku master

「デプロイするだけでオールOK!」というのは、僕のようなインフラ弱者にはとてもありがたいです!!

負荷分散とセッション共有

受講者さんは数十名規模であったため、スケールアウトによる負荷分散を考える必要がありました。アクセスを捌く意味ではインスタンス数を増やせばいいのですが、その場合セッション共有を考慮する必要があります。通常、セッションはサーバー内にインメモリで保持されるため、複数インスタンス間で共有できないからです。

Spring Sessionを使えば、セッション情報をRedisやRDBなどに保存できます。これにより、複数インスタンス間でセッション情報を共有できます。

以前Spring Session Redisを使ったことがありました。しかし今回は、Spring Session JDBCRDBにセッション情報を保存することにしました。

  • 管理するミドルウェアを増やしたくなかった
  • サーバー↔Redis間の通信暗号化をどうするのか調査する時間が無かった
  • 初めて使うけどSpring Bootだし多分簡単だろう

というのが理由です。

pom.xmlに下記のライブラリを追加して、

<dependencies>
    <dependency>
        <groupId>org.springframework.session</groupId>
        <artifactId>spring-session-jdbc</artifactId>
    </dependency>
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <scope>runtime</scope>
    </dependency>
    <!-- その他、starter-web, starter-thymeleaf, spring-boot-starter-security, mybatis-spring-boot-starterなど -->
</dependencies>

application.propertiesに下記の設定を書くだけです。

spring.session.timeout=1h
spring.session.store-type=jdbc
# デフォルトで用意されているSpring Session用のCREATE TABLE文を、起動時に実行する
spring.session.jdbc.initialize-schema=always

しかし・・・Herokuは無料プランでは1アプリ1インスタンスのみという制約がありました。しかも、実際に運用したら1インスタンスで数十名のアクセスを捌けたので(数十名が一気に同時アクセスするわけではないため)、今回はSpring Sessionは要らなかったかもしれませんw

WebSocketかポーリングか

  • 講師用画面で、受講者さんの進捗をリアルタイムで確認する
  • 受講者用画面で、講師が更新した進捗項目をリアルタイムに確認する

これを実現するには、WebSocketやポーリング(例:5秒毎にアクセスする)などを利用する必要があります。

最初はWebSocketを考えていたのですが、やりたいこと(特に、講師が項目を更新したというイベントを拾って受講者用画面を更新)をSpring Bootで実現することがどうも難しそうだったこと、WebSocketでの通信暗号化をどうするのか調査する時間が無かったことから、ポーリングを選びました。今考えても、これで十分だったと思っています。

また、受講者用画面の更新は、受講者さん自身に画面の再読み込みをしてもらえばOKと判断しました。工数的な問題もあったのですが、講師が進捗項目を更新するのは演習や作業ごとなので、そんなに頻繁ではないということが理由です。ただし、iPhoneChromeだと再読み込みの操作に手間がかかる([…]をタップ→[再読み込み]をタップ、という2ステップになる)ため、画面一番下に「再読み込み」ボタン(押されたら location.reload() するだけ)を付けることにしました。

完成したアプリ

今回は説明していませんが、もちろん講師用画面・受講者用画面ともにログイン必須になっています。

講師用画面

f:id:MasatoshiTada:20200622142713p:plain

本来だと数十名の情報が1画面で確認できます。

5秒に1回ポーリングするので、ほぼリアルタイムに進捗を確認できます。また、誰がヘルプを求めているか、誰が未報告かをひと目で確認できます。これにより、誰に声をかければよいのかすぐに判断できるようになりました。

講師名が文字化けしてるのはご愛嬌w

受講者用画面

f:id:MasatoshiTada:20200622170849p:plain

f:id:MasatoshiTada:20200622170902p:plain

ヘルプボタンを一番上に配置して、ヘルプを求めやすいようにしました。目立つように赤色、かつサイズを少し大きくしてあります。

受講者さんが自分で報告した項目が分かりやすいように、選択したボタンはベタ塗りになるようにしています。

一番下には再読み込みボタンを配置しました。

実際に運用して

作ってよかった、の一言です。誰がヘルプを求めているか・誰が未報告かがひと目で確認できるため、すぐに講師として適切なアクションを取ることができたのが、特に良かったです。

突貫工事で作ったので色々と粗はあるのですが、工数をあまり割けなかった中で「本質的な課題は何か」「それを最小限の手間で解決するにはどうするか」を真剣に考えたのは、とても良い経験になりました。

ちなみに、ソースコードがひどいのでGitHubなどで公開するつもりはありませんw