스프레드시트 자동화

구글 스프레드시트 이메일 자동 발송: Apps Script 시간 트리거 설정

0
Please log in or register to do it.
구글 스프레드시트 이메일 자동 발송: Apps Script 시간 트리거 설정

내용 검토: 2026년 10월 5일. Google Apps Script 공식 문서의 MailApp, 설치형 트리거, 권한 및 할당량 안내를 기준으로 업데이트했습니다.

Google Sheets 값을 정해진 시간에 이메일로 보내려면 Apps Script의 MailApp.sendEmail()과 설치형 시간 트리거를 사용하면 됩니다. 기본 예제는 실제 발송을 막는 테스트 모드로 시작하고, 직접 만든 코드의 권한과 수신자 한 명을 확인한 뒤에만 발송을 켭니다.

어떤 방식으로 자동 메일을 보낼까?

시트에 담긴 값을 메일로 보내는 작업은 두 갈래입니다. 부가기능은 설치 전에 개발자, 요청 권한, 이용료와 데이터 취급 방식을 살펴야 합니다. Apps Script는 시트 안에서 코드를 직접 검토하고 예약 실행 규칙을 관리할 수 있어, 아래 예제처럼 작게 시작하기 좋습니다.

  • 매일·매주 요약 발송: 설치형 시간 트리거로 함수를 예약합니다.
  • 폼 응답이 들어올 때 발송: 해당 스프레드시트의 설치형 양식 제출 트리거를 사용합니다.
  • 특정 셀 값이 바뀔 때 간단한 알림: 조건부 알림 메뉴가 보이면 상태 열과 수신자를 지정할 수 있습니다. 맞춤 이메일이나 메뉴 미지원 계정은 설치형 수정 트리거를 검토하세요. 단순 트리거 onEdit는 권한이 필요한 메일 발송에 맞지 않습니다.

설치형 트리거는 만든 사람의 계정 권한으로 실행됩니다. 그 계정이 바뀌거나 사용 중지되면 발송과 소유권 관리에도 영향이 있으므로 개인·조직 계정 정책을 먼저 확인하세요.

시트에 메일 대기열 만들기

새 시트의 첫 행에 아래 머리글을 붙여넣습니다. 수신자 한 명을 한 행에 넣고, 직접 확인한 행만 상태를 대기로 둡니다.

받는 사람	제목	본문	상태	발송 시각

실제 고객 주소나 민감한 내용을 예제 시트에 복사하지 마세요. 작업용 시트는 수신자와 발송 담당자만 접근하도록 공유 권한을 제한합니다.

메일 발송 코드

시트에서 확장 프로그램 → Apps Script를 열고 아래 코드를 붙여넣습니다. 기본값 TEST_MODE = true에서는 메일을 보내지 않고 대상 행 수만 실행 로그에 남깁니다.

const SHEET_NAME = '메일대기열';
const TEST_MODE = true; // 검토가 끝난 뒤 실제 발송 전에 false로 변경

function sendScheduledEmails() {
  const lock = LockService.getScriptLock();
  if (!lock.tryLock(30000)) return;

  try {
    const spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
    const sheet = spreadsheet && spreadsheet.getSheetByName(SHEET_NAME);
    if (!sheet) throw new Error('메일대기열 시트를 찾을 수 없습니다.');

    const values = sheet.getDataRange().getValues();
    if (values.length < 2) return;

    const headers = values[0].map(value => String(value).trim());
    const column = Object.fromEntries(headers.map((name, index) => [name, index]));
    const required = ['받는 사람', '제목', '본문', '상태', '발송 시각'];
    const missing = required.filter(name => column[name] === undefined);
    if (missing.length) throw new Error('필수 머리글 누락: ' + missing.join(', '));

    let remaining = MailApp.getRemainingDailyQuota();

    for (let i = 1; i < values.length; i++) {
      const row = values[i];
      if (String(row[column['상태']]).trim() !== '대기') continue;

      const recipient = String(row[column['받는 사람']] || '').trim();
      const subject = String(row[column['제목']] || '').trim();
      const body = String(row[column['본문']] || '').trim();
      const rowNumber = i + 1;
      const statusCell = sheet.getRange(rowNumber, column['상태'] + 1);

      if (!/^[^\s@,]+@[^\s@,]+\.[^\s@,]+$/.test(recipient) || !subject || !body) {
        statusCell.setValue('확인 필요');
        continue;
      }
      if (remaining < 1) break;
      if (TEST_MODE) {
        console.log('테스트 모드: ' + rowNumber + '행은 발송하지 않았습니다.');
        continue;
      }

      // 발송 중으로 표시해 실행 중단 뒤 자동 재발송되는 일을 줄입니다.
      statusCell.setValue('발송 중');
      try {
        MailApp.sendEmail(recipient, subject, body);
        statusCell.setValue('발송 완료');
        sheet.getRange(rowNumber, column['발송 시각'] + 1).setValue(new Date());
        remaining -= 1;
      } catch (error) {
        // 전송 직후 오류가 날 수도 있어 자동으로 다시 보내지 않습니다.
        statusCell.setValue('확인 필요');
        console.error('발송 결과를 확인하세요. 행 번호: ' + rowNumber);
      }
    }
  } finally {
    lock.releaseLock();
  }
}

