스프레드시트에 쌓인 명단을 보고 한 명 한 명 메일을 복사·붙여넣기 하다 보면, 이 반복을 컴퓨터가 대신 해줬으면 싶은 순간이 온다. 앱스 스크립트 이메일 자동화가 바로 그 지점을 파고든다. 구글 계정만 있으면 따로 설치할 것도 없이 브라우저 안에서 코드 몇 줄로 메일을 보내고, 정해둔 시각에 알아서 돌게 만들 수 있다. 코딩을 한 번도 안 해봤어도 상관없다. 이 글에서는 MailApp으로 조건에 맞는 메일만 골라 보내고, 트리거로 예약 발송까지 거는 흐름을 처음부터 하나씩 따라간다.
앱스 스크립트 이메일 자동화, 무엇이고 왜 쓰나
구글 앱스 스크립트는 구글 문서·시트·지메일 같은 서비스를 자바스크립트로 조종하는 도구다. 설치할 게 없다. 구글 시트를 열고 확장 프로그램 메뉴에서 앱스 스크립트를 누르면 편집기가 뜬다. 거기에 코드를 붙이고 실행 버튼만 누르면 끝이다.
이메일 발송에 앱스 스크립트를 쓰는 이유는 단순하다. 메일 주소와 이름, 상태 같은 데이터가 이미 스프레드시트 안에 들어 있어서다. 시트의 값을 읽어 메일 내용에 그대로 끼워 넣으면, 100명에게 저마다 다른 내용의 메일을 한 번에 뿌릴 수 있다. 마케팅 툴을 따로 결제하지 않아도 사내 알림이나 신청 확인 회신, 정기 리포트 발송 정도는 이걸로 충분하다.
MailApp과 GmailApp의 차이
앱스 스크립트에서 메일을 보내는 길은 크게 두 가지다. MailApp은 발송에만 특화된 가벼운 서비스고, GmailApp은 라벨과 스레드, 초안까지 손대는 지메일 전체 제어용이다. 그냥 “보내기”만 하면 된다면 MailApp이 배우기 쉽고 요구하는 권한 범위도 좁다. 이 글은 MailApp을 중심으로 간다.
| 구분 | MailApp | GmailApp |
|---|---|---|
| 주 용도 | 메일 발송 | 지메일 전반 관리 |
| 메일 보내기 | 가능 | 가능 |
| 라벨·초안·스레드 조작 | 불가 | 가능 |
| 배우기 | 간단 | 상대적으로 복잡 |
| 추천 대상 | 발송만 필요한 초보자 | 받은편지함까지 다루는 경우 |
가장 기본: 메일 한 통 보내기
먼저 구글 시트를 새로 만들고 상단 메뉴에서 확장 프로그램 → 앱스 스크립트를 연다. 편집기가 뜨면 기본으로 들어 있던 myFunction 껍데기를 지우고 아래 코드를 붙여넣는다.

