郵便番号から住所を自動入力する時に極力ライブラリに振り回されない実装をしたい時の話

ラノベみたいなタイトルになっちゃいましたが許してください。

# 郵便番号から住所を自動入力させたい
そんな事よくありますよね。世の中には便利なAPIがたくさんありますが、有料だったりAPIの制限があったり、Formの形式にしか対応していなかったりで、実際のサービスに組み込む時に制限を受けてしまう事が多々あります。既に作ったformに良い感じに当てはめる方法を共有します。

## 前提
厳密なデータ管理は前提としません。もし厳密にデータを管理する必要があるのであれば、公開されている情報を取得し、自前でDBを用意して更新なども自分でやる必要があると思います。
郵便番号に対して紐づけられる住所は1:1ではないので、そこも考慮してのデータ設計が必要です。
今回想定している用途としては、ディレクターから「郵便番号を入力したらそれっぽい住所が補完される機能って欲しいよねー」って言われた時ぐらいのフランクなやつです。

## 使うもの
- https://github.com/yubinbango

以上です。

## 使い方

とにかくサンプルコードをクレ！って人のためにフロントっぽい感じのものをこちらを置いておきます。
`zip.js`
```javascript
const createTag = (src) => {
  const script = document.createElement('script');
  script.src = src;
  document.body.appendChild(script);
};

const requestZip = (preZip, sufZip) => {
  const src = `https://yubinbango.github.io/yubinbango-data/data/${preZip}.js`;
  return new Promise((resolve) => {
    window['$yubin'] = callback.bind(this, `${preZip}${sufZip}`, resolve);
    createTag(src);
  });
};

const callback = (zipCode, resolve, res) => {
  const zipData = res[zipCode];
  let responseData = {
    prefecturesId: '',
    cityName: '',
    address: '',
    building: '',
  };
  if (zipData) {
    responseData.prefecturesId = zipData.shift() || '';
    responseData.cityName = zipData.shift() || '';
    responseData.address = zipData.shift() || '';
    responseData.building = zipData.shift() || '';
  }
  window['$yubin'] = () => {};
  resolve(responseData);
};

export default requestZip;
```

`index.html`
```html
<form>zip: <input id="input" type="text" /></form>
<pre id="target"></pre>
<script src="index.js"></script>
```

`index.js`
```javascript
import requestZip from './zip.js';

document.getElementById('input').addEventListener('keyup', (ev) => {
  const v = ev.target.value;
  if (/^\d{7}$/.test(v)) {
    const p = v.slice(0, 3);
    const s = v.slice(-4);
    requestZip(p, s).then((address) => {
      document.getElementById('target').innerText = JSON.stringify(address);
    });
  }
});
```

## 何が起きているの？
今回使っているのは、formに良い感じのclassを付与すると良い感じに自動入力をしてくれる[YubinBango](https://yubinbango.github.io/)の中身を使っています。かつてajaxzip3にお世話になった人もいるかと思います。

YubinBangoの内部コードは公開されており、[このあたり](https://github.com/yubinbango/yubinbango#%E8%83%8C%E6%99%AF)に書いてあるように、3つのリポジトリから成立しています。実際にライブラリで動いているCoreの部分はCoreの[yubinbango-core.ts](https://github.com/yubinbango/yubinbango-core/blob/gh-pages/yubinbango-core.ts)にほぼ書いてあります。
ここを見ると `https://yubinbango.github.io/yubinbango-data/data` に対してjsonpリクエストを実行してjsファイルを呼び出し、中のデータを取得している様がよくわかると思います。データ実態はdataリポジトリにある状態です。
実際に[dataのリポジトリ](https://github.com/yubinbango/yubinbango-data)を見てみると、dataフォルダの中に大量のjsファイルが入っているのを見る事が出来ます。

試しに適当に1ファイルを開いてみるとこんな構成になっています。

```javascript
$yubin({
    "0010000":[1,"札幌市北区",""],
    "0010010":[1,"札幌市北区","北十条西"],
    "0010011":[1,"札幌市北区","北十一条西"],
    "0010012":[1,"札幌市北区","北十二条西"],
    "0010013":[1,"札幌市北区","北十三条西"],
    "0010014":[1,"札幌市北区","北十四条西"],
    "0010015":[1,"札幌市北区","北十五条西"],
    "0010016":[1,"札幌市北区","北十六条西"],
    ...
});
```
> https://yubinbango.github.io/yubinbango-data/data/001.js より一部抜粋

fileの先頭に `$yubin` があり、これがjsonpの実行関数になり、引数にObjectが入っている状態です。
つまり、該当のファイルをscriptタグで呼び出し、 `$yubin` を実行すればObjectが引数に入ってくるので、欲しいデータを特定する事が出来ます。

## サーバーサイドでは？
サーバー側でAPIをwrapしてサービスに利用したい人もいるでしょう。
その場合は先頭の `$yubin` と末尾の閉じカッコを削除して、残ったjsonをObjectとして読み込んであげれば良い感じになります。 nodejsだと芸が無いのでPHPのサンプルコードを置いておきます。

```php
<?php
function getZipObj($pre, $suf) {
    $result = [
        'prefecturesId' => null,
        'cityName' => null,
        'address' => null,
        'building' => null
    ];
    $data = file_get_contents("https://yubinbango.github.io/yubinbango-data/data/{$pre}.js");
    // ここでゴリっと前後を切り落とす
    $data = str_replace('$yubin(', '', str_replace(');', '', $data));
    $zip = json_decode($data, true);
    if($zip[$pre . $suf]) {
        $zipData = $zip[$pre . $suf];
        $result['prefecturesId'] = array_shift($zipData);
        $result['cityName'] = array_shift($zipData);
        $result['address'] = array_shift($zipData);
        $result['building'] = array_shift($zipData);
    }
    return $result;
}
```

実際にはzip用の型とかを作った方がきれいな実装に出来るかなと思います。

## まとめ
素敵なライブラリを提供してくれる開発者に感謝！！
個人的にはベストな解決法かなと思っているのですが、意外と知り合いのエンジニアにも浸透していなかったので記事にまとめました。
(あと個人的な忘備録)