ReactとDjangoの連携したアプリを作ろう（Typescript & TailwindCSS & python）

# 前書き
ReactとDjangoを連携したアプリを作成する手順です。

フロントエンドではTypescript、Tailwind CSSを使い、バックエンドではpythonを使用する。

本文は、使った技術の設定を中心に記載している。


# 1. Django（バックエンド）の基本設定
本文は https://qiita.com/shiranon/items/8182445975dff4cf19a6 （DjangoとReactの構築及びAPI連携について～構築編～） を一部参考した。


## Djangoをインストールする
`$ pip3 install django`

## rest_frameworkインストール
`$ pip3 install djangorestframework`

## Djangoのプロジェクトを作成する
`$ django-admin startproject <projectName>`
本文ではプロジェクトの名前をbackendとする。なので、`$ django-admin startproject backend` を実行する。

### デフォルトのファイル
コマンドを実行すると、プロジェクトの名前でディレクトリが作成される。
その中身は以下の通り。（△は編集する必要があるもの）
```
projectName
├─ manage.py        【プロジェクト管理、App作成、データ管理など】
└─ projectName      
	├─ __init__.py
	├─ settings.py  【△プロジェクトの構成】
	├─ urls.py      【△URLを関数に対応】
	├─ asgi.py      【リクエスト処理】
	└─ wsgi.py      【リクエスト処理】

```

## Appを作成する
`$ python manage.py startapp <AppName>`
本文ではAppの名前をAppとする。
その他に、Api作成用のAppも作成する。
なので、
```bash
$ python manage.py startapp app
$ python manage.py startapp api
```
 を実行する。

### デフォルトのファイル
コマンドを実行すると、Appの名前でディレクトリが作成される。
その中身は以下の通り。
```
App
├─ __init__.py
├─ admin.py          【デフォルトのadmin管理者】
├─ apps.py           【app起動】
├─ migrations        【DB変更記録】
|	└─ __init__.py
├─ models.py         【△DBに対する操作】
├─ tests.py          【Unitテスト】
└─ views.py          【△関数】

```


### APPを登録する
Appを作成したら、プロジェクトのsettings.pyに作成したAppを登録する必要がある。
ここで、`rest_framework`も登録する。

settings.py
```python
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app', #追加
    'api.apps.ApiConfig', #追加
	'rest_framework', #追加
]
```

## URLとview関数との関係の設定
ブラウザからURLでウェブページを表示するには、URLとview関数との関係を設定する必要がある。

Appディレクトリでurls.pyを作成する。
```python
from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
]
```

プロジェクトのurls.pyを編集する。
```python
from django.urls import include, path # includeを追加

urlpatterns = [
    path('admin/', admin.site.urls), ＃デフォルトの管理者画面
    path('app/', include('app.urls')),
]

```


Appのviews.pyを編集する。
```python
from django.shortcuts import render, HttpResponse
def index(request):
    return HttpResponse("Hello, world")
```
指定したurlにアクセスすると、viewsでの関数が呼び出されるという仕組みである。

ここでは`localhost:8000/index/`にアクセスすると、`Hello, world`と表示されるようになる。

## サーバー起動
manage.pyがあるディレクトリで、以下のコマンドを実行する。

`$ python manage.py runserver 8000`

デフォルトのポート番号は8000番なので、ポート番号を指定せずに`$ python manage.py runserver`でも起動できるが。

ボート番号を指定したい場合は、`$ python manage.py runserver <port番号>`のようにする。

**これでDjangoの基本的な設定は完了した。**

# 2. APIを作成する
## データベースを準備する
具体的なDjangoでデータベースを操る方法はこちら：https://qiita.com/shintake_heito/private/6362c008004d97d3eeda

今回はAPI化したDBを準備する。

Appのmodels.pyを編集する。
```python
from django.db import models
# Create your models here.

class User_Model(models.Model):

    class Meta:
        db_table = 'User'

    user_id = models.CharField(
        verbose_name='userid',
        blank=True,
        null=True,
        max_length=225,
        default='',
    )

    user_name = models.CharField(
        verbose_name='username',
        blank=True,
        null=True,
        max_length=225,
        default='',
    )

    user_email = models.CharField(
        verbose_name='useremail',
        blank=True,
        null=True,
        max_length=225,
        default='',
    )

    def __str__(self):
        return self.user_id
```
ここで、user_id、user_name、user_emailの3つの項目を作成した。

`manage.py`でmigrationを行う
```bash
$ python manage.py makemigrations
$ python manage.py migrate
```

管理画面からデータ投入を行うのでadmin.pyに記載する。
```python
from django.contrib import admin
from .models import User_Model

admin.site.register(User_Model)
```

