Django

ReactとDjangoを連携する方法|REST API・CORS・JWT認証の実装手順

ReactとDjangoの連携は、Djangoを「JSONを返すAPIサーバー」、Reactを「そのJSONを表示するSPA」として役割分担させる構成が基本です。この形にすると詰まる箇所はほぼ決まっていて、Django REST Framework(DRF)でのエンドポイント作成、CORSの許可設定、axiosでのリクエスト、そしてログインを伴うアプリでのJWT認証の4点に集約されます。本記事は、この4点を最短で動かすための設定とコードを、実際のプロジェクト構成に沿って解説します。

まとめ:ReactとDjango連携の要点

新規開発なら、DjangoはDRFでAPI専用にし、ReactはViteで作った独立したSPAにする「フロントエンド分離」構成を選びます。連携の実装は次の順に進めれば動きます。

  • API:DRFのModelViewSetDefaultRouterでCRUDエンドポイントを自動生成する。
  • CORSdjango-cors-headersで、Reactの開発サーバー(localhost:5173)だけを許可する。全許可は本番で使わない。
  • リクエスト:ReactはaxiosGET/POSTを送り、useEffectuseStateでレスポンスを画面に反映する。
  • 認証:ログインが要るならSimpleJWTでトークンを発行し、ReactはAuthorizationヘッダーに付与する。

以降で、構成の選び方から各設定の具体的なコードまで順に見ていきます。

ReactとDjangoの構成パターンと選び方

「組み合わせる価値があるか」は構成次第で答えが変わります。ReactとDjangoの組み合わせ方には大きく2通りあり、開発の前提が違います。ここを最初に決めないと、後述のCORS設定が要る/要らないも変わってきます。

フロントエンド分離(React+DRF)を基本にすべきケース

ReactをViteで作った独立アプリにし、DjangoはDRFでJSON APIだけを返す構成です。フロント(例:localhost:5173)とバック(例:localhost:8000)でオリジンが分かれるためCORS設定が必須になりますが、画面遷移が多いSPA・スマホアプリとのAPI共用・フロントとバックを別チームで開発する場合はこの構成が向きます。新規開発では原則こちらを選んで問題ありません。本記事もこの構成を前提に進めます。

Djangoテンプレートに部分的にReactを埋め込む構成が向くケース

既存のDjangoアプリ(テンプレートでHTMLを返している)の一部だけをReact化する場合は、Djangoが返すHTMLの中にReactのビルド済みJSを読み込む形にします。同一オリジンで完結するためCORSは不要ですが、ルーティングや状態管理はページ単位に限定されます。「Django資産があり、特定画面だけリッチにしたい」ときの現実解で、フルSPA化の前段としても使えます。

開発環境の構築(Django+React)

バックエンドとフロントエンドを別ディレクトリで立ち上げます。Djangoは仮想環境、Reactは非推奨化されたcreate-react-appではなくViteで作成します。

Djangoプロジェクトと必要ライブラリのセットアップ

仮想環境を作り、連携に使う4つのライブラリ(DRF・CORS・JWT)をまとめて入れます。

python -m venv venv
source venv/bin/activate
pip install django djangorestframework django-cors-headers djangorestframework-simplejwt
django-admin startproject config .
python manage.py startapp api

Django本体は5.2 LTS(2028年4月までサポート)を基準にすれば長く使えます。startproject config . の末尾のドットで、余分な入れ子ディレクトリを作らずカレントに展開します。

ViteでReactプロジェクトを作成する(create-react-app非推奨化への対応)

React公式は2025年2月にcreate-react-appを非推奨とし、新規プロジェクトはViteなどのビルドツールを推奨しています。以下でReactアプリとaxiosを用意します。

npm create vite@latest frontend -- --template react
cd frontend
npm install
npm install axios
npm run dev

Viteの開発サーバーは既定で http://localhost:5173 で起動します。このポートが、次のCORS設定で「許可するオリジン」になります。

Django REST FrameworkでAPIを構築する

DRFは、DjangoのモデルをJSONを返すREST APIに変換するための標準的なライブラリです。役割は「シリアライザーでモデルをJSON化」「ViewSetとルーターでURLを生成」の2つに分かれます。DRFの全体像や導入の利点はDjango REST Frameworkとは?その特徴と利点を徹底解説で補足しています。

シリアライザーでモデルをJSON化する

シリアライザーは、DjangoモデルとJSONを相互変換するクラスです。ModelSerializerを使えば、対象モデルと公開するフィールドを指定するだけで済みます。

from rest_framework import serializers
from .models import Task

class TaskSerializer(serializers.ModelSerializer):
    class Meta:
        model = Task
        fields = ["id", "title", "done"]

