React & Django でログイン機能実装

React でログイン画面(username, password)を作成して、Django の APIを使用してログイン機能を実装します。

### 流れ
```
React: ログイン情報をDjangoへPOST
↓
Django: Reactから受け取ったログイン情報でユーザー認証
↓
Django: 認証したユーザーのアクセストークンを作成してReactへ return
↓
React: 返されたアクセストークンをブラウザに保存
↓
以降 Djangoのrequest.userでログインしているユーザーの情報が取得可
```

```py
# 事前にインストールと設定
pip install djangorestframework-simplejwt

# Djagngo.settings.py
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_simplejwt.authentication.JWTAuthentication',
    ),
}
```
## Reactコード
```tsx:Login.tsx
import React, { useState } from 'react';
import axios from 'axios';
import { useNavigate } from 'react-router-dom';

const Login = () => {
    const [username, setUsername] = useState('');
    const [password, setPassword] = useState('');
    const navigate = useNavigate();

    interface LoginResponse {
        access: string;
        refresh: string;
    }

    const handleSubmit = async (e: React.SyntheticEvent) => {
        e.preventDefault();
        try {
            const response = await axios.post<LoginResponse>('http://localhost:8000/accounts/api/login/', {
                username,
                password,
            });
            const { access, refresh } = response.data;
            localStorage.setItem('access_token', access);
            localStorage.setItem('refresh_token', refresh);
            navigate('/');
        } catch (error) {
            console.error('Login failed', error);
        }
    };

    return (
        <form onSubmit={handleSubmit}>
            <input 
                type="text" 
                placeholder="Username" 
                value={username} 
                onChange={(e) => setUsername(e.target.value)} 
            />
            <input 
                type="password" 
                placeholder="Password" 
                value={password} 
                onChange={(e) => setPassword(e.target.value)} 
            />
            <button type="submit">Login</button>
        </form>
    );
};

export default Login;

```

### 詳細
```tsx
// Djangoの指定したURLに入力したログイン情報を送信
const response = await axios.post<LoginResponse>('http://localhost:8000/accounts/api/login/', {
                username,
                password,
            });
            
// Django から返された response から アクセストークン, と リフレッシュトークンを取得
const { access, refresh } = response.data;

// ブラウザに取得したアクセストークンを保存
localStorage.setItem('access_token', access);
localStorage.setItem('refresh_token', refresh);
```

## Django コード
```py: urls.py
# project.urls.py--------------------------------
path('accounts/', include('accounts.urls')),

# accounts.urls.py---------------------------
from django.urls import path
from . import views

app_name = "accounts"
urlpatterns = [
    path('api/login/', views.login_api_view, name='api_login'),
]
```
```py: views.py
@api_view(['POST'])
def login_api_view(request):
    username = request.data.get('username')
    password = request.data.get('password')
    user = authenticate(request, username=username, password=password)
    if user is not None:
        refresh = RefreshToken.for_user(user)
        return Response({
            'refresh': str(refresh),
            'access': str(refresh.access_token),
        }, status=status.HTTP_200_OK)
    else:
        return Response({'message': 'Invalid credentials'}, status=status.HTTP_400_BAD_REQUEST)
```

### 詳細
```py
# React から送られてきた username, password でユーザー認証
user = authenticate(request, username=username, password=password)

# 認証されたユーザーに対してアクセストークンを発行し React へ返す
refresh = RefreshToken.for_user(user)
return Response({
    'refresh': str(refresh),
    'access': str(refresh.access_token),
}, status=status.HTTP_200_OK)
```

## 認証済みユーザー取得
```py:views.py
@api_view(['GET'])
@permission_classes([IsAuthenticated]) # 認証されたユーザーのみ
def channel_view_set(request):
    user = User.objects.filter(user=request.user)
    serializer = UserInfoSerializer(user, many=True)
    
    if user.exists():
        return Response(serializer.data)
    else:
        return Response(status=status.HTTP_404_NOT_FOUND)
```

Simple JWT を利用することで、リクエストに認証済みユーザーの情報を含めることが可能になり、ユーザーがログイン済みかどうかを簡単に判断できるようになりました。これにより、認証が必要な処理をセキュアかつスムーズに実装できる点が非常に便利だと思います。