ReactとNginxでリロードしても404しないSPAを作る

# はじめに

[いなたつアドカレ](https://qiita.com/advent-calendar/2019/allinatatsu)の二十四日目の記事です。
Dockerを使ってReactで作ったアプリケーションをnginx上にポイ投げするためのあれですね。

DockerってなにとかnginxってなにReactってなにってのはスルーで行きます。
今回の記事はcreate-react-app(以下CRA)を使用する前提となっています。

# つかうもの
- react
- react-router
- nginx
- docker

# 完成系
react-router使ってnginx上でリンクに#がつかないかつリロードしても404にならない構成の作成

# ディレクトリ構成

- app
    - docker
        - react
            - Dockerfile
        - nginx
            - default.conf
    - web
    - docker-compose.yml

こんなかんじです

dockerディレクトリにdockerで使用するファイルを格納しています。
webディレクトリにCRAで作成したアプリが入りますね。

# DockerでReact


```docker-compose.yml
version: '3'

services:
  react_app:
    container_name: react_app
    build: ./docker/react
    command: npm start
    volumes:
      - ./web:/app
    ports:
      - 3000:3000

```
つづいて
docker/react/Dockerfile

```
FROM node

WORKDIR /app
```
ここは基本的になんでも構いません(めんどくさかった)
作成するアプリケーションに合わせてpackage.jsonなどを用意してあげてください。
今回は ~~めんどくさいので~~ これでいきます。


# Dockerで **んぎっくす**

はい、Nginxいきます

```docker-compose.yml
nginx:
    image: nginx
    container_name: nginx
    ports:
      - 8080:80
    volumes:
      - ./web/build:/var/www
      - ./docker/nginx/:/etc/nginx/conf.d/
    depends_on:
      - react_app
```
web(CRAのディレクトリ)の中のbuildをnginxコンテナにマウントしています。
`docker/nginx`には設定ファイルですね。

```default.conf
server {
    listen       80;

    location / {
        root   /var/www;
        index  index.html index.htm;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}
```

buildをマウントしてそのマウントしたファイルのindex.htmlを表示するぜ〜って感じですね。

# Reactのコンポーネント構成

```jsx:App.js
import React from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom'
import Hello from './Pages/Hello'
import Changed from './Pages/Changed'




function App() {
  return (
    <Router>
        <Route exact path='/' component={Hello} />
        <Route exact path='/changed' component={Changed} />
    </Router>

  );
}


export default App;
```

`react-router`で2つのページを遷移できるようにします。

```jsx:/Pages/Hello,js
import React  from 'react';
import {Link} from 'react-router-dom'

const Hello = () => {

    return (
        <div>
            <div>Hello</div>
            <Link to='/changed'>ぺーじせんい</Link>
        </div>
    )
}

export default Hello;
```

```jsx:/Pages/Changed,js

import React from 'react';

const Changed = () => {

    return (
        <div>
            <div>Changed</div>
        </div>
    )
}
export default Changed;
```

ページ遷移するだけですね。

# うごかしてみる
とりあえずreactのプロジェクトをbuildしましょう。

`localhost:8080`にアクセスします。

![スクリーンショット 2019-12-24 14.05.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269859/54910482-08c3-46ca-418d-5a3d8766e235.png)

こんな感じですね。

ページ遷移してみます。

![スクリーンショット 2019-12-24 14.05.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269859/2acd6b4c-8317-e1f9-bf8f-fa4e6cbc017c.png)

遷移できましたね。おっけーです。

## ちょっとリロードしたくなってきたわ

![スクリーンショット 2019-12-24 14.05.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/269859/20e1698a-67c7-77bb-d229-a9404a811dc0.png)

あっあっあっ

だめですね。`んぎっくす`さんに怒られちゃいました。

## ハッシュルーターとかダサいじゃん？

URLに「#」とかつくHashルーターをつかうことでこれを解決することはできます。

`localhost:8080/#/changed`だっっっっっっっっっさあああああい

やだよ。

# 解決しよう
nginxの設定を少し変えましょう

```default.conf
server {
    listen       80;

    location / {
        root   /var/www;
        index  index.html index.htm;
        try_files $uri /index.html;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}
```

`try_files $uri /index.html;`この一行を書くだけですね。

ほら、リロードしてみてくださいよ、怒られますか？怒られませんよね。

これで解決ですね。

# なぜ解決できたのか

とりま解決でけたらえーねんって人はみなくてよきですえ

nginxにはtry_files ディレクティブというものが存在し、これは、引数を前から順番にファイルが存在するかをtryしまくって行ってくれます。そして見つからなかった場合は`index.html`を返却しましょうねっていう感じです。

何も見つからなかったら404を返すよ〜って実装をする場合もありますね。