function sendTestEmail() {
MailApp.sendEmail({
to: "받는사람@example.com",
subject: "테스트 메일입니다",
body: "앱스 스크립트로 보낸 첫 메일입니다."
});
}
to는 받는 주소, subject는 제목, body는 본문이다. 코드를 넣었으면 상단 실행 버튼을 누른다. 처음 실행하면 구글이 “이 스크립트가 당신의 지메일에 접근하려 한다”며 권한 승인을 요구한다. 본인 계정을 고르고 허용을 누르면 실행이 끝나고, 발신함을 열어보면 방금 보낸 메일이 남아 있다.
HTML 본문과 참조·숨은참조
본문을 서식 있는 형태로 보내고 싶으면 htmlBody 옵션을 쓴다. 참조는 cc, 숨은참조는 bcc, 답장 주소를 따로 두고 싶으면 replyTo를 넣는다.
MailApp.sendEmail({
to: "받는사람@example.com",
cc: "참조@example.com",
subject: "주간 리포트",
htmlBody: "<h3>이번 주 요약</h3><p>매출이 <b>12%</b> 늘었습니다.</p>"
});
시트 값으로 조건부 발송하기
진짜 쓸모는 여기서 나온다. 시트에 이름·이메일·상태 열이 있다고 하자. 상태가 “미발송”인 행만 골라 메일을 보내고, 보낸 뒤에는 상태를 “완료”로 바꿔 중복 발송을 막는 식이다.
예시 시트 구조는 이렇다. A열 이름, B열 이메일, C열 상태.
| A (이름) | B (이메일) | C (상태) |
|---|---|---|
| 김민수 | [email protected] | 미발송 |
| 이서연 | [email protected] | 완료 |
| 박지훈 | [email protected] | 미발송 |
function sendFromSheet() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
const rows = sheet.getDataRange().getValues();
// 0행은 머리글이므로 1행부터 순회
for (let i = 1; i < rows.length; i++) {
const name = rows[i][0];
const email = rows[i][1];
const status = rows[i][2];
if (status !== "미발송") continue; // 조건: 미발송만
MailApp.sendEmail({
to: email,
subject: name + "님께 드리는 안내",
body: name + "님, 신청이 정상 접수되었습니다."
});
sheet.getRange(i + 1, 3).setValue("완료"); // C열을 완료로 갱신
}
}
핵심은 if (status !== "미발송") continue; 이 한 줄이다. 이 조건만 손보면 “결제완료”인 사람에게만, 또는 특정 날짜 이후 신청자에게만 보내는 식으로 얼마든지 응용할 수 있다. 보낸 직후 setValue("완료")로 상태를 덮어쓰니, 스크립트를 다시 돌려도 이미 받은 사람에게 또 가지 않는다.
실제로 소규모 스터디 모임 회비 납부 안내에 이 패턴을 써봤다. 시트에 회원 30명을 적어두고, 입금이 확인된 사람은 상태를 “완료”로, 미납자는 “미발송”으로 관리했다. 매주 금요일에 스크립트를 한 번 돌리면 미납자에게만 리마인드 메일이 나갔다. 처음엔
continue없이 전원에게 보내버려서 이미 낸 사람한테까지 독촉 메일이 가는 사고를 냈다. 조건문 한 줄 넣고 나니 깔끔해졌다. 테스트할 때 받는 주소를 잠깐 내 주소로 몰아놓고 돌려보는 습관을 들이면 이런 실수를 미리 잡을 수 있다.
트리거로 자동·예약 발송 걸기
지금까지는 실행 버튼을 직접 눌러야 메일이 나갔다. 트리거를 걸면 이 과정을 정해진 시각에 자동으로 돌릴 수 있다. 매일 아침 9시 리포트, 매주 월요일 알림 같은 반복 발송이 여기 해당한다.

