プレイドインターン体験記:インターンを通じて得た技術力と開発力の学び

こんにちは!8月から12月までの5ヶ月間、KARTE Blocksチームでエンジニアインターンをしていた池田毅翔(@2002_yoshi10)です。

プレイドのインターンを知ったきっかけは、京都で開かれたハッカソンにスポンサーとして社員の方が参加されていたからです。

そこから、過去のインターン生のブログを拝見し、インターン生でもレベルの高い実装に取り組んでいることに興味を持ち、最終的にはインターンとして勤務している友人がおり話を聞く中でぜひ参加したいと思い応募しました。

KARTE Blocksについて

プレイドのインターンでは実際に運用されているプロダクトや基盤の開発チームなどにアサインにされます。その中でも私はKARTE Blocksチームに配属されました。

KARTE Blocksとは、既存のサイトにタグを設置するだけで、「サイト改修・更新の効率化」「テストによる仮説検証やパーソナライズによるパフォーマンス向上」「データによる課題発見」までを一気通貫で、誰でも簡単に実施可能にするプロダクトです。タグを設置するだけでどんなサイトもブロック化しノーコード/ローコードでサイトを編集することができます。

KARTE Blocksの技術については、プレイドのテックブログで紹介されているのでぜひ見てみてください!KARTE Blocksを支える技術

取り組んだタスク

多くのタスクを経験させてもらいましたが、大きく分けると以下2つがあり、特定の技術だけでなく、フロントエンドからインフラまでの幅広い開発、エンジニアだけでなくデザイナーとのコミュニケーションを密に取った開発まで幅広く経験させていただくことができました。

ブロック管理のためのスクリーンショットの改善

最初、粒度の細かいタスクをいくつか取り組んだ後、スクリーンショットの仕組みを改善するタスクを任せていただきました。

ブロックの編集内容と元の内容を比較できるように、ブロックのスクリーンショットを撮影して保存し、ブロック管理に役立てています。

このスクリーンショットは、KARTE Blocksチームが提供しているChrome拡張機能を使って撮影しています。しかし、エディター上で編集したブロックのスクリーンショットの撮影を行っているため撮影時に以下の課題が発生していました。(例ではKARTE BlocksのLPサイトを編集しています)

  • ブロックを選択している時に表示されるカーソルがスクリーンショットに映り込む
    fig1.webp
  • ページスクロールとスクリーンショットの撮影タイミングが重なりブロックが正しい位置に表示されない
    fig2.webp

これらの課題を解消するというタスクでした。今回は、Playwrightを使用して、スクリーンショットを撮影する仕組みを新しく作りました。エディターとは別でこの仕組みを使ってブロックの書き換えを行いスクリーンショットを撮影するようにしました。

最初、タスクの粒度が大きく何から手をつけたら良いかわからない状態でしたが、メンターと相談を行い、以下のタスクに分けて実装しました。

  • Playwrightが動作するスクリーンショットサーバーの構築
  • ブロック保存時にPlaywrightを起動したスクリーンショットサーバーに書き換えに必要な情報を乗せたリクエストを投げる
  • スクリーンショットサーバーでブロックの書き換えを行い撮影した写真をS3にuploadする

Playwrightが動作するスクリーンショットサーバーの構築

最初に行ったことは、Playwrightを起動させるスクリーンショット用のサーバーを構築することでした。当初は既存のバックエンドサーバー上でPlaywrightを動かす案も検討しました。しかし、バックエンドサーバーはAlpineで動作しており、現時点では、PlaywrightはUbuntuまたはDebianのみをサポートしているため、新たに専用のスクリーンショットサーバーを構築しました。

スクリーンショットサーバーの構築では、Dokcerコンテナの作成とKubernetesへのdeployに取り組みました。当初、Dockerコンテナを自分で作ったことがないことや、Kubernetesもこのタスクに取り組むときに初めて知ったものだったので、苦戦しましたが、メンターとチームのエンジニアのサポートがあり、コンテナの作成とKubernetes上にコンテナをデプロイすることができ、本番環境でスクリーンショットサーバーが無事動くようになりました。

ブロック保存時にPlaywrightを起動したスクリーンショットサーバーに書き換えに必要な情報を乗せたリクエストを投げる

スクリーンショットサーバーの構築を終えた後は、どのようにスクリーンショットを撮るリクエストをスクリーンショットサーバーに投げるかを検討し、その実装に取り組みました。

今回の実装では、Chrome拡張機能で撮影した写真をバックエンドサーバーでS3に保存する処理と同時にスクリーンショットサーバーにリクエストを投げるようにしました。これにより、スクリーンショット機能を冗長化し、従来の方法と新しい方法を両立しスクリーンショットを撮影できる仕組みを実現しました。

両立させた理由としては、KARTE Blocksでの管理画面では、認証が必要なページでも書き換えを行う必要があります。Playwright単体ではこのような認証が必要なページのスクリーンショットを撮影することができないからです。両立させないことによりスクリーンショットが撮影できない事態は避けたかったためこの方法を取りました。

