
* 겪고 있는 문제 상황을 최대한 자세하게 작성해주세요.
* 문제 해결을 위해 어떤 시도를 해보았는지 구체적으로 함께 알려주세요.
앱스크립트 코드 삽입 후 자동화 실행 시 아래와 같은 오류 메시지가 나옵니다.
Exception: Unexpected error while getting the method or property getFolderById on object DriveApp.
앱스크립트 화면에서 실행해보니 TypeError: Cannot read properties of undefined (reading 'values') onFormSubmit @ Code.gs:5
에러가 나옵니다.
챗지피티에게 물어보니 트리거를 추가하라는데 트리거 추가해도 해결이 안 됩니다.
작성한 코드 및 에러 메세지
// 구글 폼을 제출하면 추적하여 이메일 알림 발송하는 함수
function onFormSubmit(e) {
// 구글 폼의 응답을 찾아내서 변수로 지정
let formResponse = e.values;
// console.log(formResponse);
let timeStamp = formResponse[0];
let email = formResponse[1];
let name = formResponse[2];
let birth = formResponse[3];
let phone = formResponse[4];
let job = formResponse[5];
let requestDate = formResponse[6];
let requestTime = formResponse[7];
// 메일 발송에 필요한 정보 찾기
let myMail = '내 이메일 주소';
let mailTitle = '[면접 일정 확인 요청] 지원자가 구글 폼을 작성했어요!';
// HTML 메일 형태로 데이터 담기
let mailBody = `<div>
<h3>${mailTitle}</h3>
<p>작성시간 : ${timeStamp}</p>
<p>성함 : ${name}</p>
<p>생년월일 : ${birth}</p>
<p>희망직무 : ${job}</p>
<p>이메일 : ${email}</p>
<p>전화번호 : ${phone}</p>
<p>면접 희망일자 : ${requestDate}</p>
<p>면접 희망시각 : ${requestTime}</p>
<a href="내 구글 시트 URL">면접 신청 시트 확인하기</a>
</div>`;
// 메일로 데이터 전송
sendEmail(myMail, mailTitle, mailBody);
}
// 지메일을 보내는 함수
function sendEmail(recipient, subject, body) {
MailApp.sendEmail({
to: recipient,
subject: subject,
htmlBody: body
});
}
// 면접 사실 확인서를 만드는 함수
function createInterviewDocument(data) {
let name = data[0];
let birth = data[1];
let job = data[2];
let interviewDate = data[3];
let today = new Date(Date.now())
let writeDate = Utilities.formatDate(today, Session.getScriptTimeZone(), 'yyyy/MM/dd HH:mm');
// // 템플릿 문서 ID (구글 문서에서 File -> Document Settings -> 문서 ID를 확인할 수 있음)
let templateDocId = '1xCDVNoogxlzoDahVBylEBBNjtXy7-h12E7kGGBQsqfw';
// // 템플릿 문서 복사
let newDoc = DriveApp.getFileById(templateDocId).makeCopy(name + " 면접 일정 안내서");
let newDocId = newDoc.getId();
// // 문서 열기
let doc = DocumentApp.openById(newDocId);
// // 변수로 대체할 내용 정의
let replaceMap = {
'{{name}}': name,
'{{interviewDate}}': interviewDate,
'{{jobTitle}}': job,
'{{writeDate}}': writeDate,
'{{birth}}': birth
};
// // 템플릿 내용을 변수로 대체하고 스타일 유지
replaceTextWithMapAndStyle(doc, replaceMap);
// // 문서 저장
doc.saveAndClose();
// // 공유할 드라이브 폴더의 ID
let folderId = '1rLLP5xg1A6q0kH1WBJO57iPy6szVcKfH?ths=true';
// // 문서 이동
DriveApp.getFileById(newDocId).moveTo(DriveApp.getFolderById(folderId));
// // 문서를 공유
DriveApp.getFileById(newDocId).setSharing(DriveApp.Access.ANYONE_WITH_LINK, DriveApp.Permission.VIEW);
// // 생성한 문서 반환
return { docId: newDocId, docLink: newDoc.getUrl() };
}
function replaceTextWithMapAndStyle(doc, replaceMap) {
for (let key in replaceMap) {
let searchText = key;
let newText = replaceMap[key];
// 문서에서 특정 스타일을 가진 텍스트 찾기
let searchResult = doc.getBody().findText(searchText);
while (searchResult) {
let foundText = searchResult.getElement().asText();
// 찾은 텍스트를 새로운 텍스트로 교체
foundText.replaceText(searchText, newText);
// 찾은 텍스트의 스타일 유지 (예시로 볼드 스타일을 유지함)
foundText.setBold(true);
// 다음 찾기
searchResult = doc.getBody().findText(searchText, searchResult);
}
}
}
// 구글 시트가 열리면 실행되는 함수
function onOpen() {
let ui = SpreadsheetApp.getUi();
ui.createMenu('자동화')
.addItem('일정 예약하기', 'createCalendarSchedule')
.addToUi();
}
// 구글 일정 예약하기 버튼을 누르면 실행되는 함수
function createCalendarSchedule() {
let spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
let sheet = spreadsheet.getActiveSheet();
let startRow = 2;
let numRows = sheet.getLastRow() - 1;
let dataRange = sheet.getRange(startRow, 1, numRows, 10);
let data = dataRange.getValues();
// console.log(data);
for (let i = 0; i < data.length; i++) {
let row = data[i];
// console.log(row);
let timeStamp = row[0];
let email = row[1];
let name = row[2];
let birth = row[3];
let phone = row[4];
let job = row[5];
let requestDate = row[6];
let requestTime = row[7];
let interviewDate = row[8];
let isReserved = row[9];
if (interviewDate === '') {
console.log('일정 예약 실패 : 면접 일정 데이터가 비어있습니다');
continue;
}
if (isReserved === '완료') {
console.log('일정 예약 실패 : 이미 예약이 완료된 일정입니다');
continue;
}
interviewDate = new Date(interviewDate);
// 날짜와 시간을 문자열로 변환하여 원하는 형식으로 출력
let formattedDate = Utilities.formatDate(interviewDate, Session.getScriptTimeZone(), 'yyyy/MM/dd HH:mm');
// 문서 작성 자동화
let docData = [name, birth, job, formattedDate];
// createInterviewDocument 호출
let interviewDocumentInfo = createInterviewDocument(docData);
// HTML 메일 형태로 데이터 담기
let office = '서울특별시 르탄대로 11길 8';
let mailTitle = '안녕하세요. [대박기획]입니다. 제출해주신 면접 일정이 확정되어 공유드립니다.';
let admin = '김대박';
let mailBody = `<div>
<h3>${mailTitle}</h3>
<p>성함 : ${name}</p>
<p>희망직무 : ${job}</p>
<p>면접일자 : ${formattedDate}</p>
<p>면접장소 : ${office}</p>
<p>면접 일정 안내서 : <a href="${interviewDocumentInfo.docLink}">스파르타코딩클럽 면접 일정 안내서</a></p>
<p>인사 담당자 ${admin} 드림</p>
</div>`
// 메일로 고객에게 데이터 전송
sendEmail(email, '[대박기획] 확정된 면접 일정 공유드립니다.', mailBody);
// startDate에 30분을 추가하여 endDate 생성
let endDate = new Date(interviewDate.getTime() + 30 * 60 * 1000);
let scheduleName = `${job}-${name} 님 면접`;
let mySchedules = CalendarApp.getDefaultCalendar().getEvents(interviewDate, endDate, { search: scheduleName });
// 중복된 스케쥴이 없으면 구글 캘린더 일정으로 추가
if (mySchedules.length === 0) {
CalendarApp.getDefaultCalendar().createEvent(scheduleName, interviewDate, endDate);
}
// 자동화 마무리 셀 데이터 채우기
let statusSheet = spreadsheet.getSheetByName("설문지 응답 시트1");
statusSheet.getRange(startRow + i, 10).setValue("완료");
console.log(`예약 완료 : ${job} 으로 지원한 ${name} 님의 일정 예약이 완료되었습니다`);
}
}
