Table of Contents
はじめに
Fundastaの片田です!
今回はNFCシールを使用して会社の自動打刻システムを、完全に自分用で作ります!
要件定義
なぜつくるか?
現在、弊社の勤怠は、エクセルで管理されています。 実際の打刻フローとしては、
- 出社したら出社時刻をエクセル開いて手動で打刻
- 保存
- 退社するときに退社時刻をエクセル開いて手動で打刻
- 保存
。。。 毎日エクセルポチポチするの面倒すぎる!!!!!!!!!
作業自体も面倒なのに、何日か打刻を忘れるとまあ面倒臭いことになります。
せっかくIT企業にいるんだからいろいろスマートにやりたい… ということで、今回の自動打刻システムの開発を決意しました。
どう作るか?
今回開発する自動打刻システムでは、NFCシールを活用していきます。
処理の流れとしては、
- NFCシールにスマホをかざして専用のWEBサイトを表示する
- WEBサイトから自動打刻システムにリクエストを投げる
- 打刻する
といった感じにしようかと思います。
NFCシールにスマホかざすのとリクエストを投げるところにWEBサイト表示をはさんでいるのは、 意図しない打刻を防ぐためです。
クライアント側で打刻される時刻の確認、出社なのか退社なのかの選択できた方が、 手順は増えますが確実かなあということでワンクッションはさみました。
また、現状では個人用なのでユーザーの識別は行いません。
どう使うか?
想定される使用フローは下記のとおりです。
- 出社したらデスクのどこかしらに貼ったNFCシールにスマホをかざす
- 表示されるWEBサイトで打刻時間を確認、出社ボタンを押す
- (退社時も同じ)
かなりスマート…!(な気がする)
使用技術
インフラはAWSの各サービスを利用します。
メイン処理の部分にはLambda、エクセルファイル(勤怠管理)はS3に保存します。
サーバーサイドにnode.js、WEBフロントエンドにはVue.jsを使用しつつ、HTTP通信はaxiosを使用します。
実は今回の開発、個人的にNFCシールを使ったアプリを作ってみたかったので、NFCシールありきで考えていました。
スマホかざすだけで打刻できるのステキじゃん…
他にも java, ruby, pythonなどなど、いろんな言語で書くことができます。
フロントエンド開発に興味があり、日頃からJavaScriptを勉強しているので、 サーバーサイドもJavaScriptで書こう!ということでnode.jsを選びました。
通常は、EC2インスタンスは常時存在し、アプリケーションも常時起動されているのですが、Lambdaはリクエストが送られてきたときのみインスタンスを生成→アプリケーションを実行→インスタンスを破棄という挙動をします。
今回作成するアプリは常時起動している必要もないので、コストを抑える意味でもLambdaが適しているのではないかと考えました。
と言っても、現在時刻を表示するのと、ボタンを二つ配置するだけなので全く難しいことはしません笑
強いて言えば、ローディングのアニメーションを作り込むくらいでしょうか…。
HTTP通信はaxiosを使用します。
設計
アーキテクチャ図
アーキテクチャの全体像としては、下記の通りです。
構成は至ってシンプルで、 HTTPリクエストをAPI Gatewayで受け付け、Lambdaに投げます。
勤怠を管理しているエクセルはS3においておき、Lambdaからそのエクセルファイルに書き込みをしていく感じです。
処理が完了すると、処理結果をSuccessかFailでクライアントに通知します。
アプリケーションの実装
自動打刻システム(node.js)実装
コードの全貌は下記のGitHubリポジトリを御覧ください。GitHub リポジトリ
エクセルファイルの操作には、「xlsx-populate」というライブラリを使用しました。
最初は「xlsx」というライブラリを使って実装していましたが、このライブラリだと処理をして、保存するとマクロや書式が無効化された状態になってしまうのでつかえず…。
個人的にはドキュメントも「xlsx-populate」のほうが読みやすかったです!処理としてはファイルを読み込んで、シートを指定して、セルを指定して値を書き込み、保存しているだけです。
弊社の勤怠表は月ごとにシートが分かれているので、処理の頭でDateオブジェクトを生成して、得られた各値でシートや記入するセルを判定しています。
また、弊社は30分ごとに勤務時間として打刻できるので、打刻する時刻を30分単位に変換する関数を用意しています。
今後もっと本格的に運用していくことになったら、このあたりで拡張の余地がありますね。
実装で苦労したのは非同期処理とAWS S3からファイルを取得して、書き込んだものをアップロードし直す処理のところ。
1 2 3 4 5 6 7 |
const params = { Bucket: 'バケット名', Key: 'キー' } s3.getObject(params, (err, data) => { } |
上記のように記述すれば、指定されたバケットのオブジェクト(ファイル)を取得できて、data変数に格納されます。
また、アップロードするときは、
1 2 3 4 5 6 7 8 |
const params = { Bucket: 'バケット名', Key: 'キー', Body: 'アップロードしたいファイル' } s3.putObject(params, (err, data) => { }) |
でアップロードできます!
ここがnode.jsの情報がなかなか転がってなくて苦労しました。
取得も書き込みも注意点としては、取ってきたり送信するためには、データ形式に気をつけなければなりません。
今回僕は、これらの処理の前後にエクセルファイルをバッファーに変換する処理をはさみ、変換したものをparams変数のBodyとしています。
クライアントサイド(vue.js)実装
クライアントサイド(WEB)はVue.jsで作りました。
最終的には静的サイトとしてビルドして、Netlifyでホスティングします。
こちらは特に難しいことはしていません。
UIはVuetifyを使ったので適当に作った割には整っています。
スクショですが、下記のようになりました。
打刻すると、vue-loading-templateを使用したアニメーションが流れて、レスポンスが帰ってくるとアラートが表示されます。
(若干左によってるのはスクショが下手だからです…笑)
インフラ環境構築
構築したもの
いよいよインフラの構築に入ります。
今回は、メインのAPIをLambdaで動かします。
勤怠表(エクセルファイル)はS3にアップロードしておき、Lambdaから読み取り、書き込みを行います。
HTTPリクエストの受け口として、APIGatewayを配置します。
ここに想定されるリクエストがとんできたら、それをトリガーにLambda関数が動く仕組みにしていきます。
また、クライアントサイド(WEBアプリ)はNetlifyという静的サイトのホスティングサービスを利用します。
Netlifyに関しては後日別記事で言及します。
ハマったポイント
私はインフラ超初心者なので、インフラ構築でかなりつまづきました…。
「LambdaからS3のファイルをとってこれない」
作成したLambdaに正しくロールを付与していなかったため、アクセス権限 is 何の状態が1時間くらい続きました…。
「Lambda関数(メインAPI)が非同期処理になっていて肝心の処理を行う前にLambdaが終了してしまっていた」
今回使用した「xlsx-populate」は非同期処理をすることが前提のライブラリです。
恥ずかしながら、node.jsだけでなく非同期処理の知識も乏しく、Lambdaはエラーなく終了するのに肝心の処理が実行できてない…。
という状態で約5日間潰しました。
エラー箇所の切り出しが下手だったなあと反省しています。
いろんな記事や書籍を読み漁りつつ、async awaitを駆使してなんとか解決しました。
「CROS」
実際にクライアントサイドからリクエストを投げる時にはまりました。
今までなーんとなくしか理解してなかったですが、これを機にしっかり学べました。
CROSに関しては別記事でまとめます。
実際につかってみた
明日から出社と退社が楽しみになりそう。
(ちょっと処理は遅いですが)個人的にほぼノンストレスに打刻できるようになったので満足です。
ただ、本当にきちんと勤怠をつけるためには小難しい会社のルールがあるみたいなので、そのうちきちんとしたものも作りたいです。
今のところはほぼ毎日きっちり定時に退社しているので細かい調整はそんなに必要なさそう…だと思ってます笑
おわりに
NFCさいこう!!!!!たのしい!!!!!