管理サイトに入るためのスーパーユーザ作成する。
```bash
python manage.py createsuperuser
```
スーパーユーザを作成したら、`localhost:8000/admin/`にアクセスし、ログインする。

![django_admin_page_login.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3578789/0b232d2f-bcad-16c1-2f73-bf2fa5396fac.png)

データを投入する。
![django_input_data.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3578789/14583a88-a986-4474-bdb4-0e877b4974b2.png)



## DBシリアライズ化の設定
次にDBのシリアライズ化を行う。
apiアプリフォルダにserializers.pyを作成し、以下のように記載する。
```python
from rest_framework import serializers
from app.models import User_Model


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User_Model
        fields = ('user_id', 'user_name','user_email')
```

apiアプリフォルダのviews.pyを以下のように記載する。
```python
from rest_framework import viewsets, routers
from app.models import User_Model
from .serializers import UserSerializer

class UserApi(viewsets.ModelViewSet):
    queryset = User_Model.objects.all()
    serializer_class = UserSerializer

    def get_queryset(self):
        queryset = User_Model.objects.all()
        L_id = self.request.query_params.get('id')
		# queryでidを受けるとidでDBに検索をかけるようにしている。

        if L_id:
            queryset = queryset.filter(user_id=L_id)
        return queryset

router = routers.DefaultRouter()
router.register(r'user', UserApi)
```

## ルーティングの設定
プロジェクトのurls.pyを以下のように記載する。
```python
from django.contrib import admin
from django.urls import include, path
from api import views as api_views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('app/', include('app.urls')),
    path('api/', include(api_views.router.urls)), #追加
]
```	

これで、APIの準備は完了した。

`runserver`し、`http://localhost:8000/<apiアプリ>`にアクセスすると、DBのデータが表示される。

# 3. React（フロントエンド）の設定
Reactのプロジェクトを作成し、TailwindCSSを使用する。

## Reactのプロジェクトを作成する
`$ npm create-react-app <projectName>`
本文ではプロジェクトの名前をfrontendとする。なので、`$ npm create-react-app frontend` を実行する。


## TypescriptベースのReactプロジェクトを作成
Typescriptを使用したReactプロジェクトを作成するには

`create-react-app <projectName> --template typescript` コマンドを実行する

ここで、`--template typescript` でtypescriptのテンプレートを指定する

## TailwindCSSの使用
`create-react-app`ではPostCSSの設定、postcss-importなどの重要なツールの設定に対応しないため、 Vite, Parcel, Next.js, or Remix でプロジェクトを作成するのをTailwindCSS公式では推奨している。（具体的なやり方はTailwindCSSドキュメントを参照、例：https://tailwindcss.com/docs/guides/vite）

エラー出る可能性あるので、「build時のエラー対応」の節に参照してください。

しかし、`create-react-app`で作成したプロジェクトでもTailwindCSSを使用することは可能である。
インストール手順は以下の通り。
1
```
$ npx create-react-app my-project
$ cd my-project
```
2
```
npm install -D tailwindcss
npx tailwindcss init
```

3 
tailwind.config.jsを設定する
```javascript
/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    "./src/**/*.{js,jsx,ts,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
```

4
index.cssを作成する
`./src/index.css`に以下のように記載する。
```css
@tailwind base;
@tailwind components;
@tailwind utilities;
```
これでTailwindCSSの設定は完了した。

# 4. ReactとDjangoとの連携
## Django側でindex.html作成
Django側のアプリのディレクトリにtemplatesフォルダを作成し、その中にindex.htmlを作成する。
```html
<!DOCTYPE html>

{% load static %}

<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Reactアプリ</title>
</head>
<body>
    <div id="root"></div>
    <script type="text/javascript" src="{% static 'js/bundle.js' %}" ></script>
</body>
</html>
```
ここで、Bodyの内容はstatic/js/bundle.jsから読み込むようにしている。
bundle.jsはReactのビルドファイルで、React側でbuildを行うと作成される（React側での設定が必要）。

## React側で必要なモジュールをインストールする

JavaScriptの場合には、babel-loaderを使用するが、TypeScriptの場合には、ts-loaderを使用する。

JavaScript： `$ npm install --save-dev babel-loader`

TypeScript： `$ npm install --save-dev ts-loader`

