PAY.JPアンチパターンという内容でLTした

PAY.JPのプラットフォーム向けサービスである、 「PAY.JP Platform」のリリース記念ミートアップで、「PAY.JP アンチパターン」という内容でLTをさせていただきました。

内容

ざっくり見てもらえればわかるのですが、 PAY.JPを使っていく際のアンチパターンを3つほどまとめたものになります。 基本的には定額課金周りのアンチパターンが多めな感じです。

雑感

  • はじめてのLTだったので、それなりに緊張したが、なんとかやりおおした。
  • 発表のしかたがあまり良くない気もしたが、LTの内容は自分のが一番テックな内容だと思っていて、その点はよかった。
  • 今後もLT参加したい。
  • 今後PAY.JPのアンチパターンのスライドの完全版を作りたい。
    • いくつかのパターンを追加するのと、図解や補足説明を入れるようにしたい。
  • ちなみにPAY.JP Platformはうまく使えばかなり便利なのと、競合他社製品に比べると安さ・シンプルさがウリなので、ぜひつかって見てほしい。

また、LTにお誘いいただいた、 PAYの皆様、ありがとうございました。そしてお疲れ様でした。

いちばんやさしい新しいSEOの教本を読んだ

個人的興味や、知識の補完のためにSEO入門するに当たり、みんながおすすめしているように見えたので、この本を買って読んで見ることにした。

前提として、SEO対策についての知識は簡単にWebで調べれば出るものくらいは知っている感じ。

内容について

目次は公開情報なので、下記に記載してみる。

◆Chapter 1 SEOの目的と考え方を身に付けよう

◆Chapter 2 Webサイトの目的と訪問者の目的を考えよう

◆Chapter 3 有効な検索キーワードを調査しよう

◆Chapter 4 業種別に最適なサイト構成を考えよう

◆Chapter 5 適切な内部対策でSEOの効果を高めよう

◆Chapter 6 質の高い外部対策でWebサイトの価値を高めよう

◆Chapter 7 ソーシャルメディアからWebサイトに集客しよう

◆Chapter 8 技術的な問題を解決して優れたWebサイトを目指そう

◆Chapter 9 SEOの効果を分析してさらなる改善を進めよう

・付録 1 業種・ジャンル別キーワード一覧表

・付録 2 サイト全体のテンプレート一覧管理表

・付録 3 今すぐ使えるSEORFPフォーマット

・用語集

Chapter1はともかく、 Chapter2,3,4はサイトを作る前の準備段階の話に3Chapter分の枠を割いていて、 本書を読み進めるごとに実感するのは、SEO対策はとにかく調査と事前準備・設計が大事だということだった。

当たり前の事しか書いていない

少し話は変わるのだけど、 ちょっと前に、メディアサイトのリニューアルにエンジニアとして携わった事があった。

そのときにSEOの面倒を見てくれた方がいて、SEO対策をエンジニアの立場から色々やったりしたのですが、 一般的に言われている「ちゃんとmeta titleつけよう」「H1タグはページ内に1つだけにしようね」とか、「記事のリンク先変わったらちゃんと301リダイレクトしよう」そういう「当たり前の事の徹底」の泥臭い事だったように記憶している。

しかし、そういう当たり前な、簡単な施策をやっておくだけだったのだけど、 リニューアルから数ヶ月後で検索結果に対してもしっかりと効果が現れているそうだ。

本書でも、ごくごく当たり前の事を当たり前にやっていく。 そんな当たり前なことだからこそ、本書紹介されているページ設計やテクニックは今に通ずるものがほとんどだと思っており、 数年しか効果がないような雑なものはないのだろう。

どうしてこの設計にするべきなのかなどの、施策に対しての理由についても結構しっかりめに説明されており、 前述のメディアリニューアル案件で「そういえばこんなページ構成だったな…」「回遊性を上げる理由はそこにあったのかー」とか思い出して、かなり納得感を持って読み進めることができた。

タイトルに「新しい」と入れたのは失敗

流石に最初に出版されたのは5年前で、新しくないのは当たり前。 日進月歩進化していっているWeb界隈や、SEO関連で近年あったような大型アップデート*1については当然触れられてはいない。 いい意味でタイトルに「新しい」と入れたのは失敗だと思っており、SEO入門の古典的な本だと思ってる。

Webエンジニア目線から見て

本書はWebエンジニア目線ではなく、Web担当者やPM向けの印象を受ける。*2

なので、技術的にどうすればいいかみたいなところの章にページは余り割かれておらず*3、 キーワード設計、サイトのページ設計などの事前作業や設計についての大事さがやっぱりメインな感じ。

エンジニアからみたSEO観が変わった

  • SEO対策は当たり前の連続だけど、とくに最初のページ・キーワード設計周りなどがめっちゃ大変。できれば専業でお願いしたほうが良い。
  • エンジニアリングだけでSEOを良くすることはできない。必ずそこにはSEO上の設計が必要。
  • SEO対策の仕事は、サイトを作ったときに見てもらえないという、「検索上のもったいない」をなくす仕事。

