Gmailに送られてくるキャンペーン情報を手でさばいているけど、量が多いため、自動化したい。
決まったキャンペーン情報を含むメールに対し、そこからキャンペーンの終了日を抽出して、Googleカレンダーに登録する方法を教えて欲しい。
こんなお悩みを解決します。
Googleが提供しているサービスは様々なものがあり、複数のサービスを組み合わせることで、さらに使いやすくなります。
今回は、Gmailに送られてくるキャンペーン情報をフィルタリング・情報抽出した上で、Googleカレンダーに登録するところまでを自動的に実施する方法について紹介します。
フィルタリングルールをGoogleスプレッドシートに切り出して管理することで、GUIで管理ができるように工夫しています。
また、自動化のためにGoogle Apps Scriptを利用しますが、この導入方法も含めて解説するので、興味がある方はぜひご覧ください。
効率良く技術習得したい方へ
短期間でプログラミング技術を習得したい場合は、経験者からフォローしてもらえる環境下で勉強することをおすすめします。
詳細は、以下の記事をご覧ください。
【比較】プログラミングスクールおすすめランキング6選【初心者向け】
続きを見る
【追記】Google Apps Script(GAS)のプログラム一式と設定情報のサンプルを追記いたしました。
【追記2】Googleカレンダー向けにカスタマイズした記事を作成いたしました。
やりたいこと
今回のケースでは、以下に示すように、Gmailから条件に該当するメールを取り出した上で、キャンペーンの情報を抽出し、カレンダーに反映する部分までを自動化します。
実現方法としては、図中の矢印にあたる処理をGoogle Apps Scriptで実装していくことになります。
完成形のイメージ
最終的に完成するものは、以下のようになります。
上記のように、キャンペーン終了日がカレンダーに登録されていることが分かります。
また、Gmailの条件や情報抽出時のルールは、以下に示すような表をGoogleスレッドシートで管理しているため、追加・変更が容易にできるようになっています。
以降では、実装例も含めて、具体的なステップに分けて紹介します。
準備
今回の自動化には、Google Apps Scriptというものを利用します。
javascriptによる実装が必要になりますが、該当箇所のコードもあわせて記載するので、コピペで同様の処理を実現できます。
Google Apps Scriptを利用するための準備
最初に、Google Apps Scriptを利用するための準備を進めていきます。
Google Driveを開き、適当な作業フォルダを作成します。今回は、「GoogleAppsScript」というフォルダを作成しました。
Google Driveの左上の「新規」を押下します。
そして、「その他」→「Google Apps Script」の順に押下して、Google Apps Scriptを起動します。
起動後は、以下のような画面が表示されると思います。
Googleカレンダーを利用するための準備
今回は、Googleカレンダーとも連携させるため、該当するサービスを追加します。
左側のメニューの「サービス」部分にある「+」を押下します。
一覧の中から「Google Calendar API ドキュメント」を選択し、「追加」を押下します。
元の画面に戻ると、カレンダーが追加されていることが確認できると思います。
Googleスプレッドシートの用意
一度、Google Driveに戻り、設定情報を記載するためのGoogleスプレッドシートを用意します。
先程と同様に、Google Driveの左上の「新規」を押下後、「Googleスプレッドシート」を押下します。
Excel風の画面が表示されると思います。
今後、シートが増えても対応できるように、今回使用するシートに名前を付けておきます。
以降の説明では、「カレンダー追加時の設定情報」というシート名を設定した前提で話を進めていきます。
また、Google Apps ScriptからGoogleスプレッドシートにアクセスするために、IDが必要になります。
画面に表示されている以下のIDをコピーしておいて下さい。(後ほど利用します。)
自動実行のための権限付与
Gmail、Googleスプレッドシート、Googleカレンダーは、その所有者もしくは権限が与えられた人のみが操作できます。
これは、他のユーザに勝手に操作されないようにするため、Googleの標準的な仕組みとして備わっています。
自動化時も、この仕組みが関係するため、Google Apps Scriptにも権限を与える必要があります。
権限の付与は、一度行っておけば良いため、先ほど作成したGoogle Apps Scriptにサンプルコードを記載し、権限を付与する作業を行っておきます。
記載するサンプルコードを以下に示します。
function myFunction() {
// =========
// データ定義
// =========
// スプレッドシートの情報
const spreadSheetParams = {
fileID: 'ABCDEFG0123456789abcdefg0123456789', // 先ほど調べたIDを入力する(ここではダミーを入力)
sheetName: 'カレンダー追加時の設定情報',
};
// スプレッドシートのサンプル
const spreadsheet = SpreadsheetApp.openById(spreadSheetParams.fileID);
const sheet = spreadsheet.getSheetByName(spreadSheetParams.sheetName);
// Gmailのサンプル
const threads = GmailApp.search('is:unread', 1, 10);
// Googleカレンダーのサンプル
const today = new Date();
const tomorrow = new Date();
tomorrow.setDate(today.getDate() + 1);
const calendar = CalendarApp.getEvents(today, tomorrow);
console.log('ok');
}
上記を記載後に、「実行」を押下します。
以下のようなメッセージが表示されるため、「権限を確認」を押下します。
Googleアカウントの選択画面が表示されるため、自分のアカウントであることを確認の上、アカウントを押下します。
次のページで警告画面が表示されますが、左下の「詳細」を押下し、その下に表示されたメッセージを押下します。
最後に、権限を付与するアプリ一覧が表示されます。
Gmail、Googleスプレッドシート、Googleカレンダーが選択肢に挙がっていることを確認した上で、右下の「許可」を押下します。
最後に、以下の実行時ログが表示されていることを確認してください。
以降では、処理を進めていく上での具体的な設定や処理内容を紹介します。
【コピペで再現可能】Google Apps Scriptによる自動化
今回は、Googleスプレッドシートに設定情報を記載していくことにしたため、以下のステップで解説します。
- Googleスプレッドシートを用いた設定情報の定義
- Google Apps Scriptによる実装
設定情報の定義
まずは、Googleスプレッドシートに設定情報を定義していきます。
今回は、以下の構成で設定情報を管理します。
項目 | プログラム上での名称 | 内容 | 例 |
---|---|---|---|
id | id | 項番 | 1 |
検索クエリ | query | Gmailで検索する際のクエリ | is:unread from:ec-mail@a8.net subject:"Xserver OR エックスサーバー" subject:"キャンペーン OR 報酬増額" |
タイトル | title | カレンダーに登録する際のタイトル | [A8.net]XServerキャンペーン期間 |
オプション | option | カレンダーに登録する際のカラー情報(後述) | PALE_GREEN |
開封処理 | markType | データ抽出後(成功・失敗は考慮しない)のメールの扱い ・read:既読にする ・unread:未読にする ・null:何もしない | null |
取得パターン | collectionPattern | メールの本文中から取得する文字列のパターン(要:正規表現) | (?<=キャンペーン期間\r\n).*〜(.*)\r\n |
抽出パターン | extractionPattern | 取得したパターンに対し、抽出したい文字列のパターン(要:正規表現) | (\d+年\d+月\d+日).*$ |
ここで、オプションにはGoogleカレンダーで利用する際のプロパティ名(下記参照)を設定する必要があります。
プロパティ名(オプションで指定するカラー情報) | カラーコード | 説明 |
---|---|---|
PALE_BLUE | #a4bdfc | 淡い青色("1") |
PALE_GREEN | #7AE7BF | 青緑色("2") |
MAUVE | #BDADFF | Mauve("3") |
PALE_RED | #FF887C | 淡い赤("4") |
YELLOW | #FBD75B | 黄("5") |
ORANGE | #FFB878 | オレンジ("6") |
CYAN | #46D6DB | シアン("7") |
GRAY | #E1E1E1 | グレー("8") |
BLUE | #5484ED | 青("9") |
GREEN | #51B749 | 緑("10") |
RED | #DC2127 | 赤色("11") |
私の環境では、以下のようなデータの入力規則を作成し、プルダウンメニューとして選択できるようにしています。
必要な情報を作成した後のGoogleスプレッドシートは、以下のようになります。
あくまで参考情報の位置づけとなるため、ご自身の環境に合わせて設定してください。
また、下記のリンクに設定情報のサンプル(今回用いたものと同様のもの)を格納しました。
詳細は下記のリンクをご覧ください。
https://docs.google.com/spreadsheets/d/1UUv2xwpkV5l-UikmGuKTVCwQ_m46il_QmVeijkD-lvo/edit?usp=sharing
【補足】正規表現で抽出される情報の例
正規表現に初めて触れる方に向け、上記に示した取得パターンや抽出パターンの出力のされ方を解説したいと思います。
メールの本文から青枠内の文字列を取り出したいとします。この場合、以下の手順で読み取っていけばよいことが分かります。
- 「キャンペーン期間\r\n」という文字列を探す。(\r\nは改行を表します)
- 1.で見つけた文字列に対し、次の改行(\r\n)が登場するまで読み出す。
- 2.の文字列のうち、「~」以降の文字を取得対象として取り出す。(取得したい情報が取り出せる)
- 3.の結果から「〇年〇月〇日」となっている部分を抽出対象として取り出す。(抽出したい情報が取り出せる)
上記のうち、1.~3.を一気に実行するのが、下記の正規表現となります。
/(?<=キャンペーン期間\r\n).*〜(.*)\r\n/
上記は、以下のように対応付きます。
後は、以下の正規表現を用いることで、4.の処理が実現できます。
/(\d+年\d+月\d+日).*$/
正規表現に関する補足は以上となります。
Google Apps Scriptsによる実装
以降では、Google Apps Scriptにより、先ほど示した処理内容を実現していきます。
Googleスプレッドシートから設定情報を取得する処理
/**
* @brief Google SpreadSheetから設定情報を取得
* @param[in] params
* fileID: Google SpreadSheetのファイルID
* sheetName: 設定情報が記載されたシート名
* debug デバッグモード(true: オン、false: オフ)
* @return results 設定情報一覧
*/
const getConfigFromSpreadSheet = (params, debug=true) => {
// スプレッドシートから該当するシートを取得
const spreadsheet = SpreadsheetApp.openById(params.fileID);
const sheet = spreadsheet.getSheetByName(params.sheetName);
// データ取得
results = sheet.getRange(2, 1, sheet.getLastRow() - 1, sheet.getLastColumn()).getValues();
if (debug) {
// 取得データの出力
console.log('設定情報一覧')
console.log(results);
}
return results;
};
サンプルで作成した処理と似ていますね。
データを取得する際は、getRangeとgetValuesを利用しています。
getRangeは引数が必要なため、各引数の意味を以下に示します。
引数 | 役割 | 補足 |
---|---|---|
row | 範囲開始時の行番号 | - |
column | 範囲開始時の列番号 | A列が1となる |
numRows | 読み取る行数 | sheet.getLastRow()で、データがある最後の行を指定できる |
numColumns | 読み取る列数 | sheet.getLastColumn()で、データがある最後の列を指定できる |
指定したクエリに合致するメール一覧を取得する処理
/**
* @brief 指定したクエリに合致するメール一覧を取得
* @param[in] query 検索クエリ
* @param[in] debug デバッグモード(true: オン、false: オフ)
*
* @return results 該当メッセージ一覧(mapオブジェクト)
*/
const getGmailMessages = (query, debug=true) => {
// スレッドの取得
const threads = GmailApp.search(query);
// スレッドからメッセージの中身を取得
const messagesForThreads = GmailApp.getMessagesForThreads(threads);
// メッセージ一覧
const results = {};
const callback = debug ? ((msg) => console.log(` ${msg.getSubject()}`)) : ((msg) => null);
if (debug) {
console.log(`query: ${query}`);
console.log('取得したメッセージ(件名)');
}
for (const thread of messagesForThreads) {
for (const message of thread) {
// メッセージを取得
results[message.getId()] = message;
callback(message);
}
}
return results;
};
こちらはほぼ定型文となるため、このまま利用していただければ問題ないです。
また、それぞれの処理内容はコメントにある通りとなります。
パターンに基づく探索を行う処理
/**
* @brief パターンに基づく探索
* @param[in] messages 探索対象メッセージ一覧
* @param[in] pattern 検索パターン
* @param[in] debug デバッグモード(true: オン、false: オフ)
*
* @return results 探索結果
*/
const searchMessage = (messages, pattern, debug=false) => {
const results = [];
const re = new RegExp(pattern);
for (const key of Object.keys(messages)) {
const msg = messages[key];
const body = msg.getPlainBody();
// 本文からパターンに合致する情報を抽出
const matched = body.match(re, 'g');
// 該当するデータの有無をチェック
if (matched) {
const output = matched.slice(1);
results.push(...output);
if (debug) {
const subject = msg.getSubject();
const out = output.join(',');
console.log(`subject: ${subject}`);
console.log(` matched: ${out}`);
}
}
}
return results;
};
デバッグ処理が含まれているため、様々な処理をしているように見えますが、核となる部分は、以下の2点となります。
- メールの本文中から、正規表現に合致する箇所を取り出し、matchedに格納する。
- 該当するパターンが存在する場合、その内容をmatched.slice(1)で取り出し、スプレッド構文を用いて要素ごとに展開して配列に格納する。
※スプレッド構文:...outputとなっている箇所が該当し、オブジェクトや配列のすべての要素をリストに入れる場合などに使われる。
パターンに基づく後処理
/**
* @brief パターンに基づく後処理
* @param[in] matched 探索結果
* @param[in] pattern 後処理パターン
* @param[in] debug デバッグモード(true: オン、false: オフ)
*
* @return results 処理結果
*/
const extractData = (matched, pattern, debug=false) => {
const results = [];
const re = new RegExp(pattern);
for (const target of matched) {
const arr = target.match(re);
if (arr) {
const vals = arr.slice(1);
results.push(...vals);
if (debug) {
const out = vals.join(',');
console.log(`target: ${target}`);
console.log(` pattern: ${pattern}`);
console.log(` matched: ${out}`);
}
}
}
return results;
};
こちらも先ほどとほぼ同様で、パターンに合致したものに対し、正規表現を適用し、該当するデータを取り出していきます。
設定情報ごとにデータを処理(上記処理の組み合わせ)
これまで、個別の内容について紹介しました。
残りの部分では、上記の2つの関数(searchMessage、extractData)を用いて、情報抽出を行う処理を紹介します。
/**
* @brief 設定情報ごとにデータを処理
* @param[in] cells 設定情報一覧
* @param[in] debug デバッグモード(true: オン、false: オフ)
*
* @return results 集計結果
*/
const process = (cells, debug=false) => {
const results = [];
// 行ごとに処理
for (const row of cells) {
// ID以外のデータを取得
const [query, title, option, markType, collectionPattern, extractionPattern] = row.slice(1);
// メッセージ一覧を取得
const messages = getGmailMessages(query, debug);
// パターンに基づく探索を実行
const matched = searchMessage(messages, collectionPattern, debug);
const extracted = extractData(matched, extractionPattern, debug);
// データ格納
results.push({
title: title,
option: option,
targets: extracted,
});
// メッセージの後処理
let callback = (msg) => null;
switch (markType) {
case 'read':
callback = (msg) => (!debug ? msg.markRead() : null);
break;
case 'unread':
callback = (msg) => (!debug ? msg.markUnread() : null);
break;
case 'null':
break;
default:
break;
}
for (const key of Object.keys(messages)) {
const msg = messages[key];
callback(msg);
}
}
return results;
};
今回は、メールの開封状態も情報として与えているため、28行目以降で、与えられたパラメータに基づいて開封状態を切り替える処理を追加しています。
メイン処理(Googleカレンダーへの追加を含む)
これまでの結果をもとに、Googleカレンダーに予定を追加する部分について紹介します。
今回、メイン処理として呼び出す関数は、searchMessageAndAddCalender
という名前にしております。
また、流用する際は、スプレッドシートのfileID
をご自身の環境に合わせて変更してください。
function searchMessageAndAddCalender() {
// =========
// データ定義
// =========
// スプレッドシートの情報
const spreadSheetParams = {
fileID: 'ABCDEFG0123456789abcdefg0123456789', // 自身の環境に合わせて変更する
sheetName: 'カレンダー追加時の設定情報',
};
// デバッグモード
const debug = false;
// 設定情報一覧を取得
const cells = getConfigFromSpreadSheet(spreadSheetParams, debug);
// メール検索処理を実行
const outputs = process(cells, debug);
if (debug) {
console.log(outputs);
}
// =====================
// Google Calendarに追加
// =====================
calendar = CalendarApp.getDefaultCalendar();
for (const item of outputs) {
for (const val of item.targets) {
// 文字列から日付を取得
const date = new Date(val.replace(/[^0-9]+/g, '/'));
const humanReadableFormat = Utilities.formatDate(date, 'JST', 'yyyy/MM/dd');
if (debug) {
const colorCode = CalendarApp.EventColor[item.option];
console.log(`Target: ${humanReadableFormat}, color: ${item.option}(${colorCode})`);
}
else {
try {
// 登録済みのイベントの有無チェック
const endDate = new Date(date.getFullYear(), date.getMonth(), date.getDay());
endDate.setDate(date.getDate() + 1);
const registeredEvents = CalendarApp.getEvents(date, endDate);
let exists = false;
if (registeredEvents) {
for (const event of registeredEvents) {
const targetTitlte = event.getTitle();
// 同じタイトルのイベントが登録済みの場合
if (targetTitlte === item.title) {
exists = true;
break;
}
}
}
if (!exists) {
// 終日の予定として追加
const color = CalendarApp.EventColor[item.option];
event = calendar.createAllDayEvent(item.title, date);
event.setColor(color);
console.log(`register event "${item.title}" on ${humanReadableFormat}`);
}
else {
console.log(`skip registration "${item.title}" on ${humanReadableFormat}`);
}
}
catch (err) {
console.error(`Error: ${err}`);
}
}
}
}
}
上記のプログラムを見ていただくと分かりますが、すでに予定が追加されている場合は、スキップできるように工夫しております。
また、今回利用したGoogleカレンダーのうち、他のAPIの使い方に関しては以下を参照してください。
https://developers.google.com/apps-script/reference/calendar/calendar?hl=ja
【追記】プログラム一式と設定情報のサンプル
これまでに紹介したGoogle Apps Scriptのプログラムをまとめると以下のようになります。
function searchMessageAndAddCalender() {
// =========
// データ定義
// =========
// スプレッドシートの情報
const spreadSheetParams = {
fileID: '1UUv2xwpkV5l-UikmGuKTVCwQ_m46il_QmVeijkD-lvo', // ★自身の環境にあわせて変更する
sheetName: 'カレンダー追加時の設定情報',
};
// デバッグモード
const debug = false;
// 設定情報一覧を取得
const cells = getConfigFromSpreadSheet(spreadSheetParams, debug);
// メール検索処理を実行
const outputs = process(cells, debug);
if (debug) {
console.log(outputs);
}
// =====================
// Google Calendarに追加
// =====================
calendar = CalendarApp.getDefaultCalendar();
for (const item of outputs) {
for (const val of item.targets) {
// 文字列から日付を取得
const date = new Date(val.replace(/[^0-9]+/g, '/'));
const humanReadableFormat = Utilities.formatDate(date, 'JST', 'yyyy/MM/dd');
if (debug) {
const colorCode = CalendarApp.EventColor[item.option];
console.log(`Target: ${humanReadableFormat}, color: ${item.option}(${colorCode})`);
}
else {
try {
// 登録済みのイベントの有無チェック
const endDate = new Date(date.getFullYear(), date.getMonth(), date.getDay());
endDate.setDate(date.getDate() + 1);
const registeredEvents = CalendarApp.getEvents(date, endDate);
let exists = false;
if (registeredEvents) {
for (const event of registeredEvents) {
const targetTitlte = event.getTitle();
// 同じタイトルのイベントが登録済みの場合
if (targetTitlte === item.title) {
exists = true;
break;
}
}
}
if (!exists) {
// 終日の予定として追加
const color = CalendarApp.EventColor[item.option];
event = calendar.createAllDayEvent(item.title, date);
event.setColor(color);
console.log(`register event "${item.title}" on ${humanReadableFormat}`);
}
else {
console.log(`skip registration "${item.title}" on ${humanReadableFormat}`);
}
}
catch (err) {
console.error(`Error: ${err}`);
}
}
}
}
}
/**
* @brief Google SpreadSheetから設定情報を取得
* @param[in] params
* fileID: Google SpreadSheetのファイルID
* sheetName: 設定情報が記載されたシート名
* debug デバッグモード(true: オン、false: オフ)
* @return results 設定情報一覧
*/
const getConfigFromSpreadSheet = (params, debug=true) => {
// スプレッドシートから該当するシートを取得
const spreadsheet = SpreadsheetApp.openById(params.fileID);
const sheet = spreadsheet.getSheetByName(params.sheetName);
// データ取得
results = sheet.getRange(2, 1, sheet.getLastRow() - 1, sheet.getLastColumn()).getValues();
if (debug) {
// 取得データの出力
console.log('設定情報一覧')
console.log(results);
}
return results;
};
/**
* @brief 設定情報ごとにデータを処理
* @param[in] cells 設定情報一覧
* @param[in] debug デバッグモード(true: オン、false: オフ)
*
* @return results 集計結果
*/
const process = (cells, debug=false) => {
const results = [];
// 行ごとに処理
for (const row of cells) {
// ID以外のデータを取得
const [query, title, option, markType, collectionPattern, extractionPattern] = row.slice(1);
// メッセージ一覧を取得
const messages = getGmailMessages(query, debug);
// パターンに基づく探索を実行
const matched = searchMessage(messages, collectionPattern, debug);
const extracted = extractData(matched, extractionPattern, debug);
// データ格納
results.push({
title: title,
option: option,
targets: extracted,
});
// メッセージの後処理
let callback = (msg) => null;
switch (markType) {
case 'read':
callback = (msg) => (!debug ? msg.markRead() : null);
break;
case 'unread':
callback = (msg) => (!debug ? msg.markUnread() : null);
break;
case 'null':
break;
default:
break;
}
for (const key of Object.keys(messages)) {
const msg = messages[key];
callback(msg);
}
}
return results;
};
/**
* @brief 指定したクエリに合致するメール一覧を取得
* @param[in] query 検索クエリ
* @param[in] debug デバッグモード(true: オン、false: オフ)
*
* @return results 該当メッセージ一覧(mapオブジェクト)
*/
const getGmailMessages = (query, debug=true) => {
// スレッドの取得
const threads = GmailApp.search(query);
// スレッドからメッセージの中身を取得
const messagesForThreads = GmailApp.getMessagesForThreads(threads);
// メッセージ一覧
const results = {};
const callback = debug ? ((msg) => console.log(` ${msg.getSubject()}`)) : ((msg) => null);
if (debug) {
console.log(`query: ${query}`);
console.log('取得したメッセージ(件名)');
}
for (const thread of messagesForThreads) {
for (const message of thread) {
// メッセージを取得
results[message.getId()] = message;
callback(message);
}
}
return results;
};
/**
* @brief パターンに基づく探索
* @param[in] messages 探索対象メッセージ一覧
* @param[in] pattern 検索パターン
* @param[in] debug デバッグモード(true: オン、false: オフ)
*
* @return results 探索結果
*/
const searchMessage = (messages, pattern, debug=false) => {
const results = [];
const re = new RegExp(pattern);
for (const key of Object.keys(messages)) {
const msg = messages[key];
const body = msg.getPlainBody();
// 本文からパターンに合致する情報を抽出
const matched = body.match(re, 'g');
// 該当するデータの有無をチェック
if (matched) {
const output = matched.slice(1);
results.push(...output);
if (debug) {
const subject = msg.getSubject();
const out = output.join(',');
console.log(`subject: ${subject}`);
console.log(` matched: ${out}`);
}
}
}
return results;
};
/**
* @brief パターンに基づく後処理
* @param[in] matched 探索結果
* @param[in] pattern 後処理パターン
* @param[in] debug デバッグモード(true: オン、false: オフ)
*
* @return results 処理結果
*/
const extractData = (matched, pattern, debug=false) => {
const results = [];
const re = new RegExp(pattern);
for (const target of matched) {
const arr = target.match(re);
if (arr) {
const vals = arr.slice(1);
results.push(...vals);
if (debug) {
const out = vals.join(',');
console.log(`target: ${target}`);
console.log(` pattern: ${pattern}`);
console.log(` matched: ${out}`);
}
}
}
return results;
};
また、下記のリンクに設定情報のサンプル(今回用いたものと同様のもの)を格納しました。
ご自由にご利用ください。
https://docs.google.com/spreadsheets/d/1UUv2xwpkV5l-UikmGuKTVCwQ_m46il_QmVeijkD-lvo/edit?usp=sharing
まとめ
今回は、Gmail、Googleスプレッドシート、Googleカレンダーを組み合わせて、「決まったキャンペーン情報を含むメールに対し、そこからキャンペーンの終了日を抽出して、Googleカレンダーに登録する」という処理を自動化する方法について紹介しました。
かなり用途が限定されていますが、普段の作業を自動化したいと思っている方も多いと思います。
今回の事例を参考にご自身の目的に合わせてアップデートしていただければと思います。
【追記】続きの記事を執筆しましたので、ぜひご覧ください。
効率良く技術習得したい方へ
今回の話の中で、プログラミングについてよく分からなかった方もいると思います。
このような場合、エラーが発生した際に対応できなくなってしまうため、経験者からフォローしてもらえる環境下で勉強することをおすすめします。
詳細は、以下の記事をご覧ください。
【比較】プログラミングスクールおすすめランキング6選【初心者向け】
続きを見る