1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

5年ぶりにReact.jsで作ったアプリをCloud Run functionsにあげてみた

Last updated at Posted at 2025-09-02

はじめに

こんにちは。
Google Cloudを業務で使い初めて、覚えることがたくさんありますが、勉強の一環で簡単なことでもなるべくGoogle Cloudのサービスを使うようにしています。

今回は作成したアプリをGoogle CloudのCloud Run functionsのサーバレス環境において共有する仕組みを利用したいと思います。

デプロイするアプリ

デプロイするアプリですが、React.jsを使ってアプリを作ってみました。趣味や仕事でアプリを作っていたのが5年くらい前のため、久しぶりのコーディングで、だいぶ仕様が変わった気がしますが、Geminiにアドバイスをもらいながら作りました。

昔コーディング練習でよく作っていたソートアルゴリズムをビジュアライズするものです。ソート自体は数字の並べ替えの仕組みなので、それをビジュアライズするといったものです。コーディングの練習として楽しいのでおすすめです。今回は久しぶりなので実装しやすいマージソートを選択しました。バブルソートが一番簡単そうですが、アルゴリズムがシンプルで、ビジュアライズしたときの様子も地味なのでバブルソートはやめました。

マージソートとは、データを小さな要素に分割し、それぞれをソート済みリストとして統合する「分割統治法」に基づくソートアルゴリズムです。具体的には、データを繰り返し2分割して要素が1つになるまで細分化し、その後、ソート済みになった小さなリスト同士を比較・結合(マージ)していくことで、最終的に全体を一つのソート済みリストにします。

詳細はWikipediaをご参照ください。また、以下のWikipediaページの右側の「マージソートの様子」というGIFアニメーションがビジュアライズのイメージとなります。

ソートにおけるビジュアライズは処理の途中に一時停止して、ブラウザに情報を反映する形となります。実装としてはジェネレータ関数およびyield関数を使いました。

以下は、マージソートアルゴリズム部分を記載します。これはCloud Run functionにデプロイするときにApp.jsxとして利用します。全体はReactJSで実装しています。

App.jsx

省略

function* mergeSortGenerator(inputArray) {
  const a = inputArray.slice();
  const aux = a.slice();
  let comparisons = 0;
  let writes = 0;
  let maxDepth = 0;
  let step = 0;

  function* merge(lo, mid, hi, depth) {
    for (let k = lo; k <= hi; k++) aux[k] = a[k];

    let i = lo;
    let j = mid + 1;

    for (let k = lo; k <= hi; k++) {
      let chosenIdx = -1;
      if (i > mid) {
        a[k] = aux[j++];
        chosenIdx = j - 1;
      } else if (j > hi) {
        a[k] = aux[i++];
        chosenIdx = i - 1;
      } else if (aux[j] < aux[i]) {
        comparisons++;
        a[k] = aux[j++];
        chosenIdx = j - 1;
      } else {
        comparisons++;
        a[k] = aux[i++];
        chosenIdx = i - 1;
      }
      writes++;
      step++;
      yield {
        array: a.slice(),
        highlights: {
          lo,
          mid,
          hi,
          i,
          j,
          k,
          chosenIdx,
        },
        stats: { comparisons, writes, depth, maxDepth: Math.max(maxDepth, depth), step },
        event: "write",
      };
    }
  }

  function* sort(lo, hi, depth) {
    maxDepth = Math.max(maxDepth, depth);
    if (lo >= hi) {
      yield {
        array: a.slice(),
        highlights: { lo, mid: lo, hi, i: lo, j: hi, k: lo, chosenIdx: lo },
        stats: { comparisons, writes, depth, maxDepth, step },
        event: "base",
      };
      return;
    }
    const mid = Math.floor(lo + (hi - lo) / 2);

    yield* sort(lo, mid, depth + 1);
    yield* sort(mid + 1, hi, depth + 1);

    // Merge start marker
    yield {
      array: a.slice(),
      highlights: { lo, mid, hi, i: lo, j: mid + 1, k: lo, chosenIdx: -1 },
      stats: { comparisons, writes, depth, maxDepth, step },
      event: "mergeStart",
    };

    yield* merge(lo, mid, hi, depth);

    // Merge end marker
    yield {
      array: a.slice(),
      highlights: { lo, mid, hi, i: hi, j: hi, k: hi, chosenIdx: -1 },
      stats: { comparisons, writes, depth, maxDepth, step },
      event: "mergeEnd",
    };
  }
  
  yield* sort(0, a.length - 1, 0);
}

