144
107

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[実務案件]営業から開発まで!Laravel + Docekr + AWS + CircleCI を使って介護施設の課題を解決する物語

Last updated at Posted at 2022-01-03

1.はじめに

突然ですが!
私はエンジニアになりたいと思った日から、エンジニア人生の最終目標として「IT技術を使って地方に便利を届けたい」という思いがあります。

今回のWebアプリケーションは自分の最終目標の1歩目として作成したものになります。
まだまだ未完全でツッコミたくなるところも多々あるかと思いますが、温かい目で一読していただけると幸いです!

作成したWebアプリケーションは「介護施設で使っていただく体重記録アプリ」です

今後もどんどんアップデートを繰り返したいと考えているのでアドバイス、ご指摘等あればコメントいただけると嬉しいです。

2.簡単に自己紹介

  • 北海道でバックエンドエンジニアをしている大学4年生
  • プログラミングを使って母の職場の課題解決をきっかけにWebエンジニアを目指す
  • 趣味はIotディバイスを部屋に導入して生活を豊かにしたり、読書したりすること

3.開発背景

なぜこのアプリを作ったのか・どんな課題解決をしたのか」を記載したいと思います。

まず、なぜこのアプリを作ったのかについて
きっかけは、母の「会社が月1回の体重記録を紙で管理しており、まったく管理ができていないし、施設利用者の急激な体重の変化に気づくことができないから心配」という言葉が始まりです。

ここから私の営業が始まりました。事前に画面遷移図やWebアプリケーションを利用することでどんなメリットが有るのかをまとめ実際に役員の方とお話させていただきました。

なぜ役員の方とお話させていただいたかというと、Webアプリケーションを作成するのにともなって利用者さんの個人情報を開示していただく必要があったからです。

実際に作成した画面遷移図
スクリーンショット 2022-01-02 22.00.49.png

お話させていただいた結果、**「ぜひ作ってみてほしい」**といっていただきWebアプリケーション開発がスタートしました。

次に、どんな課題を解決したのか
今回解決できた課題は大きく3つあります。

1.ペーパーレス化
今まで紙で体重記録をとっていたため、紛失して書き直したり人によって字体がちがって記録をとっても読みづらい問題を解決しました。さらに毎月必ず利用者さんの体重を見やすい形にまとめるという作業がなくなったことで無駄な作業時間を省くことができました。

実際に使用されていた紙類(許可をいただいてます)
1111.png

2.体重のグラフ化
今までは体重を数値としてしか記録していなかったため年単位でみたときに利用者さん1人1人の急激な体重の変化に気がつくことができなかったのが数値をグラフ化することによって体重の変化に容易に気づくことができるようになりました。また体重の変化を把握できるようになったことで食事のメニューの選定が容易になったという感想もいただきました。

3.クライアント専用のWebアプリケーションの作成
お話を伺ったところ、以前にもExcelを使っての体重記録に挑戦されたことがあったみたいですが機能数が多すぎて挫折してしまったという背景がありました。今回作成したWebアプリケーションは必要最低限の機能だけをつけて見た目もシンプルすぎるほどシンプルに作成したことでクライアントにとって使いやすいWebアプリケーションを作成することができITへの苦手意識を無くし便利さを伝えることができました。

まずは本当にシンプルなものを作り継続的に利用できることを最優先。そこから必要になった機能を追加していきましょうと提案させていただきました。結果、1月の初めまでに納品する形になり実際にWebアプリ開発がスタートしました。(提案当時は11月初め)

前置きが長かったですがここからが開発の話になります:pray:

4.使用技術

  • フロントエンド:HTML/Bootstrap4/Scss/Javascript(Chart.js)
  • バックエンド:Laravel(6.2)
  • DB:開発環境はDockerのMySQLコンテナ、本番環境はRDS(MySQL)
  • 開発環境:Docker/Docker Compose(LEMP環境)
  • インフラ:AWS(EC2/Route53/ALB/RDS/CloudFormation)
  • CI:CircleCI
  • ソース管理:Git/GitHub

