1ヶ月〇〇○円で速度改善！？事業でも個人開発でも導入できる画像リサイズのAPI

この記事は[Ateam Brides Inc. Advent Calendar 2019](https://qiita.com/advent-calendar/2019/a-t-brides)　6日目の記事です。

3日目に続きまた書きます[@fussy113](https://qiita.com/fussy113)です。

今回は実際に担当をしているWEBサービス、[HIMARI](https://hi-ma-ri.net/)で行なった簡単速度改善のお話です。

少し前の話にはなりますが、速度改善の一貫として画像のリサイズ、CDNサービスに利用したキャッシュをAWSサービスのみで実装しました。

## この記事のターゲット

- ページスピード改善を目指すエンジニア
  - 一つのやり方として参考にしてなれば！

## システムの構成

導入したHIMARIはRailsで作られています。
active_storageを利用して一部画像をS3は置いてあります。

画像のリサイズ、CDN利用により、どれほどの効果が見込めるのかの検証をするため、ミニマムに一部分だけ導入を進めることとなりました。

- AWSサービスのみで完結することができる
- サービス、CDN利用箇所の規模

CDNサービスも様々なものがありますが、上記を考えて、CDNはCloudfrontを利用し、リサイズの機能もLambda + API Gatewayを利用して実装をしました。

Lambda@edgeの利用も検討しましたが、[公式](https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/lambda-edge-how-it-works-tutorial.html)よりリージョンが限定されてしまうこと、他のエンジニアメンバーが利用、改修することも考えて最低限のシンプルさを求めたので今回は採用しませんでした。

### 構成と仕様要件

<img width="700" alt="構成図.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166596/b83e6a53-c132-e7d5-2dd3-7ced21cfc8dd.png">

1. LambdaでS3から画像を取得。受け取ったパラメータに応じてリサイズを行い、画像を返す関数を作成。(w=700が渡されたら、画像の幅を700にリサイズする)
2. API GatewayでLambda関数をWeb APIにする。
3. CloudFront経由でAPIを叩く

Lambda + API Gatewayの非常にシンプルな構成をしております。

## では作っていく

### Lambda関数を作成する

ライブラリを利用したいのでローカルでライブラリをインストールして圧縮、Lambdaにアップロードする方法を取ることになります。

- imagemagick : 言わずもがな、画像の操作に利用します。
- image-size : 画像のサイズの変更に利用します

```sh:ライブラリのインストール
npm i imagemagick image-size
```

続いてjsファイルです。

```js:index.js
const im = require('imagemagick');
const aws = require('aws-sdk');
const is = require('image-size');
const s3 = new aws.S3({
  apiVersion: '2006-03-01'
});

exports.handler = (event, context, callback) => {
  const key = event.filename; // リサイズ対象のファイル名
  const bucket = event.bucket; // S3のバケット名
  const params = {
      Bucket: bucket,
      Key: key,
  };

  s3.getObject(params, (err, data) => {
    if (err) {
      console.log(err);
      const message = `Error getting object ${key} from bucket ${bucket}. Make sure they exist and your bucket is in the same region as this function.`;
      callback(message);
    } else {
      const originalSize = is(data.Body);

      // リサイズ処理
      im.resize({
        srcData: data.Body,
        format: 'jpeg',
        width: originalSize.width > event.w ? event.w : originalSize.width
      }, function(err, stdout, stderr) {
        if (err) {
          callback('resize failed', err);
        } else {
          console.log("resized!");
          callback(null, new Buffer(stdout, 'binary').toString('base64'))
        }
      });
    }
  });
};
```

Lambda関数は以上で作成完了です。割とシンプルかと思います。

API Gatewayの設定、CloudFrontの設定は他でもよくまとめられているので割愛します。
[同期が去年まとめてくれています。有難や](https://qiita.com/kiitan/items/4bdc530c66d6cdfbd294)

### Rails側の設定

ここからはアプリケーション側の設定です。

- cdn_hostという環境変数を用意して、CloudFrontのURLを格納します
  - 開発、ステージング、本番でURLが異なり、変更を容易にしたいという意図がありました。

```rb:config/application.rb
config.cdn_host = ENV["CDN_HOST"]
```

ActiveStorageがCDNに対応しておらず、試行錯誤した結果、ヘルパーでURLを変換するメソッドを作成しました。

```rb:app/helpers/cdn_helper.rb
# S3のURLをCloudFrontのURLに変更するためのヘルパー
module CdnHelper
  # cdn経由でs3から画像を取得するurlを生成する
  def cdn_url(service_url, width = 700)
    # 環境変数CDN_HOSTが設定されていなければS3のURLを返す
    if User::Application.config.cdn_host.nil?
      service_url
    else
      service_url.gsub(URI.parse(service_url).host, User::Application.config.cdn_host).gsub(/\?.*/, '?w=' + width.to_s)
    end
  end
end
```

ヘルパーを利用することで、比較的きれいにCDNを介して画像を呼び出すことができるようになります。

```erb
<%= image_tag(cdn_url(target.service_url) %>
```

## 結果

|もともと|導入後|
|-----|-----|
|![現状.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166596/faa9f8e9-c37e-e387-4f15-153377542138.png)|![CDN噛んだ後(キャッシュ済).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166596/dd2af4bc-8263-16d4-6bdc-07564d7e057a.png) |

少々見辛いですが、上の画像7~8枚ほどを見比べていただければと思います。
リサイズにより画像のサイズが1/10ほどになり、CDNのキャッシュ後は爆速で読み込まれていることがわかります。

画像読み込み速度の改善によって、**ユーザーの離脱を防ぐ**、**SEO的なメリット**など、もろもろの利点が見込めそうですね！
しかもこちら、画像数がもともと多くはないこともあり、**1ヶ月およそ1,000円かからないくらい**で運用しています。個人開発でも導入できそうな手軽感です！

## 終わりに

いかがだったでしょうか？
シンプルな構成で画像のリサイズの仕組みを実現することができました。
AWSのアカウントを持っているだけで実現可能なので、画像の表示速度に悩んでいる方いればぜひ検討してみてください！

## 私たちのチームで働きませんか？

![alt](https://brides.a-tm.co.jp/wp-content/uploads/2019/11/ATEAM_BRIDES.jpg)
[エイチーム](https://www.a-tm.co.jp/)は、インターネットを使った多様な技術を駆使し、幅広いビジネスの領域に挑戦し続ける**名古屋の総合IT企業**です。
そのグループ会社である**株式会社エイチームブライズ**では、一緒に働く仲間を募集しています！

- [ハナユメ サイト開発グループ](https://jobs.qiita.com/employers/brides-a-tm/development_teams/138)
- [HIMARI開発チーム](https://jobs.qiita.com/employers/brides-a-tm/development_teams/147)
- [技術開発部](https://jobs.qiita.com/employers/brides-a-tm/development_teams/148)

上記求人をご覧いただき、少しでも興味を持っていただけた方は、まずは**チャットでざっくばらんに話**をしましょう。
技術的な話だけでなく、私たちが大切にしていることや、お任せしたいお仕事についてなどを詳しくお伝えいたします！

[Qiita Jobs](https://jobs.qiita.com/)よりメッセージお待ちしております！