## React側でwebpack.config.jsを作成
```javascript
const path = require('path');

module.exports = {
    mode: 'development',
    entry: './src/index.js',　//buildするファイル
    output: {
        filename: 'bundle.js',　//build後のファイル名
        path: path.join(__dirname, '../django_react/react_app/static/js') //buildファイルが作成される場所
    },
    module: {
        rules: [
		//JavaScriptの場合のloader設定
			{
				test: /\.js[x]?$/,  
				exclude: /node_modules/,
				use: {
					loader: 'babel-loader',
					options: {
						presets: [
							'@babel/preset-env',
							'@babel/preset-react' 
						],
						plugins: ['@babel/plugin-syntax-jsx'] 
					}
				}
			}
        
		//Typescriptの場合のloader設定
			{
				test: /\.ts[x]?$/,  
				exclude: /node_modules/,
				use: 'ts-loader',
			},

		//TailwindCSSファイルを読み込むための設定
			{
				test: /\.css$/,
				use: [
					'style-loader',
					'css-loader',
					{
						loader: 'postcss-loader',
						options: {
							postcssOptions: {
								ident: 'postcss',
								plugins: [
									require('tailwindcss'),
									require('autoprefixer'),
								],
							},
						},
					},
				],
			},
		],	
    },

    resolve: {
        extensions: ['.js', '.jsx', '.json']
	}
};
```


## .envファイルを作成
パッケージ間の依存関係のチェックをスキップするようにするのである。
.envファイルを作成して下記のように記載する。
```
SKIP_PREFLIGHT_CHECK=true
```

## build
buildしてみる。

`$ npx webpack` でnpm scriptによらず、直接webpackコマンドを実行する。（npm 5.2+ ）

## build時のエラー対応
### エラー内容：TypeScript emitted no output for…
```
Module build failed (from ./node_modules/ts-loader/index.js):
Error: TypeScript emitted no output for...
```
**対応法**
`tsconfig.json`ファイルをチェック
1.	`noEmit` がtrueの場合、 TypeScriptはファイルを生成しない。
	```json
	{
		"compilerOptions": {
			// ...
			"noEmit": false,
			// ...
		}
	}
	```
2. `sourceMap`の生成を禁止する
	```json
	{
		"compilerOptions": {
			// ...
			"sourceMap": false,
			// ...
		}
	}
	```
### TailwindCSSのエラー
```
ERROR in ./src/index.css 1:0
Module parse failed: Unexpected character '@' (1:0)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
> @tailwind base;
| @tailwind components;
| @tailwind utilities;
```
**対応手順**
1. 必要とされるloadersとPostCSSをインストールする
   1. npm: `$ npm install style-loader css-loader postcss-loader tailwindcss postcss autoprefixer --save-dev`
   2. yarn: `$ yarn add style-loader css-loader postcss-loader tailwindcss postcss autoprefixer --dev`

2. webpackを設定
	webpack.config.jsに以下を追加する。
   ```javascript
    module: {
        rules: [
		//TailwindCSSファイルを読み込むための設定
		{
			test: /\.css$/,
			use: [
				'style-loader',
				'css-loader',
				{
					loader: 'postcss-loader',
					options: {
						postcssOptions: {
							ident: 'postcss',
							plugins: [
								require('tailwindcss'),
								require('autoprefixer'),
							],
						},
					},
				},
			],
		},
		],	
	},
   ```
3. PostCSSファイルを設定する
   1. postcss.config.jsがない場合は、Reactプロジェクトのディレクトリで作成する。
   2. TailwindCSSとAutoprefixer設定を以下のように記入する
   ```javascript
   module.exports = {
  		plugins: {
			tailwindcss: {},
			autoprefixer: {},
  		},
	}
   ```

# 5. ReactでAPIを叩く
## Axiosをインストール
ReactでAPIを叩くためのライブラリをインストールする。
`$ npm install axios`

## エラー対応
下記のエラーが出る可能性がある。

```
npm ERR! code ERESOLVE
npm ERR! ERESOLVE could not resolve
npm ERR! 
npm ERR! While resolving: react-scripts@5.0.1
npm ERR! Found: typescript@5.2.2
```
対応法として、package.jsonのdependenciesに記載されているバージョンを変更する。
package.jsonに下記のように追加して記載する
```json
"overrides": {
  "typescript": "^5.2.2",
},
```
## APIを叩く
Reactのコンポーネントの中でaxiosを使用してAPIを叩く。
```javascript
import React, {useState, useEffect} from 'react';
import axios from 'axios';


const Content = () => {
    interface User {
        user_id: string;
    }

    const [userInfo, setUserInfo] = useState<User[]>([]);

    useEffect(() => {
        axios.get('http://localhost:8000/api/user/')
            .then(res => {
                setUserInfo(res.data);
            });
    }, []);

    return (
        <div>
            <div>
                {userInfo.map(user => <div key={user.user_id}>{user.user_id}</div>)}
            </div>
        </div>
    );
}

export default Content;
```
ここで、axiosでAPIにアクセスし、DBのデータを取得し、そのデータを画面に表示している。

## 以上でReactとDjangoの連携は完了した。