省略

Cloud Run functionsへデプロイ

Cloud Run functionsにデプロイします。サーバレスなのでその他の設定は自動で実行されます。以下は、デプロイ向けに必要な資材とその中身です。

(1) ディレクトリ構成

merge-sort-visualizer-fn/
├─ package.json
├─ vite.config.js
├─ index.html
├─ postcss.config.js
├─ tailwind.config.js
├─ src/
│  ├─ main.jsx
│  ├─ App.jsx        ← 作成したReactJSのコード
│  └─ index.css
└─ index.js          ← Functions 入口(Express)

(2) 各資材の中身

package.json

package.json
{
  "name": "merge-sort-visualizer-fn",
  "private": true,
  "version": "0.1.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "gcp-build": "vite build",
    "start": "functions-framework --target=app"
  },
  "dependencies": {
    "@google-cloud/functions-framework": "^3.4.0",
    "express": "^4.19.2",
    "framer-motion": "^11.2.6",
    "lucide-react": "^0.468.0",
    "react": "^18.3.1",
    "react-dom": "^18.3.1"
  },
  "devDependencies": {
    "@vitejs/plugin-react": "^4.3.1",
    "autoprefixer": "^10.4.20",
    "postcss": "^8.4.41",
    "tailwindcss": "^3.4.10",
    "vite": "^5.4.2"
  }
}

index.js

index.js
import functions from '@google-cloud/functions-framework';
import express from 'express';
import path from 'path';
import { fileURLToPath } from 'url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const app = express();

const distDir = path.join(__dirname, 'dist');

app.use('/assets', express.static(path.join(distDir, 'assets'), {
  immutable: true, maxAge: '1y',
}));

app.use(express.static(distDir));

app.get('*', (_req, res) => {
  res.sendFile(path.join(distDir, 'index.html'));
});

functions.http('app', app);

vite.config.js

vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
  plugins: [react()],
  base: '/merge-sort-viz-fun/'
  build: { outDir: 'dist' }
});

index.html

index.html
<!doctype html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1.0" />
    <title>Merge Sort Visualizer</title>
  </head>
  <body class="min-h-screen">
    <div id="root"></div>
    <script type="module" src="/src/main.jsx"></script>
  </body>
</html>

tailwind.config.js

tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
  content: ["./index.html","./src/**/*.{js,jsx,ts,tsx}"],
  theme: { extend: {} },
  plugins: []
};

src/index.css

src/index.css
@tailwind base;
@tailwind components;
@tailwind utilities;

:root { color-scheme: dark; }

src/main.jsx

src/main.jsx
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.jsx';
import './index.css';

createRoot(document.getElementById('root')).render(
  <React.StrictMode><App /></React.StrictMode>
);

(3) Cloud Run functionsへのデプロイ

準備ができましたので、これらの資材をCloud Run functionsへデプロイします。

bash
# Google Cloud のプロジェクト
gcloud config set project <PROJECT_ID>

# Functions (Gen2) にデプロイ
gcloud functions deploy merge-sort-viz-fn \
  --gen2 \
  --region=asia-northeast1 \
  --runtime=nodejs20 \
  --entry-point=app \
  --trigger-http \
  --allow-unauthenticated
  --set-build-env-vars NPM_CONFIG_PRODUCTION=false

デプロイ中に npm install → gcp-build(= vite build)が自動で実行され、dist/ が生成されます。

完了後に表示される URL にアクセスすれば、ブラウザでアプリが動作します。

ソート開始前

image.png

ソート中

image.png

ソート終了後

image.png

終わりに

今回はCloud Run functionsで簡単にデプロイし、実行することできました。少ない手順で動作でき、実行環境を気にせず、アプリ実装など自分の関心事に集中できました。
引き続き楽しみながらGoogle Cloudを勉強していきます。

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?