スプレッドシートのタスク管理が変わる!GAS自動リマインダー術

スプレッドシート×GASリマインダーのイメージ画像
t.inoue

「あの件、どうなった?」から解放される未来、スプレッドシートで実現しませんか?

「〇〇さん、例の案件の納期、明日までだけど大丈夫?」 「あ!すみません、すっかり忘れてました…!」

私たちの町工場でも、こんなヒヤリとする会話が日常茶飯事でした。複数の案件が同時進行する中で、スプレッドシートの管理表だけでは、どうしても「うっかり忘れ」が起きてしまうんですよね。

タスク管理ツールを導入するほどの規模でもないし、何より普段使っている管理表から離れたくない…。

もし、あなたが毎日使っているそのスプレッドシートを開いた瞬間、「今日やるべきこと」が自動でポップアップ表示されたらどうでしょう?まるで優秀な秘書が隣にいるかのように、あなたのタスクをそっと教えてくれる。

今回は、そんな夢のような仕組みをGoogle Apps Script(GAS)を使って実現する方法をご紹介します。プログラミングなんてやったことない…という方もご安心ください!この記事の通りに手順を進めれば、誰でも「絶対に忘れないタスク管理システム」を手に入れることができます。

さあ、日々の「面倒」を「快感」に変えるDXの第一歩を、一緒に踏み出しましょう!

なぜ「スプレッドシート×GASリマインダー」が町工場に最適なのか?

 町工場とリマインダーのイメージ画像

世の中には便利なタスク管理ツールがたくさんありますが、なぜ私たちはスプレッドシートにこだわるのでしょうか?それには、小さな組織ならではの明確な理由があります。

理由1:見逃さない!「ポップアップ通知」の絶大な効果

メールでのリマインドは、他の業務連絡に埋もれてしまいがち。しかし、この仕組みなら業務で必ず開くスプレッドシート上に直接ポップアップが表示されるため、嫌でも目に入ります。この「強制力」が、うっかり忘れを防ぐ最強の武器になるのです。

理由2:追加コスト「ゼロ」で始められる

新しいツールを導入するには、月々の利用料や学習コストがかかります。その点、GoogleスプレッドシートとGASは、Googleアカウントさえあれば無料で利用可能。いつもの管理表に少し手を加えるだけで、高機能なシステムを構築できるのは大きな魅力です。

理由3:誰でも直感的に使える「使いやすさ」

リマインダーの設定からタスクの完了まで、操作は非常にシンプル。PCが苦手な方でも、ボタンをクリックしたり、チェックボックスに印をつけたりするだけで、直感的にタスクを管理できます。

3ステップで完成!「絶対に忘れない」リマインダーシステムの作り方

お待たせしました!ここからは、実際にリマインダーシステムを構築していく手順を、画像を交えて分かりやすく解説していきます。今回は、コピペで使えるサンプルコードも用意したので、安心してついてきてくださいね。

ステップ1:準備編 – システムの土台となるシートを用意しよう

まずは、リマインダーの情報を記録しておくための専用シートを2つ作成します。

現在お使いの管理表があるスプレッドシートを開きます。

今回もサンプルで進めます。

管理表サンプル
シート左下の「+」(シートを追加)ボタンをクリックし、新しいシートを2つ作成します。
スプレッドシート シート追加
それぞれのシート名を「リマインダー設定」と「リマインダーアーカイブ」に変更してください。

シート名をダブルクリックすると変更できます。

スプレッドシート シート名変更

次に、「リマインダー設定」シートに、以下の見出しをA1セルから順に入力します。

スプレッドシート 記載例

これで下準備は完了です!

ステップ2:実装編 – 魔法のコードを貼り付けよう

ここからが本番のGAS設定です。少し専門的に見えますが、やることはコピー&ペーストだけなのでご安心を!

  • スプレッドシートのメニューから「拡張機能」>「Apps Script」を選択します。
  • GASエディタが開きます。まず、コード.gs というファイルに、以下のスクリプトを全文コピーして貼り付けてください。元々書かれている内容は全て消してしまってOKです。

 コードの貼り付け方法などについてはこちらの記事を参考に 

