はじめに
Google Apps Script(略してGAS)は、Googleのサービス(スプレッドシート、ドキュメント、Gmailなど)を自動化したり拡張したりするためのJavaScriptベースのプログラミング言語です。
初めてGASを使う方は、通常1つのファイルにすべてのコードを書いていくことが多いでしょう。それでも小規模なプロジェクトであれば問題ありませんが、機能が増えてコードが長くなってくると、管理が難しくなってきます。
この記事では、GASのコードを効率的に分割する方法について、初心者の方にも分かりやすく解説します。
Google Apps Scriptとは?
まず最初に、GASについて簡単におさらいしておきましょう。
Google Apps Script(GAS)は、Googleが提供するサービスを自動化するためのスクリプト言語です。JavaScriptをベースにしていますが、Google独自の機能が追加されています。例えば:
- スプレッドシートのデータを自動で更新する
- 指定した時間にメールを送信する
- ドキュメントを自動生成する
- フォームの回答を集計してレポートを作成する
などの作業を自動化できます。
なぜコードを分割すべきなのか?
GASでプロジェクトを作成し始めた頃は、1つのファイル(例:コード.gs
)に全ての機能を書くことが多いでしょう。しかし、プロジェクトが成長するにつれて以下のような問題が発生します:
- コードの可読性が低下する: 数百行、数千行になると目的の部分を見つけるのが困難になります。「あの関数はどこだっけ?」と探す時間が増えてしまいます。
- 保守性が悪化する: 機能追加や修正が難しくなります。コードを変更する際に、他の機能に影響がないか確認するのが大変になります。
- 再利用性が失われる: 同じコードを別プロジェクトで使いたい場合に、必要な部分だけを抽出するのが難しくなります。
- 協業が難しくなる: チームで開発する場合、同じファイルを複数人で編集すると変更の衝突が増えます。
コード分割の基本的な方法
GASでは、複数のスクリプトファイル(.gs)を作成することで、コードを論理的に分割できます。以下に基本的な分割方法を紹介します。
1. 機能別にファイルを分ける
まずは機能やドメインごとにファイルを分けましょう。例えば:
Main.gs
– メイン処理やトリガー関数SpreadsheetOperations.gs
– スプレッドシート操作関連DriveOperations.gs
– Googleドライブ操作関連MailOperations.gs
– メール送信関連Utilities.gs
– 汎用的なユーティリティ関数
2. 新しいスクリプトファイルの作成方法
- GASエディタで左側の「ファイル」パネルを開く
- 「+」ボタンをクリック
- 「スクリプト」を選択
- ファイル名を入力して「OK」をクリック
3. 関数の移動
既存のコードから関連する関数を新しいファイルにコピー&ペーストします。この時、関数名の衝突がないよう注意しましょう。
実践例:ウェブスクレイピングとファイル保存
ここでは、ウェブサイトからデータを取得し、それをGoogleドライブに保存し、メールで通知する一連の処理を実行するGoogle Apps Script (GAS)のコードについて、わかりやすく解説します。
分割前のコード
// すべての機能が1つのファイルに
function main() {
const url = "https://example.com/data";
const data = scrapeWebsite(url);
saveToSpreadsheet(data);
const fileId = saveAsPDF(data);
sendEmail("user@example.com", "データ取得完了", "PDFファイルを作成しました。", fileId);
}
function scrapeWebsite(url) {
const response = UrlFetchApp.fetch(url);
const content = response.getContentText();
// データ抽出処理
return extractedData;
}
function saveToSpreadsheet(data) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName("Data");
// スプレッドシートへの書き込み
}
function saveAsPDF(data) {
// PDFファイル作成
const blob = createPDFBlob(data);
// Driveに保存
const folder = DriveApp.getFolderById("フォルダID");
const file = folder.createFile(blob);
return file.getId();
}
function sendEmail(recipient, subject, body, attachmentId) {
// メール送信処理
const attachment = DriveApp.getFileById(attachmentId);
GmailApp.sendEmail(recipient, subject, body, {
attachments: [attachment.getBlob()]
});
}
分割後のコード構成
コードを機能ごとに整理すると管理や修正が容易になります。たとえば、以下のような構成になります。
Main.gs
メイン処理を記述
function main() {
const url = "https://example.com/data";
const data = scrapeWebsite(url);
saveToSpreadsheet(data);
const fileId = saveAsPDF(data);
sendEmail("user@example.com", "データ取得完了", "PDFファイルを作成しました。", fileId);
}
WebScraper.gs
ウェブスクレイピングの処理
function scrapeWebsite(url) {
const response = UrlFetchApp.fetch(url);
const content = response.getContentText();
// データ抽出処理
return extractedData;
}
SpreadsheetHandler.gs
スプレッドシートへの保存処理
function saveToSpreadsheet(data) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName("Data");
// スプレッドシートへの書き込み
}
DriveHandler.gs
PDFを作成してGoogleドライブに保存する処理
function saveAsPDF(data) {
// PDFファイル作成
const blob = createPDFBlob(data);
// Driveに保存
const folder = DriveApp.getFolderById("フォルダID");
const file = folder.createFile(blob);
return file.getId();
}
// PDFブロブを作成する補助関数
function createPDFBlob(data) {
// PDF作成ロジック
return pdfBlob;
}
MailHandler.gs
メール送信処理
function sendEmail(recipient, subject, body, attachmentId) {
// メール送信処理
const attachment = DriveApp.getFileById(attachmentId);
GmailApp.sendEmail(recipient, subject, body, {
attachments: [attachment.getBlob()]
});
}
より高度なコード分割テクニック
1. 名前空間の活用
関数名の衝突を避け、コードを整理するために名前空間を使いましょう。
// DriveUtils.gs
var DriveUtils = (function() {
// プライベート関数
function _getFolder(folderId) {
return DriveApp.getFolderById(folderId);
}
// パブリックAPI
return {
saveFile: function(blob, fileName, folderId) {
const folder = _getFolder(folderId);
return folder.createFile(blob).getId();
},
getFileLink: function(fileId) {
return DriveApp.getFileById(fileId).getUrl();
}
};
})();
使用例:
// Main.gs
function main() {
const blob = Utilities.newBlob("テストデータ", "text/plain", "test.txt");
const fileId = DriveUtils.saveFile(blob, "test.txt", "フォルダID");
const link = DriveUtils.getFileLink(fileId);
Logger.log(link);
}
2. クラスライクな構造の使用
ES6のクラス構文はGASで完全にサポートされていませんが、プロトタイプを使用して似たような構造を作れます。
// SpreadsheetManager.gs
function SpreadsheetManager(spreadsheetId) {
this.spreadsheet = SpreadsheetApp.openById(spreadsheetId);
}
SpreadsheetManager.prototype.getSheet = function(sheetName) {
return this.spreadsheet.getSheetByName(sheetName);
};
SpreadsheetManager.prototype.appendRow = function(sheetName, rowData) {
const sheet = this.getSheet(sheetName);
sheet.appendRow(rowData);
};
使用例:
// Main.gs
function main() {
const ssManager = new SpreadsheetManager("スプレッドシートID");
ssManager.appendRow("シート1", ["データ1", "データ2", "データ3"]);
}
ライブラリの活用
再利用性を高めるために、よく使う機能はGASのライブラリとして公開することも検討しましょう。
ライブラリの作成手順
- 再利用したい機能を別のGASプロジェクトとして作成
- 「ファイル」→「プロジェクトのプロパティ」で「スクリプトID」を確認
- 利用するプロジェクトの「ライブラリ」→「+」からスクリプトIDを入力して追加
ライブラリの利用例
// ライブラリ内のコード
// MyUtilities.gs
function formatDate(date) {
return Utilities.formatDate(date, "Asia/Tokyo", "yyyy/MM/dd HH:mm:ss");
}
// ライブラリを使用するコード
// Main.gs
function main() {
const now = new Date();
const formattedDate = MyLibrary.formatDate(now);
Logger.log(formattedDate);
}
コード分割のベストプラクティス
- 一貫した命名規則を使用する:ファイル名や関数名は目的が分かりやすいものにしましょう
- 関連する機能をまとめる:論理的な単位でファイルを分割しましょう
- 依存関係を最小限に:ファイル間の依存関係はできるだけシンプルにしましょう
- 共通の処理は抽出する:繰り返し使われる処理は別ファイルのユーティリティ関数にしましょう
- 適切なコメントを入れる:各ファイルの先頭には目的を記述し、複雑な部分には説明を加えましょう
注意点
- GASでは分割したファイルは内部的に統合されるため、グローバル変数や関数は全ファイルで共有されます
- ファイルの読み込み順序は保証されていないため、ファイル間の依存関係には注意が必要です
- あまりに多くのファイルに分割すると逆に管理が難しくなることがあります
まとめ
GASのコードを適切に分割することで、開発効率や保守性が大幅に向上します。プロジェクトの規模や複雑さに応じて、この記事で紹介した手法を適用してみてください。最初は少し手間に感じるかもしれませんが、長期的には大きなメリットがあります。
ちょっと変更したいなという時に、どのファイルのどの部分をいじれば良いかが格段に分かりやすいためです。
コードの分割は、単なる整理整頓ではなく、良質なソフトウェア開発の基本です。特にチームで開発する場合や、将来的に機能拡張が予想される場合は、早い段階から適切な構造を意識しましょう。
コメント