Next.jsとPostgresを使って郵便番号検索フォームを作成

[前回](https://qiita.com/ume-bar/items/2659102c0048b66ea10c)はデータベースに全国郵便番号データをCSV形式で入れる事ができたので、今回は実際にサーバーで郵便番号が検索できるフォームを作成します。前回の記事でPostgres(データベース)を確認してみるとencoding collate ctypeが`en_US.UTF-8`になっていて、ログ確認時にブラウザで文字化けを起こしている挙動を得ました。なので、新たにaddressというデータベースを作りエンコーディングを`ja_JP.UTF-8`に書き換えることにしました。

```
create database address template template0 ENCODING = 'UTF8' LC_COLLATE = 'ja_JP.UTF-8' LC_CTYPE = 'ja_JP.UTF-8';

```
同時にテーブルも作り直し、以下の通り作成し直しました。

```
                Table "public.address"
     Column     |       Type        | Collation | Nullable | Default 
----------------+-------------------+-----------+----------+---------
 post           | character(21)     |           | not null | 
 prefectures    | character varying |           | not null | 
 municipalities | character varying |           | not null | 
 address        | character varying |           | not null | 
```
`\COPY`でCSVファイルをデータベースに保存し、`select * from address;`で確認。

```
post          | prefectures | municipalities     | address                                    
--------------+-------------+--------------------+----------------------------------------------
 600000       | 北海道       | 札幌市中央区         | 以下に掲載がない場合
 640941       | 北海道       | 札幌市中央区         | 旭ケ丘
 600041       | 北海道       | 札幌市中央区         | 大通東
 600042       | 北海道       | 札幌市中央区         | 大通西（１～１９丁目）
 640820       | 北海道       | 札幌市中央区         | 大通西（２０～２８丁目）
 600031       | 北海道       | 札幌市中央区         | 北一条東
 600001       | 北海道       | 札幌市中央区         | 北一条西（１～１９丁目）
 640821       | 北海道       | 札幌市中央区         | 北一条西（２０～２８丁目）
:
```
結局、ブラウザ確認時の文字化けは解消されずでしたが。細かな検証は今後の課題として、進んで行きたいと思います。今回は、React(JavaScriptのライブラリ)のSSR(サイバーサイドレンダリング)を可能にするJavaScriptのフレームワークである`Next.js`を導入し実装していきたいと思います。`node.js`がインストールされていない方は[こちら](https://nodejs.org/en/)をインストールして下さい。チュートリアルに従って、ターミナルから次のコマンドを実行します。

```
npx create-next-app nextjs-blog --use-npm --example "https://github.com/vercel/next-learn-starter/tree/master/learn-starter"
```
ディレクトリである`NESTJS-BLOG`が作成され、各パッケージが入りました。
![スクリーンショット 2021-05-08 13.36.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/b559e759-60e2-87a6-8bc6-9b6354de031e.png)
続けてIDE(統合開発環境)を整えます。開発時からエラーに気づけるように設定を書く為、タッチコマンドで`tsconfig.json`を入れ、npmやtypescriptなどのパッケージをsave-devの方にインストール。一度`npm run dev`を実行します。

```
touch tsconfig.json
```

```
npm install --save-dev typescript @types/react
```

```
npm run dev
```
の準にターミナルで実行すると、以下の画面になり、
![スクリーンショット 2021-05-08 14.03.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/29390d5c-a0ef-7ff1-f7c1-b5288cf16d70.png)
`http://localhost:3000`で確認すると、無事ローカル上で挙動を確認できました。
![スクリーンショット 2021-05-08 14.12.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/cb48c1df-3808-72c0-bb44-f6885583b4f1.png)
ここまで来たら、いよいよ本題である**ページのレンダリング**(データをもとに表示内容を作る)や**ルーティング**(pagesディレクトリ内のファイルの結び付け)、**Reactフック**(クラスを作成せずに、状態やその他のReact機能を使用を可能にするもの)を使ってコードを書いていきましょう。pagesディレクトリ内にapiディレクトリを作ってaddress.tsファイルを作成し記述していきます。まずはSequelizeを使用してデータベースを呼び出します。Sequelizeとは、Node.js上でORMを提供するライブラリです。以下のコマンドでインストールしましょう。

```
npm install --save sequelize
```
構文・継承を用いた`class 子クラス extends 親クラス`でAddressとModelを定義し、データベースのテーブルに文字列で値を渡します。`init()`内に各テーブルの要素を入れます。Sequelizeでデータベースのカラムを変更せずModelを定義するために、主キーに対応する`post`(カラム)に`primaryKey:true`を追加。createdAt,updatedAtを追加しない`freezeTableName:true`と`timestamps:false`を利用してテーブルのスキーマを現状の形で使えるようにします。郵便番号データをasyncで以下のように記述。

```pages/api/address.ts
import { Sequelize, Model, DataTypes } from 'sequelize';

const sequelize = new Sequelize({
  database: 'address',
  password: null,
  dialect: 'postgres',
  port: 5432,
  host: 'localhost',
  timezone: 'Asia/Tokyo',
});


class Address extends Model {}
  Address.init({
    post: {type:DataTypes.STRING,primaryKey:true},
    prefectures: DataTypes.STRING,
    municipalities: DataTypes.STRING,
    address: DataTypes.STRING
  }, { sequelize, modelName: 'address',freezeTableName:true,timestamps:false });

export default async(req, res) => {
  await sequelize.sync();
  console.log(await Address.count());
  const address = await Address.findOne({
    where: {
     post: req.query['post'],
    }
  });
  console.log(address)
  res.status(200).send(JSON.stringify(address))
}
```
pagesディレクトリ内にある`index.jsファイル`を`index.tsx`に書き換えてコードを以下のように記述します。

```pages/index.tsx
import Head from 'next/head'
import React, { Props } from "react";

const Home = (props: Props) => {
  const [value, setValue] = React.useState("");
  const [prefectures, setPrefectures] = React.useState("");
  const [municipalities, setMunicipalities] = React.useState("");
  const [address, setAddress] = React.useState("");
  
  React.useEffect(()=>{
    fetch('/api/address?post='+value)
    .then(response => response.json())
    .then(data => {
      if(data !== null){ 
        setPrefectures(data.prefectures)
        setMunicipalities(data.municipalities)
        setAddress(data.address)}});
  },[value])
  
  return (
    <div>
      <h3>郵便番号を入力して下さい</h3>
      <label style={{ display: "block" }}> 
        <input type='text'
          placeholder="半角数字で入力"
          maxLength={8}
          size={14}
          value={value}
          onChange={event => setValue(event.target.value.replace("-",""))} />
      </label>
      {prefectures!=""&& <h3>住所</h3>}
      {prefectures!=""&& 
      
      <div><label style={{ display: "block" }}> 
          <input type='text'
          value={prefectures}
          onChange={event => setValue(event.target.value)} /><br/>
       </label>
          <label style={{ display: "block" }}> 
          <input type='text'
          value={municipalities}
          onChange={event => setValue(event.target.value)} /><br/>
       </label>
       <label style={{ display: "block" }}> 
          <input type='text'
          value={address}
          onChange={event => setValue(event.target.value)} /><br/>
          </label>
      </div>}
    </div>
  )
}

export default Home;
```
ターミナルで実行してみます。

```
nmp run dev
```
![スクリーンショット 2021-05-08 17.48.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/848bcba6-4986-d595-b0bf-83792dcc8f46.png)
`http://localhost:3000/`にて挙動を検証。
![スクリーンショット 2021-05-08 17.53.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/c6561c63-b3ae-a795-ec35-a92a49fea669.png)
`郵便番号を入力してみます！`
![スクリーンショット 2021-05-08 17.53.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1134006/54b20d0c-ff33-721f-2b72-a063b8251fd5.png)
`なんとか、それらしきものが出来上がりました。`