はじめに
高校の進路指導において、生徒の受験校や合否結果の集約は非常に煩雑な業務です。
Googleフォームでは「入力済みの内容を後から修正する」「大学マスタから検索する」といった要件を満たすのが難しく、かといって専用のSaaSを導入するにはコストがかかります。
そこで、Google Apps Script (GAS) + スプレッドシート をバックエンドに据え、完全レスポンシブ対応のWebアプリを開発しました。
GAS特有の「同時書き込み問題」や「キャッシュ容量制限」を乗り越え、実運用に耐えうる堅牢なシステムを構築できたので、その技術的な工夫を共有します。
作ったもの
受験校調査システム
生徒が自身のスマホから受験予定校を登録し、教員がリアルタイムで管理できるシステムです。
以下のURLでテンプレートを公開しています。
👉【テンプレート】受験校調査Webアプリ
MITライセンスで配布します。著作権 (c) 2025 Shigeru Suzuki
ソフトウェアは「現状のまま」提供され、明示的または黙示的な保証は一切ありません。
ご質問や改善提案があれば、この記事のコメントでお知らせください。
-
技術スタック: Vanilla JS / CSS Grid / Google Apps Script / Google Spreadsheet
こだわった技術ポイント 4選
1. 徹底した「排他制御」と「なりすまし防止」
GASでWebアプリを作る際、最も怖いのが 「生徒Aと生徒Bが同時に保存ボタンを押してデータが消える(レースコンディション)」 と 「他人のデータを書き換える(なりすまし)」 です。
排他制御 (LockService)
LockService を使用し、書き込み処理中は他のアクセスを待機させることでデータの整合性を保ちました。
function saveExamDataList(strJuken, mailAddr) {
const lock = LockService.getDocumentLock();
try {
// 30秒間ロックを取得(取れなければエラー)
lock.waitLock(30000);
// ... スプレッドシートの読み書き処理 ...
} catch (e) {
throw new Error('他ユーザーが処理中です。時間をおいて再試行してください。');
} finally {
lock.releaseLock(); // 必ずロックを解放
}
}
2. GASの「100KBキャッシュ制限」を突破するチャンク分割
大学マスタデータ(Benesseコード等)はサイズが大きく、そのまま CacheService に入れると 「100KB制限」 に引っかかります。 そこで、データを100KBごとのチャンク(塊)に分割して保存し、読み込み時に結合するロジックを実装しました。
// キャッシュ保存(分割対応)
function putCache(key, value, expirationInSeconds) {
const cache = CacheService.getScriptCache();
const chunkSize = 100000; // 安全マージンをとって100KB
if (value.length <= chunkSize) {
cache.put(key, value, expirationInSeconds);
return;
}
// 分割保存
let chunkCount = 0;
for (let i = 0; i < value.length; i += chunkSize) {
const chunk = value.substring(i, i + chunkSize);
cache.put(`${key}_${chunkCount}`, chunk, expirationInSeconds);
chunkCount++;
}
// 分割数を記録
cache.put(key, `CHUNKED_${chunkCount}`, expirationInSeconds);
}
3. CSS Gridによる「完全レスポンシブ」UI
生徒は99%スマホで操作します。PC用のテーブル表示をそのまま縮小するのではなく、CSS Grid と Media Query を駆使して、スマホでは「カード型」、PCでは「テーブル型」にレイアウトが動的に変更されます。
/* PC表示: 通常のテーブル */
.input-table tr {
display: table-row;
}
/* スマホ表示: カード型 */
@media screen and (max-width: 700px) {
.input-table tr {
display: flex;
flex-direction: column;
border-bottom: 1px solid #ccc;
margin-bottom: 10px;
}
/* data-label属性を使って見出しを表示 */
.input-table td::before {
content: attr(data-label);
font-weight: bold;
display: block;
}
}
4. サーバー負荷を下げる「デバウンス検索」
大学検索はインクリメンタルサーチですが、一文字打つたびに google.script.run を呼ぶとサーバー(GAS)のクォータを消費し、動作も重くなります。 フロントエンド側で setTimeout を使ったデバウンス処理(入力が止まってから検索)を実装しました。
let debounceTimer;
function searchKeyword() {
clearTimeout(debounceTimer);
// 最後の入力から一定時間待ってから実行
debounceTimer = setTimeout(() => {
// ... google.script.run.getUniversityDataList() ...
}, 800);
}
5. 運用面での工夫
- キャッシュの自動更新: スプレッドシートの onChange トリガーを使い、管理者がマスタデータを修正したら自動でキャッシュがクリアされるようにしました
- 調査書発行願: 入力データを元にスプレッドシート上で帳票レイアウトを作成し、PDF化してメール送信する機能を実装しました。一時ファイルは finally ブロックで確実にゴミ箱へ移動させています
6. まとめ
「GASでWebアプリは遅い・脆い」と思われがちですが、以下のポイントを押さえれば十分に実用的なシステムが作れます。
- LockService で排他制御をする
- CacheService を限界まで活用する(分割保存)
- クライアント側 でできる処理(バリデーション、整形)はクライアントに任せる
- レスポンシブ はCSSで解決する
- コストゼロでここまで作れるGASの可能性を改めて感じました。 どなたかの開発の参考になれば幸いです!
Tags: #GAS #GoogleAppsScript #JavaScript #GoogleSpreadsheet #個人開発 #EdTech