5.(ひとまず)完成したアプリ

クライアントにとって使いやすいものを1番に考えた結果「シンプルすぎる体重管理」アプリができました。完成までの過程はもうすこし先に書いています。

データはダミーです
シンプルすぎる.gif

6.その他でやったこと

  • 課題定義
  • 要件定義
  • 画面遷移図作成
  • ER図を書いてDB設計
  • 開発環境DockerでLEMP環境構築(もちろんLaradockじゃないです)
  • PHPUnitで単体テスト
  • phpcsを用いてコード整形
  • Larastanを用いて静的コード解析
  • Circle CIで自動テスト
  • AWS CloudFormationでインフラをコード化
  • AWSにデプロイ
  • SSL化

赤字のものが今回初挑戦したことになります。全体的に今後のアップデートや長期の運用を想定して機能づくり以外にも力をいれました!

7.作業内容

ここまでの振り返り
ここまでで自己紹介、(ひとまず)完成したアプリ、開発背景の解説がおわりました。
ここからが具体的な作業内容になりますのでこれまでの情報と答え合わせをするような形で見ていただけると幸いです!

7-1.課題定義

一番最初におこなったのは課題定義です!
クライアントの何の課題を解決するのか、どのような状態になればよいのかを決めるフェーズです。課題定義を最初にしっかりとやっておかないと後々作るWebアプリケーションが意味のないものになってしまう可能性があるため、クライアントと何度かお話させていただき課題定義させていただいた。

結果以下のような課題定義をおこないました。

  • 背景・目的
    • 施設利用者さんの体重に急激な変化がないかをデータをもとに察知したい。
    • データをグラフ化して直感的に認知できるようにしたい。
    • 機能数が多いExeclを使いこなすのが難しかった過去があり、Excelよりも簡単に体重を記録できるWebアプリが欲しい。
    • 操作等を聞ける相手がいないのでWebアプリを使いこなすのがむずかしい。
  • ゴール(達成状態)
    • 過去の施設利用者さんのデータが既に登録されていて分析できる状態
      • 1, 2, 3, 4, 5年単位でグラフ化できる(最初は1年分のみ)
    • クライアントが直感的に操作できるUI
    • 施設利用者のデータの登録・更新・削除・表示ができる
    • 必要な機能要件だけをまずはそろえる(使っていて必要あればその都度機能追加していく)
    • ログイン認証機能をつける
  • やらないこと
    • クライアントにとって不必要な機能は一切つけない(良さそうなものは随時提案する)
    • スマホ利用はしないとのことだったのでPC画面のみを作成
    • 施設別に管理番号は付けない

課題定義によって、誰のどんな問題を解決するのかが明確になったので次に要件定義をおこないました。

7-2.要件定義

続いてシステムを作る上で「必要な条件」を定義して、システムに落とせるように要件定義をおこないました。
要件定義をおこなうことで、やるべきことを具体的にし迷わないようにしました。

結果、以下のような要件定義をおこないました。

  • 機能要件(以下4点をAdobe XDを使用して作成しました)
    • 業務フロー
    • ページ遷移図
    • WF(ワイヤーフレーム)
    • 機能・データ

実際に作成した機能要件
画面遷移図2.gif
こんなふうに画面を1枚1枚、洗い出してとなりに機能とデータを書きました。

  • 非機能要件

    • 拡張性(拡張のしやすさ)
      • 共通処理ははクラスにして使い回す
      • CSSはSCSSを用いて共通部分をまとめる
    • 可用性(サーバーが落ちないか、どれくらいのアクセスが来ても問題ないか)
      • 数人での管理を想定しているのでアクセスに関して問題なし
      • サーバーが落ちたときは検知できるようにする
    • セキュリティ(安全性)
      • CSRF対策
      • SQLインジェクション対策
      • XSS対策
      • SSL化
  • 運用体制

    • php7.4/Laravel6.2
    • HTML/CSS(Sass)
    • AWS(RDS, EC2, Route53, ALB)
    • MySQL
  • 保守体制

    • 月1回使用レビューをもらう
      • 可能であれば面談の時間をいただく
  • 障害時体制

    • cloudwatchを使ってサーバーのダウンを検知する

