はじめに

こんにちは。第一開発事業部に配属された25卒の大瀧優杏です。
第一開発事業部では、配属後研修として、約2ヶ月半の間でAWS CDKを利用したサーバレス開発を行いました。
この記事では、研修で作成した成果物についての振り返りを行いたいと思います。

新卒課題

配属後研修の中の新卒課題として、AWS CDKとVue.jsを使った画像アプリケーションの開発を行いました。

構成

AWS CDKとPython/lambdaを使ったAPI作成

  • 画像一覧取得API
  • 画像詳細取得API
  • 画像情報登録編集API(投稿・編集・削除)

Vue.js(Vue3 Composition API)を使ったフロントの開発

  • フロント部分作成
  • 作成した3つのAPIとAmazon Cognito API の呼び出し
    →Cognitoとの通信にはAWS Amplifyを使用。

画面構成

  • ログイン画面
  • 画像アップロード画面
  • 画像一覧画面
  • 画像詳細画面
  • 画像編集画面
  • パスワード変更画面
  • パスワードリセット画面
  • 確認コード送信画面
  • パスワード変更完了画面

構成図

構成図は下記になっています。

画像一覧取得、画像詳細取得、画像登録編集(画像アップロード・画像編集・画像削除)の3つのAPIを、AWS Lambdaを用いて実装しました。
認証では、Amazon Cognitoユーザープールでユーザー管理を行いました。ブラウザの認証処理にはAWS Amplifyのライブラリを使用し、認証済みのユーザーのみがアプリケーションにアクセスできるログイン機能を実装しました。
S3バケットは用途に応じて2つの異なる役割を持たせています。

  • フロントエンド用 S3バケット:静的ファイルを格納するバケットとして図のようにAmazon CloudFrontと連携し、ユーザーのブラウザにWebページを高速に配信(静的ウェブサイトホスティング)する役割。
  • 画像データ格納用 S3バケット:画像情報登録編集Lambdaによって、ユーザーがアップロードした画像ファイルを保存するためのストレージとしての役割。

Amazon RDSでは、アップロードされた画像そのものではなく、それに関連する以下のメタデータを管理しました。

  • s3_url(S3に保存された画像の公開URL)

    s3_url = f"https://d3vy256pq52nmq.cloudfront.net/{s3_key}"

  • title(タイトル)
  • comment(コメント)
  • location(撮影地)
  • user(ユーザー情報)
  • created_at / updated_at(登録日時・更新日時)

主機能紹介

画像一覧取得API

画像一覧画面

上記は私が作成した実際の画像一覧画面になります。
フロントエンドから画像一覧APIにリクエストが送信されると、RDSに保存されている情報が返却され、一覧画面として表示されます。

画像情報詳細API

画像詳細画面

上記は実際の画像詳細画面になります。
フロントエンドから画像詳細APIにリクエストが送信されると、ユーザーのリクエストに応じてRDSに保存されている特定の一件の画像情報が返却され、詳細画面として表示されます。

画像情報登録編集API(投稿・編集・削除)

画像情報編集画面におけるフロントエンドは実行したい操作に応じて、operationキーに ‘R’, ‘E’, ‘D’ のいずれかを入れて画像情報登録編集APIにリクエストを送信します。リクエストを受け取ったAPIは、operation の値を見て、処理を分岐しています。

  • ‘R’ (登録) の場合
    • フロントから送られた画像データをデコード
    • 画像ファイルをS3にアップロード
    • RDSに接続し、テーブル情報を更新
  • ‘E’ (編集) の場合
    • 既にS3に存在する古い画像ファイルを削除し、新しい画像ファイルをアップロード
    • RDSに接続し、テーブル情報を更新
  • ‘D’ (削除) の場合
    • RDSに接続し、削除対象のIDからS3のファイル名を取得
    • S3から該当する画像ファイルを削除
    • RDSのテーブルから情報を削除

学んだこと

取得APIのクエリの違い

画像一覧取得APIのSQL

sql = "SELECT id, s3_url as photo_url, title FROM m_photo ORDER BY created_at DESC"

画像情報詳細取得APIのSQL

sql = ("SELECT m_photo.id, s3_url, m_photo.title, m_photo_detail.comment, m_photo_detail.location, m_photo_detail.user "
              "FROM m_photo JOIN m_photo_detail ON m_photo.id = m_photo_detail.id WHERE m_photo.id = %s")

画像一覧取得APIはアップロードした画像を一覧表示するのに対し、画像情報詳細取得APIではユーザーが選択したIDがパラメーターとして送られてくるため、プレースホルダ(%s)を使用することで可変値を扱えるよう処理を実装し、同じ取得APIでも取得したい内容によってクエリを分ける方法を学びました。

DB処理の共通化

conn, cursor = start_transaction()
try:
    sql_insert_photo = """
                       INSERT INTO m_photo (s3_url, title, created_at, updated_at)
                       VALUES (%s, %s, %s, %s)
                       """
    params_photo = (s3_url, data['title'], now, now)
    cursor.execute(sql_insert_photo, params_photo)
    new_photo_id = cursor.lastrowid
    sql_insert_detail = """
                        INSERT INTO m_photo_detail (id, comment, location, user, created_at, updated_at)
                        VALUES (%s, %s, %s, %s, %s, %s);
                        """
    params_detail = (
        new_photo_id,
        data['comment'],
        data['location'],
        data['user'],
        now,
        now
    )
    cursor.execute(sql_insert_detail, params_detail)
    commit_transaction(conn)
except Exception as e:
    rollback_transaction(conn)

「トランザクションの開始、クエリの実行、トランザクションの終了」というDB接続の一連の処理を、別のファイルに共通関数として切り出すことで処理を集約し、それを各APIから呼び出すことで、冗長な実装を避ける構成を学びました。

おわりに

未経験で、コードもほぼ読めない、書けない状態から始まりましたが、最終的にはなんとか自力でアプリケーションを完成させることができ、大きな達成感を得ることができました。
デプロイ時やAPIの連携でエラーが出るたび心が折れそうになりましたが、その度にドキュメントや記事を読んだり、AIで調べたり、OJT担当の方や上司・先輩方に質問したりと、自分なりに粘り強く向き合いました。
来年以降は私がOJT担当として指導する立場になりますので、未経験者の気持ちが理解できる立場として、後輩の気持ちに真摯に寄り添い、サポートしてあげたいです。
また、案件に入ってからは、まずは先輩方に負担をかけずに独り立ちをし、自分の任された仕事を責任を持って行えるようにしたいです。