おすすめしたい人

  • とにかくSEOの基礎知識を身に着けたい人。
    • 個人的観測だと、基礎はほとんど、全体の60 - 70%はカバーできるのでは。
    • Webに載せるまでもない情報が貴重なので、この本でなくてもいいので、1冊SEO入門書を読んでおいたほうが学習効率は高いと思う。
  • メディア案件でSEO意識して!なんて言われてみたものの、SEOよくわからない。
  • ディレクターや担当者からの301リダイレクトの依頼を面倒で断っちゃうめんどくさいエンジニア

とにかくSEOに入門するに当たり、この本はおすすめ。 ぜひ読んでみてください。

最後に

  • SEO対策をプロジェクトのメインの仕事にするのは正直やりたくないなと思いました。
    • キーワード設計したり、ホント泥臭い仕事。
  • 興味を持って読んでみてすごく理解したし納得感あったが、自分でやりたいなと思えなかった。
    • 技術的に新しみがないし、面白味もぶっちゃけあんまりない。
  • この記事はSEOを意識しないでパパっと書きました。

追記

第二版が出ているらしいです。 ちょっとこれもさらっと読んでみるかなー

*1:いわゆるWELQアップデートとか

*2:本書の中でも結構「エンジニアに協力を…」なんて事を言っているので。

*3:そもそも余り書くことが無い

SendGridで1つのメールを最大1000件のメールアドレスにまとめて送信する方法

これはよく見るとドキュメントに載っているのだけど、 結構見落としがちだったので、記載しておく。

ドキュメントの内容

v3 Mail Send API概要 - ドキュメント | SendGrid

SendGridのMailSendAPIを使えばOKな感じです。

Rubyのコード

これで終了だとあまりにもつまらないので、 SendGrid公式でRubyのGem(SDK)が提供されているので、それを使った際のサンプルを載せておく。 Rubyのコードにするとざっくりこんな感じだろう。

def mail_send
  sendgrid = SendGrid::API.new(api_key: ENV['SENDGRID_API_KEY'])
  sendgrid.client.mail._("send").post(request_body: params)
end

def params
  param = {
    "custom_args" => {
      "test_mode" => true
    },
    "from" => {
      "email" => 'example@example.com',
      "name" => 'サイト名'
    },
    "content" => [
      {
        "type" => 'text/html',
        "value" => 'テスト'
      }
    ],
    "mail_settings" => {
      "sandbox_mode" =>  {
        "enable" => false
      }
    }
  }
  
  param["personalizations"] = User.all.find_each do |user|
    {
      "to" => [
        {
          "email" => user.email,
          "name" => user.name
        }
      ],
      "substitutions" => {
        "[USER_NAME]" => user.name,
        "[USER_EMAIL]" => user.email,
      },
      "subject" => 'メールタイトル',
      "custom_args" => {
        "site_user_id" => user.id.to_s,
      }
    }
  end
  param
end

mail_send

この方法で運用すれば1000つの宛先へのメール送信をまとめることができる。 配信メールアドレスが30000件なら、30回のリクエストに分割してメール配信リクエストをSidekiqやDelayedJob等でWorkerの数を調整して処理してやれば送信リクエストはサクッと完了するだろう。 ただ、流石に30,000件とかのユーザーへの一斉送信はMarketing Campaigns API Overview - ドキュメント | SendGrid を使うべきでは…? と思ってしまっています。*1

テンプレートに変数をアサイ

他にもMailAPIでは、テンプレートを変数を定義しておき、送信時に変数を展開することができる。(コードのsubstitutionsあたりを参照してください。) それぞれのユーザーでメールアドレスだけでなく、ユーザー名や宛名なんかも異なるだろうし、メールの文章中にそういった動的な文言も含めることができる。

まさにSendGridさまさまである。

*1:今回はAzureからの利用だったため、Marketing Campaigns APIは使えなかったのです。

ransackで雑にRDBMSを使った簡易的なキーワードのOR検索を実装する

一般的なRDBMSを使ったよくあるLIKEでの曖昧検索実装では、検索ボックスに「寿司 和食」と入力してもただの単語での曖昧検索だと、 当たり前なんだけど、キーワードのOR検索を実現することができない。 select * from contents where LIKE body '%寿司 和食%' となってしまって、空白を含めた1単語として認識されてしまい、思うような結果が得られないと思う。

実際に組み立てるクエリは…?

多分イマイチなんだろうけど、簡易的にOR検索を実現するにはこんな感じになると思う。

"SELECT "contents".* FROM "contents" WHERE (("title" LIKE '%寿司%' OR "title" LIKE '%和食%') OR ("body" LIKE '%寿司%' OR "body" LIKE '%和食%'))"

これで擬似的にキーワードのOR検索を実装することができた。精度もまぁ無いよりかはマシくらいなもんだろう。 これでOKな事例というのは、検索対象が小規模な場合で、設定できるキーワードに上限を一応設けたほうが良いかもしれない。 以上の注意点を踏まえた上で、Railsで簡単に上記SQLを作る方法に進んでみよう。

