every Tech Blog

株式会社エブリーのTech Blogです。

簡単に高品質なドキュメントスキャナーが実装できる、ML Kit Document Scanner API がリリースされたので使用してみた。

はじめに

トモニテでAndroid開発を担当している岡田です。

先日、ML Kit Document Scanner API のベータ版がリリースされました。
公式ドキュメントやサンプルアプリを参考に、今回はAndroidでの実装方法・内容をご紹介したいと思います。
以下に参考にしたサイトのリンクを示します。是非覗いてみてください。

android-developers.googleblog.com

developers.google.com

github.com

ML Kit とは

Googleの機械学習の機能を、Android/iOSアプリとして提供するモバイルSDKです。
例えば顔検出やバーコードスキャンなどの機能を簡単に実装することができます。

ML Kit Document Scanner API とは

紙の資料をカメラでスキャンして、デジタル資料として読み込むことができる、ドキュメントスキャナーSDKのAPIです。
用意されたスキャナーは自動キャプチャ・切り抜き・自動回転検出機能だけでなく、フィルター機能など編集もできます。
つまり、すごいリッチなスキャナーを簡単に実装できるということです。
スキャナーの使用感については公式のサンプルアプリでご確認ください。

実装の簡単な流れは以下の通りです。

  1. スキャナーのオプションを決める
  2. スキャナーを呼び出す
  3. スキャナーから結果を取得する

主なクラスについて

主に以下の3つのクラスで構成されています。

  • GmsDocumentScannerOptions
  • GmsDocumentScanning
  • GmsDocumentScanningResult

それぞれ名前の通り、スキャナーのオプション、スキャン開始、スキャン結果に関するクラスになっています。
これらについて、実際に確認してみたいと思います。

GmsDocumentScannerOptions (オプション)

GmsDocumentScannerOptions でスキャナーに関してのオプションを設定できます。
公式ドキュメントのコードでは、以下のように紹介されています。

val options = GmsDocumentScannerOptions.Builder()
    .setGalleryImportAllowed(false) // フォトギャラリーからのインポートの可否
    .setPageLimit(2)                // 最大ページ枚
    .setResultFormats(RESULT_FORMAT_JPEG, RESULT_FORMAT_PDF)    // 結果のフォーマット
    .setScannerMode(SCANNER_MODE_FULL)  // スキャナのモード
    .build()

設定できるオプションは以下の通りです。

  • スキャナーのモード
  • 最大ページ枚
  • 結果のフォーマット
  • フォトギャラリーからのインポートの可否

それぞれ見ていきます。

スキャナーのモード

setScannerMode() を用いて、スキャナーの設定ができます。 現段階では3種類のモードが要されています。

public static final int SCANNER_MODE_BASE = 3;
public static final int SCANNER_MODE_BASE_WITH_FILTER = 2;
public static final int SCANNER_MODE_FULL = 1;

SCANNER_MODE_BASE
基本的な編集機能(ページの切り抜き、回転、並べ替えなど)が使用できます。

SCANNER_MODE_BASEのプレビュー画像

SCANNER_MODE_BASE_WITH_FILTER
SCANNER_MODE_BASEモードに画像フィルタ(グレースケール、自動画像補正など)が追加されます。

SCANNER_MODE_BASE_WITH_FILTERのプレビュー画像

SCANNER_MODE_FULL(デフォルト)
SCANNER_MODE_BASE_WITH_FILTERモードの機能に加えて画像クリーニング機能(汚れや指の消去など)が追加されます。

SCANNER_MODE_FULLのプレビュー画像

最大ページ数

setPageLimit() を用いて、最大のページ数を指定できます。int型で指定します。
以下は最大ページ数を2とした場合のスクリーンショットです。最大数に達すると、ページを追加する "+" アイコンが出ないことがわかると思います。

最大ページ数のプレビュー画像

フォトギャラリーからのインポートの可否

フォトギャラリーからのインポートの可否を設定できます。
setGalleryImportAllowed() を用い、Booleanで指定します。

結果のフォーマット

