こんにちは!
ビジネスアクセラレーション事業部の大瀧優杏です🎀

前回の記事で少し触れさせていただきましたが、
AIを使わないでバックエンドとフロントエンドをそれぞれ作成して繋ぎ込みまで行い、
簡易的なじゃんけんアプリ(ローカル)が完成しました!!🥳

真剣に頑張りましたので、
最後まで見ていただけたら嬉しいです。

Next.jsのセットアップ

https://nextjs-ja-translation-docs.vercel.app/docs/getting-started
上記の記事を参考にNext.jsをセットアップします。

yarn create next-app
Next.jsを自動的にセットアップするためのコマンド

yarn create next-app --typescript
Typescriptのプロジェクトで始めたいので、--typescriptフラグを使用する

上記のコマンドで新たにプロジェクト用のフォルダが作成されるので、そのフォルダに移動し、

yarn dev
を実行すれば、

開発サーバーが立ち上がります。

無事に立ち上がりました!

フロントエンド

'use client';

export default function Home() {
  const play = (hand: string) => {
    console.log(`${hand}が押されました!`);
  };
  return (
    <div className="flex flex-col items-center justify-center h-screen gap-8">
      <h1 className="text-5xl font-bold mb-10">じゃんけんゲーム</h1>
      <button onClick={() => play('グー')} className="bg-pink-400 hover:bg-pink-500 text-white font-bold text-2xl py-4 px-8 rounded-xl shadow-lg transition"
        >グー</button>
        <button onClick={() => play('チョキ')} className="bg-pink-400 hover:bg-pink-500 text-white font-bold text-2xl py-4 px-8 rounded-xl shadow-lg transition">チョキ</button>
        <button onClick={() => play('パー')} className="bg-pink-400 hover:bg-pink-500 text-white font-bold text-2xl py-4 px-8 rounded-xl shadow-lg transition">パー</button>
    </div>
  );
}

Next.js のトップページ(app/page.tsx)で、じゃんけんの3つのボタンをそれぞれ出し、押したらコンソールにログを出すだけのシンプルなクライアントコンポーネントです。

'use client';
Next.js ではファイル先頭にこれを書くと Client Component になります。
Client Component で、onClick などのブラウザイベントが使えるようになりました。

「じゃんけんゲーム」と、「グー」「チョキ」「パー」に最小限のCSSをつけて、文字を大きく、水平方向、垂直方向ともに中央に揃え、ボタンをピンクにしました。

バックエンド

import random

janken = ["グー", "チョキ", "パー"]

print(f"じゃんけんをします!「グー」「チョキ」「パー」を入力してください><")
# ランダムにコンピューターが選択
computer_hand = random.choice(janken)
# ユーザーが手を入力
user_hand = input("あなたの手を入力してください:")

print(f"あなたの手:{user_hand}、コンピュータの手:{computer_hand}")

if user_hand == computer_hand:
    print("あいこです")
elif user_hand == "グー" and computer_hand == "チョキ":
    print("あなたの勝ちです")
elif user_hand == "チョキ" and computer_hand == "パー":
    print("あなたの勝ちです")
elif user_hand == "パー" and computer_hand == "グー":
    print("あなたの勝ちです")
elif user_hand == "チョキ" and computer_hand == "グー":
    print("あなたの負けです")
elif user_hand == "パー" and computer_hand == "チョキ":
    print("あなたの負けです")
elif user_hand == "グー" and computer_hand == "パー":
    print("あなたの負けです")

ターミナルで動く じゃんけんCLI です。
ユーザーが自分の手を入力し、コンピュータと勝敗を判定して表示します。

処理の流れ
1. コンピュータがランダムに手を選ぶ
1. 自分の手を入力
1. 両方の手を表示
1. if / elif で勝敗を判定して表示

FastAPIをインストール

python3 -m pip install fastapi uvicorn
FastAPIとuvicornをインストール

python3 -m uvicorn app:app --reload
仮想環境でバックエンドを起動


上記のコードだと、

APIとして繋がらない問題発生

何事 ><:sweat_drops:

「じゃんけんCLI」のままだと、
「FastAPIのインポート」・「リクエストボディ・レスポンスボディの設定」・「CORSの設定」がないため、
APIとしてフロントエンドと繋げることができない
みたいです汗

リクエストボディ:フロントからバックエンドへ送信するデータ
レスポンスボディ:バックエンドからフロントへ送信するデータ

FastAPIのインポート

FastAPIをインポート

from fastapi import FastAPI

インスタンス化

app = FastAPI()

参照:https://fastapi.tiangolo.com/ja/#installation

リクエストボディとレスポンスボディの設定

リクエストボディを宣言するには、Pydantic モデルを使用し、その強力な機能とメリットをすべて利用します。

