【単独クエスト】 新規サイトをローンチせよ! フルスタックエンジニアのススメ【NewsVideo開発】
初めまして!
最近、メインの担当業務が「イノベーション」になりました、デジタルイノベーション本部の小室です。スキルとしてはフロントエンドを中心にフルスタックで開発を行なっています。ICTRADとは一緒に勉強会をしたり、組織としてもデータ活用・分析などで協働したりしています。
早速本題ですが、
近年エンジニアが単独でサイト開発を行なっていくケースが増えてきているように感じます。
DXの広がりから従来の縦割り関係でなく、エンジニアをビシネスパートの近くに配置することも増え、近年ではデータアナリストやコーポレートエンジニアと言った職種も生まれて、すでに多くのメガベンチャーがポジションを作っているところです。
こう言ったエンジニアは少数、もしくは単独でクエストに当たることが必然的に多くなります。私もそう言ったエンジニアの一人ですが、最近「newsvideo.jp」というサイトを単独で構築する機会がありました。このサイトは運営する新聞社のYouTubeチャネルの動画を自動収集&リストアップして表示することで、より新聞社のニュース動画に身近に触れられるようになることを目的に構築しました。
そこで、本投稿では最近私が経験したサイト構築案件に関して、ぼっち開発における技術選定、進め方、ポイントなどをまとめて行きたいと思います!
クエスト内容
■ サイト概要
* 自社と協業他社のYouTube上のニュース動画を自動で収集
* 収集した動画を公開するフロントページ(一覧 + 詳細)
* 動画のメタ情報は管理画面から本サイト用にカスタマイズしたい
■ 条件
* 技術選定は自由
* デザインは社内のデザイナーさんにご依頼可能
* 初期構築期間はおおよそ1.5 ~ 2ヶ月
武器・防具
今回の技術選定をざっくり説明します。
フロント => Nuxt.js
構築時点で2000以上の動画が存在し、動的なコンテンツの配信が必要になります。そこで、SSR(Server Side Rendering)が可能なSPA(Single Page Application)フレームワークを選定しました。
いわゆるSPAの3大フレームワーク(react, vue, angular)系は全て業務で利用した経験がありますが、今回vue系を選定した理由として、扱いがもっとも簡単だからというのが大きいです。
Reactのようにstateの管理を意識する必要がないし、Vuexは最低限な仕様だし、Nuxt.jsのビルド・サーバミドルウェア設定は非常に多彩で、見るだけも面食らってしまいそうな程です。
ちなみに、個人的な感覚として、中規模以上のユーザサービスを開発する時は厳密にTSの型管理ができて、Hooksによりコードの分離性が高いReact系がいいと思いますし、管理画面系を作成する場合は純正でForm開発用API(リアクティブフォーム)が組み込まれているAngularも優れていると思います。
インフラ => AWS + AWS CDK
単独開発ならFirebaseやApp Engineでいいんじゃないの?と思われるかと思います。いや、私もFirebaseを使いたかったですw (AuthenticationもFireStoreも素晴らしい!)
ただ、管理画面は二段階認証が必要とか、検証系はIP制限をかけるとか細かな要件が多く、全ての実現性を事前に調査しきるのは大変なので、社内標準として実績のあるAWSを使用しました。
( 意外とみなさんもそんな経験は多いのではないでしょうか。)
ただ、ここであえて言っておきたいのが、AWSって意外と簡単に開発ができる!ってことです。なぜなら、TypeScriptネイティブなプロビジョニングツールAWS CDKがAWS純正でリリースされているからです。
私は最初フロントから入ったので、Herokuやnetlifyを愛好するフロントエンジニアがAWSとかインフラ管理を嫌う気持ちが非常に分かります。EC2という言葉に恐怖すら感じました。
しかし、サーバーレスリソースが発達した近年のAWSはまさにマネージドサービスの宝庫ですし、なるべくサーバを持たないことがインフラ構築のBPになりつあると思いますので、数年前のようなとっつきにくさは感じないかと思います。
その上、プロビジョニングがフロントと同じ言語(TS)で完結しますので、従来の開発フローに取り込みつつ、今後の保守・運用性能などをトータルで考えると、Firebaseなどを使用する場合とそこまで開発速度への懸念はないのかと考えられます。
ちなみにCDKはAWSが開発をしており、deploy時にはCloudFormationテンプレートを一旦生成してから、CFを実行していく仕組みなので、純正の安心があります。
また、どうしてもCDKで '難しいこと' や 'できないこと' は、そもそもCloudFormationでも出来ないことなので、全てをCDKで表現しようと思いすぎないようにすることは意外とポイントです。どうしてもコード化して起きたい場合は、AWS SDKを使ってnodeスクリプトを作ってあげるといいと思います!
バックエンド => Express.js
APIの部分です。特に面白い部分はありません。
次回は別のmeteorやkoaなども使ってみたいと思っています。
言語 => TypeScript
既にお気づきかもしれませんが、今回はTS以外の言語を一切使用しません!
ぼっち開発はタダでさえやることが多い中で、コンテクストスイッチは最小にしたいところです。また、開発は単独だったとしてもその後の担当の交代や、保守の依頼などをする時に、なるべく簡単に共有できる状態にしておくべきですよね。
インフラはTerraformでHCL、バックエンドはPython, Goで……
といった環境は、技術的な優位性がはっきりしている、もしくはそもそも複数人のエンジニアが作業を分担するなどの状況でないと、メリットはないかと思っています。
そこで、フロントからインフラまで一切TSで完結させます。というかAWS CDKのおかげでできる時代になりました!TypeScriptは型アノテーションが追加されただけのJSですので、引き継ぎがしやすいのもポイントです。
設計
フロントページ
フロントページはほとんどのwebページで構築する必要があるので、雛形になるような最小構成を目指しました。以下の通りです。
全体的にサーバレス構成を意識していますが、SSRのためアプリケーションの配信はECSを使用しました。ECSはシンプルで簡単にコンテナを管理できます。K8sに詳しい方であればそれでもいいと思います。
また、SSR含めてLambdaでサーバレス配信すればいいのでは?という意見もありそうですが、LambdaでSSRする場合はファイルサイズの上限、配信パスの設定、Coldスタート問題など、考慮点が多い印象です。Next.js + Vercelのようなエコシステムがない限り、無理にサーバーレスで構築する必要はないかと思います。(もちろんSSGで済む要件であれば、SSGの方がいいと思います)
フロントページへのアクセスのルートはCloudFront => ALB => ECSという流れです。ポイントとして、なにかあった時、CDNキャッシュがデバッグの妨げになることが多いので、社内からはALBに直アクセスするルートを作ります。ここの制限は、ALBのリスナールールでIP制限をかけました。
DBはDynamoDBを選定しました。DynamoDBはオンデマンドプランの料金が1,000,000読み込みユニットあたり0.25$程と驚く程安いのが魅力です。RDS系と比較すると、起動料金がかからないので、大きく差が出る部分ですね。
クエリに関してはRDSと比較すると貧弱なので、諦めながら要件に合わせて使用する必要があります。ただサーバレス構成の場合はDynamoDBのスケーラビリティや、インフラそのものの管理コストがかからない点は優位です。今回は動画情報テーブル、マスタテーブル(タグ、YouTubeチャネル)の二つを作成して対応しました。
管理画面
ここで画面をお見せすることができませんが、管理画面ではログイン機能と、データのCRUDを操作するためのフォームがあります。全体的なデザインとフォーム開発のUIライブラリにはVuetifyを使用しました。
インフラ構成は以下の通りです。
管理画面はフロントページ違って、SEOやパフォーマンスを気にする必要がないのでNuxt.jsでgenerateした静的ファイルを配信することにしました。ただ、管理画面そのもの(ログインページを除く)に認証をかける必要があるので、S3 Hosting+CloudFrontは採用できません。
そこでExpressを導入し、Lambdaの中に持たせた静的なファイルをExpressから静的アセットとして配信することしました。
管理画面でもっとも重い部分は認証かと思いますが、認証サービスとしてはAWS Cognitoと使用しました。
ところで、CognitoはもっともとっつきにくいAWSサービスの一つかと思います。
そもそもの認証自体が複雑なフローの上に成り立っているため、必然的にCognito自体の機能も非常に豊富になっています。ただ、要件を絞って使用する段階では、ある程度ポイントを押さえておくことで簡単に利用が開始できます。
ここを深掘りすると、ある程度長くなってしまいますので、今後別のエントリーに分けてご紹介できればと思います!
YouTubeコンテンツの収集
構成は以下の通りです。
コンテンツの取得にはYouTube Data API V3を使用します。左のLambdaがYouTubeの最新自社コンテンツを定期クロールするためのものです。初期状態ではYouTube Data APIの割り当てが厳しめなので、Googleさんに上限緩和申請をしました。英語のフォームのみでサイトのポリシー監査と合わせて進める形でしたが、丁寧に返信をいただけて、スムーズに承認していただきました。
ただし、クロールはあくまで補助としてで、実際はリアルタイムにコンテンツが追加・作成されたタイミングで push通知 をうけ、そのコンテンツの詳細をYouTube Data APIで収集しています。Push通知はWebSubというGoogleサービスが利用できますので、ぜひ参考にしてみてください。
レポジトリ
今回の開発レポジトリは1つのレポジトリに複数のパッケージを含むモノレポ構成にしました。
root/
├ front/
├ infra/
├ admin-front/
├ admin-api/
└ common/
モノレポ構成の動機ですが、やはり共有するコードが出てくる部分です。今回はcommonディレクトリ配下に関しては各所から突き合うという構成にしました。package.jsonやtsconfig.jsonは各ディレクトリ配下に設置しています。
ノウハウとして、せっかく全てTSで完結しているので、importパスにはpath aliasを使いましょう!
../../../../../common/functions/hoge.ts => @common/functions/hoge.ts
のようなやつです。相対パスは人にも見にくいですし、エディタのリファクタツールもより正確になる印象があります。ちなみに、webpackを利用している場合は設定でpath aliasを解決することができますが、単にtscコンパイルした時は、@common/hogeが相対パスに変換されるわけではありません。そこでmodule-aliasを設定しエントリーポイントで読み込んでおいてあげるのがおすすめです。
また、循環して参照する形だとビルドツールやエディターのインデックスが悲鳴を上げそうなので、common => [any where] という参照の方向を一方向にしております。(こちらは自助努力として、、)
ログイン画面
通常CognitoはホストされたUIがあるので、独自にログインページを実装する必要がありませんが、今回はMFAとして、Time-based One-Time Password(6桁のやつ)をかけるという要件があったため、ログインページを作成する必要がありました。
フロントからCognitoを操作するには amazon-cognito-identity-js を使用します。
モニタリング
Cloud Watch LogsのLog Groupに蓄積するログ(Lambda, ECSなど)に関しては、メッセージをLambda関数へ転送するサブスクリプション機能があるので、'Error' などの文字をフィルターしてSlackなどに通知するようにしています。
また、CloudWatch Alarmでのリソース監視で、ECSタスクの数などはサービスに直接影響する可能性があるので、CW Alerm => SNS => Lambda => Slackと通知します。
さらに、最近になって CloudWatch Synthetics というE2Eテストのマネージドサービスがリリースされています。今回はHeart-Beatテストだけですが、試用しております。
フルスタックへ
私はフロントエンドから入っているため、以前はインフラ構築に関して苦手意識を持っていました。最初にAWSと仲良くなったのはAWS CDKを通してです。もともとTSが得意だったので、とっつきやすかったのもありますが、一般的なプログラミング言語でインフラをかけるというのが一番大きかったと思います。また、実際にプロビジョニングを行ってみると、必ずなんとなくでは触れない部分が出てきます。個人的にはここがポイントで、インフラってフロントと違ってついつい目に見えない感じがしてしまうのですが、実際はどこがどう繋がっているのかがコード上に明示されることで一気にサービスへの理解が深まった経験があります。Infrastructure as Codeはそもそもインフラ構築作業を開発プロセスの中に取り込むことなので、フロントエンドエンジニアにとってはインフラ構築に置けるfurther moreな取り組みではなく、むしろ入り口にあると思います。
まとめ
* やっぱりやることが多い
* TSでコンテクストスイッチ最小にして、DXを爆上げできる
* AWS CDKは使いやすい
* WEBサイト制作には雛形として使える知識が多いので、次回以降の構築にはコードだけ出なく知見も流用していきたい
* テスト駆動ではありません
初稿につき、拙文失礼いたしましたが、みなさまに何か発見があれば幸いです。
(デジタルイノベーション本部・小室雅春)