こんにちは、デザイン本部の藤原です。
私は Pococha というサービスの Web チームにいて React / Next.js を使ったフロントエンドの開発を担当しています。
Pococha の web リポジトリでは、
- 公式サイト
- ブラウザから配信を視聴できる Web 版
- アプリ内で参照されるイベント一覧・個別ページ
を中心に、その他様々な機能を提供しています。
そんな中、今回は普段そこまで日の目を浴びることのない「LP を支える技術」に焦点を当てて、深堀りしていきたいと思います。
Pococha における LP とは?
一般的に LP というと、広告をクリックした先にあるページのことを指すことが多いかと思いますが、Pococha では、アプリを開いたときにトップに出ているバナーをタップした先にあるページのことを指しています。
Pococha でよくある LP の例
普段どんな LP をつくることが多いかというと
- オフラインイベントや Zoom を使ったオンラインイベントの参加者を募集する LP
- 他 IP とのタイアップ企画について告知する LP
- アンケートの募集をする LP
などが多いです。
なぜあえて LP をつくるのか
これくらいの要件であれば、わざわざ LP など作らず note やはてなブログなど、外部のブログサービスを用いて LP としても良いような気もしますが、Pococha では、
- なるべくデザインにこだわってユーザーに情報を伝えたい
- ユーザーの認証情報に紐付けてイベントの参加やアンケートに答えてもらうことによってオペレーションコストを削減したい
と言った事情があり、内製でつくることが多いです。
ちなみに、ユーザーの認証情報と紐付けてアレコレしない場合や、記事全体としてデザインにこだわる必要性が低いページについては、note も併用して 運用しています。
どれくらいの頻度でつくるのか
さて、Pococha における LP の作成業務ですが、これが意外と高頻度で発生していて、少ない月でも 2,3 本、多い月だと 7,8 本くらい存在します。
冒頭にも書いたように、Pococha の Web チームの主な業務は Web 版の開発やイベントページの保守・運用であり、LP だけをつくっていればいいわけではないため、結構工数を圧迫します。
そこで Pococha では、最低限の要件やデザイン性は担保しつつ、あとは効率に振り切った方法で LP を作成しています。
画像 LP
最低限の要件やデザイン性は担保しつつ、あとは効率に振り切った LP 作成の方法...
チーム内では、それを 「画像 LP」 と呼んでいます。
簡単な仕組みの説明
仕組みは簡単です。
1. Figma や Sketch からデザイン全体を png 形式で切り出します
2. HTML 上に div タグを用意して、1 で用意した png 画像の横幅と縦幅を指定します
3. 2 で用意した div タグに backgroud-image として 1 で切り出した画像を指定します
はい、以上! LP 完成! 簡単!
って、え〜っ!?!?!?!?!?
嘘のようで本当の話
嘘のようで本当です、基本的にはこの仕組みで 2 年以上運用しています。(厳密に言うとこの仕組みを元に拡張して運用している)
なぜこれで良いことになっているのかというと、それは基本的に LP はアプリ内ブラウザからの参照がメインであることに起因しています。
これが理由で
- 検索エンジン最適化
- ラップトップやデスクトップに最適化されたデザイン
を気にする必要がありません。
あとは単純に、通常のやり方より 圧倒的にシュッと出せる と言ったメリットがあります。
肌感ですが、通常 5 日くらいかかりそうな LP が半日もかからずできていくのですごいです。
もちろん、デメリットもあって、
- パフォーマンス観点でユーザーの通信容量に負荷をかけがち
- アニメーションにこだわったリッチな表現ができない
- ページの中にツイッターのタイムラインや YouTube のプレイヤーなどを embed できない
などなど... があります。
パフォーマンス観点で、画像が大きくなりすぎるときは、複数の LP に分けて、それぞれをリンクさせたり、ある程度の劣化は覚悟で jpg 形式で書き出して圧縮するなどしています。 (スマホで見ると、そこまで気にならない)
しかし、アニメーションでリッチにしたい場合や、何かしらを embed したい場合は、画像 LP の守備範囲外です。 普通にマークアップして LP を作ります。
画像 LP を拡張する
さて、そんな画像 LP ですが、さすがに画像を表示するだけでは、機能不足です。 筆頭がボタンです。
ボタン
当たり前と言えば当たり前なのですが、画像を切り出して表示するだけだと、ボタンの一つ満足に提供できません。
そこで、
1. デザインから、動的な部分(ボタン)と、静的な部分(ボタン以外の全て)を分けて書き出す
2. 静的な部分(ボタン以外の全て)の上に、動的な部分(ボタン)を a タグとして配置する
としています。
静的な部分と動的な部分を分けずに、リンクなどがある部分を area タグで覆って href 属性を設定することも考えたのですが、ボタン以外への応用や、認証状態のときに出すボタンと非認証状態のときに出すボタンの出し分けなどすることを考えると、Pococha では、静的な部分と動的な部分を別に書き出して、実装するようにしました。
レスポンシブ対応
あと、いくらアプリ内ブラウザからのアクセスがメインだと言えど、スマホのサイズはまちまちなので、申し訳程度にはレスポンシブ対応をする必要があります。
「Pococha でよくある LP の例」であげた LP にアクセスしていただいて、ブラウザのウィンドウ幅をグリグリいじってもらえれば分かると思うのですが、320 px から 750 px のあいだで申し訳程度にレスポンシブ対応しています。
これは、ウィンドウ幅が変更されたことを検知して、そのたびに全ての画像やボタンの横幅や高さ、位置などを再計算して style 属性で指定する... と言った、かなり泥臭いことをやっています。
まあ画像でやる以上、こう言った泥臭い実装になるのは、致し方なしです。
その他
その他にも、ページ内でコンテンツを切り替えられるタブナビゲーションの設置や、ページの下部に固定できるリンクを簡単に設置できたりしますが、仕組みはボタンの設置とほぼ一緒です。
基本は、静的な背景の上に、動的な要素を乗せる、以上!
効率化、最適化...
ここまで話してきた画像 LP ですが、私が Pococha チームにジョインした当初はかなりしんどい技術構成で実現されており、この 1,2 年は効率化、最適化の連続でした。
Rails 時代
私がジョインした当初、画像 LP は、歴史的経緯から server リポジトリ、つまり Rails のビューで実装されていました。
なので、毎回 LP をつくるたびに、コントローラを生成し、ルーティングを書いて、Slim でテンプレートを書いて、Ruby Sass と Compass の秘伝の mixin を使ってスタイルを書く... 何か問題が起きれば、我々フロントエンドエンジニアとしては、全く土地勘のない Ruby 文化圏のことをググり倒してトラブルシューティングして...etc
そして何より、画像 LP 自体が Pococha 固有の実装方法であることに加えて、Ruby Sass や Compass と言ったレガシーな技術を用いて作られた数々の mixin に依存していて、属人化が激しい、気軽に後輩に引き継げないと言った問題がありました。
いや、しんどい、しんどい。
脱 Rails、React コンポーネント化
そこで、前述のつらみを解消するため、画像 LP を Rails が使われている server リポジトリから、React / Next.js が使われている web リポジトリに移行することにしました。
過去の画像 LP の構造を調べ直し、image-based-landing-page
モデルとして定義して、これに準拠したオブジェクトを props として渡すことによって、イイ感じに画像 LP を表示してくれる React コンポーネントを実装しました。
実装のサンプルを見る
/**
* 本来はファイルを分けていますが、便宜上まとめて書きます
**/
type ImageBasedLandingPageModel = {
// LP のパス(https://pococha.com/lp/[id] になる)
id: string;
// <title> で利用する
title: string;
// <meta name="description"> で利用する
description?: string;
// <meta property="og:image"> で利用する
ogpImageURI?: string;
// ページの横幅 px
width: number;
// タブナビゲーションを表示する場合に定義する(今回は割愛)
navigation?: NavigationModel;
// タブナビゲーションありの場合は、sheet を複数定義する
sheets: {
// sheet のタイトル
title: string;
// sheet の高さ px
height: number;
// sheet の画像の URI
imageURI: string;
// ボタンの情報
buttons?: {
// ボタンをタップしたときの遷移先
href: string;
// アプリ内ブラウザでボタンをタップしたときの遷移先
hrefInApp?: string;
// ボタンの横幅 px
width: number;
// ボタンの高さ px
height: number;
// 左からのボタンの位置 px
positionX: number;
// 上からのボタンの位置 px
positionY: number;
// 画像の URI
imageURI: string;
// ログインが必要かどうか
isNeedLogin: boolean;
// ログインが必要な場合の画像の URI
// - 例:「ログインして応募する」と書かれたボタンの画像
needLoginImageURI?: string;
}[];
}[];
// ページの下に固定するナビゲーションを表示する場合に定義する(今回は割愛)
bottomFixedNavigation?: BottomFixedNavigationModel;
};
const SamplePage: ImageBasedLandingPageModel = {
// ImageBasedLandingPageModel に従って、
// ページの情報やデザインの情報やボタンの情報を書く
};
// ~/src/pages/lp/[id].tsx で import して、ページとして公開する
export { SamplePage };
これでようやく Rails 時代にあったような属人性はなくなり、デザインから画像を切り出して、オブジェクトさえ書けるフロントエンドエンジニアであれば、誰でも画像 LP を実装できるようになりました。
画像 LP ジェネレーター、爆誕
さて、React コンポーネント化によって、属人性排除には成功したものの、依然、デザインを見ながら、地道にオブジェクトを書いていくという作業が残っています。
しかし、思うわけです。
モデルとして定義できるということは... デザインからオブジェクトを自動生成できるのでは...? と。
ちょうど、そんなことを思っていた矢先、
- デザインチームがメインのデザインアプリを Sketch から Figma に移行するということ
- Pococha Web チームに内定者アルバイトのエンジニアがジョインするということ
を知りました。
幸い Figma には様々な API が生え揃っています... よし、内定者アルバイトの人に作ってもらおう!
ということで、大体の画像 LP の成り立ちと仕組み、Figma プラグインを使って自動化したいという概要を伝えたところ、本当にサクッと作ってくれました。
このプラグインを用いると、「ボタンポチ-」で、画像を書き出して、画像 LP を構成するオブジェクトを吐き出すことができます。
あとは画像は s3 に、コードはリポジトリに入れてデプロイすれば OK です。 実装自体にかかる時間は 30 秒もかからないですし、デプロイを含めても数分から数十分で完了します。
革命的。
詳細は、そのプラグインを実装してくれた本人が DeNA 21 新卒 Advent Calendar 2020 17 日目の Figma プラグインを作って Pococha の LP 実装をほぼ自動化した に書いてくれたので、ぜひ併せて読んでみてください。
さいごに
さて、いかがだったでしょうか?
このような問題は、華があるかと言われると微妙で、どちらかと言うと泥臭いことにあたると思います。 しかし、我々フロントエンドエンジニアは、サービスにおけるフロントエンドの全域をカバーすることを求められることも多々あり、こういった泥臭い問題に向き合っていくこともしばしば必要です。
Pococha では、2 年間の歳月の中でメインの業務の空き時間を見つけては、徐々に改善を重ねて、一旦「やれるところまでやったかな」というところまでたどり着きました。
もし、同じような課題感を感じている現場があれば、この記事を参考にしてみてください。
そして、他の方法や答えにたどり着いたという方がいらっしゃいましたら、ぜひその知見をインターネットに発信していただけると幸いです。
ここから宣伝
この記事を読んで「いい話!」と思ったら Twitter やはてなブックマークなどで、ぜひリアクションください。
また DeNA 公式 Twitter アカウント @DeNAxTech にて、弊社のもつ様々な技術的知見や勉強会情報を発信していますので、ぜひフォローしてみてください。
宣伝ここまで
それでは。