スクリーンショットサーバーの構築をする際に、コンテナ間通信などの基礎部分は実装していたため、バックエンドサーバーからスクリーンショットサーバーまでリクエストを送る処理は比較的早く実装することができました。

下記は、動作の流れのイメージ図です。
fig3.webp

ブロックの書き換えを行い撮影した写真をs3にuploadする

最後に、Playwrightで開いたブラウザで、ブロックの変更内容に合わせてサイト上の一部を書き換え、スクリーンショットを撮影しS3にuploadする実装を行いました。KARTE Blocksではサイト訪問時にDOMを書き換えることをサードパーティスクリプトを使用して実現しています。(参考記事

KARTE Blocksの管理画面ではサードパーティスクリプトによる書き換えを擬似的に実行するためのスクリプトを使用して表示しています。このスクリプトをPlaywrightで開いたブラウザにPlaywrightのAPIであるaddInitScriptメソッドを活用し、要素の書き換えを行いました。

要素を書き換えた後、指定した要素のスクリーンショットを撮影し、S3に保存します。この際、Chrome拡張機能で撮影した写真をS3に保存したときに発行されるURLに上書きする形で保存処理を実装しました。これにより、Playwrightで撮影したスクリーンショットがブロックの写真として保存されます。

エラーの把握

上記のように、Playwrightでは認証が必要なページの撮影ができないエラーが起きる可能性があります。その他にも、書き換えを擬似的に実行するためのスクリプトでブロックが書き換えられなかった時など複数のパターンでスクリーンショットが撮影できないケースがありました。本番環境でスクリーンショットが撮れないケースを細かく把握するためにdatadogにメトリクスを送信するようにしました。

送信したメトリクスには以下のタグを設定しています。

  • isTimeoutError: 処理が時間切れで失敗した場合はture、それ以外はfalseを設定
  • type: エラーの原因を識別するための情報を設定 (selector,url,…)

送信されるメトリクスで分かることには以下のパターンがあります。

  • isTimeoutError: true
    • selectorが見つからない
    • サイトが表示できない
  • isTimeoutError: false
    • urlが見つからない
    • サードパーティスクリプトによる書き換えを擬似的に実行するためのスクリプトでの書き換えに失敗する
    • 認証が必要なページを開く

ここで投げたメトリクスを元にdatadogのダッシュボードで可視化し、安定した運用のために役立てるようにしました。以下の写真は、フロントエンドからスクリーンショットサーバーまでのスクリーンショットを撮影するリクエスト中に起きたエラーを可視化しています。

fig4.webp

実装を終えて

スクリーンショットサーバーの実装を終え、Playwrightでブロック書き換え後のスクリーンショットを撮影できるようになり、課題としていたカーソルの表示とブロックの位置ずれを解消することができました。しかし、バックエンドサーバーへChrome拡張機能で撮影した写真とブロックの書き換えに必要なデータを同時にリクエストに乗せる実装が原因で、ブロックの書き換えに必要なデータが大きい場合に、バックエンドサーバーがハングしてしまうという不具合が起こってしまいました。

具体的には、ファイルアップロード処理に使用していたExpressのミドルウェアであるMulterに問題があることがわかりました。multerのsingleメソッドを使用していたのですがこのメソッドは、画像データをreq.fileに、その他のtextデータをreq.bodyに格納します。今回の実装では、スクリーンショットのデータがreq.fileに、ブロックの書き換えに必要なデータがreq.bodyに格納される実装になっていました。

データをreq.bodyに格納する際にデータが大きい場合は処理が滞り、結果としてバックエンドサーバーがハングする状態になっていました。現在はフロントエンドからのリクエストに乗せるデータに余分なものが含まれていたので乗せないようにしたことと、Multerの処理を3秒で強制終了することで対応しました。

このスクリーンショット機能は、ブロック編集後すぐのプレビュー用とブロック管理画面用の2つの用途で利用されています。

  • プレビュー用
    fig5.webp

  • ブロック管理画面用
    fig6.webp

今回の実装では両方のスクリーンショットを以前までのスクリーンショットの課題を解消した綺麗な状態で撮影できるようにすることを目指しました。しかし、特に綺麗になっていて欲しいのはブロック管理画面用でのスクリーンショットでした。プレビュー用のスクリーンショットは編集後に表示されるものであり、ユーザーはブロックの変更内容を既に把握してる可能性が高いためです。

この点を後から振り返り、プレビュー用のスクリーンショットは従来通りChrome拡張機能に任せ、管理画面用のスクリーンショットをPlaywrightで撮影し、ブロックの書き換え内容をDBに保存するタイミングでスクリーンショットサーバーを起動するという選択肢があったことに気づきました。

fig7.webp

この選択肢を設計段階で考慮し、検討していれば、不具合も防ぐことができたかもしれません。

後から気づけたことは自分にとって大きな学びでした。今後は設計の段階で複数の選択肢を洗い出し、それぞれのメリット・デメリットを考慮した上で、最適な実装方針を選べるようになりたいと感じました。

また、短時間で大量にスクリーンショットが失敗している場合はアラートをSlackに投げるようにも設定しました。

実装を終えた後にリリースノートを書くこともできました!

ページエディターの改善

クライアントが登録したページをiframeで開いて、実際のサイトを見ながらブロックを編集する画面を「ページエディター」と呼んでいます。

ページエディターでは様々な改善を行いましたが、特に印象に残ったタスクとして、ブロック名を変更できるようにする機能の追加があります。

KARTE Blocksではサイトのブロックごとの訪問ユーザーを配信条件ごとに集計し表示しています。その際に、ブロック名が違うことによって、管理がしやすくなるのですが、ブロック名を変更する操作の動線が深かったことにより、多くのブロックが名前の初期値である「ブロック」のまま保存されてしまい、管理が困難になるという課題がありました。

この課題を解消するため、ブロック名の変更をより直感的に行えるよう、機能をページエディターに追加する実装に取り組みました。

このタスクは以下二点の観点で学びがありました。

  • デザイナーとのコミュニケーション
  • プロダクトへの貢献

デザイナーとのコミュニケーション

スクリーンショット改善のタスクはバックエンドとインフラがメインの開発だったので、画面の開発はありませんでした。しかし、このタスクは画面に新しい表示を加えるものであったので、Figmaのデザインカンプがありそれを実装する形になります。

デザインカンプを基に実装を進めましたが、カンプだけでは意図を伝える/受け取ることには限界があります。そのため、実装中に質問や改善アイデアを積極的に共有し作り上げていくことの重要性を感じました。

実装段階でも適宜質問を行いましたが、最後動作確認するだけという段階でも認識違いが多くありました。

fig8.webp

この経験を通じて、デザイナーだけでなく、自分とは異なる専門領域を持つ人と協力して仕事を進めることの重要性を強く感じました。一人で複数の専門性を持つことが理想ではありますが、それには限界があります。

今回であれば「デザイン」がその専門分野でしたが、今後、自分の専門外の分野の知識が必要となった場合には、人に相談し力を貸してもらう姿勢を大切にしたいと思います。また、KARTE Blocksチームの雰囲気が、メンターだけでなくチーム全体に何でも相談しやすい環境だったことにもとても助けられました。

プロダクトへの貢献

このタスクが上がったのは、インターン期間中にクライアントからのプロダクトへのFBから生まれたものでした。インターンだと、インターンとして始まる前にある程度固定されたタスクを渡されることが多いのかなと思います。そうではなく、チームの状況によって取り組むものをきめ、アサインできそうであればアサインを貰いタスクに取りかかれたことで、チームの一員として働いている実感とプロダクトに本当に貢献できているんだなと感じることができました。

タスクに取り掛かり、実装を無事に終えることができました。今回の実装はフロントエンドの修正だけで完結する内容だったため、技術的に特別難しいことはありませんでした。しかし、このタスクを通じて学んだのは、技術的な難易度の高さだけがプロダクトへの最大の価値提供ではないということです。

エンジニアとして、より専門性を必要とする難易度の高い技術を使えるようになるというのも勿論必要ですが、目的はプロダクトや顧客への貢献であることを改めて認識できたタスクでした。

リリース後にしたチーム内での報告もビジネス側のメンバーから声をいただき、やりがいを感じられたタスクでした。

fig9.webp

この実装もリリースノートで公開することができました!

最後に

5ヶ月間、高い技術力を持つエンジニアの方々や、デザイナー、ビジネス領域のチームメンバーと共に開発に取り組むことで、多くの刺激を受け、学びと成長を実感できたインターンシップでした。

インターンシップ開始前は、フロントエンド開発が中心で、バックエンドは少し触った程度、インフラには全く触れたことがありませんでした。しかしこのインターンでは、フロントエンドからインフラまで幅広く実装を行うタスクに取り組めたことで、技術的な視野を広げることができました。特に、インフラ領域の実装経験を積めたことは、エンジニアとして大きな成長を感じました。

また、多様な人を巻き込んで開発することの重要性も実感しました。エンジニアだけでなく、デザイナーの方と適時相談しながら実装を進めれたことにより、最終的なアウトプットがより良いものになったことを実感しています。

今後も、より難易度の高い課題を解決し、価値を提供できるエンジニアを目指していきたいと思います。そのために、技術の幅と質を向上させ専門性を高めていくことと、チーム全体を巻き込みながら開発に取り組む姿勢を大切にしていきます。

プレイドのインターンでは、高い技術力も持つエンジニアからの技術の刺激は勿論ですが、実際のチームにアサインされることで技術以外にも、いろいろな刺激を受けることができます!
必ず成長できますので、興味ある方は応募してみてください!!