setResultFormats() を用いて、出力結果のフォーマットを指定できます。
現段階では、JPEGかPDF、またはその両方を選択できるようです。

public static final int RESULT_FORMAT_JPEG = 101;
public static final int RESULT_FORMAT_PDF = 102;

GmsDocumentScanning (スキャナーの開始)

スキャナーはGmsDocumentScanningを用いて、以下のように記述できます。
コードは公式のドキュメントとサンプルを参考にしました。

GmsDocumentScanning.getClient(options)  // optionsは先ほど紹介したGmsDocumentScannerOptions
  .getStartScanIntent(activity)
  .addOnSuccessListener { intentSender ->
     scannerLauncher.launch(IntentSenderRequest.Builder(intentSender).build())
   }
  .addOnFailureListener {
    // 失敗した際の処理
  }

メソッドチェーンでわかりにくいので、順を追って説明します。

はじめに、GmsDocumentScanninggetClient()を呼び出します。 getClient()GmsDocumentScannerOptionsを引数にとり、GmsDocumentScannerを返します。

public final class GmsDocumentScanning {
    @androidx.annotation.NonNull
    public static com.google.mlkit.vision.documentscanner.GmsDocumentScanner getClient(@androidx.annotation.NonNull com.google.mlkit.vision.documentscanner.GmsDocumentScannerOptions options) { /* compiled code */ }

    private GmsDocumentScanning() { /* compiled code */ }
}

返ってくるGmsDocumentScannerはInterfaceです。getStartScanIntent()というメソッドが用意されています。
こちらはActivityを引数にとり、Task<IntentSender>を返します。

Taskが返されるので、addOnSuccessListeneraddOnFailureListenerが使えます。
成功時にIntentSenderが返ってきます。IntentSenderはスキャナーを起動するために使用します。

public interface GmsDocumentScanner extends com.google.android.gms.common.api.OptionalModuleApi {
    @androidx.annotation.NonNull
    com.google.android.gms.tasks.Task<android.content.IntentSender> getStartScanIntent(@androidx.annotation.NonNull android.app.Activity activity);
}

scannerLauncherは後述する、ActivityResultLauncher<IntentSenderRequest>型の変数です。 こちらは終了したActivityの結果を受け取り、処理します。

GmsDocumentScanningResult (結果処理)

GmsDocumentScanningResultを用いて、結果を処理できます。
公式ドキュメントにて、以下のように記述されています。

val scannerLauncher = registerForActivityResult(StartIntentSenderForResult()) {
  result -> {
    if (result.resultCode == RESULT_OK) {
      val result =
        GmsDocumentScanningResult.fromActivityResultIntent(result.data) // ここで結果を受け取る
      result.getPages()?.let { pages ->
        for (page in pages) {
          val imageUri = pages.get(0).getImageUri()
          // imageUriを用いた処理
        }
      }
      result.getPdf()?.let { pdf ->
        val pdfUri = pdf.getUri()
        val pageCount = pdf.getPageCount()
        // pdfUriやpageCountを用いた処理
      }
    }
  }
}

GmsDocumentScanningResultfromActivityResultIntent()を用いて、結果を受け取ります。 Intentを引数に取り、GmsDocumentScanningResultとして返してくれます。

@androidx.annotation.Nullable
public static com.google.mlkit.vision.documentscanner.GmsDocumentScanningResult fromActivityResultIntent(@androidx.annotation.Nullable android.content.Intent data) { /* compiled code */ }

返されるGmsDocumentScanningResultですが、PagePdfを持っています。

public abstract class GmsDocumentScanningResult implements android.os.Parcelable {   
    ...
    public static abstract class Page implements android.os.Parcelable {
        @androidx.annotation.NonNull
        public abstract android.net.Uri getImageUri();

        public Page() { /* compiled code */ }
    }

    public static abstract class Pdf implements android.os.Parcelable {
        public abstract int getPageCount();

        @androidx.annotation.NonNull
        public abstract android.net.Uri getUri();

        public Pdf() { /* compiled code */ }
    }
}