fields"__all__"でも動きますが、パスワードや内部フラグを誤って公開しないよう、返す列を明示するのが安全です。フィールド指定やバリデーションの詳細はDRF Serializerの役割とは?Pythonでの基本的な使い方を解説を参照してください。

ViewSetとルーターでエンドポイントを自動生成する

ModelViewSetは一覧・取得・作成・更新・削除の5操作をまとめて提供し、DefaultRouterがそれらのURLを自動で割り当てます。

from rest_framework import viewsets
from rest_framework.routers import DefaultRouter
from .models import Task
from .serializers import TaskSerializer

class TaskViewSet(viewsets.ModelViewSet):
    queryset = Task.objects.all()
    serializer_class = TaskSerializer

router = DefaultRouter()
router.register(r"tasks", TaskViewSet)
urlpatterns = router.urls

このルーターはプロジェクトのurls.pypath("api/", include("api.urls"))のようにapi/配下へマウントします。こうすると/api/tasks/(一覧・作成)と/api/tasks/{id}/(個別操作)になり、後述のaxiosのベースURL/api/と一致します。settings.pyINSTALLED_APPS"rest_framework"と自作アプリの追加を忘れないようにします。

CORSとCSRFの設定(連携で最初に詰まる箇所)

フロントエンド分離構成でほぼ全員がつまずくのがCORSエラーです。原因はブラウザのオリジン制限で、Django側で許可オリジンを明示すれば解消します。あわせて、SPAでは似た用語のCSRFと混同しやすいため、違いを整理しておきます。

django-cors-headersで許可オリジンを設定する

settings.pyに3か所追記します。ミドルウェアの位置が重要で、CorsMiddlewareCommonMiddlewareより前に置きます。

INSTALLED_APPS = [
    # ...
    "corsheaders",
]

MIDDLEWARE = [
    "corsheaders.middleware.CorsMiddleware",
    "django.middleware.common.CommonMiddleware",
    # ...
]

CORS_ALLOWED_ORIGINS = [
    "http://localhost:5173",
]

ここで許可するのはReactの開発サーバーのオリジンです。CORS_ALLOW_ALL_ORIGINS = Trueは手早く動きますが、任意サイトからのアクセスを許すことになるため本番では使わず、信頼するオリジンだけを列挙します。CORSの仕組み自体はクロスオリジンリソース共有(CORS)とは何か?その基本概念と重要性を解説で詳しく扱っています。

SPAではCORSとCSRFを取り違えない

CORSは「別オリジンからのアクセスを許すか」の話、CSRFは「なりすましリクエストを防ぐか」の話で、対象が違います。JWTのようにトークンをAuthorizationヘッダーで送る認証では、Cookieに依存しないためCSRF対策の優先度は下がります。逆に、DjangoのセッションCookie認証をSPAから使う場合はCSRFトークンの送信が必要になります。「JWTならCSRF不要、セッションCookieならCSRF必要」と切り分けると迷いません。CSRFの攻撃手法とリスクはCSRF(クロスサイトリクエストフォージェリ)とは何か?概要とそのリスクについて解説で確認できます。

ReactからDjango APIにリクエストを送る

APIが用意できたら、Reactから呼び出します。fetchでも可能ですが、ベースURL設定や後述のトークン付与が書きやすいため、ここではaxiosを使います。

axiosでGET・POSTを送る

ベースURLを持つaxiosインスタンスを1つ作り、API関数を分離しておくと再利用しやすくなります。

import axios from "axios";

const api = axios.create({ baseURL: "http://localhost:8000/api/" });

export async function fetchTasks() {
  const res = await api.get("tasks/");
  return res.data;
}

export async function addTask(title) {
  const res = await api.post("tasks/", { title, done: false });
  return res.data;
}

取得はGET、作成はPOST、更新はPUT/PATCH、削除はDELETEと、DRFのViewSetがHTTPメソッドと操作を対応づけています。POSTでは第2引数のオブジェクトがリクエストボディとしてJSON送信されます。

useEffectとuseStateでレスポンスを画面に反映する

取得したデータはコンポーネントの状態に入れて描画します。読み込み中と失敗時の表示も同時に用意しておくと、通信の遅延やAPI停止でも画面が壊れません。

import { useEffect, useState } from "react";
import { fetchTasks } from "./api";

function TaskList() {
  const [tasks, setTasks] = useState([]);
  const [error, setError] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchTasks()
      .then(setTasks)
      .catch(() => setError("取得に失敗しました"))
      .finally(() => setLoading(false));
  }, []);

  if (loading) return <p>読み込み中</p>;
  if (error) return <p>{error}</p>;
  return (
    <ul>
      {tasks.map((t) => (
        <li key={t.id}>{t.title}</li>
      ))}
    </ul>
  );
}