7-3.タスクばらし編

要件定義で具体化したものを1つ1つのタスクに細分化していきます。

タスクばらしは全体の作業量と進め方を確認するために実装でやるべきタスクを全て洗い出す作業です。達成すべきゴールまでの道のりをしっかりと1つ1つのタスクに落とし込むことで迷わずにゴールまで進むことができます。また進んでいる実感も湧くのでモチベーション維持にも繋がります。

タスクばらし
タスクばらし.gif

7-4.DB設計編

DB設計ではER図を用いてWebアプリケーションを運用するにあたってどのような情報が必要なのかを洗い出し、その関係性を明確にしました。

結果、以下のようなER図を作成しました。

ER図

シンプルすぎる体重管理DB設計図.drawio (2).png

作成したテーブルについて

テーブル名 説明
personal_infos 施設利用者さんの個人情報を管理
weight_months 施設利用者さんの毎月の体重記録を管理
users_table クライアントのアカウント情報を管理

ポイントとしてはweigh_monthsテーブルに外部キーをもたせることでLaravelのORMという機能を使用することでDBを容易に操作できるようにしたところです。

今回はクライアントファーストでシンプルすぎるWebアプリケーションを作成しているのでDB設計もシンプルすぎるものとなりました。

7-5.環境構築編

環境構築はDockerでLEMP環境を構築しました。

LEMP環境は以下の通りです。

L:Linux(OS)
E:Nginx(Webサーバー)
M:MySQL(DBサーバー)
P:PHP(アプリケーション)

今回はじめてDocker/Docker Composeを使用しました。Docker imageをpullするだけではなくDocker fileの内容が理解できるように学習したのでそれなりに学習コストはかかりましたが使えるようになると本当に便利すぎて感動しました!

7-6.インフラ編

インフラはAWSを使用しました。またCloud Formationを使用してインフラのコード化もおこないました!

インフラ設計図
体重管理_インフラ設計図.drawio (1).png

今回初めてインフラのコード化に挑戦しました。最初は yml?Json??って何っていうレベルだったのでかなり苦戦しました。ひたすらAWSの公式ドキュメント(英語)を翻訳しながら try and errorを繰り返して必要なリソースが1発でできたときは最高の気分でした!

Cloud Formation(example.yml)

長いので興味ある方のみ見てやってください
AWSTemplateFormatVersion: 2010-09-09