現時点でpageは画像のUriPdfはページ数とUriを取得できるようです。
それぞれ結果に合わせて、処理を記述できます。

説明については、以上になります。

まとめ

ML Kit Document Scanner API を用いると、簡単に高品質なドキュメントスキャナーが実装できました。
実装の簡単な流れは以下の通りです。

  1. スキャナーのオプションを決める
  2. スキャナーを呼び出す
  3. スキャナーから結果を取得する

余談

未実装のキャプチャーモード

com.google.mlkit.vision.documentscanner.GmsDocumentScannerOptions のコードには CaptureMode なるものが存在しました。

public static final int CAPTURE_MODE_AUTO = 1;
public static final int CAPTURE_MODE_MANUAL = 2;

現在はオートのみだが、今後はマニュアルで選択できるような機能が追加されるかもしれない……?
アップデートが楽しみです。今後も追っていきたいと思います!

資格試験(AWS Certified)の受験を通じた業務での活用

こんにちは 開発本部データ&AIチームでデータエンジニアを担当している塚田です。

今回は、AWS Certified Data Engineer - Associateを受験しましたので、準備と感想とエブリー(業務)でどのように活かせそうかをまとめたいと思います。

なお、AWS 認定プログラムアグリーメントに則り試験内容については触れませんのでご承知おきください。

本試験について

AWS Certified Data Engineer - AssociateAWS(トレーニングと認定)で以下のように説明されています

AWS Certified Data Engineer - Associate は、コアデータ関連の AWS サービスに関するスキルと知識や、データの取り込みと変換、プログラミングの概念を適用しながらのデータパイプラインのオーケストレート、データモデルの設計、データライフサイクルの管理、データ品質の確保といった能力を検証します。

データ分析というよりかはデータパイプライン全般に対してのスキルを評価するものだと感じました。

AWSで適切なサービスを利用してスケールのしやすさや保守運用・セキュリティなどの能力を評価すると試験ガイドにも記載されているので、体験したものと差異はないように感じています。

なぜ、受験しようと思ったか

現在の業務範囲に通じるものがあることが一番大きな理由ですが、他に理由があるとすれば以下が上げられます。

  • AWS Certified Data Engineer - Associateは2024年4月現在、比較的新しい試験であること
  • 今まで取得したすべてのAWS Certifiedの有効期限が切れてしまったこと

試験準備

  • 公式問題集を解く
    • ある程度AWSサービスを利用したことがあったため事前準備なしで回答しました(何度でも受験可能です)
      • 悩んだ問題や間違えたものは解説を読んで理解できたかを再度解くことで確認しました
    • どのような粒度の問題が出るかなどイメージすることができとても有用でした
    • 上記以外にも各資格試験で公式問題集があるので合わせて解いて周辺知識の補完に利用しました
  • 試験ガイドを読む
    • 聞いたことがない・実際利用するとしたときに手が動かせないと思ったサービスはサービス別資料から該当のサービス資料を確認し、実際にAWSコンソール上で操作してみました

あまり特筆する部分はありませんが、今までの経験と未経験の部分を勉強によって補足するイメージで準備を行いました。

受験・受験後

  • 余裕を持って予約した時間・会場で受験しました
    • 80分程度で問題を一周し再確認などして20分残して退出しました
  • その場では結果は受け取れず、後日連絡が来る形でした
    • 結果としては合格はしたものの、まだまだ経験が必要だなと感じるものでした

業務で活かせるか

何かインプットしたらアウトプットしていきたいと思うところですが、 今回は本試験を通じて得たものについてどのように活かせるかを考えたいと思います。

現在、エブリーのデータ&AIチームではDatabricksを活用したデータフローの作成やML・ABテストの運用を行なっています。

tech.every.tv

AWSをフルに活用している構成ではありませんが、 セキュリティやコストについてはどのプラットフォームを利用したとしても意識するべきことだと感じていて 知識としてアップデートでき、そういった重要性について再認識することができました。

まとめ