LockService는 예약 실행이 겹칠 때 같은 행이 동시에 처리되지 않게 돕습니다. 발송 상태를 먼저 기록하므로 실행이 중간에 멈춘 행은 발송 중으로 남을 수 있습니다. 이 행은 메일함과 Apps Script 실행 기록을 직접 확인한 뒤 상태를 정리하세요. 자동 재시도는 중복 메일을 만들 수 있습니다.

실제 발송 전에 확인할 순서

  1. 메일대기열 시트에 본인 테스트 주소 한 개, 테스트 제목·본문, 상태 대기를 넣습니다.
  2. 코드의 SHEET_NAME이 시트 이름과 같은지 확인하고 TEST_MODE = true로 함수를 직접 실행합니다. 실행 기록에서 메일 발송이 없었는지 확인합니다.
  3. 코드가 직접 작성한 내용이며 요청하는 Sheets·메일 권한이 작업에 필요한 범위인지 확인합니다. 출처를 모르는 스크립트의 권한 승인이나 경고 우회는 하지 마세요.
  4. 테스트 행 하나만 남기고 수신자를 본인 주소로 확인한 뒤 TEST_MODE = false로 바꿔 한 번 수동 실행합니다. 받은편지함과 시트의 발송 상태·시각을 확인합니다.
  5. 수동 테스트가 성공한 뒤에만 예약을 설정합니다. 테스트 행은 다시 보내지 않도록 발송 완료 상태를 유지하세요.

매일 오전에 실행하는 시간 트리거

아래 함수를 추가하고 한 번 직접 실행하면, 같은 처리 함수의 일일 트리거가 없을 때만 오전 시간대 예약을 만듭니다. 프로젝트 설정에서 시간대를 먼저 확인하세요.

function createDailyEmailTrigger() {
  const handler = 'sendScheduledEmails';
  const exists = ScriptApp.getProjectTriggers()
    .some(trigger => trigger.getHandlerFunction() === handler);
  if (exists) return;

  ScriptApp.newTrigger(handler)
    .timeBased()
    .everyDays(1)
    .atHour(9)
    .create();
}

시간 트리거는 9시 정각 보장을 의미하지 않습니다. Google은 지정한 시간대 안에서 실행 시각을 정하고 반복 시각을 유지할 수 있습니다. 정확한 분 단위 실행이 필요하면 외부 예약 시스템 등 다른 방식을 검토하세요. 예약이 만들어진 계정으로 권한 승인을 완료해야 합니다.

발송량과 실패 확인

메일 수신자 할당량과 실행 제한은 계정 유형에 따라 다르고 변경될 수 있습니다. 예제는 발송 전 MailApp.getRemainingDailyQuota()를 확인하며, 현재 기준은 Apps Script 서비스 할당량 공식 문서에서 다시 확인하세요. 행마다 수신자 한 명을 전제로 하므로 여러 명에게 보내려면 주소를 분리해 각각 검증하고 남은 수신자 수를 계산해야 합니다.

오류나 발송 중 상태가 남으면 상태만 대기로 바꾸지 말고, 먼저 보낸편지함과 Apps Script → 실행 기록에서 발송 여부를 확인하세요. 이미 전송된 메일을 다시 보내는 상황을 막을 수 있습니다.

공식 참고 자료와 다음 단계

메일 자동 발송을 다른 시트 작업과 연결하려면 스프레드시트 업무 자동화 허브를, 메일에 넣을 날짜 범위를 계산하려면 영업일 계산 함수 가이드를 이어서 참고하세요.

구글 폼으로 QR코드 시스템 만들기.
글 발행시간보다 '색인 생성'이 더 중요해요 [SEO노하우]

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

  1. 안녕하세요 혹 메일을 보낼때 시트 그대로 보낼 수 는 없나요?
    안된다면 내용의 줄 바꿈은 어떻게 하나요?