はじめに
私のチームではスプレッドシートで作成したWBSを使って、案件状況の管理をしています。
ただ自分の案件のみを一元的に見たい、完了予定日が過ぎているタスクが分かりにくい、などの微妙な悩みがあったのでカンバンボードとしても見れるようにしました。
完成イメージ
先に完成したアプリの動きです。
WBS(スプレッドシート)の情報をカンバンUIでそれぞれ表示し、D&D(ドラッグ & ドロップ)で進捗を変更できます。
またタスクごとの納期もアプリ側から変更でき、変更した内容はWBSへ反映されます。
作業の流れ
- 環境構築(★今回の記事)
- WBSデータを読み込み
- D&D機能を実装 & WBSと同期して進捗変更
- 日付変更機能の実装 & ガントチャートへの自動書き込み
今回はGeminiと壁打ちをしながら実装を行いました。
技術スタック
- Frontend: React (TypeScript)
- Backend: Google Apps Script (GAS)
- Deploy: Clasp
- Build Tool: Vite, vite-plugin-singlefile (GAS用にHTMLを1ファイルにまとめる)
ディレクトリ構成(configファイルは省略)
├── dist(ファイルのコンパイル出力先)
│ ├── Code.js
│ ├── appsscript.json
│ ├── index.html
├── gas
│ ├── Code.js
│ └── appsscript.json
├── index.html
└── src
├── App.css
├── App.tsx
├── index.css
└── main.tsx
各ファイルの役割
Code.js
読み込みや入力など、SpreadsheetApp(スプレッドシート)が伴う処理は全て本ファイルに記載されます。
Googleのサーバー上で動き、唯一スプレッドシートの中身を読み書きできます。
App.tsx
アプリの見た目や、ユーザーの操作(クリックやドラッグ)が伴う処理が記載されます。
D&Dの動きやローディング表示などは全て本ファイルです。
index.html
Reactが描画される場所だけではなく、今回はApp.tsxのjs処理を全て本ファイルに最終的に記載します。
これはGASが 「同じGASプロジェクト内の別ファイル」 を読み込むことができないためです。
1. 環境構築手順
Step 1: プロジェクト作成
ViteでReactプロジェクトを作成します。
npm create vite@latest wbs-kanban -- --template react-ts
cd wbs-kanban
npm install
Step 2: 必要なパッケージの導入
GASへのデプロイツール claspと、ビルドを単一HTMLにするプラグインを入れます。
npm install -D @google/clasp @types/google-apps-script
npm install -D vite-plugin-singlefile
Step 3: Claspの初期設定
インストールしたclaspを使って、Googleアカウントとの認証とプロジェクトの紐付けを行います。
1. Googleログイン
ブラウザが立ち上がるので、GASを使用するGoogleアカウントでログインします。
npx clasp login
2. プロジェクトの作成
今回はビルド後のファイル(distフォルダ)のみをGASにアップロードしたいので、rootDirを指定してプロジェクトを作成します。
npx clasp create --type webapp --title "WBS Kanban" --rootDir ./dist
※ 既存のスプレッドシートに紐付いたGASを使用する場合は、npx clasp clone <スクリプトID> --rootDir ./distを実行
3. 設定ファイルの確認
プロジェクトルートに.clasp.jsonが生成されるので、rootDirが./dist になっていることを確認します。ここが間違っていると、ビルド前のソースコードがアップロードされたり、不要なファイルが混ざったりします。
{
"scriptId": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"rootDir": "./dist"
}
scriptIdはGASエディタの「プロジェクトの設定(歯車)」から確認できます。
Step 4: 設定ファイルの編集
ビルド時にCSSやJSを全てindex.htmlにインライン化する設定です。
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { viteSingleFile } from "vite-plugin-singlefile"
export default defineConfig({
plugins: [react(), viteSingleFile()],
build: {
outDir: 'dist',
minify: true, // GASの容量制限対策
}
})
ビルドからデプロイまでを一発で行うコマンドを定義します。
※ Mac/Linux環境を想定(WindowsでもPowerShellであれば cp が動作する可能性がありますが、エラーが出る場合は copy に変更してください)
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build && cp gas/Code.js dist/Code.js",
"deploy": "npm run build && npx clasp push"
}
Step 5: GASバックエンドの準備
プロジェクトルートにgas/Code.jsを作成し、Webアプリのエントリーポイントを作ります。
doGetはすでにGAS側で予約されている関数で、アプリがGoogleサーバーへGETリクエストを送ると実行されます。
関数内ではindex.htmlの内容を取得し、viewportなどを追加した上でその内容を表示する、というシンプルな処理を実行します。
function doGet() {
return HtmlService.createHtmlOutputFromFile('index')
.addMetaTag('viewport', 'width=device-width, initial-scale=1')
.setTitle('WBS Kanban');
}
Step 6: GASをデプロイ
一旦ここまでの内容をブラウザで確認します。
下記でソースがApps Scriptのエディタに反映されるので
clasp push
エディタ右上のデプロイボタンを押して発行されるURLから、実際のアプリを確認できます。
※まだ何もコンテンツを入れていないので画面上は真っ白です。
2. 遭遇したエラーと解決策
環境構築中に躓いたポイントとその対処法です。
1. Project file already exists
clasp create や clasp clone をしようとした時に発生。
原因: 過去の操作により、カレントディレクトリ(または親ディレクトリ)に.clasp.jsonや appsscript.jsonが残っていたため。
対応: ls -a (Mac) や Get-ChildItem -Force (Win) で隠しファイルを探し.clasp.jsonを削除した。ただそれでも解決できず・・結局手動で.clasp.jsonを作成して紐付けを行った。
2. Unknown command "clasp"
原因: clasp をローカルインストール (npm install -D) したのみで、パスが通っていなかった。
対応: コマンドの先頭に npx をつける (npx clasp ...) か、グローバルインストール (npm i -g @google/clasp) で解決。
3. Request contains an invalid argument (clasp pull 時)
原因: .clasp.jsonに記述したIDが間違っていた。URLに含まれる「プロジェクトID」ではなく、設定画面にある 「スクリプトID」 を使う必要があった。
対応: GASエディタの「プロジェクトの設定(歯車)」からスクリプトIDをコピーし、.clasp.jsonを修正。
4. スクリプト関数が見つかりません: doGet
デプロイ後、アプリを開くと発生。
原因: ビルドスクリプトの cp コマンドがうまくいっておらず、gas/Code.js が dist フォルダ(アップロード対象)にコピーされていなかった。
対応: npm run build後にdistフォルダを目視確認。コマンドを修正し、確実に Code.js が生成されるようにした。
5. ファイル名が コード.gs (カタカナ) になる
原因: GASの仕様。また、これとは別に Code.gs (英語) が存在し、ファイルが重複していたり、デフォルトの myFunction が残っていたりした。
対応: ブラウザのGASエディタ上で不要な コード.gs を削除し、ローカルの dist フォルダも一度空にしてから再デプロイ (clasp push) した。
6. 「index」という HTML ファイルは見つかりませんでした
Pushは成功しているのに、ブラウザで開くとエラーになる。
原因: 「Webアプリのバージョン」の罠。GASの公開URL (/exec) はデプロイ時点のスナップショットを見るため、最新の修正が反映されていなかった。
対応: 「デプロイ」>「デプロイをテスト」から取得できる 末尾が/dev のURL を使用することで解決。
まとめ
単純なコード記述以外に 「Claspの挙動」 や 「GAS特有の仕様(ファイル名、デプロイバージョン)」 で思ったよりハマってしまいました。
ひとまず環境は整ったので、やっと次回からはアプリ開発に入ります。