データエンジニアとしてはデータを安全に使いやすく必要なセキュリティを担保していくことはもちろんですが、 データ&AIチームではAI・MLをプロダクト導入していくことも推進しています。 tech.every.tv tech.every.tv

今回はAWSの資格試験を切り口に記事を書かせていただきましたが、 ここで得た知識以外にも新しい技術、考え方を柔軟に取り入れていきデータをより利活用できる環境を作っていきたいと考えています。

FlutterでiOS、AndroidアプリをWebで動かせるようにする

DELISH KITCHEN 開発部で小売向き合いでFlutterのアプリ開発をしている野口です。

本記事では、弊社で開発しているFlutterのアプリをFlutter Webでリリースできるかどうかの調査を行った時の知見についてお話しします。

FlutterアプリをWebで動かすとは

Flutterはマルチプラットフォーム開発できるので、Android / iOS / Web / Windows / macOS / Linuxで同じソースコードで開発できます。なので、iOS、Android用に作成したアプリでもリリースできます。 一般的なWebサイトを作るときは、HTMLやCSS、JavaScriptを使用しますが、FlutterはiOS、Androidと同じ見た目になるように、HTML、CSS、Canvasなどを使用して描画してくれます。また、FlutterはDartという言語で書かれていますが、それをJavaScriptに変換してくれています。

ただ、パッケージを使用した場合、モバイル特有の機能(ネイティブコードでないと実現できないもの)などDartで書かれてない可能性があるため、パッケージの公式ドキュメントを見てWebに対応しているか確認する必要があります。ここにWebの記載があればWeb対応しているパッケージだと判断できます。

https://pub.dev/packages/flutter_riverpod

riverpod

FlutterアプリをWebで動かすにあたっての課題

まとめると以下のような課題がありました。

  • パッケージがWebに対応しているか
  • Platform.isAndroid Platform.isIOSの分岐エラー

具体的な対応

パッケージがWebに対応しているか

対応していないもの

そもそもWebに対応してないパッケージがあるので、その場合は代替を探すか、Javascriptで書くか、Webではその機能を諦めるかをしないといけません。 今回は以下のパッケージが使用できませんでした。 - firebase_crashlytics(そもそもWebはクラッシュしないのでいらない) - path_provider - flutter_html - adjust_sdk - flutter_appauth - dart_jsonwebtoken

対応していたが、途中で動かなくなったもの

isar

isarはv3ではエラーが出て動かなくなっていました。

エラー内容

Error: The integer literal 288085404374050446 can't be represented exactly in JavaScript. Try changing the literal to something that can be represented in JavaScript. In JavaScript 288085404374050432 is the nearest value that can be represented exactly. id: 288085404374050446,

https://pub.dev/packages/isar

issueも出ており、v4では動くようになっているようですが、公式ドキュメントにISAR V4 IS NOT READY FOR PRODUCTION USEとあるので本番環境で使用するのは避けたほうが良さそうです。

https://pub.dev/documentation/isar/4.0.0-dev.14/

対応としては、 isarはローカルデータベースを扱うためのパッケージなので代替になるパッケージに書き換えるが良いかと思います。

v4がstableになるのが待てるのであれば待った方がいいですが、、、v4のPrereleaseが出てから時間が経っており、いつstableになるかわからない状態なので、一旦考えない方針にしています。

バージョンを上げれば対応されるもの

flutter_secure_storage

エラー内容

Unsupported operation: Platform._operatingSystem

使用しているバージョンではWebが対応していないため、5.0.0に上げれば解決します。

Platform.isAndroid Platform.isIOSの分岐エラー

エラー内容

Unsupported operation: Platform._operatingSystem