引用:https://fastapi.tiangolo.com/ja/tutorial/body/

らしいので、、👇🏻

pydantic から BaseModel をインポート

from pydantic import BaseModel

BaseModel を継承するクラスとしてデータモデルを宣言

class jyanken_request (BaseModel):
    user_hand: str

引数に BaseModelで作ったクラス を指定すると、FastAPIがリクエストボディを自動で受け取るようになる

パスオペレーションを定義

@app.post("/")

「/ 」というパスに POST メソッドでアクセスされたら動く処理を定義

パスオペレーションに 「リクエストボディ」を宣言・追加

def jyanken(request: jyanken_request):
    user_hand = request.user_hand

これにより、
フロントエンドから届いた{ "user_hand": "..." }というJSONが、自動的に request という変数に入ります。

参照:https://fastapi.tiangolo.com/ja/tutorial/body/#create-your-data-model

CORSの設定

CORSMiddlewareをインポート

from fastapi.middleware.cors import CORSMiddleware

CORSを設定する

app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

allow_origins
アクセスを許可するURLのリスト

allow_credentials
クレデンシャル情報 を一緒に送信することを許可。

allow_methods
特定のHTTPメソッド (GET, POST, PUT, DELETE) を、ワイルドカード "*"を使用してすべて許可。

allow_headers
特定のHTTPヘッダー制限。
ワイルドカード"*"を使用してすべて許可。

参照:https://fastapi.tiangolo.com/ja/tutorial/cors/#use-corsmiddleware

上記を踏まえて完成したコード

バックエンド

from fastapi import FastAPI
from pydantic import BaseModel
from fastapi.middleware.cors import CORSMiddleware
import random

class jyanken_request (BaseModel):
    user_hand: str

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:3000"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.post("/")
def jyanken(request: jyanken_request):
    user_hand = request.user_hand

    hand = ["グー", "チョキ", "パー"]
    computer_hand = random.choice(hand)

    if user_hand == computer_hand:
        result_message = "あいこです"
    elif user_hand == "グー" and computer_hand == "チョキ":
        result_message = "あなたの勝ちです"
    elif user_hand == "チョキ" and computer_hand == "パー":
        result_message = "あなたの勝ちです"
    elif user_hand == "パー" and computer_hand == "グー":
        result_message = "あなたの勝ちです"
    else:
        result_message = "あなたの負けです"

    return {
            "user_hand": user_hand,
            "computer_hand": computer_hand,
            "result_message": result_message
            }

フロントエンド

'use client';

export default function Home() {
  const play = async (hand: string) => {
    console.log(`${hand}が押されました!`);

const res = await fetch('http://localhost:8000', {
   method: 'POST',
   headers: {
     'Content-Type': 'application/json',
   },
   body: JSON.stringify({ user_hand: hand }),
   });

  const data = await res.json();
  console.log(data.result_message);
  };


  return (
    <div className="flex flex-col items-center justify-center h-screen gap-8">
      <h1 className="text-5xl font-bold mb-10">じゃんけんゲーム</h1>   
      <button onClick={() => play('グー')} className="bg-pink-400 hover:bg-pink-500 text-white font-bold text-2xl py-4 px-8 rounded-xl shadow-lg transition"
        >グー</button>
        <button onClick={() => play('チョキ')} className="bg-pink-400 hover:bg-pink-500 text-white font-bold text-2xl py-4 px-8 rounded-xl shadow-lg transition">チョキ</button>
        <button onClick={() => play('パー')} className="bg-pink-400 hover:bg-pink-500 text-white font-bold text-2xl py-4 px-8 rounded-xl shadow-lg transition">パー</button>
    </div>
  );
}

バックエンドとの通信部分

 const res = await fetch('http://localhost:8000', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ user_hand: hand }),
  });

上記の部分でリクエストボディを作成し、バックエンドに送信しています。

具体的には、
fetchの第一引数にバックエンドのURLを入れてHTTPリクエストを送信
第二引数で、メソッド、ヘッダー、ボディを設定
しています。

すると、、、

💚200 OK💚

が確認でき、バックエンドと通信できたことが確認できました!🙌🏻

やったー!!!🙌🏻

終わりに

AIに頼らずバックエンドとフロントエンドを自力で作成したことで、PythonやNext.jsの基本的な文法、HTML/CSSの基礎を改めてしっかり学ぶことができました。

また、今回はあえてAWS環境を使わなかったことで、API連携に必要なリクエスト・レスポンスボディの設定やCORSの仕組みなど、Webの根本的な理解も深まりました。

超シンプルな「じゃんけんアプリ」ですが、多くのことを学ぶきっかけになりました。

最後まで読んでいただきありがとうございました!🥰