1. 개요
이 Codelab에서는 Google Apps Script를 사용하여 사용자가 Gmail 내에서 직접 이메일의 영수증 데이터를 스프레드시트에 추가할 수 있는 Gmail용 Google Workspace 부가기능을 작성합니다. 사용자가 이메일로 영수증을 받으면 이메일에서 관련 비용 정보를 자동으로 가져오는 부가기능을 엽니다. 사용자는 비용 정보를 수정하고 제출하여 Google Sheets 스프레드시트에 비용을 기록할 수 있습니다.
학습할 내용
- Google Apps Script를 사용하여 Gmail용 Google Workspace 부가기능 만들기
- Google Apps Script로 이메일 파싱
- Google Apps Script를 통해 Google Sheets와 상호작용
- Google Apps Script의 속성 서비스를 사용하여 사용자 값 저장
필요한 항목
- 인터넷 및 웹브라우저 액세스
- Google 계정
- Gmail의 일부 메시지(이메일 영수증이 바람직함)
2. 샘플 코드 가져오기
이 Codelab을 진행할 때 작성할 코드의 작동하는 버전을 참고하면 도움이 될 수 있습니다. GitHub 저장소에는 참조로 사용할 수 있는 샘플 코드가 포함되어 있습니다.
샘플 코드를 가져오려면 명령줄에서 다음을 실행합니다.
git clone https://github.com/googleworkspace/gmail-add-on-codelab.git
3. 기본 부가기능 만들기
이메일과 함께 비용 양식을 표시하는 간단한 버전의 부가기능 코드를 작성하는 것으로 시작합니다.
먼저 새 Apps Script 프로젝트를 만들고 매니페스트 파일을 엽니다.
- script.google.com으로 이동합니다. 여기에서 Apps Script 프로젝트를 만들고, 관리하고, 모니터링할 수 있습니다.
- 새 프로젝트를 만들려면 왼쪽 상단에서 새 프로젝트를 클릭합니다. 새 프로젝트가
Code.gs라는 기본 파일과 함께 열립니다. 지금은Code.gs을 그대로 두세요. 나중에 사용합니다. - 제목 없는 프로젝트를 클릭하고 프로젝트 이름을 Expense It!로 지정한 다음 이름 바꾸기를 클릭합니다.
- 왼쪽에서 프로젝트 설정
을 클릭합니다. - 편집기에 'appscript.json' 매니페스트 파일 표시 체크박스를 선택합니다.
- 편집기
를 클릭합니다. - 매니페스트 파일을 열려면 왼쪽에서
appscript.json를 클릭합니다.
appscript.json에서 부가기능과 연결된 메타데이터(예: 이름, 필요한 권한)를 지정합니다. appsscript.json의 내용을 다음 구성 설정으로 바꿉니다.
{
"timeZone": "GMT",
"oauthScopes": [
"https://www.googleapis.com/auth/gmail.addons.execute"
],
"gmail": {
"name": "Expense It!",
"logoUrl": "https://www.gstatic.com/images/icons/material/system/1x/receipt_black_24dp.png",
"contextualTriggers": [{
"unconditional": {
},
"onTriggerFunction": "getContextualAddOn"
}],
"primaryColor": "#41f470",
"secondaryColor": "#94f441"
}
}
매니페스트의 contextualTriggers 부분을 특히 주의하세요. 매니페스트의 이 부분은 부가기능이 처음 활성화될 때 호출할 사용자 정의 함수를 식별합니다. 이 경우 열린 이메일에 관한 세부정보를 가져오고 사용자에게 표시할 카드 세트를 반환하는 getContextualAddOn를 호출합니다.
getContextualAddOn 함수를 만들려면 다음 단계를 따르세요.
- 왼쪽에서 포인터를
Code.gs위로 가져간 다음 메뉴
> 이름 바꾸기를 클릭합니다. GetContextualAddOn를 입력하고Enter키를 누릅니다. Apps Script는 파일 이름에.gs를 자동으로 추가하므로 파일 확장자를 입력할 필요가 없습니다.GetContextualAddOn.gs을 입력하면 Apps Script에서 파일 이름을GetContextualAddOn.gs.gs로 지정합니다.GetContextualAddOn.gs에서 기본 코드를getContextualAddOn함수로 바꿉니다.
/**
* Returns the contextual add-on data that should be rendered for
* the current e-mail thread. This function satisfies the requirements of
* an 'onTriggerFunction' and is specified in the add-on's manifest.
*
* @param {Object} event Event containing the message ID and other context.
* @returns {Card[]}
*/
function getContextualAddOn(event) {
var card = CardService.newCardBuilder();
card.setHeader(CardService.newCardHeader().setTitle('Log Your Expense'));
var section = CardService.newCardSection();
section.addWidget(CardService.newTextInput()
.setFieldName('Date')
.setTitle('Date'));
section.addWidget(CardService.newTextInput()
.setFieldName('Amount')
.setTitle('Amount'));
section.addWidget(CardService.newTextInput()
.setFieldName('Description')
.setTitle('Description'));
section.addWidget(CardService.newTextInput()
.setFieldName('Spreadsheet URL')
.setTitle('Spreadsheet URL'));
card.addSection(section);
return [card.build()];
}
모든 Google Workspace 부가기능의 부가기능 사용자 인터페이스는 하나 이상의 섹션으로 분할된 카드로 구성되며, 각 섹션에는 사용자로부터 정보를 표시하고 가져올 수 있는 위젯이 포함됩니다. getContextualAddOn 함수는 이메일에서 발견된 비용에 관한 세부정보를 가져오는 단일 카드를 만듭니다. 카드에는 관련 데이터의 텍스트 입력 필드가 포함된 섹션이 하나 있습니다. 이 함수는 부가기능의 카드 배열을 반환합니다. 이 경우 반환된 배열에는 카드 하나만 포함됩니다.
Expense It! 부가기능을 배포하기 전에 Apps Script 프로젝트에서 승인, 고급 서비스, 기타 세부정보를 관리하는 데 사용하는 Google Cloud Platform (GCP) 프로젝트가 필요합니다. 자세한 내용은 Google Cloud Platform 프로젝트를 참고하세요.
애드온을 배포하고 실행하려면 다음 단계를 따르세요.
- GCP 프로젝트를 열고 프로젝트 번호를 복사합니다.
- Apps Script 프로젝트의 왼쪽에서 프로젝트 설정
을 클릭합니다. - 'Google Cloud Platform (GCP) 프로젝트'에서 프로젝트 변경을 클릭합니다.
- GCP 프로젝트의 프로젝트 번호를 입력한 다음 프로젝트 설정을 클릭합니다.
- 배포> 테스트 배포를 클릭합니다.
- 배포 유형이 Google Workspace 부가기능인지 확인합니다. 필요한 경우 대화상자 상단에서 배포 유형 사용 설정
을 클릭하고 배포 유형으로 Google Workspace 부가기능을 선택합니다. - 애플리케이션: Gmail 옆에 있는 설치를 클릭합니다.
- 완료를 클릭합니다.
이제 Gmail 받은편지함에서 부가기능을 확인할 수 있습니다.
- 컴퓨터에서 Gmail을 엽니다.
- 오른쪽 측면 패널에 Expense It!
부가기능이 표시됩니다. '더 많은 부가기능'
을 클릭해야 찾을 수 있습니다. - 이메일을 엽니다. 영수증과 비용이 포함된 이메일이 좋습니다.
- 오른쪽 측면 패널에서 Expense It!을 클릭하여 부가기능을 엽니다.
. - 액세스 승인을 클릭하여 Expense It!에 Google 계정 액세스 권한을 부여하고 표시되는 메시지를 따릅니다.
부가기능은 열려 있는 Gmail 메일 옆에 간단한 양식을 표시합니다. 아직 다른 작업은 실행되지 않지만 다음 섹션에서 기능을 빌드합니다.
이 실습을 진행하면서 애드온의 업데이트를 확인하려면 코드를 저장하고 Gmail을 새로고침하기만 하면 됩니다. 추가 배포는 필요하지 않습니다.
4. 이메일 메시지 액세스
이메일 콘텐츠를 가져오는 코드를 추가하고 코드를 모듈화하여 좀 더 체계적으로 정리합니다.
파일 옆에 있는 추가
> 스크립트를 클릭하고 Cards라는 파일을 만듭니다. Helpers라는 두 번째 스크립트 파일을 만듭니다. Cards.gs는 카드를 만들고 Helpers.gs의 함수를 사용하여 이메일의 콘텐츠를 기반으로 양식의 필드를 채웁니다.
Cards.gs의 기본 코드를 다음 코드로 바꿉니다.
var FIELDNAMES = ['Date', 'Amount', 'Description', 'Spreadsheet URL'];
/**
* Creates the main card users see with form inputs to log expenses.
* Form can be prefilled with values.
*
* @param {String[]} opt_prefills Default values for each input field.
* @param {String} opt_status Optional status displayed at top of card.
* @returns {Card}
*/
function createExpensesCard(opt_prefills, opt_status) {
var card = CardService.newCardBuilder();
card.setHeader(CardService.newCardHeader().setTitle('Log Your Expense'));
if (opt_status) {
if (opt_status.indexOf('Error: ') == 0) {
opt_status = '<font color=\'#FF0000\'>' + opt_status + '</font>';
} else {
opt_status = '<font color=\'#228B22\'>' + opt_status + '</font>';
}
var statusSection = CardService.newCardSection();
statusSection.addWidget(CardService.newTextParagraph()
.setText('<b>' + opt_status + '</b>'));
card.addSection(statusSection);
}
var formSection = createFormSection(CardService.newCardSection(),
FIELDNAMES, opt_prefills);
card.addSection(formSection);
return card;
}
/**
* Creates form section to be displayed on card.
*
* @param {CardSection} section The card section to which form items are added.
* @param {String[]} inputNames Names of titles for each input field.
* @param {String[]} opt_prefills Default values for each input field.
* @returns {CardSection}
*/
function createFormSection(section, inputNames, opt_prefills) {
for (var i = 0; i < inputNames.length; i++) {
var widget = CardService.newTextInput()
.setFieldName(inputNames[i])
.setTitle(inputNames[i]);
if (opt_prefills && opt_prefills[i]) {
widget.setValue(opt_prefills[i]);
}
section.addWidget(widget);
}
return section;
}
createExpensesCard 함수는 양식을 미리 채울 값의 배열을 선택적 인수로 사용합니다. 이 함수는 선택적 상태 메시지를 표시할 수 있으며, 상태가 'Error:'로 시작하면 빨간색으로 표시되고 그렇지 않으면 녹색으로 표시됩니다. 각 필드를 양식에 수동으로 추가하는 대신 createFormSection라는 도우미 함수가 텍스트 입력 위젯을 만드는 프로세스를 반복하고 setValue로 각 기본값을 설정한 다음 위젯을 카드에서 각 섹션에 추가합니다.
이제 Helpers.gs의 기본 코드를 다음 코드로 바꿉니다.
/**
* Finds largest dollar amount from email body.
* Returns null if no dollar amount is found.
*
* @param {Message} message An email message.
* @returns {String}
*/
function getLargestAmount(message) {
return 'TODO';
}
/**
* Determines date the email was received.
*
* @param {Message} message An email message.
* @returns {String}
*/
function getReceivedDate(message) {
return 'TODO';
}
/**
* Determines expense description by joining sender name and message subject.
*
* @param {Message} message An email message.
* @returns {String}
*/
function getExpenseDescription(message) {
return 'TODO';
}
/**
* Determines most recent spreadsheet URL.
* Returns null if no URL was previously submitted.
*
* @returns {String}
*/
function getSheetUrl() {
return 'TODO';
}
Helpers.gs의 함수는 getContextualAddon에 의해 호출되어 양식에 미리 입력된 값을 결정합니다. 지금은 나중에 단계에서 미리 채우기 로직을 구현할 것이므로 이러한 함수는 'TODO' 문자열만 반환합니다.
다음으로 Cards.gs 및 Helpers.gs의 코드를 활용하도록 GetContextualAddon.gs의 코드를 업데이트합니다. GetContextualAddon.gs의 코드를 다음 코드로 바꿉니다.
/**
* Copyright 2017 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Returns the contextual add-on data that should be rendered for
* the current e-mail thread. This function satisfies the requirements of
* an 'onTriggerFunction' and is specified in the add-on's manifest.
*
* @param {Object} event Event containing the message ID and other context.
* @returns {Card[]}
*/
function getContextualAddOn(event) {
var message = getCurrentMessage(event);
var prefills = [getReceivedDate(message),
getLargestAmount(message),
getExpenseDescription(message),
getSheetUrl()];
var card = createExpensesCard(prefills);
return [card.build()];
}
/**
* Retrieves the current message given an action event object.
* @param {Event} event Action event object
* @return {Message}
*/
function getCurrentMessage(event) {
var accessToken = event.messageMetadata.accessToken;
var messageId = event.messageMetadata.messageId;
GmailApp.setCurrentMessageAccessToken(accessToken);
return GmailApp.getMessageById(messageId);
}
Gmail에서 제공한 이벤트를 사용하여 사용자가 현재 열고 있는 메시지를 읽는 새로운 getCurrentMessage 함수를 참고하세요. 이 함수가 작동하려면 Gmail 메시지에 대한 읽기 전용 액세스를 허용하는 추가 범위를 스크립트 매니페스트에 추가하세요.
appscript.json에서 https://www.googleapis.com/auth/gmail.addons.current.message.readonly 범위도 요청하도록 oauthScopes를 업데이트합니다.
"oauthScopes": [
"https://www.googleapis.com/auth/gmail.addons.execute",
"https://www.googleapis.com/auth/gmail.addons.current.message.readonly"
],
Gmail에서 부가기능을 실행하고 Expense It!이 이메일 메시지를 볼 수 있도록 액세스를 승인합니다. 이제 양식 필드가 'TODO'로 미리 입력됩니다.
5. Google Sheets와 상호작용
Expense It! 부가기능에는 사용자가 비용에 관한 세부정보를 입력할 수 있는 양식이 있지만 이 세부정보를 보낼 곳이 없습니다. 양식 데이터를 Google 시트로 전송하는 버튼을 추가해 보겠습니다.
버튼을 추가하려면 ButtonSet 클래스를 사용합니다. Google Sheets와 인터페이스하려면 Google Sheets 서비스를 사용합니다.
createFormSection를 수정하여 카드 양식 섹션의 일부로 '제출'이라는 라벨이 지정된 버튼을 반환합니다. 다음 단계를 따릅니다.
CardService.newTextButton()를 사용하여 텍스트 버튼을 만들고CardService.TextButton.setText()를 사용하여 버튼에 '제출'이라는 라벨을 지정합니다.- 버튼을 클릭하면
CardService.TextButton.setOnClickAction()을 통해 다음submitForm작업이 호출되도록 버튼을 설계합니다.
/**
* Logs form inputs into a spreadsheet given by URL from form.
* Then displays edit card.
*
* @param {Event} e An event object containing form inputs and parameters.
* @returns {Card}
*/
function submitForm(e) {
var res = e['formInput'];
try {
FIELDNAMES.forEach(function(fieldName) {
if (! res[fieldName]) {
throw 'incomplete form';
}
});
var sheet = SpreadsheetApp
.openByUrl((res['Spreadsheet URL']))
.getActiveSheet();
sheet.appendRow(objToArray(res, FIELDNAMES.slice(0, FIELDNAMES.length - 1)));
return createExpensesCard(null, 'Logged expense successfully!').build();
}
catch (err) {
if (err == 'Exception: Invalid argument: url') {
err = 'Invalid URL';
res['Spreadsheet URL'] = null;
}
return createExpensesCard(objToArray(res, FIELDNAMES), 'Error: ' + err).build();
}
}
/**
* Returns an array corresponding to the given object and desired ordering of keys.
*
* @param {Object} obj Object whose values will be returned as an array.
* @param {String[]} keys An array of key names in the desired order.
* @returns {Object[]}
*/
function objToArray(obj, keys) {
return keys.map(function(key) {
return obj[key];
});
}
CardService.newButtonSet()를 사용하여 버튼 세트 위젯을 만들고CardService.ButtonSet.addButton()를 사용하여 텍스트 버튼을 버튼 세트에 추가합니다.CardService.CardSection.addWidget()를 사용하여 버튼 세트 위젯을 카드 양식 섹션에 추가합니다.
코드 몇 줄만으로 URL을 통해 스프레드시트를 열고 해당 시트에 데이터 행을 추가할 수 있습니다. 양식 입력은 이벤트 e의 일부로 함수에 전달되며 사용자가 모든 필드를 제공했는지 확인합니다. 오류가 발생하지 않는다고 가정하면 상태가 양호한 빈 비용 카드가 생성됩니다. 오류가 발견되면 오류 메시지와 함께 원래 작성된 카드가 반환됩니다. objToArray 도우미 함수를 사용하면 양식 응답을 배열로 쉽게 변환할 수 있으며, 이 배열을 스프레드시트에 추가할 수 있습니다.
마지막으로 appsscript.json에서 oauthScopes 섹션을 다시 업데이트하고 https://www.googleapis.com/auth/spreadsheets 범위를 요청합니다. 이 범위를 승인하면 부가기능이 사용자의 Google Sheets를 읽고 수정할 수 있습니다.
"oauthScopes": [
"https://www.googleapis.com/auth/gmail.addons.execute",
"https://www.googleapis.com/auth/gmail.addons.current.message.readonly",
"https://www.googleapis.com/auth/spreadsheets"
],
아직 새 스프레드시트를 만들지 않았다면 https://docs.google.com/spreadsheets/에서 만드세요.
이제 부가기능을 다시 실행하고 양식을 제출해 보세요. 스프레드시트 URL 양식 필드에 도착 URL의 전체 URL을 입력해야 합니다.
6. 속성 서비스로 값 저장
사용자는 동일한 스프레드시트에 많은 비용을 기록하는 경우가 많으므로 가장 최근의 스프레드시트 URL을 카드에 기본값으로 제공하는 것이 편리합니다. 최신 스프레드시트의 URL을 알기 위해서는 부가기능이 사용될 때마다 해당 정보를 저장해야 합니다.
Properties 서비스를 사용하면 키-값 쌍을 저장할 수 있습니다. 이 경우 적절한 키는 'SPREADSHEET_URL'이고 값은 URL 자체입니다. 이러한 값을 저장하려면 시트에 새 행을 추가할 때 스프레드시트의 URL이 속성으로 저장되도록 Cards.gs에서 submitForm를 수정해야 합니다.
속성은 스크립트, 사용자 또는 문서의 세 가지 범위 중 하나를 가질 수 있습니다. 문서 범위는 Gmail 부가기능에는 적용되지 않지만, 특정 Google 문서 또는 시트와 관련된 정보를 저장할 때는 별도의 부가기능 유형과 관련이 있습니다. 부가기능의 경우 개인이 양식에서 다른 사람의 스프레드시트가 아닌 자신의 최신 스프레드시트를 기본 옵션으로 볼 수 있는 것이 바람직합니다. 따라서 스크립트 범위 대신 사용자 범위를 선택합니다.
PropertiesService.getUserProperties().setProperty()을 사용하여 스프레드시트 URL을 저장합니다. Cards.gs의 submitForm에 다음을 추가합니다.
PropertiesService.getUserProperties().setProperty('SPREADSHEET_URL',
res['Spreadsheet URL']);
그런 다음 사용자가 부가기능을 사용할 때마다 최신 URL이 표시되도록 Helpers.gs에서 getSheetUrl 함수를 수정하여 저장된 속성을 반환합니다. PropertiesService.getUserProperties().getProperty()를 사용하여 속성 값을 가져옵니다.
/**
* Determines most recent spreadsheet URL.
* Returns null if no URL was previously submitted.
*
* @returns {String}
*/
function getSheetUrl() {
return PropertiesService.getUserProperties().getProperty('SPREADSHEET_URL');
}
마지막으로 속성 서비스에 액세스하려면 스크립트도 승인해야 합니다. 이전과 같이 매니페스트에 https://www.googleapis.com/auth/script.storage 범위를 추가하여 부가기능이 속성 정보를 읽고 쓸 수 있도록 합니다.
7. Gmail 메일 파싱
사용자의 시간을 절약하기 위해 이메일의 비용 관련 정보를 사용하여 양식을 미리 입력해 보겠습니다. 이 역할을 수행하는 함수는 이미 Helpers.gs에서 만들었지만 지금까지는 비용의 날짜, 금액, 설명에 'TODO'만 반환했습니다.
예를 들어 이메일을 수신한 날짜를 가져와서 비용의 날짜에 대한 기본값으로 사용할 수 있습니다.
/**
* Determines date the email was received.
*
* @param {Message} message - The message currently open.
* @returns {String}
*/
function getReceivedDate(message) {
return message.getDate().toLocaleDateString();
}
나머지 두 함수를 구현합니다.
getExpenseDescription에는 발신자 이름과 메시지 제목을 모두 결합하는 것이 포함될 수 있지만 메시지 본문을 파싱하고 훨씬 더 정확한 설명을 제공하는 더 정교한 방법도 있습니다.getLargestAmount의 경우 돈과 관련된 특정 기호를 찾아보세요. 영수증에는 세금 및 기타 수수료와 같은 여러 값이 나열되는 경우가 많습니다. 올바른 금액을 식별하는 방법을 생각해 보세요. 정규 표현식도 유용할 수 있습니다.
추가 영감이 필요하다면 GmailMessage 참조 문서를 살펴보거나 Codelab 시작 시 다운로드한 솔루션 코드를 확인하세요. Helpers.gs의 모든 함수에 대한 자체 구현을 고안한 후에는 부가기능을 사용해 보세요. 영수증을 열고 스프레드시트에 기록을 시작하세요.
8. 카드 작업으로 양식 지우기
열려 있는 이메일에서 지출을 잘못 식별하여 잘못된 정보로 양식을 자동 작성하면 어떻게 되나요? 사용자가 양식을 지웁니다. CardAction 클래스를 사용하면 작업을 클릭할 때 호출되는 함수를 지정할 수 있습니다. 이를 사용하여 사용자에게 양식을 빠르게 지울 수 있는 방법을 제공해 보겠습니다.
반환되는 카드에 '양식 지우기'라는 카드 작업이 있고 클릭하면 다음 clearForm 함수를 호출하도록 createExpensesCard를 수정합니다. 이 함수는 Cards.gs에 붙여넣을 수 있습니다. 양식이 지워질 때 상태 메시지가 유지되도록 하려면 opt_status를 'Status'라는 이름의 매개변수로 작업에 전달해야 합니다. 작업의 선택적 매개변수는 Object.<string, string> 유형이어야 하므로 opt_status를 사용할 수 없는 경우 {'Status' : ''}를 전달해야 합니다.
/**
* Recreates the main card without prefilled data.
*
* @param {Event} e An event object containing form inputs and parameters.
* @returns {Card}
*/
function clearForm(e) {
return createExpensesCard(null, e['parameters']['Status']).build();
}
9. 스프레드시트 만들기
Google Apps Script를 사용하여 기존 스프레드시트를 수정하는 것 외에도 프로그래매틱 방식으로 완전히 새로운 스프레드시트를 만들 수 있습니다. 부가기능에서는 사용자가 비용을 위한 스프레드시트를 만들 수 있도록 허용해 보겠습니다. 시작하려면 createExpensesCard에서 반환하는 카드에 다음 카드 섹션을 추가하세요.
var newSheetSection = CardService.newCardSection();
var sheetName = CardService.newTextInput()
.setFieldName('Sheet Name')
.setTitle('Sheet Name');
var createExpensesSheet = CardService.newAction()
.setFunctionName('createExpensesSheet');
var newSheetButton = CardService.newTextButton()
.setText('New Sheet')
.setOnClickAction(createExpensesSheet);
newSheetSection.addWidget(sheetName);
newSheetSection.addWidget(CardService.newButtonSet().addButton(newSheetButton));
card.addSection(newSheetSection);
이제 사용자가 '새 시트' 버튼을 클릭하면 부가기능이 항상 표시되도록 고정된 헤더 행으로 서식이 지정된 새 스프레드시트를 생성합니다. 사용자가 양식에 새 스프레드시트의 제목을 지정합니다. 양식이 비어 있는 경우를 대비해 기본값을 포함하는 것이 좋습니다. createExpensesSheet 구현에서 기존 카드와 거의 동일한 카드를 반환하되, 적절한 상태 메시지를 추가하고 URL 필드를 새 스프레드시트의 URL로 미리 채웁니다.
10. 축하합니다.
이메일에서 비용을 찾아 사용자가 몇 초 만에 스프레드시트에 비용을 기록할 수 있도록 지원하는 Gmail 부가기능을 성공적으로 설계하고 구현했습니다. Google Apps Script를 사용하여 여러 Google API와 인터페이스하고 부가기능의 여러 실행 간에 데이터를 유지했습니다.
가능한 개선사항
상상력을 발휘하여 Expense It!을 개선해 보세요. 더 유용한 제품을 만들기 위한 몇 가지 아이디어를 소개합니다.
- 사용자가 비용을 기록한 후 스프레드시트로 연결
- 비용 로깅을 수정/실행취소하는 기능 추가
- 사용자가 결제하고 송금을 요청할 수 있도록 외부 API 통합