Webで実行時にPlatform.isAndroid Platform.isIOSがあると起こるようです。 この記事のように、Webの分岐を入れるか、universal_platform(https://pub.dev/packages/universal_platform)を使用することで対応できるかと思います。

https://zenn.dev/ryo_ryukalice/articles/140a64f894afad

Flutte Webを採用して開発運用を行う上でのビジネス上のリスク(考慮事項)

ビジネス上のリスクは以下が挙げられるかなと思います。アプリの複雑度によってリスクの重みは変わるかもしれないですが、これらが許容できればいい選択肢かなと思います。

  1. やりたいことを実現するためのWebに対応しているパッケージがない
  2. isarのようにWebに対応していたパッケージが、更新されなくなりWebが動かなくなる
  3. 2が原因でflutterのバージョンを上げづらくなる

iOS、AndroidアプリをFlutter Webで動かす

iOS、Androidアプリ

Flutter Webアプリ

Flutter Webを動かした結果、画像のようになりました。 見た目としてはiOS、Androidアプリがブラウザのサイズに合わせてそのまま大きくなっています。 このままでも見た目はそんなに悪くないかなと思いますが、商品情報が大きすぎるなどの場合はレスポンシブ対応か、モバイルのサイズに統一するなどすると良くなると思います。 動作が重くなる様子はなかったのでリリースはできるかなと思いました。

まとめ

Flutter Webで開発をする際の主な考慮点は使用するパッケージがWebに対応しているかどうかということがわかりました。 ただ、Webに対応していても動かなくリスクがあるので、Webだけは使えない機能が出る可能性もありそうですね。

個人的にはシンプルなアプリであれば基本的には動きそうなので用途によっては良い選択なのではと思いました。

認証・課金の共通基盤

はじめに

エブリーでソフトウェアエンジニアをしている本丸です。
先日、弊社からヘルスケアアプリ「ヘルシカ」がリリースされたのはご存知でしょうか?ヘルシカは弊社のサービスであるDELISH KITCHENのヘルスケア機能を切り出したサービスなのですが、ヘルシカの裏側で認証・課金の共通基盤が動いています。
今回はこの認証・課金の共通基盤(社内でDAPと呼んでいるため、以降はDAPと表記します)についてお話しできればと思います。なお、実装の詳細には触れず概要の説明に留める予定です。

システムの概要

DAPとは

DAPとは、認証・課金の共通基盤で、IdP(IDプロバイダー: ユーザーIDを保存および検証するサービス)としての役割と、課金を管理する役割を持っています。
DAPという名称は、一般的に使われるものではなくいわゆる造語なのですが、社内やチーム内で認識を合わせるために命名されたという経緯があります。
DAPの目的は、複数サービスでのユーザーの管理を一元化することです。

下図はDAPとそれに関わるものを表した概要図です。
DAPではSNSを用いた認証をサポートしているので、LINEやAppleといった外部のプラットフォームを利用します。以降は、DAP内の認証サーバーのことをInternal IdP、認証に利用する外部のプラットフォームのことをExternal IdPとします。
矢印は、依存の方向を示していてPaymentはInternal IdPに依存しているという関係になっています。DAPはExternal IdPや外部の課金プラットフォームに依存しており、弊社のサービスがDAPを利用するという形になっています。

認証サーバとしての役割

認証サーバーとしての役割は、ユーザーがどのアカウントと紐づくのかの認証を行うというのが主な役割になります。サインアップの時は図のようなフローになるのですが、全て説明すると長くなってしまうので要点だけお話しします。

Internal IdPとExternal IdPの間の認証情報

Internal IdPはExternal IdPに認証を委譲しています。Internal IdPはExternal IdPからIDトークンを受け取るのですが、このIDトークンの中にIDトークンの発行者や一意の識別子などが含まれており、それをもとにどのユーザーなのかの判断を行います。

Internal IdPとApplication ServerとClientの間の認証情報

ClientからApplication ServerのAPIを呼び出す時にはAccess Tokenを認証済みかどうかの判定に利用します。このAccess TokenはInternal IdPで発行しています。Application ServerはClientからAccess Tokenを受け取った時に、Internal IdPを通して認証を行い、認証が成功した場合に後続の処理を行うことになります。

Web View

図の中で、web viewに言及している箇所があるのですが、これはClientがスマホのアプリの時の挙動を示したものです。ClientとExternal IdPの間で直接認証する場合は、External IdPが用意してくれているSDKを利用した方が便利ではあるのですが、Internal IdPを経由させる目的でweb viewから認証を行うようにしています。

課金サーバとしての役割

課金サーバとしての役割は主に2つです。

1つ目はユーザーが商品を購入した際にappleから受け取ったレシートを検証して、商品の有効性を確かめることです。

2つ目はレシートとユーザー状態の管理・更新です。DAPではappleからレシートの情報が更新された時に通知を受け取り、それをトリガーとしてユーザーの状態の更新を行なっています。

レシートとユーザー状態の管理については、弊社ブログの過去記事にもありますのでよければご覧ください。
https://tech.every.tv/entry/2022/04/07/170000

RFCに則った実装

少し、話は逸れるのですがDAPの中のInternal IdPに関してはRFCやOIDCのドキュメントに則った実装が基本方針になっています。
社内用にカスタマイズされたドキュメントではないので、一見とっつきにくくもあるのですが、Internal IdPに関しては下記の理由などでドキュメントに則った方が良いという判断になったようです。 - IdPなので社内特有のロジックが入り込みにくい - 社内ドキュメントよりドキュメントのメンテナンスが維持されやすい - セキュリティ的な要件も満たせる

動作確認の困難さ

要件として、従来のDAPの仕様に則っていないサービスと新規のDAPの仕様に則っているサービスでユーザーのアカウント・課金状態を紐づける必要がありました。
この連携パターンが、従来のサービスでSNS連携されているか、新規サービスでSNS連携されているかなどに加えて課金状態の確認まで必要だったため、かなり複雑に感じました。
動作確認の段階でどのようなパターンがあるか洗い出してテストをしたのですが、動作確認の段階で考慮漏れなどが見つかり修正に追われるといったこともありました。複雑なシステムを作るときは想定されるパターンをあらかじめ洗い出してから開発すべきだったかなというのが反省です。

まとめ

記事にしたこと以外でもリリースまでに色々と大変なこともあったのですが、なんとか致命的なバグはなく動いているようなので一安心といったところです。 複雑なシステムなので概要を話すだけの形にはなってしまいましたが、認証・課金基盤でどのようなことをやっているのかの導入になれば幸いです。

最後に宣伝になりますが、このDAPを裏で利用しているヘルシカというサービスがリリースしたのでよければ使ってみてください。

参考資料

エブリーで新卒2年目を迎えて

エブリーで新卒2年目を迎えて

目次

はじめに

こんにちは。 トモニテ開発部ソフトウェアエンジニア兼、CTO室Dev Enableグループのktanonymousです。
4月1日をもって新卒入社してから1年が経ちました。 そこで、今回の記事では、これまでの振り返りと2年目を迎えた今感じていることについて書きたいと思います(文字ばかりですがご容赦ください)。

1年目を振り返る

入社前について

1年目を振り返る前に、入社前の経緯について少しだけ触れておこうと思います。
大学では工学部の電気情報物理工学科に所属し、情報分野だけでなく物理学や電磁気学なども学んでいました。 また、大学院ではマルチエージェントシミュレーション1に関する研究を行っていました。 講義や研究を通してAIやコンピュータの基礎を学んでいく中で、情報分野/エンジニアリングに興味を持つようになりました。
しかし、実際にものづくりをした経験は無く、エンジニアリングに関しては専門的な知識がほとんどありませんでした。 そのため、大学院時代にはプログラミングの基礎を学ぶために、Pythonを中心に学習を始めました。 また、A Tour of Goも少しだけやりました。
そのほかにも、友人と一緒に簡単なwebアプリを作ったり、ハッカソン型のインターンに参加したりして、 少しでも開発経験を多く積めるよう努力をしました。 そして、就職活動をする中で、イベントの中でエブリーと出会い入社まで至りました。

実際に入社してから

入社してからは、1週間の新卒研修を受けた後、実際の業務に携わることになりました。 OJTですぐに実際の業務に携わり、入社直後に事業貢献できるスピード感はベンチャー企業ならではだと思います。
トモニテではバックエンドにはGo、フロントエンドにはJavaScript/TypeScript(フレームワークはReact/Next.js)を使って開発をしています。 初めのうちは、既に実装されているAPIの改修やコンポーネントの表示ロジックの改修など、バックエンド/フロントエンドどちらかのみのタスクを担当していました。 徐々にタスクの幅も広がり、新規APIを作成したり画面を作成したりすることも増えました。 それからは、APIと画面を実装して疎通させたりLPを1から作成したりもしました。
また、サービスのリブランディングという大きなプロジェクトも経験することができました。 リブランディングプロジェクトを通じてサービスの目指す方向性を改めて考えることができ、 サービスに込めた想いをチームの一員として実現していくことに対する責任感も強くなりました。

社内では、事業向き合いの業務がメインでありつつ、積極的な技術的挑戦の機会を提供するための施策も用意されています。 11月に社内で行われた挑戦weekでは、ChatGPTを利用した社内ChatAppのテンプレート機能の実装にも挑戦しました。

実務未経験から約1年を経て、今では、新機能の開発および開発のリードを担当させていただけるようにもなりました。 自分が開発をリードすることになるため、新機能の開発に必要となるAPIやDB設計なども担当し、 モバイルアプリ開発側との連携やプロダクトマネージャーとの仕様・工数の調整なども行っています。 今開発している新機能では複雑なロジック・仕様も含まれていて、実装は簡単ではありませんが、 サービスを大きく成長させる機能にするために、チーム全体で協調しつつ開発に取り組んでいます。 この新機能も近いうちにリリースされる予定ですので、リリースされたら是非使い倒していただけると幸いです。 責任範囲の広いタスクは大変ではありますが、難しいタスクに挑戦できることや施策をリードする側として動くことで プロダクトに対する責任感も高まり、技術的な視点だけでなくマネジメント・ビジネス的な視点を持つことの重要性も感じることができました。
CI/CDやインフラ周りの知識についてもまだまだ浅識なので、隙を見て地道なキャッチアップを続けています。

また、冒頭でも書いている通り、最近はDev Enableグループも兼任させていただいています。 Dev Enableグループでは、開発本部を横断し、組織の活性化・成長環境の提供・発信・広報の強化・採用など、さまざまな課題解決の推進を目指します。 詳しくは以下の記事をご覧ください。

1年目は実務経験の無い状態からのスタートでしたが、 自分のスキルアップ・マインドの醸成のために積極的にチャレンジし、新しい技術や知識を吸収することができました。 任せてもらえる領域も広がり、自身の成長を大きく感じられる1年でした。

2年目に突入して

4月1日には新卒社員の入社式も行われました。 後輩を迎え、改めて自分が2年目を迎えたことを実感しました。
4月に入り、新卒社員のオンボーディングプロジェクトにも携わっています。 また、今年度から新卒エンジニア向けの研修プログラムも予定されています。 これは、Dev Enableグループが主導で行っているプロジェクトの一つです。

サービス開発を通じての事業貢献だけではなく、組織の活性化やエンジニアの成長など会社全体への貢献もできるようになり、 エブリーの一員としての責任感もより一層強くなってきました。
今後も、自分のスキルアップはもちろん、トモニテ/エブリーの成長に貢献できるよう積極的にチャレンジしていきたいと思います。

まとめ

今回の記事では、4月を迎えた今だからこそ書ける内容だと思い、新卒1年目を迎えての振り返りと2年目を迎えて感じたことについて書いてみました。
就職活動中、ハッカソン型のインターンシップに参加することはありましたが、実務経験は一切ない状態での入社だったので経験豊富な同期や先輩方に負けないよう必死でした。
この記事を書くことで、自分のこれまでの挑戦を振り返ることやこれからについて改めて考えることができる良い機会になりました。
まだまだ未熟で、今でも日々のキャッチアップや新しい技術の習得に励んでいますが、1年目を振り返ると、非常に大きく成長できた1年だったと感じています。 2年目も、サポートしてくれる周囲の人への感謝を忘れずに、1年目以上に成長できるようにチャレンジしていきたいと思います。
最後まで読んでいただき、ありがとうございました。