参考記事
GASでスプレッドシート入力爆速化!コピペで始める自動化入門
GASでスプレッドシート入力爆速化!コピペで始める自動化入門
// ====================================================================
// ★★★【リマインダー機能】完全版 ★★★
// ====================================================================

// リマインダー情報を管理するシート名
const REMINDER_SHEET_NAME = "リマインダー設定";
const ARCHIVE_SHEET_NAME = "リマインダーアーカイブ";

// ▼▼▼ あなたの環境に合わせて、この部分を編集してください ▼▼▼
// リマインダー設定ダイアログに表示する担当者リスト
const 担当者リスト = {
  '山田': 'yamada@your-company.com',
  '鈴木': 'suzuki@your-company.com',
};
// ▲▲▲ あなたの環境に合わせて、この部分を編集してください ▲▲▲

/**
 * スプレッドシートを開いた時にカスタムメニューを追加する関数
 */
function onOpen() {
  const ui = SpreadsheetApp.getUi();
  ui.createMenu('リマインダー機能')
      .addItem('リマインダーを新規設定', 'showReminderDialog')
      .addSeparator()
      .addItem('完了タスクを整理する', 'archiveCompletedReminders')
      .addToUi();
}

/**
 * リマインダー設定用のダイアログを表示します。
 */
function showReminderDialog() {
  const ui = SpreadsheetApp.getUi();
  const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  const HEADER_ROW = 1; // 見出し行が1行目の場合

  const activeRange = sheet.getActiveRange();
  const activeRow = activeRange.getRow();

  if (activeRow <= HEADER_ROW) {
    ui.alert(`データ行(${HEADER_ROW + 1}行目以降)を選択してから実行してください。`);
    return;
  }
  
  // ★注意:管理番号(A列)と案件名(G列)の列番号を実際のシートに合わせてください
  const managementNumber = sheet.getRange(activeRow, 1).getDisplayValue();
  const projectName = sheet.getRange(activeRow, 7).getDisplayValue();
  const sheetUrl = SpreadsheetApp.getActiveSpreadsheet().getUrl() + "#gid=" + sheet.getSheetId() + "&range=A" + activeRow;

  if (!managementNumber) {
    ui.alert("選択された行の管理番号(A列)が空白です。");
    return;
  }

  const htmlTemplate = HtmlService.createTemplateFromFile('ReminderDialog');
  htmlTemplate.managementNumber = managementNumber;
  htmlTemplate.projectName = projectName;
  htmlTemplate.sheetUrl = sheetUrl;
  htmlTemplate.assigneeList = 担当者リスト;

  const htmlOutput = htmlTemplate.evaluate().setWidth(500).setHeight(520);
  ui.showModalDialog(htmlOutput, 'リマインダーを新規設定');
}

/**
 * ダイアログからデータを受け取り、リマインダー設定シートに書き込みます。
 */
function saveReminder(formData) {
  try {
    const reminderSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(REMINDER_SHEET_NAME);
    if (!reminderSheet) {
      throw new Error(`「${REMINDER_SHEET_NAME}」シートが見つかりません。`);
    }

    const linkFormula = `=HYPERLINK("${formData.sheetUrl}", "元の行へ")`;
    const newRow = [
      formData.managementNumber,
      formData.projectName,
      formData.assigneeEmail,
      formData.startDate,
      formData.deadline,
      formData.taskContent,
      false, // G列 (完了チェックボックス)
      linkFormula
    ];
    
    reminderSheet.appendRow(newRow);

    const lastRow = reminderSheet.getLastRow();
    reminderSheet.getRange(lastRow, 7).insertCheckboxes(); // G列にチェックボックスを挿入

    SpreadsheetApp.getActiveSpreadsheet().toast('リマインダーを設定しました。');
    return { success: true };
  } catch (e) {
    Logger.log("エラーが発生しました: " + e.message);
    return { success: false, message: e.message };
  }
}

/**
 * リマインダーをチェックして、ポップアップを表示する関数
 */