実際にRansackで雑に実装

もちろん複雑なことはやっていないので、自分でさっきのSQL文を組み立てて投げるのでも良いのだけど、 面倒だし、検索機能を実装する時によく使われているRansackで実装してみようと思う。

github.com

以下のようにかけばOK。

Content.search(title_or_body_cont_any: ['寿司', '和食']).result

それでは上記で実行されるSQLを見てみよう。

irb(main):007:0> Content.search(title_or_body_cont_any: ['寿司', '和食']).result.to_sql

=> "SELECT \"contents\".* FROM \"contents\" WHERE ((\"contents\".\"title\" LIKE '%寿司%' OR \"contents\".\"title\" LIKE '%和食%') OR (\"contents\".\"body\" LIKE '%寿司%' OR \"contents\".\"body\" LIKE '%和食%'))"

おお、大丈夫そうだ。 あとは、JOINでスペースを区切り文字にして、splitでキーワードの配列作ればOKな感じ。

 # controller 
  def index
    if params[:keyword]
      @contents = Content.search(title_or_body_cont_any: params[:keyword].split(' ')).result
    else
      @contents = Content.all
    end
  end

これで完成。 意外とシュッとできた。やっぱり小規模用途だとransackは便利だった。

SendGridのWebhookを受けるのにFluentdが重宝した話

SendGridのWebhookについて

SendGrid の Event Webhook は、SendGrid経由でメールを送信する際に発生するイベントを、指定したURLにPOSTすることができます。 このデータの用途は、配信停止アドレスの削除、迷惑メール報告への対応、エンゲージできなかった受信アドレスの判定、バウンスされたメールアドレスの特定、メールプログラムの高度な分析などです。ユニーク引数やカテゴリパラメータを使用して、動的なデータを挿入することができるため、あなたのメールのシャープでクリアなイメージを構築するのに役立ちます。

ということなんだけど、 1度に送信件数が5000件以上だったりするとそれなりにOpenや送信したというイベントが飛んで来るので、 このWebhookをどうさばくかというのがちょっとした課題になる。 多分BigQueryに送信するだけであればGCPのLambda相当のサービスを利用すればOKな気がする。 というかGCPがサンプル乗っけてるので、コレはコレで便利なのでぜひ見て欲しい。

SendGrid Tutorial  |  Cloud Functions Documentation  |  Google Cloud

Webhookを受けて、レスポンス内容をMongoに格納する例

今回はFluentdのデフォルトに同梱されているHTTP Input Pluginを利用して、Webhookを受けて、Mongoに送信する方式を取った。 Webhookを受けるだけのなにかアプリケーションを作る必要あったのかと思ったけど、Fluentdでシンプルに解決できる方法があってよかった。

Fluentdのプラグインとして追加が必要なのは、fluent-plugin-mongo くらい。

そのためのfluentd.confをココにおいておく。

<source>
  @type forward
</source>

<source>
  @type http
  port 8080
</source>

<filter **>
  @type stdout
</filter>

<match sendgrid.**>
  @type mongo

   database "#{ENV['DB_NAME']}"
   host "#{ENV['DB_HOST']}"
   port "#{ENV['DB_PORT']}"

   collection "#{ENV['DB_COLLECTION']}"

   user "#{ENV['DB_USER']}"
   password "#{ENV['DB_PASSWORD']}"

   capped
   capped_size 100m

   ssl true
</match>

これでFluentdを再起動し、SendGrid上でport:8080を接続先に設定すればOK。

そもそもBigQueryやTresureDataではなく、 Mongoに送っている理由としては、短い保持期間であればMongoの方が短い時間で結果が返ってくるのかなということでそうしている。 この場合たくさんデータがあふれてくると結局処理時間が長くなるのでは??? という懸念があるので、格納するデータの保持期間については別途検討する必要がありそう。

保持期間を設定してそれまでは保存しておき、古いドキュメントを自動で削除するみたいなのが、 少し簡単にできるかも。今後はコレを参考にして検証する予定である。 qiita.com

サンプルはMongoに送信しているだけだけど、TresureDataやBigQuery、はたまたフォーマッティングして、 S3にもOutputするようにすれば良いんじゃないですかねという感じ。

余談だけど、どこでも動かせるようにということでDockerイメージに固めている。 Azureを利用しているのでWeb App for Containersで動かしている感じ。 多分AWSではECSでも良いだろうし、きっともっと面倒ならElasticBeanstalkでも良いと思う。

Webhookを受けるのにも便利なFluentd

Fluentdはバッファリングしてくれる機構を持っているし、こういったものを自前で実装するよりも信頼性は高い。 Webhook受けて何処かに書き込むような簡単な処理だけなら、わざわざRailsアプリケーションを立てる必要はなくFluentdで十分だと思っている。*1

*1:ロスト確率と、対応時間のコストの兼ね合い考えると、少し重複する分に問題ないなら良い選択と思っている。