こんにちは、Zero-Cheeseです。
本記事では、
- フロントエンドにReact(TypeScript)
- バックエンドにFlask
を使用する「SPA WEBアプリ」の実装方法を、ご紹介します。
Reactは、リリース前にビルドするのが、通常です。
しかし、 「ビルド済React(TypeScript) + Flask」 の情報がネット上に少ないため、その実装方法について、今回取り上げました。
本記事は、
- React(TypeScript)
- Flask
初心者以上の方を、対象にしています。
本記事で作成する、Webアプリ
本記事で作成するWebアプリの「フロントエンド」側は、簡潔化のために、
- 「npx create-react-app (アプリ名)」コマンド(TypeScriptの場合は、npx create-react-app –template typescript (アプリ名))で作成した、初期状態を使用します。
すなわち、下記画面が、表示される状態です。
本記事で紹介する、フォルダ構成
本記事で紹介する、フォルダ構成は以下の通りです。
- Flaskアプリは、直下のapp.pyのみ
- React関連は全て、「frontend」フォルダに格納
Flaskから、呼出す方法
全体手順は、以下の通りです。
- React(TypeScript)をビルドする
- Flaskから呼出す
① React(TypeScript)をビルドする
Reactが入っているフォルダ(本記事の場合、「frontend」フォルダ)に移動し、以下のコマンドを実行し、「ビルド」します。
npm run build
「frontend」下に、「build」フォルダが作成され、その配下に必要ファイルが作成されます。
Flaskのファイル構成から考えると、画像ファイル類は「static」フォルダ下に、配置するのが望ましいです。
そのため、以下のようにファイルを移動します。
また、それに合わせて、index.htmlファイルを、以下のように変更して下さい。
下記コードの、6, 10, 11行目の、href属性値の先頭に
- /static/
を追記します。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="/static/favicon.ico" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="Web site created using create-react-app" />
<link rel="apple-touch-icon" href="/static/logo192.png" />
<link rel="manifest" href="/static/manifest.json" />
<title>React App</title>
<script defer="defer" src="/static/js/main.c8248ffc.js"></script>
<link href="/static/css/main.073c9b0a.css" rel="stylesheet">
</head>
<body><noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
この手順は、ビルドするたびに、行う必要があります。
それが手間だと感じる場合は、「public」フォルダ下のindex.htmlを、変更する事で対応できます。
そのファイルが、ビルドのベースとなるため、毎回の変更が不要になります。
② Flaskから呼出す
Flaskに、HTMLファイルや画像ファイルの保存場所を、さきほど作成した「build」フォルダだと、教える必要があります。
下記コードを、ご参照ください。(4, 5行目の、template_folder、static_folder引数で、フォルダ場所を、教えています。)
from flask import Flask
from flask import render_template
app = Flask(__name__, template_folder='./frontend/build',
static_folder='./frontend/build/static')
@app.route('/')
def home():
return render_template('index.html'), 200
if __name__ == '__main__':
app.run(debug=True)
Flaskを実行してみると、Reactで作成した画面が、表示されます。
ただし、React上でルーティングを設定している場合は、Flaskのコードを以下のように変更します。
これにより、全てのエンドポイントが、home( )関数に紐づけられます。
from flask import Flask
from flask import render_template
app = Flask(__name__, template_folder='./frontend/build',
static_folder='./frontend/build/static')
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def home(path):
return render_template('index.html'), 200
if __name__ == '__main__':
app.run(debug=True)
ビルド前のReact(TypeScript)と、連携したい場合
リリースする前に、ローカル上で動作確認する場合は、ReactもFlaskもlocalhost上で実行し、挙動を確認することが一般的です。
しかし、その際にReactのWebページから、FlaskのURLを叩くと、CORSエラーが発生します。
CORS(Cross-Origin Resource Sharing)は、異なるオリジン間でのリソース共有を可能にする仕組みです。
ReactとFlaskをlocalhostで実行すると、ポート番号が異なるため、別のオリジンとみなされます。
その結果、リソースの共有が、セキュリティ観点から制限されます。
このCORSエラーを回避するためには以下の手順を行います。
ただしセキュリティ上、リリース前に、下記コードは無効にするようにしてください。
まず、pipを使用してCORS関連のパッケージをインストールします。
pip install flask-cors
その上で、Flaskのコードを、以下のように変更します。(3, 7行目を、追加しています。)
from flask import Flask
from flask import render_template
from flask_cors import CORS
app = Flask(__name__, template_folder='./frontend/build',
static_folder='./frontend/build/static')
CORS(app)
@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def home(path):
return render_template('index.html'), 200
if __name__ == '__main__':
app.run(debug=True)
最後に
本記事では、理解しやすさを優先して、簡単な構成で説明しました。
プロジェクトが大規模になると、もう少し複雑なフォルダ構成とビルドプロセスが、必要になります。
たとえば、ビルドされたReactアプリケーションを別のサーバーにデプロイする場合、フロントエンドとバックエンドのコードは、異なるリポジトリで管理することが一般的です。
そして、フロントエンドとバックエンドは異なるCI/CDパイプラインを通じて、デプロイする事になるかと思います。
しかし、小規模なプロジェクトやプロトタイプを作成する際や、ReactとFlaskを学ぶ初期段階では、同一のリポジトリ内で両方を管理し、一緒に実行する方が、簡単で便利です。
FlaskとReactの統合には多くの方法があります。
本記事で紹介した方法は一つの例としてご理解ください。
Flaskを使用する場合、標準では「jinja」を用いますが、Reactを採用することで、プログラミングを活用したHTMLの生成が可能となり、その魅力は大いにあります。
少し前(執筆時:2023年7月)に、「Qiita記事を分析して、オススメのUdemy講座をランキング講座で紹介するサイト」を作りました。
このサイトの分析結果を見ていると、React系の講座が常にランキング上位にあり、一般的にも、その魅力が認知されていることを実感しました。
本記事を最後までお付合い頂き、ありがとうございました。
それではまた、お会いしましょう!