function checkAndShowReminders() {
  try {
    const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
    const sheet = spreadsheet.getSheetByName(REMINDER_SHEET_NAME);
    if (!sheet || sheet.getLastRow() < 2) return;

    const currentUserEmail = Session.getActiveUser().getEmail();
    if (!currentUserEmail) return;

    const dataRange = sheet.getRange(2, 1, sheet.getLastRow() - 1, sheet.getLastColumn());
    const values = dataRange.getValues();
    const today = new Date();
    today.setHours(0, 0, 0, 0);

    const tasksForPopup = [];
    values.forEach(function(row, index) {
      const assignedUserEmail = row[2]; // C列
      const startDate = new Date(row[3]);   // D列
      const deadline = new Date(row[4]);    // E列
      const isCompleted = row[6];           // G列
      
      const isMyTask = (assignedUserEmail.trim() === currentUserEmail);
      const isWithinRange = (today >= startDate && today <= deadline);

      if (isMyTask && !isCompleted && isWithinRange) {
        const projectName = row[1];
        const taskContent = row[5];
        const formattedDeadline = Utilities.formatDate(deadline, Session.getScriptTimeZone(), 'yyyy/MM/dd');
        
        tasksForPopup.push({
          row: index + 2, // 実際の行番号
          text: `[${projectName}] ${taskContent} (期限: ${formattedDeadline})`
        });
      }
    });

    if (tasksForPopup.length > 0) {
      const htmlTemplate = HtmlService.createTemplateFromFile('ReminderPopup');
      htmlTemplate.tasks = tasksForPopup;
      const htmlOutput = htmlTemplate.evaluate().setWidth(600).setHeight(400);
      SpreadsheetApp.getUi().showModalDialog(htmlOutput, '【重要】タスクのお知らせ');
    }

  } catch (e) {
    Logger.log("リマインダーチェック中にエラー: " + e.message);
  }
}

/**
 * ポップアップから渡された行番号のタスクを完了済みにする関数
 */
function markRemindersAsComplete(rowNumbers) {
  try {
    const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(REMINDER_SHEET_NAME);
    rowNumbers.forEach(row => {
      // G列(7番目の列)のチェックボックスをTRUEにする
      sheet.getRange(row, 7).setValue(true);
    });
    return { success: true };
  } catch (e) {
    Logger.log("完了チェック処理中にエラー: " + e.message);
    return { success: false, message: e.message };
  }
}

/**
 * 完了済みのリマインダーをアーカイブシートに移動する関数
 */
function archiveCompletedReminders() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sourceSheet = ss.getSheetByName(REMINDER_SHEET_NAME);
  let archiveSheet = ss.getSheetByName(ARCHIVE_SHEET_NAME);

  if (!archiveSheet) {
    archiveSheet = ss.insertSheet(ARCHIVE_SHEET_NAME);
    const headers = sourceSheet.getRange(1, 1, 1, sourceSheet.getLastColumn()).getValues();
    archiveSheet.getRange(1, 1, 1, headers[0].length).setValues(headers);
  }

  const range = sourceSheet.getRange(2, 1, sourceSheet.getLastRow() - 1, sourceSheet.getLastColumn());
  const values = range.getValues();
  
  const rowsToMove = [];
  const rowsToDelete = [];

  values.forEach((row, index) => {
    const isCompleted = row[6]; // G列の完了チェック
    if (isCompleted === true) {
      rowsToMove.push(row);
      rowsToDelete.push(index + 2);
    }
  });

  if (rowsToMove.length > 0) {
    archiveSheet.getRange(archiveSheet.getLastRow() + 1, 1, rowsToMove.length, rowsToMove[0].length).setValues(rowsToMove);
    
    for (let i = rowsToDelete.length - 1; i >= 0; i--) {
      sourceSheet.deleteRow(rowsToDelete[i]);
    }
    SpreadsheetApp.getUi().alert(`${rowsToMove.length}件の完了済みタスクを整理しました。`);
  } else {
    SpreadsheetApp.getUi().alert('整理する完了済みタスクはありませんでした。');
  }
}