설치형 트리거를 만드는 순서는 이렇다.
- 앱스 스크립트 편집기 왼쪽 메뉴에서 시계 모양의 트리거 항목을 연다.
- 오른쪽 아래 트리거 추가 버튼을 누른다.
- 실행할 함수로
sendFromSheet를 고른다. - 이벤트 소스는 시간 기반으로 선택한다.
- 일 단위 타이머, 오전 9시~10시처럼 주기와 시간대를 지정한다.
- 저장을 누르면 권한 승인 뒤 트리거가 등록된다.
주의할 점이 하나 있다. 시간 기반 트리거의 실행 시각은 “오전 9시~10시 사이”처럼 구간으로 지정되지, 9시 정각을 정확히 보장하지는 않는다. 몇 분 정도 오차는 감안하는 게 좋다. 대신 시트가 열려 있지 않아도, 심지어 브라우저를 꺼둬도 트리거는 구글 서버에서 알아서 돈다.
편집기 실행과 트리거 실행의 차이
편집기에서 직접 누르는 “단순 실행”과 트리거가 대신 돌리는 “설치형 트리거 실행”은 권한 처리가 조금 다르다. 트리거를 처음 저장할 때 권한 승인 창이 한 번 더 뜨는데, 이걸 허용해야 무인 실행이 된다.
발송 한도, 이것만은 기억하자
구글은 스팸을 막으려고 앱스 스크립트의 하루 메일 발송량에 한도를 둔다. 이 글을 쓰는 시점 기준으로 무료 지메일(gmail.com) 계정은 하루 수신자 100명, 유료 워크스페이스 계정은 하루 수신자 1,500명 안팎으로 알려져 있다(메일 ‘건수’가 아니라 받는 사람 수 기준이다). 다만 이 수치는 계정 종류와 구글 정책에 따라 얼마든지 바뀌고, 무료 체험 계정이나 최근 만든 계정은 더 낮게 잡히기도 한다. 발송량이 많다면 실행 전에 구글 공식 문서에서 현재 한도를 한 번 확인하고 가는 편이 안전하다.
한도를 넘기면 스크립트가 오류를 내며 멈추고, 그날 남은 메일은 나가지 않는다. 대량 발송을 계획 중이라면 남은 할당량을 코드로 먼저 확인해 두는 습관이 도움이 된다.
function checkQuota() {
const remaining = MailApp.getRemainingDailyQuota();
Logger.log("오늘 남은 발송 가능 수: " + remaining);
}
getRemainingDailyQuota()는 오늘 더 보낼 수 있는 남은 수신자 수를 돌려준다. 반복문 안에서 이 값을 확인해 0에 가까우면 발송을 멈추도록 짜두면, 한도 초과로 스크립트가 통째로 실패하는 일을 피할 수 있다.
자주 묻는 질문
보낸 메일의 발신자는 누구로 표시되나요?
기본적으로 스크립트를 승인한 본인 구글 계정 주소로 나간다. 받는 사람 눈에는 평소 내가 지메일에서 직접 보낸 것과 똑같이 보인다. 표시 이름은 name 옵션으로 바꿀 수 있다.
받는 사람이 이게 자동 발송인 걸 알 수 있나요?
메일 자체에 “자동 발송” 표시가 붙지는 않는다. 다만 이름 같은 개인화 요소 하나 없이 똑같은 내용을 한꺼번에 뿌리면 스팸으로 분류될 여지가 커진다. 시트 값으로 이름 정도는 넣어주는 편이 도달률에 낫다.
첨부파일도 보낼 수 있나요?
가능하다. attachments 옵션에 구글 드라이브 파일이나 직접 만든 blob을 배열로 넣으면 된다. 예를 들어 DriveApp으로 파일을 가져와 첨부하는 식이다.
실행이 갑자기 멈췄어요. 왜 그런가요?
흔한 원인은 두 가지다. 하루 발송 한도 초과, 그리고 한 번 실행이 너무 오래 걸려 시간 제한에 걸리는 경우. 수백 건을 한 번에 돌리기보다 트리거로 나눠 보내거나, 남은 할당량을 확인하며 도는 방식이 안정적이다.
여기까지 따라왔다면 메일 한 통 보내기부터 시트 조건부 발송, 트리거 예약까지 자동화의 기본 뼈대는 다 갖춘 셈이다. 처음엔 받는 주소를 본인 것으로 두고 몇 번 테스트해 본 뒤 실제 명단에 적용하길 권한다. 조건문 하나, 상태 갱신 한 줄만 신경 써도 반복 메일 업무의 대부분은 손을 뗄 수 있다.
공식 참고: Google Apps Script 공식 문서
관련 글
메일 발송의 원천이 되는 데이터부터 모으고 싶다면 구글 폼 응답을 시트로 자동 정리하기를 먼저 보면 좋다. 시트에 모인 값을 더 똑똑하게 다루고 싶다면 구글 스프레드시트에서 GPT 활용하기도 함께 참고하자.