Parameters:
  VpcCidrBlock:
    Type: String
    Default: 172.18.0.0/16
  PublicSubnetCidrBlock:
    Type: String
    Default: 172.18.3.0/24
  PrivateSubnet01CidrBlock:
    Type: String
    Default: 172.18.6.0/24
  PrivateSubnet02CidrBlock:
    Type: String
    Default: 172.18.7.0/24
  Ec2ImageId:
    Type: String
    Default: ami-0f310fced6141e627
  Ec2InstanceType:
    Type: String
    Default: t2.micro
  Ec2KeyName:
    Type: String
    Default: [ec2_key_name]
  DBMasterUserName:
    Type: String
    Default: [user_name]
  DBMasterUserPassword:
    Type: String
    Default: [password]

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VpcCidrBlock
      Tags:
        - Key: Name
          Value: !Ref AWS::StackName
  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: !Ref PublicSubnetCidrBlock
      MapPublicIpOnLaunch: true
      VpcId: !Ref VPC
      AvailabilityZone: ap-northeast-1a
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-public-subnet
  PrivateSubnet01:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: !Ref PrivateSubnet01CidrBlock
      MapPublicIpOnLaunch: false
      VpcId: !Ref VPC
      AvailabilityZone: ap-northeast-1a
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-private-subnet-01
  PrivateSubnet02:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: !Ref PrivateSubnet02CidrBlock
      MapPublicIpOnLaunch: false
      VpcId: !Ref VPC
      AvailabilityZone: ap-northeast-1c
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-private-subnet-02
  DBSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      DBSubnetGroupDescription: !Ref AWS::StackName
      DBSubnetGroupName: !Ref AWS::StackName
      SubnetIds:
        - !Ref PrivateSubnet01
        - !Ref PrivateSubnet02
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Ref AWS::StackName
  AttachInternetGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      InternetGatewayId : !Ref InternetGateway
      VpcId: !Ref VPC
  RouteTableForPublicSubnet:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-rt-for-public-subnet
  RouteForPublicSubnet:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref RouteTableForPublicSubnet
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway
  AssocciateRouteTableForPublicSubnet:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId: !Ref RouteTableForPublicSubnet
      SubnetId: !Ref PublicSubnet
  SecurityGroupForPublicServer:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub ${AWS::StackName}-sg-for-public-server
      GroupDescription: !Sub ${AWS::StackName}-sg-for-public-server
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp #ssh
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp #http
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp #https
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-sg-for-public-server
  PublicServer:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref Ec2ImageId
      InstanceType: !Ref Ec2InstanceType
      KeyName: !Ref Ec2KeyName
      NetworkInterfaces:
        - SubnetId: !Ref PublicSubnet
          GroupSet:
            - !Ref SecurityGroupForPublicServer
          AssociatePublicIpAddress: true
          DeviceIndex : 0
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-ec2
  ElasticIpForPublicServer:
    Type: AWS::EC2::EIP
    Properties:
      InstanceId: !Ref PublicServer
  SecurityGroupForPrivateServer:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupName: !Sub ${AWS::StackName}-sg-for-private-server
      GroupDescription: !Sub ${AWS::StackName}-sg-for-private-server
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 3306
          ToPort: 3306
          SourceSecurityGroupId: !Ref SecurityGroupForPublicServer
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-sg-for-private-server
  AppDatabase:
    Type: AWS::RDS::DBInstance
    Properties:
      Engine: MySQL
      EngineVersion: 8.0
      DBInstanceClass: db.t3.micro
      AllocatedStorage: 20
      MasterUsername: !Ref DBMasterUserName
      MasterUserPassword: !Ref DBMasterUserPassword
      DBSubnetGroupName: !Ref DBSubnetGroup
      VPCSecurityGroups:
        - !Ref SecurityGroupForPrivateServer

インフラのコード化に挑戦した理由としては2つあって
1.今回の(EC2, RDS ...)構成はよく使う構成なので次回からGUIで作成する手間が大幅に楽になるのとコード化したらどのように作成したのかすぐに把握することができるので変更も容易になる。

2.AWSは縁あって実務案件をおこなったことがありそのときにGUIでおこなったのですこしレベルアップしてインフラのコード化に挑戦してみようという好奇心です。

AWSの実務案件もQiita記事にまとめていますので、ご興味あれば一読していただけると嬉しいです!

7-7.実装編

Webアプリケーションの実装編になります。ここまで(ひとまず)完成したアプリを紹介してからながかったのでもう一度同じGIFをのせます:joy:

(ひとまず)完成したWebアプリケーション
シンプルすぎる.gif

実装した機能としては下記になります

  • ログイン/ログアウト(本番では新規登録は無効にしてます)
  • CRUD機能
  • データ作成時の確認画面
  • ページネーション
  • 体重のグラフ化(データの抽出)
  • トランザクション処理(機能ではないですが一応)

こだわりポイントとしてはとにかくクライアントと話し合ってクライアントにとって使いやすいものを作った点です。

具体的に書きますと
1.見た目はシンプルすぎるほどシンプルに色味のある色は3色まで

最初に提案したときは色味がpopなものを提出しましたが、色が多すぎると迷うとなんども再提出を繰り返して全体的な色味はグレーで統一、ボタン類(なにかしらのアクションが起きる)は色をつけて目立つようにしました。