担当者リスト の部分は、あなたの会社のメンバーの名前とGoogleアカウントのメールアドレスに書き換えてくださいね。

  • 次に、ポップアップウィンドウの見た目を作るためのHTMLファイルを作成します。エディタの左側にある「ファイル」の横の「+」をクリックし、「HTML」を選択します。
  • ファイル名を「ReminderPopup」として作成してください。
  • 作成されたReminderPopup.htmlファイルに、以下のコードを全文コピーして貼り付けてください。
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
    <style>
      body { font-family: 'Roboto', sans-serif; }
      .container { 
        padding: 15px 25px 20px 25px; 
        display: flex; 
        flex-direction: column; 
        height: 98%;
        box-sizing: border-box;
      }
      p.description { color: #444; margin-bottom: 15px; }
      .task-list { 
        flex-grow: 1; 
        overflow-y: auto; 
        border: 1px solid #ccc; 
        padding: 12px; 
        border-radius: 5px; 
        background-color: #f9f9f9;
      }
      .task-item { 
        display: flex; 
        align-items: center; 
        margin-bottom: 14px; 
      }
      .task-item input[type="checkbox"] { 
        margin-right: 12px; 
        min-width: 18px; 
        height: 18px; 
      }
      .task-item label { 
        font-size: 14px; 
        line-height: 1.5;
      }
      .button-bar { 
        flex-shrink: 0; 
        padding-top: 20px; 
        text-align: right; 
      }
      #status { 
        text-align: left; 
        font-size: 12px; 
        color: #666; 
        height: 1.2em; 
        float: left;
        line-height: 2.5;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <p class="description">以下のタスクが未完了です。完了した項目にチェックを入れて保存してください。</p>
      
      <div class="task-list">
        <? if (tasks && tasks.length > 0) { ?>
          <? tasks.forEach(function(task) { ?>
            <div class="task-item">
              <input type="checkbox" value="<?= task.row ?>">
              <label><?= task.text ?></label>
            </div>
          <? }); ?>
        <? } else { ?>
          <p>表示するタスクはありません。</p>
        <? } ?>
      </div>

      <div class="button-bar">
        <span id="status"></span>
        <button class="action" id="saveButton">完了を保存</button>
        <button onclick="google.script.host.close()">閉じる</button>
      </div>
    </div>

    <script>
      document.getElementById("saveButton").addEventListener("click", function() {
        const checkedBoxes = document.querySelectorAll('input[type="checkbox"]:checked');
        const statusEl = document.getElementById("status");
        
        if (checkedBoxes.length === 0) {
          statusEl.style.color = 'red';
          statusEl.textContent = '完了にするタスクが選択されていません。';
          return;
        }

        const rowNumbersToComplete = [];
        checkedBoxes.forEach(function(checkbox) {
          rowNumbersToComplete.push(parseInt(checkbox.value, 10));
        });

        this.disabled = true;
        statusEl.style.color = '#333';
        statusEl.textContent = '保存中...';

        google.script.run
          .withSuccessHandler(function(response) {
            if (response.success) {
              statusEl.style.color = 'green';
              statusEl.textContent = '保存しました!';
              setTimeout(function() {
                google.script.host.close();
              }, 1000);
            } else {
              statusEl.style.color = 'red';
              statusEl.textContent = 'エラー: ' + response.message;
              document.getElementById("saveButton").disabled = false;
            }
          })
          .markRemindersAsComplete(rowNumbersToComplete);
      });
    </script>
  </body>
</html>
  • 次に、ポップアップウィンドウの見た目を作るためのHTMLファイルを作成します。エディタの左側にある「ファイル」の横の「+」をクリックし、「HTML」を選択します。
  • ファイル名を「ReminderDialog」として作成してください。
  • 作成されたReminderDialog.htmlファイルに、以下のコードを全文コピーして貼り付けてください。
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <link rel="stylesheet" href="https://ssl.gstatic.com/docs/script/css/add-ons1.css">
    <style>
      body { font-family: 'Roboto', sans-serif; }
      .container { padding: 10px 20px; }
      .form-row { margin-bottom: 15px; }
      label { font-weight: bold; display: block; margin-bottom: 5px; font-size: 13px; color: #444; }
      input[type="text"], input[type="date"], select, textarea {
        width: 100%;
        padding: 8px;
        border: 1px solid #ccc;
        border-radius: 4px;
        box-sizing: border-box;
      }
      textarea { height: 80px; resize: vertical; }
      .button-bar { padding-top: 10px; text-align: right; border-top: 1px solid #eee; margin-top: 15px; }
      .info-box { background-color: #f9f9f9; border: 1px solid #ddd; border-radius: 4px; padding: 12px; margin-bottom: 20px; font-size: 13px; }
      #status { margin-top: 10px; font-size: 12px; color: red; }
    </style>
  </head>
  <body>
    <div class="container">
      <div class="info-box">
        <strong>管理番号:</strong> <?= managementNumber ?><br>
        <strong>案件名:</strong> <?= projectName ?>
      </div>

      <form id="reminderForm">
        <input type="hidden" name="managementNumber" value="<?= managementNumber ?>">
        <input type="hidden" name="projectName" value="<?= projectName ?>">
        <input type="hidden" name="sheetUrl" value="<?= sheetUrl ?>">

        <div class="form-row">
          <label for="assigneeEmail">担当者</label>
          <select id="assigneeEmail" name="assigneeEmail" required>
            <option value="" disabled selected>選択してください</option>
            <? for (var name in assigneeList) { ?>
              <option value="<?= assigneeList[name] ?>"><?= name ?></option>
            <? } ?>
          </select>
        </div>
        
        <div class="form-row">
          <label for="taskContent">タスク内容</label>
          <textarea id="taskContent" name="taskContent" required placeholder="例:〇〇の図面を修正して提出"></textarea>
        </div>

        <div class="form-row">
          <label for="startDate">通知開始日</label>
          <input type="date" id="startDate" name="startDate" required>
        </div>

        <div class="form-row">
          <label for="deadline">期限</label>
          <input type="date" id="deadline" name="deadline" required>
        </div>
      </form>
      <div id="status"></div>
      
      <div class="button-bar">
        <button class="action" id="saveButton">リマインダーを設定</button>
        <button onclick="google.script.host.close()">キャンセル</button>
      </div>
    </div>

    <script>
      // デフォルトの通知開始日を今日に設定
      document.getElementById('startDate').valueAsDate = new Date();

      document.getElementById("saveButton").addEventListener("click", function() {
        var form = document.getElementById('reminderForm');
        var statusEl = document.getElementById('status');
        
        if (!form.checkValidity()) {
          statusEl.textContent = 'エラー: 全ての項目を入力してください。';
          return;
        }

        this.disabled = true;
        statusEl.style.color = '#333';
        statusEl.textContent = '保存中...';
        
        var formData = {
          managementNumber: form.elements.managementNumber.value,
          projectName: form.elements.projectName.value,
          sheetUrl: form.elements.sheetUrl.value,
          assigneeEmail: form.elements.assigneeEmail.value,
          taskContent: form.elements.taskContent.value,
          startDate: form.elements.startDate.value,
          deadline: form.elements.deadline.value
        };
        
        google.script.run
          .withSuccessHandler(function(response) {
            if (response.success) {
              google.script.host.close();
            } else {
              statusEl.style.color = 'red';
              statusEl.textContent = '保存に失敗しました: ' + response.message;
              document.getElementById("saveButton").disabled = false;
            }
          })
          .withFailureHandler(function(error) {
            statusEl.style.color = 'red';
            statusEl.textContent = 'サーバーエラー: ' + error.message;
            document.getElementById("saveButton").disabled = false;
          })
          .saveReminder(formData);
      });
    </script>
  </body>
</html>

これで3つのファイルが出来ました。

最後に、フロッピーディスクのアイコン(プロジェクトを保存)をクリックして、変更を保存します。

GAS入力・保存

ステップ3:トリガーを追加する

動作をより確実なものにするためトリガーを設定していきます。

トリガーを選択

目覚まし時計マークの「トリガー」を選択

トリガーボタンを押す

右下にある「トリガーを追加」ボタンをクリック

トリガーを編集

画像のように設定し「保存」ボタンをクリック

ステップ4:実行編 – スクリプトの実行と権限の承認

コードを保存したら、一度だけスクリプトを実行して、Googleに「このプログラムを動かしてOKです」という許可を与える必要があります。

GAS 手動動作
承認が必要です
  1. GASエディタの上部にある関数選択プルダウンで onOpen を選び、「実行」ボタンをクリックします。
  2. 「承認が必要です」というポップアップが表示されるので、「権限を確認」をクリックします。
  3. 自分のGoogleアカウントを選択し、「詳細」→「(安全でないページ)に移動」をクリックします。
  4. 最後に「許可」をクリックすれば完了です。

承認作業の操作はこちらの記事にも記載しているので参考にしてください。

この承認作業は最初の1回だけなので、ご安心ください。 Google Apps Scriptについてはこちら。

GAS 実行ログ

このように実行ログが「実行完了」になればOKです。

実際に使ってみよう!感動のリマインダー体験

さあ、これで全ての準備が整いました!スプレッドシートに戻り、ページを再読み込みしてみてください。メニューバーに「リマインダー機能」という項目が追加されているはずです。

カスタムメニューが追加

1. 新しいタスクをリマインダーに登録する

追加したい案件の行にセルを置いて、新しくできたリマインダー機能メニューから「リマインダーを新規設定」を押してみます。

「リマインダー設定」シートに、以下のようにテストデータを入力してみてください。

リマインダーポップアップ

入力したら「リマインダーを設定」ボタンをクリックします。

2. ポップアップ通知を受け取る

スプレッドシートを一度閉じて、再度開いてみてください。

…どうでしょう?

タスクのお知らせ ポップアップ

画面中央に、先ほど登録したタスクが表示されたのではないでしょうか?この通知を見た瞬間の「おおっ!」という感動、ぜひ味わってみてください。

あとは、完了したタスクにチェックを入れて「完了を保存」ボタンを押すだけ。これであなたのタスクは完了済みとして記録されます。

3. 完了したタスクを整理(アーカイブ)する

「リマインダー設定」シートに完了済みのタスクが溜まってきたら、メニューの「リマインダー機能」>「完了タスクを整理する」をクリックしてみてください。完了済みのタスクが一瞬で「リマインダーアーカイブ」シートに移動し、タスクリストがスッキリ整理されます。

【応用編】AI(Gemini)と一緒にコードを改造してもっと便利に!

このままでも十分に便利ですが、GASの面白いところは、自分の好みに合わせて自由にカスタマイズできる点です。「でも、コードなんていじれない…」という方も、AIアシスタントのGeminiがいれば大丈夫!

例えば、こんな風にお願いしてみてください。

お願いの例①:通知のタイミングを変えたい!

「Google Apps Scriptで、期限の3日前から通知を開始するように checkAndShowReminders 関数を修正してください。現在は期限当日が含まれる場合のみ通知されます。」

お願いの例②:見た目を変えたい!

ReminderPopup.html のCSSを修正して、タスク一覧の背景色を薄い黄色(#FFFFE0)に変更してください。」

Geminiは、まるでプロのプログラマーのように、あなたの要望に合わせたコードを提案してくれます。ぜひ、AIとの対話を楽しみながら、あなただけの最強のタスク管理システムを作り上げてみてください!

【まとめ】小さな自動化が、未来の大きな余裕を生み出す

 スプレッドシートとリマインダーのイメージ画像

今回は、GoogleスプレッドシートとGASを使って、自動でタスクをお知らせしてくれるリマインダー機能の作り方をご紹介しました。

この仕組みが一つあるだけで、「あの件どうなった?」という確認の手間や、「忘れてた!」という冷や汗から解放されます。それは単なる業務効率化だけでなく、働く人の精神的な安心感にも繋がるはずです。

私たちのブログでは、これからも、こんな風に日々の「ちょっとした面倒」を「気持ちいいくらいの快感」に変える、小さな町工場だからこそできるDXのアイデアを発信していきます。

今回のリマインダー機能が、あなたの仕事に少しでも多くの「余裕」を生み出すきっかけになれば、これ以上嬉しいことはありません。

ABOUT ME
TAKA社長
TAKA社長
Google Workspace見習い
小さな町工場の社長。 日々業務に追われ何とか打開する策として『Google Workspace』を契約。実際にアナログで手間だった作業の問題事例とDX化で改善していく方法を詳しく紹介していきます。 DX化・システム化とか言ってますが、基本的にはAI(Gemini)任せでお願いしているだけなので作業としてはとても簡単。 ですので、GoogleWorkspaceの各アプリを効率的に使う方法や便利な機能などを理解してもらえば嬉しいです。
記事URLをコピーしました