フルサーバーレス「グローバル下ネタ検索サービス」

# はじめに
　先日、フルサーバーレスで「グローバル下ネタ検索サービス」を作成しました。皆さんも知人に尋ねた経験があるのではないでしょうか。
「チ〇コって、英語でなんて言うの?」
このサービスは、そんな質問にスラングも含めた言葉でお答えすることを目的としています。「dick」「cock」等など。国際社会でも重要な潤滑油となる下ネタを、もっと手軽に調べられたらとの思いで始めました。
ネタはあらゆる意味で完全に終わっていますが、今どきのSPA on AWSで実現させたので、備忘録として残しておきたいと思います。

　OutWord:グローバル下ネタ検索サービス
　https://outword.biz/
※Googleでは「グローバル下ネタ検索」で検索してもらうと、比較的上の方に出てきます。

　現在、英語と日本語のみ対応しています。刺激の強いアフィリエイト載せてますけど、どうか許してください…。

【追記】
　上記サイトのNEWSに、データベース登録された言葉を随時追記する様にしました。

# なぜこんなものを作ったのか
　全ての原因は[この記事](https://togetter.com/li/1695752)です。笑うしかない。ロイヤルチ〇ポと言われても仕方がないですね。
　ただ、一つ疑問に思ったのが本当に誰も何も言わなかったのかということです。私に一言言ってくれれば、現物見せて伝えることもできたのに。さらにもう一歩踏み込むと、このCHIMP〇を全世界に公開しても問題がないのかを調べる手段が、もしかしたら本当になかったのかもしれません。どこの国に行っても下ネタがあり、他国から来た方は必ず知りたがります。にも関わらず、なかなかドメスティックの域を超えないのが下ネタ。やはり義務教育に入っていないというのは大きいです。こんな悲劇というか喜劇は繰り返していただいても良いのですが、せめて繰り返すかどうかの判断材料ぐらいはあってもよいのではという考えで作り始めました。暇だったし。

# 全体構成
　非常にシンプルなものです。AWSのWell-architectを参考にしたものとなります。最高のテクノロジーで最低のアイディアの実現ですね。上段のCloudFrontからDynamoDB(下ネタデータベース)まででサービスとしては動作します。SQSより下段の部分では、検索対象の単語を後から解析できるようにしました。CloudFrontにはRoute53に登録したドメインをベースにしたSSL証明書をACMで実装する事で、https通信を実現しています。なお、リージョンはバージニアです。
![OutWord構成図.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1689863/157a441b-8f4b-bb3b-3e7b-525b1accaf43.jpeg)
【追記】
　少しわかりにくかったので、メインサービス機能と統計機能で大枠を追記しました。

# メインサービス機能部
　主に[こちら](https://qiita.com/ritya/items/d19f9379bc96fb63ad34)と[こちら](https://d0.awsstatic.com/events/jp/2017/summit/devday/D4T7-2.pdf)を参考とさせて戴きました。LambdaはPython3.8で書いています。ポイントはLambdaでのCORS処理です。JavaScriptで飛ばしたリクエストをAPI Gatewayで受けてLambdaに渡しますが、設定を間違えるとLambdaのeventに何も入っていなかったりします。ここ、もう少し簡単に実装できるようにしてほしい・・・。

　ちなみに、手っ取り早くて「多少」マシな方法として以下の方法があります。
・API GatewayのLambda Proxy統合をしてしまう。とりあえず、eventの中がスッカラカンという事態は防げます。
・小細工をしたコードで誤魔化す。

```python
ORIGIN_HOST = ['https://example.com', 'https://www.example.com']

def lambda_handler(event, context):
    requestContext = event.get('requestContext')
    identity = requestContext.get('identity')
    header_info = event['headers']
    origin_header = header_info.get('origin')
    if identity.get('sourceIp') != os.environ['source_ip'] and origin_header not in ORIGIN_HOST:
        return {
            'statusCode': 200,
        }

    print('chinko')

    return {
        'statusCode': 200,
        'headers': {
            'Content-Type': 'application/json',
            'Access-Control-Allow-Origin': origin_header,
            'Access-Control-Allow-Methods': 'POST',
        },
        'body': 'chinpoko'
    }
```

　print('chinko')の部分で好きな処理を行います。どういう事かと言うと、eventの中にoriginの情報とかも入っているので、その情報をORIGIN_HOSTと照合させて一致するならばその後の処理に進めます。もし一致しないようならば、申し訳程度に"'statusCode': 200"を返却してその後の処理は一切行いません。Access-Control-Allow-Originもorigin_headerを返却することで、CORSでブラウザから弾かれるということもなくなります。ちなみに"identity.get('sourceIp')"の条件を追加しているのはデバッグ用です。開発用に固定IPアドレスを持ったVPNサーバー等を用意して(1つあると結構使えます)、環境変数にそのIPアドレスを記入しておきます。テストするときはVPNサーバーにつなぎます。xamppなどを使ってローカル環境でHTMLやらJavaScriptを動かすと、originは 'http://localhost' であるにもかかわらずIPアドレスはVPNサーバーのものになるため、ローカルからのアクセスでもLambdaの処理を最後まで行うことができます。フロントサイドのテストが何気に楽になります。そして'Access-Control-Allow-Origin': '*'よりかはセキュリティ的にもマシ…かな?一緒な気もしますが。カスタムドメインとかやった方が本当は良いと思いますが、途中で気が萎えた。

　もう一点は、サーバーレスの宿命とも言えるコールドスタートの時間短縮です。[こちら](https://overworker.hatenablog.jp/entry/2021/03/22/002508)の記事を参考にメモリ容量の調整を行なっています。この部分のLambdaを軽量化したのは、コールドスタートへの影響を少しでも小さくするためでもあります。どこまで役に立ったかは分からんが。

# 統計機能部
　こちらは飛んできた言葉をキューにためて、Lambdaバッチ処理を行うことでどのような言葉が検索されているかの統計を取る機能です。一例を挙げると何回「ちんこ」という検索がされたかをDynamoDBに記録する箇所です。あ、「ちんこ」って書いちゃった。上段部分と疎結合とすることで、お互いにほとんど影響を与えないようにしています。疎結合にする手段として、SQSはアリかと思います。私一人が知っている下ネタの数なんて大したことがないので、検索された言葉のキューから「よく検索されているが下ネタデータベースに無い言葉」を抽出し、統計データベースに記録していきます。その中なら、下ネタを引きずり出して調べて下ネタデータベースに登録。これで下ネタデータベースはより強力なものになっていく…。

# フロントエンド
　白状します。私、全然絵心がありません。HTMLやCSSを地道に書いていく自信が全くありません…。SPAにしたのも、たくさんのページをデザインする気力が全くなかったからであります。そこで、最近流行りのノーコードサービスを使ってみました。
　https://webflow.com/
　ここでデザインを行い、HTMLとCSSをエクスポートしてS3に保存するという方法をとっています。ただし、一部修正は必要。ボタンのonclick関連付とか。なかなか銀の弾丸はないものです。SPAでやるなら、ホームページビルダーでもよかったかも。

# まとめ
　なんだかんだでこのサービス作るのに10日かかりました。しかも対象としているのが「世界で利用されている下ネタ」という、スラングで満ち溢れたコンテンツ…。軽い気持ちで作り始めましたが、下ネタデータベースを更新するたびにサグラダファミリアに挑んでいるような気持ちになります。ただ、こうやって何かを作り公開してアップデートしていくというのは、世の中にインパクトを与える役目を背負ったCHief IMPact Officerとしての本来の役目かもしれません。そういう意味では、世の中のクリエイターは全てCHief IMPact Officerと言っても過言ではないかもしれません。

ところで、CHief IMPact Officerの俗称をご存知でしょうか。

CHIMPOです。