Shifterの強みとハマりどころ

## Shifterとは

Shifterとは株式会社デジタルキューブが提供するWordPressを静的に活用できるSaaSサービスです。

https://ja.getshifter.io/

「Shifter Static」と「Shifter Headless」がありますが、私は「Shifter Static」の方を利用しました。

「Shifter Static」を平たく言うとWordPressはShifterの管理サービス上に閉じ込めてしまって、実際の一般エンドユーザはこのサービスから静的ファイルとして生成されS3に設置されたファイルをCloudFront経由で閲覧させるといったものです。

「Shifter Headless」の詳細は詳しくわかりませんが、microCMSなどを使ったときの構成と似たイマドキっぽいJamstackなサイトが作れそうですね。

:::note
デジ庁が色々とやってみた結果、脱Jamstackしたこと等とても共感が持てますし、[WordPressを扱うエンジニアに対する質問事項](https://qiita.com/koji-yamauchi/items/31edcfe443285b6d7194)でも少し触れた通りCMSからのプレビュー確認をどう実装するか等色々と悩ましい部分があるため、Headless CMSが常に良い選択とは限らないと考えています。（もちろん良い選択のときもあるでしょう。）
:::

## メリット

一般的なメリットについては、前述のサービスサイトに記載のとおりです。
公式の案内と一部重複しますが、個人的に良いと思う点は下記のようなものです。

* S3+CloudFrontなので負荷やセキュリティの耐性が強そう
* 生成ごとのバージョンが管理されるので、問題が生じたときに旧バージョンへ容易に戻せる
* 新バージョン切り替え前にプレビュー用URLが発行できる（更新から承認のフローなどを確立しやすい）
* クエリーパラメータを受け付けるような実装はできないため、PrettyなURL設計を強制される

## デメリット

* 記事数が莫大な場合はビルドに時間がかかりすぎるので不向き
* WordPress管理画面の立ち上げに長い時間待たされることがある
* UserAgent判定での出し分け、タイマーでの表示制御などサーバーサイド側の処理が動作しない
* 問い合わせフォームなどが必要な場合は外部サービスと連携する必要あり
* 技術的な制約やハマりどころがあるが、解決方法を探しにくい
* 既存サイトのデータをrsyncなどで移すすべがないので、インポート・エクスポートはAll-in-One WP Migrationなどでブラウザ経由で出来るデータ量に限る
* トラブルシューティングで、従来のサイトはSSHでログインしてWP CLIやSQLを用いて調査できるがそのようなアプローチがとれない
* クエリーパラメータを受け付けるような実装はできないため、PrettyなURL設計を強制される

オウンドメディアサイトなど記事が数万点などあるような巨大なサイトとは相性が悪いです。一般的な小規模コーポレートサイトなどの場合はデメリットを超えてるメリットがあるかもしれません。

## ハマりどころ

デメリットで記載したとおり、技術的な制約やハマりどころがありますが、解決策に辿り着くのが難しいです。
一方でチャットのサポートはあるので、問い合わせるとその内容が記載しているURLを教えてくれたりもします。

### 特定のURLが生成対象とならずNot Foundとなる

Shifter（というよりWordPress自体の話だと思いますが）がサイト全体のURLを適切に認識できず、生成対象から漏れてしまうことがあります。
エンドユーザが訪問する本番サイトはS3+CloudFrontなので、生成対象とならなければもちろんNot Foundの扱いとなります。

きっと何らかのフックが用意されているだろうなとは思いましたが、うまく見つけられずサポートに問い合わせて教えてもらいました。

https://support.getshifter.io/en/articles/4233156-appending-urls-to-the-artifacts

上記ページにあるように`ShifterURLS::AppendURLtoAll`を用いて任意のURLを追加できます。

```php
function my_append_urls( $urls ) {
    $urls[] = home_url('/wp-json/wp/v2/posts/');
    return $urls;
}
add_action( 'init', function(){
    add_filter( 'ShifterURLS::AppendURLtoAll', 'my_append_urls' );
} );
```

この問題は特にアーカイブページのページネーションで起きることが多く、この場合は下記のように対応しました。

```php
function my_append_urls( $urls ) {
  $limit = 15;
  $args = array(
    'post_type' => 'post',
    'post_status' => 'publish',
    'posts_per_page' => -1,
    'fields' => 'ids',
    'no_found_rows' => true,
  );
  $the_query = new WP_Query($args); 
  if ($the_query->have_posts()) {
    $last_page = ceil(count($the_query->posts) / $limit);
    for ($i = 2; $i <= $last_page; $i++) {
      $urls[] = home_url('/news/page/' . $i . '/');
    }
  }
  return $urls;
}
add_action( 'init', function(){
  add_filter( 'ShifterURLS::AppendURLtoAll', 'my_append_urls' );
} );
```

### Zipファイルは上げられない

https://support.getshifter.io/en/articles/4901894-allowed-filetypes-to-upload

Shifterは仕様上、テーマファイル内であれ、メディア内であれ、ZIPファイルはデプロイの対象外となるようです。
対象のファイルはShifter外の別サイトやサービスに設置してそちらにリンクするか、ZIPにはせずリンクに`download`属性を付加させて任意のファイル名でダウンロードさせるなどの対応が考えられます。

## 検証方法

### wget

Shifter Staticを用いた場合、静的ファイルをビルドしてからはじめてリンク切れに気がつくことがあります。
ローカルでの開発時、テーマファイルをShifterにデプロイ後してからWordPress上での確認は問題なくてもビルドするとリンク切れになるケースがあるため、確認が漏れてしまうケースがあります。

表示されているコンテンツが適切なものかは、目視でみる必要がありますが、機械的にざっとリンク切れが起きていないか併せて確認すると良いでしょう。
この場合は、`wget`コマンドを用いると便利です。

```bash
wget --spider -o wget.log -e robots=off --wait 1 -r -p https://example.com/
```

### Shifter (Static) - Local

他にも「Shifter (Static) - Local」の[Debugging](https://github.com/digitalcube/shifter-static-local?tab=readme-ov-file#debugging)の機能（`?url=`パラメータ）を用いるとShifter上でビルドしなくてもローカル環境にて事前に生成URLの確認が可能です。

先日、「WordPress標準制作フロー2024」で[ローカル開発環境](https://qiita.com/koji-yamauchi/items/6a72ca8c2550f3433ce4#%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E9%96%8B%E7%99%BA%E7%92%B0%E5%A2%83)について記載しましたが、Shifter特有のこれらのトラブルシューティングにはshifter-static-localは便利です。

## Shifterを使う場合も使わない場合も

メリットとデメリット両方に「クエリーパラメータを受け付けるような実装はできないので、PrettyなURL設計を強制される」と書きましたが、Shifterでの安定した動作を目指すには、WordPressのお作法に従った適切な開発を行う必要があります。

PHP_CodeSnifferを用いてWordPressのコーディングルールを守れているか確認したり、ハンドブックなどの公式情報から理解を深めると良いでしょう。

https://ja.wordpress.org/team/handbook/