useEffectの第2引数を空配列にすると、初回マウント時に1度だけAPIを呼びます。catchでエラーメッセージ、finallyでローディング解除を必ず行うのが、実運用で崩れないコツです。

JWT認証でAPIを保護する

ログインが必要なアプリでは、誰でもAPIを叩ける状態は避けます。フロントエンド分離構成では、サーバーにセッションを持たせないトークン方式のJWTが扱いやすく、SimpleJWTで実装します。

SimpleJWTでトークン発行エンドポイントを用意する

DRFの認証クラスをJWTに切り替え、ログイン用とトークン更新用のURLを追加します。

# settings.py
REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": (
        "rest_framework_simplejwt.authentication.JWTAuthentication",
    ),
}

# urls.py
from rest_framework_simplejwt.views import (
    TokenObtainPairView,
    TokenRefreshView,
)

urlpatterns += [
    path("api/token/", TokenObtainPairView.as_view()),
    path("api/token/refresh/", TokenRefreshView.as_view()),
]

/api/token/にユーザー名とパスワードをPOSTすると、アクセストークンとリフレッシュトークンが返ります。ViewSet側にpermission_classes = [IsAuthenticated]を付ければ、そのエンドポイントは認証必須になります。上の設定は認証をJWTのみに絞るため、開発中にDRFのブラウザブルAPIからログインしたい場合は"rest_framework.authentication.SessionAuthentication"も併記します。SimpleJWTは執筆時点で5.5.1が最新です。

Reactでトークンを保存しAuthorizationヘッダーへBearer付与する

ログインで受け取ったトークンを保存し、以降のリクエストに自動で付けます。axiosのリクエストインターセプターを使うと、API関数ごとに書かずに済みます。

api.interceptors.request.use((config) => {
  const token = localStorage.getItem("access");
  if (token) config.headers.Authorization = `Bearer ${token}`;
  return config;
});

リフレッシュトークンと保存先のセキュリティ判断

アクセストークンは有効期限が短いため、切れたら/api/token/refresh/にリフレッシュトークンをPOSTして再取得します。保存先は判断が分かれますが、localStorageはXSSでトークンを盗まれるリスクがあるため、機密性の高いアプリではリフレッシュトークンをhttpOnly Cookieに置き、アクセストークンはメモリ保持にする方が安全です。学習用や社内ツールならlocalStorageでも実用上は動きますが、公開サービスではXSS対策とセットで設計してください。

よくある質問

ReactとDjangoの認証は何を使えばよいですか?

フロントエンドを分離したSPA構成なら、SimpleJWTによるJWT認証が標準的です。サーバーにセッションを持たせず、Reactがトークンをヘッダーで送るだけで済むためです。DjangoのテンプレートにReactを埋め込む同一オリジン構成なら、DjangoのセッションCookie認証(+CSRFトークン)でも構いません。

ReactとDjangoはどちらから学ぶべきですか?組み合わせる価値はありますか?

API側の設計が先に決まるため、Django(DRF)でエンドポイントを作れるようになってからReactで表示を組むと進めやすいです。組み合わせる価値は構成によります。画面遷移の多いSPAやモバイルとのAPI共用があるなら分離構成の価値は高く、静的に近いサイトならDjangoテンプレートだけで十分なこともあります。

CORSエラーが消えないときは何を確認しますか?

まずCORS_ALLOWED_ORIGINSにReactの実際の起動オリジン(Viteならhttp://localhost:5173)が入っているかを確認します。次にMIDDLEWARECorsMiddlewareCommonMiddlewareより前にあるか、末尾スラッシュの有無でURLがずれていないかを見ます。ブラウザの開発者ツールのネットワークタブで、失敗したリクエストのオリジンとレスポンスヘッダーを照合すると原因を特定できます。

create-react-appはもう使えないのですか?

2025年2月にReact公式が非推奨化しました。既存プロジェクトが動かなくなるわけではありませんが、新規ではVite(本記事の構成)やNext.jsなどのフレームワークが推奨されます。Viteはビルドと開発サーバーの起動が速く、Django APIと組み合わせるSPA用途に適しています。

本番環境へのデプロイはどう構成しますか?

フロント(Reactのビルド成果物)とバック(Django API)を分けてデプロイするのが基本です。Reactはnpm run buildで生成した静的ファイルをCDNや静的ホスティングに置き、DjangoはAPIサーバーとして稼働させます。本番ではCORSの許可オリジンを本番ドメインに変更し、HTTPSを前提にトークンの保存先を見直してください。デプロイ先(クラウドやPaaS)ごとの手順は本記事の範囲を超えるため、各サービスの公式ドキュメントに従います。

関連記事

資料請求

RELATED POSTS 関連記事