2.普段の感覚より文字やボタンを大きめに作成

パソコンに不慣れなクライアントが使用するのでパソコンなれている私が普段使用しているサイズよりも1.3倍ほど全体的に大きくなるように作成しました。

3.機能数を必要最低限に

表示する情報や機能もクライアントにとって必要なものをなんどか話し合いまずは必要最低限にして必要になれば都度機能を足していく形にして使うのが辛くならないようにしました。

7-8.クライアントへ納品編

ここまで長いことお付き合いいただきありがとうございました!いよいよ**[活動編]**の最後クライアントへの納品になります。

記事の中での紹介では個人情報の観点からダミーデータでしたが、1月はじめの納品の前に私が施設利用者さん34人分それぞれの1年分のデータを入れ提出し実際に使っていただきました。

使っていただいたところ
:dancer:「見た目がシンプルで迷うこともないし、グラフ化してるから体重の変動も把握できていいですね!」

という感想をいただくことができました。また2022年1月からは新規の体重記録はクライアントにしていただいて本格稼働になります!

今後は1ヶ月に一度レビューしていただける機会を設けていただけることになったのでアップデートを繰り返してクライアントにとってより良いWebアプリケーションにしていきます!

8.今後の課題

今後の課題に関しては山盛りな気しかしないのですが列挙させていただこうと思います。

  • UIをpopに
    • 納品時とは異なってWebアプリケーションの使いかたに慣れてきたら見た目を可愛くしてほしいという要望は事前にいただいているのでUIのアップデート
  • 機能数を増やす(こちらもクライアントがなれてきたら)
    • 過去の体重記録の反映(約5年分)を追加し年ごとに切り替えて表示できるように
    • 検索機能の追加

どちらもクライアントから「なれたら追加してほしい」という要望をいただいているので随時追加していきます。

  • テストの追加

    • PHPUnitを使ってテストコードを書きましたが網羅的にテストできていないので追加します
  • リファクタリング

    • まずは1月の体重記録に間に合うように作成してほしいという話だったのでWebアプリケーションとして使えるものを作成しました。しかし書いているコードには改善できるところが多々あると思うので長期的な運用をを考えてリファクタリングしていきます。

まだまだやるべきことはあると思うのですがきりがないと思うので直近での課題を記載させていただきました。

当分は「クライアントがWebアプリケーションになれる」のを待ちつつ開発環境で機能を開発してすぐに本番環境に反映できる状態にしながら、テストコードの追加、リファクタリング等をおこなっていこうと思います!

9.最後に感想

ここまで読んでくださりありがとうございました!最後に開発を通しての感想を書いて終わりにしたいと思います。

まずは、なにはともあれ2022年の1月の納品に間に合い、クラアントに納得していただけるものを作成し実際に使ってもらえるというのが何よりも嬉しいです!

約1年前に独学で学習をはじめたところから、約50人(クライアント + 施設使用者)に影響を与えられるWebアプリケーションをつくれたことに本当に感無量です。また私のエンジニア人生の最終目標である「IT技術を使って地方に便利を届けたい」という思いの第一歩目を地元の企業さんで実現できたことも嬉しく思います。

本当に私的な話ではありますが23年間の人生の中で1番自ら考え、提案し、実現したことだとおもいます。私にとってとても大きな経験だったので作って終わりではなく長く使い続けていただけるようにアップデートを繰り返していきます!

技術的な話では、今回のWebアプリケーションは業務時間外で個人的に作成したものになります。企業で働く中ではなかなか技術選定から自分でできる機会はないので自分が触れたことのない新しい技術を沢山取り入れることができ本当に楽しかったし学びになりました。

触れたことのない技術を沢山取り入れることができたのでこれからはこれらの技術を突き詰めていけるように学習およびアップデートを続けていきます。

以上!駆け出しが自分の夢の第一歩を踏み出したそんな物語でした!
ここまで読んでいただき本当にありがとうございました。

144
107
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
144
107

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?