소개
시도하고자 했던 것과 그 이유를 알려주세요.
민족 대명절 설을 맞아ㅎㅎ 1년 간 함께 고생했던 감사한 분들에게 새해인사를 보내고 싶었습니다.
구글 앱스크립트와 GPT를 사용하여, 최근 1년 간 나의 메일발송이력이 있는 분들에게 자동으로 명절 인사가 전달되도록 했습니다.
진행 방법
어떤 도구를 사용했고, 어떻게 활용하셨나요?
Tip: 사용한 프롬프트 전문을 꼭 포함하고, 내용을 짧게 소개해 주세요.
Tip: 활용 이미지나 캡처 화면을 꼭 남겨주세요.
Tip: 코드 전문은 코드블록에 감싸서 작성해주세요. ( / 을 눌러 '코드 블록'을 선택)
구글 앱스크립트와 스프레드시트, GPT를 사용했어요.
-- 250210 베스트발표 ver. --
1) GPT에 코드 요청 > 앱스크립트에 입력
"앱스크립트에 입력할 코드를 짜줘. 지난 1년 간 지메일 발송 이력을 확인해서, 구글 스프레드시트에 <이름ㅣ메일주소ㅣ메일발송횟수>의 형식으로 출력되게 할 거야."
처음에 어떤 코드를 알려줬었는데 저장해두질 못했어요..ㅎㅎ-다만, 위 코드로는
1. 이름이 중구 난방으로 적히거나 'Unknown'으로 표시되고 / 2. 메일주소가 총 10개만 불러와지는 현상 발생. 아래와 같은 GPT의 조언(?)에 따라 코드 수정해 입력.
1. 이름이 중구난방으로 적히거나 'Unknown'으로 나옴
✅ 해결 방법
이름이 없을 경우 이메일의 앞부분(@ 이전)을 이름으로 대체
Unknown을 없애기 위해 코드 수정
2. 메일주소가 총 10개만 불러와지는 현상
✅ 해결 방법
검색 범위 늘리기
→GmailApp.search(query, 0, 2000);로 확장 (최대 2000개)CC, BCC 수신자도 포함
→message.getCc()와message.getBcc()도 포함하도록 수정
function getSentEmailContacts() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
sheet.clear(); // 기존 데이터 삭제
// 헤더 설정
sheet.appendRow(["이름", "메일주소", "메일발송횟수"]);
var oneYearAgo = new Date();
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1); // 1년 전 날짜 계산
var query = 'after:' + formatDate(oneYearAgo); // 1년 내 발송된 이메일 검색
var emailCount = {}; // 이메일별 발송 횟수 저장
var start = 0;
var batchSize = 500; // Gmail API의 최대 검색 개수
while (true) {
var threads = GmailApp.search(query, start, batchSize);
if (threads.length === 0) break; // 더 이상 가져올 데이터가 없으면 종료
threads.forEach(thread => {
var messages = thread.getMessages();
messages.forEach(message => {
if (message.getFrom().includes(Session.getActiveUser().getEmail())) { // 내가 보낸 메일인지 확인
var recipients = [].concat(
message.getTo().split(","),
message.getCc() ? message.getCc().split(",") : [],
message.getBcc() ? message.getBcc().split(",") : []
); // TO + CC + BCC 포함
recipients.forEach(recipient => {
var emailMatch = recipient.match(/<(.+?)>/);
var email = emailMatch ? emailMatch[1] : recipient.trim();
// 새로운 이름 추출 방식: <> 내부의 이메일을 제외한 부분을 가져옴
var name = recipient.replace(/<.*?>/, "").trim();
// 이름이 없거나 이메일이 그대로 나올 경우 보정
if (!name || name === email) {
name = email.split("@")[0]; // 이름이 없으면 이메일 앞부분 사용
}
if (!emailCount[email]) {
emailCount[email] = { name: name, count: 0 };
}
emailCount[email].count += 1;
});
}
});
});
start += batchSize; // 다음 500개 검색
}
// 데이터 시트에 저장
Object.keys(emailCount).forEach(email => {
sheet.appendRow([emailCount[email].name, email, emailCount[email].count]);
});
Logger.log("✅ 이메일 데이터 수집 완료!");
}
// 날짜 포맷 함수 (YYYY/MM/DD 형식)
function formatDate(date) {
return date.getFullYear() + '/' + (date.getMonth() + 1) + '/' + date.getDate();
}2) 아래와 같이 입력되어 수정 필요
"이름 칼럼(A열)에 앞뒤로 큰따옴표가 붙어있을 경우 모두 삭제되도록 해줘. 그리고 11행의 jeon****@*****edata.com 이메일의 경우 내가 지메일에서 봤을 땐 '한**'이라는 이름이 확인되는데, 왜 이름 데이터가 제대로 불러와지지 않지? 이런 행이 많아서 이름의 호출 방식을 전체적으로 다시 한 번 체크해줘. 그리고 전체 데이터는 메일발송 횟수를 기준으로 내림차순 정렬되게 해줘."
function cleanAndSortEmailData() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var data = sheet.getDataRange().getValues();
if (data.length < 2) {
Logger.log("⚠️ 데이터가 충분하지 않습니다.");
return;
}
// 1. 헤더를 제외한 데이터만 처리
var headers = data[0];
var emailData = data.slice(1);
// 2. 큰따옴표 제거 및 이메일 기반 이름 보정
var emailToName = {
"[email protected]": "한정석",
"[email protected]": "전준",
"[email protected]": "원딕"
};
for (var i = 0; i < emailData.length; i++) {
var name = emailData[i][0].trim();
var email = emailData[i][1].trim();
// 큰따옴표 제거
emailData[i][0] = name.replace(/^"|"$/g, "");
// 이메일 기반 이름 보정
if (emailToName[email]) {
emailData[i][0] = emailToName[email];
}
}
// 3. 메일 발송 횟수(C열) 기준 내림차순 정렬
emailData.sort(function(a, b) {
return b[2] - a[2];
});
// 4. 기존 데이터 삭제 후 정리된 데이터 삽입
sheet.clear();
sheet.appendRow(headers);
sheet.getRange(2, 1, emailData.length, emailData[0].length).setValues(emailData);
Logger.log("✅ 데이터 정리 및 정렬 완료!");
}3) 최종 다음과 같이 데이터 출력 완료
4) 메일 초안 요청
"오키. 이제 이 불러와진 이메일들에 메일이 나가도록 할 거야. 우선 '명절 연휴(최근에 한국은 민족 대명절 설이었단다..)는 끝났지만 힘내서 일하자!' 의 느낌으로 메일 초안을 작성해줘."
5) 메일 초안에 맞게 코드 수정 필요
"메일의 [이름] 부분의 경우, 이름 칼럼에서 한국어 이름을 확인가능한 경우에 한해 그 한글 이름만 포함되도록 할 수 있어?
예를 들어,
-이름 칼럼에 '홍해성'이라고 적혀있을 경우 > 안녕하세요, 홍해성님.
-이름 칼럼에 'Stella홍해성'이라고 적혀있을 경우 > 안녕하세요, 홍해성님.
-이름 칼럼에 'Stella'라고 적혀있을 경우 > 안녕하세요.
인삿말을 이렇게 적을 수 있도록 앱스크립트 코드를 짜줘."
function sendHolidayEmails() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var data = sheet.getDataRange().getValues();
if (data.length < 2) {
Logger.log("⚠️ 데이터가 충분하지 않습니다.");
return;
}
var emailSubject = "💪 설 연휴가 끝났습니다! 힘내서 새 출발해요!";
for (var i = 1; i < data.length; i++) {
var name = data[i][0].trim(); // 이름 칼럼 (A열)
var email = data[i][1].trim(); // 이메일 칼럼 (B열)
if (!email) continue; // 이메일이 없으면 스킵
// 1. 한글 이름만 추출
var koreanName = extractKoreanName(name);
// 2. 인사말 구성
var greeting = koreanName ? `안녕하세요, ${koreanName}님.\n\n` : "안녕하세요.\n\n";
// 3. 이메일 본문 작성
var emailBody = greeting +
"설 연휴 잘 보내셨나요? 가족과 함께 따뜻한 시간을 보내셨길 바랍니다.\n\n" +
"연휴가 끝나고 다시 바쁜 일상이 시작되었지만, 새로운 마음으로 힘차게 출발할 수 있길 바랍니다!\n\n" +
"올해도 건강하시고, 계획하신 일들이 순조롭게 진행되길 기원합니다.\n" +
"저희도 좋은 에너지를 담아 열심히 달려가겠습니다. 함께 멋진 한 해 만들어가요! 🚀\n\n" +
"혹시 필요하신 것이 있거나 편하게 이야기 나누고 싶다면 언제든 연락 주세요.\n" +
"좋은 하루 보내시고, 활기찬 한 주 시작하세요!\n\n" +
"감사합니다.\n[보낸 사람 이름] 드림";
// 4. 메일 발송
GmailApp.sendEmail(email, emailSubject, emailBody);
Logger.log("✅ 이메일 발송 완료: " + email);
}
}
// 한글 이름만 추출하는 함수
function extractKoreanName(name) {
var koreanMatch = name.match(/[가-힣]+/g); // 한글만 추출
return koreanMatch ? koreanMatch.join("") : ""; // 여러 조각이 있으면 합쳐서 반환
}6) 일부 동료들만 모은 시트2 생성 + 시트2에만 메일이 나갈 수 있는 코드 짜 달라
"위 코드 그대로, 시트2에만 메일이 나갈 수 있도록 코드를 생성해줘"
한국 텍스트가 강조된 Google 스프레드 시트의 스크린 샷
function sendHolidayEmails() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet1 = ss.getSheetByName("시트1"); // 데이터가 있는 시트
var sheet2 = ss.getSheetByName("시트2"); // 허용된 이메일이 있는 시트
if (!sheet1 || !sheet2) {
Logger.log("⚠️ 시트1 또는 시트2를 찾을 수 없습니다.");
return;
}
var data = sheet1.getDataRange().getValues();
var allowedEmails = getAllowedEmails(sheet2); // 시트2에서 허용된 이메일 가져오기
if (data.length < 2) {
Logger.log("⚠️ 데이터가 충분하지 않습니다.");
return;
}
var emailSubject = "💪 설 연휴가 끝났습니다! 힘내서 새 출발해요!";
for (var i = 1; i < data.length; i++) {
var name = data[i][0].trim(); // 이름 칼럼 (A열)
var email = data[i][1].trim(); // 이메일 칼럼 (B열)
if (!email || !allowedEmails.includes(email)) {
Logger.log("❌ 발송 제외: " + email);
continue; // 이메일이 없거나, 허용된 이메일 목록에 없으면 발송하지 않음
}
// 1. 한글 이름만 추출
var koreanName = extractKoreanName(name);
// 2. 인사말 구성
var greeting = koreanName ? `안녕하세요, ${koreanName}님.\n\n` : "안녕하세요.\n\n";
// 3. 이메일 본문 작성
var emailBody = greeting +
"설 연휴 잘 보내셨나요? 가족과 함께 따뜻한 시간을 보내셨길 바랍니다.\n\n" +
"연휴가 끝나고 다시 바쁜 일상이 시작되었지만, 새로운 마음으로 힘차게 출발할 수 있길 바랍니다!\n\n" +
"올해도 건강하시고, 계획하신 일들이 순조롭게 진행되길 기원합니다.\n" +
"저희도 좋은 에너지를 담아 열심히 달려가겠습니다. 함께 멋진 한 해 만들어가요! 🚀\n\n" +
"혹시 필요하신 것이 있거나 편하게 이야기 나누고 싶다면 언제든 연락 주세요.\n" +
"좋은 하루 보내시고, 활기찬 한 주 시작하세요!\n\n" +
"감사합니다.\n[보낸 사람 이름] 드림";
// 4. 메일 발송
GmailApp.sendEmail(email, emailSubject, emailBody);
Logger.log("✅ 이메일 발송 완료: " + email);
}
}
// 📌 한글 이름만 추출하는 함수
function extractKoreanName(name) {
var koreanMatch = name.match(/[가-힣]+/g); // 한글만 추출
return koreanMatch ? koreanMatch.join("") : ""; // 여러 조각이 있으면 합쳐서 반환
}
// 📌 시트2에서 허용된 이메일 리스트 가져오는 함수
function getAllowedEmails(sheet) {
var emailData = sheet.getDataRange().getValues();
var emailList = [];
for (var i = 1; i < emailData.length; i++) { // 헤더 제외
var email = emailData[i][0].trim();
if (email) {
emailList.push(email);
}
}
Logger.log("✅ 허용된 이메일 목록 로드 완료 (" + emailList.length + "개)");
return emailList;
}7) 실제 발송(이 되어버림...😂) 그러나 코딩은 잘 짜여진
8) 이모티콘 깨짐 현상 수정. 그런데 네이버 메일에서만 정상적으로 보임
"메일은 잘 발송됐어. 그런데 지메일에서는 여전히 이메일이 깨진 것처럼 보이는데, 메일을 수신한 네이버 메일에서는 이상없이 잘 보여. 왜 그런거야?"
function sendHolidayEmails() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet3 = ss.getSheetByName("시트3"); // ✅ 메일 발송이 허용된 시트3만 사용
if (!sheet3) {
Logger.log("⚠️ 시트3을 찾을 수 없습니다.");
return;
}
var allowedEmails = getAllowedEmails(sheet3); // ✅ 시트3에서 허용된 이메일 가져오기
if (allowedEmails.length === 0) {
Logger.log("⚠️ 시트3에 이메일 데이터가 없습니다.");
return;
}
var emailSubject = "설 연휴가 끝났습니다! 힘내서 새 출발해요!"; // ✅ 이모티콘 제거 (Gmail 인코딩 문제 해결)
for (var i = 0; i < allowedEmails.length; i++) {
var email = allowedEmails[i];
// 1. 한글 이름만 추출
var koreanName = extractKoreanName(email);
// 2. 인사말 구성
var greeting = koreanName ? `안녕하세요, <b>${koreanName}님</b>.<br><br>` : "안녕하세요.<br><br>";
// 3. 이메일 본문 작성 (HTML 포맷 적용, Unicode HTML 엔티티 사용)
var emailBody = greeting +
"🎉 설 연휴 잘 보내셨나요? 가족과 함께 따뜻한 시간을 보내셨길 바랍니다.<br><br>" +
"연휴가 끝나고 다시 바쁜 일상이 시작되었지만, 새로운 마음으로 힘차게 출발할 수 있길 바랍니다!<br><br>" +
"올해도 건강하시고, 계획하신 일들이 순조롭게 진행되길 기원합니다.<br>" +
"저희도 좋은 에너지를 담아 열심히 달려가겠습니다. 함께 멋진 한 해 만들어가요! 🚀<br><br>" +
"✨ 혹시 필요하신 것이 있거나 편하게 이야기 나누고 싶다면 언제든 연락 주세요.<br>" +
"💪 좋은 하루 보내시고, 활기찬 한 주 시 작하세요!<br><br>" +
"감사합니다.<br><b>홍해성 드림</b>"; // ✅ 보낸 사람 이름 고정
// 4. HTML 형식으로 이메일 발송
GmailApp.sendEmail(email, emailSubject, "", { htmlBody: emailBody });
Logger.log("✅ 이메일 발송 완료: " + email);
}
}
// 📌 한글 이름만 추출하는 함수
function extractKoreanName(email) {
var nameMatch = email.match(/[가-힣]+/g); // 한글만 추출
return nameMatch ? nameMatch.join("") : ""; // 여러 조각이 있으면 합쳐서 반환
}
// 📌 시트3의 B열에서 허용된 이메일 리스트 가져오는 함수
function getAllowedEmails(sheet) {
var emailData = sheet.getDataRange().getValues();
var emailList = [];
for (var i = 1; i < emailData.length; i++) { // 헤더 제외
var email = emailData[i][1].trim(); // ✅ B열(두 번째 열)에서 이메일 가져오기
if (email) {
emailList.push(email);
}
}
Logger.log("✅ 허용된 이메일 목록 로드 완료 (" + emailList.length + "개)");
return emailList;
}8) 찐_최종
한국의 메시지 스크린 샷
결과와 배운 점
배운 점과 나만의 꿀팁을 알려주세요.
앱스크립트 오류가 잦다. F5 자주 눌러야 했음.
확실히 구글 앱스크립트를 통해 > 구글 지메일로 업무를 연동하니 흐름이 매끄럽다. 내 요청사항을 잘 이해한다는 게 느껴지고, 시스템도 잘 구축되어 있는 느낌.
ex) 코드에 새로운 TASK를 추가하면 해당 작업에 대한 권한 요청이 온다. (메일 전송을 허용하시겠습니까 팝업창 등등)광밤님 tip: GPT가 내 말을 잘 이해하지 못하면 구체적인 예시를 줘라. 실제로 코드 수정 속도 훨씬 2배 이상 빨라짐!
-- 250206 스터디 발표 ver. --
Google Apps 스크립트
function getSentEmailContacts() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
sheet.clear(); // 기존 데이터 삭제
// 헤더 설정
sheet.appendRow(["이름", "메일주소", "메일발송횟수"]);
var oneYearAgo = new Date();
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1); // 1년 전 날짜 계산
var query = 'after:' + formatDate(oneYearAgo); // 1년 내 발송된 이메일 검색
var threads = GmailApp.search(query, 0, 500); // 최대 500개 스레드 검색
var emailCount = {}; // 이메일별 발송 횟수 저장
threads.forEach(thread => {
var messages = thread.getMessages();
messages.forEach(message => {
if (message.getFrom().includes(Session.getActiveUser().getEmail())) { // 내가 보낸 메일인지 확인
var recipients = message.getTo().split(","); // 여러 수신자 처리
recipients.forEach(recipient => {
var emailMatch = recipient.match(/<(.+?)>/);
var email = emailMatch ? emailMatch[1] : recipient.trim();
var nameMatch = recipient.match(/"(.+?)"/);
var name = nameMatch ? nameMatch[1] : "Unknown";
if (!emailCount[email]) {
emailCount[email] = { name: name, count: 0 };
}
emailCount[email].count += 1;
});
}
});
});
// 데이터 시트에 저장
Object.keys(emailCount).forEach(email => {
sheet.appendRow([emailCount[email].name, email, emailCount[email].count]);
});
Logger.log("✅ 이메일 데이터 수집 완료!");
}
// 날짜 포맷 함수 (YYYY/MM/DD 형식)
function formatDate(date) {
return date.getFullYear() + '/' + (date.getMonth() + 1) + '/' + date.getDate();
}
Google 애드워즈
다만 일부 수정이 필요했어요.
노란 표시한 것처럼 Unknown이라고 뜨거나, 메일주소가 그대로 '이름' 칼럼에 입력됨.
내가 1년 동안 이 분들한테만 메일을 보냈다고?
한국 및 영어 텍스트가있는 스프레드 시트의 스크린 샷
위에 대한 GPT의 대답
이름 관련
1️⃣ 이름 없이 이메일 주소만 존재하는 경우
어떤 이메일은
"홍길동 <[email protected]>"형태로 이름이 포함됨하지만
"[email protected]"같은 순수 이메일 주소만 있을 경우,nameMatch에서 이름을 찾지 못해서Unknown으로 표시됨
2️⃣ 데이터 추출 과정에서 이름을 제대로 인식하지 못한 경우
예를 들어
"홍길동 <[email protected]>"이 아니라"홍길동님 <[email protected]>"처럼 변형된 형식이 있으면 문제 발생 가능
✅ 해결 방법
이름이 없을 경우 이메일의 앞부분(@ 이전)을 이름으로 대체
Unknown을 없애기 위해 코드 수정
데이터 수 관련
🚨 원인 분석
GmailApp.search(query, 0, 500);→ 최대 500개의 메일 스레드만 검색Gmail에서는 1개의 스레드에 여러 개의 메일이 포함될 수 있음, 하지만 일부 스레드가 검색되지 않을 수도 있음
message.getTo()만 확인하고 있지만, CC, BCC에 포함된 사람들은 집계되지 않음
✅ 해결 방법
1️⃣ 검색 범위 늘리기
→GmailApp.search(query, 0, 2000);로 확장 (최대 2000개)2️⃣ CC, BCC 수신자도 포함
→message.getCc()와message.getBcc()도 포함하도록 수정
function getSentEmailContacts() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
sheet.clear(); // 기존 데이터 삭제
// 헤더 설정
sheet.appendRow(["이름", "메일주소", "메일발송횟수"]);
var oneYearAgo = new Date();
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1); // 1년 전 날짜 계산
var query = 'after:' + formatDate(oneYearAgo); // 1년 내 발송된 이메일 검색
var threads = GmailApp.search(query, 0, 1000); // 더 많은 이메일 검색
var emailCount = {}; // 이메일별 발송 횟수 저장
threads.forEach(thread => {
var messages = thread.getMessages();
messages.forEach(message => {
if (message.getFrom().includes(Session.getActiveUser().getEmail())) { // 내가 보낸 메일인지 확인
var recipients = message.getTo().split(","); // 여러 수신자 처리
recipients.forEach(recipient => {
var emailMatch = recipient.match(/<(.+?)>/);
var email = emailMatch ? emailMatch[1] : recipient.trim();
var nameMatch = recipient.match(/"(.+?)"/);
var name = nameMatch ? nameMatch[1] : email.split("@")[0]; // Unknown 대신 이메일 앞부분 사용
if (!emailCount[email]) {
emailCount[email] = { name: name, count: 0 };
}
emailCount[email].count += 1;
});
}
});
});
// 데이터 시트에 저장
Object.keys(emailCount).forEach(email => {
sheet.appendRow([emailCount[email].name, email, emailCount[email].count]);
});
Logger.log("✅ 이메일 데이터 수집 완료!");
}
// 날짜 포맷 함수 (YYYY/MM/DD 형식)
function formatDate(date) {
return date.getFullYear() + '/' + (date.getMonth() + 1) + '/' + date.getDate();
}
검색 범위 재조정 - 페이지네이션(반복호출)
function getSentEmailContacts() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
sheet.clear(); // 기존 데이터 삭제
// 헤더 설정
sheet.appendRow(["이름", "메일주소", "메일발송횟수"]);
var oneYearAgo = new Date();
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1); // 1년 전 날짜 계산
var query = 'after:' + formatDate(oneYearAgo); // 1년 내 발송된 이메일 검색
var emailCount = {}; // 이메일별 발송 횟수 저장
var start = 0;
var batchSize = 500; // Gmail API의 최대 검색 개수
while (true) {
var threads = GmailApp.search(query, start, batchSize);
if (threads.length === 0) break; // 더 이상 가져올 데이터가 없으면 종료
threads.forEach(thread => {
var messages = thread.getMessages();
messages.forEach(message => {
if (message.getFrom().includes(Session.getActiveUser().getEmail())) { // 내가 보낸 메일인지 확인
var recipients = [].concat(
message.getTo().split(","),
message.getCc() ? message.getCc().split(",") : [],
message.getBcc() ? message.getBcc().split(",") : []
); // TO + CC + BCC 포함
recipients.forEach(recipient => {
var emailMatch = recipient.match(/<(.+?)>/);
var email = emailMatch ? emailMatch[1] : recipient.trim();
var nameMatch = recipient.match(/"(.+?)"/);
var name = nameMatch ? nameMatch[1] : email.split("@")[0]; // Unknown 대신 이메일 앞부분 사용
if (!emailCount[email]) {
emailCount[email] = { name: name, count: 0 };
}
emailCount[email].count += 1;
});
}
});
});
start += batchSize; // 다음 500개 검색
}
// 데이터 시트에 저장
Object.keys(emailCount).forEach(email => {
sheet.appendRow([emailCount[email].name, email, emailCount[email].count]);
});
Logger.log("✅ 이메일 데이터 수집 완료!");
}
// 날짜 포맷 함수 (YYYY/MM/DD 형식)
function formatDate(date) {
return date.getFullYear() + '/' + (date.getMonth() + 1) + '/' + date.getDate();
}이름에 여전히 오류: 이름 설정이 되어있는데도 이메일의 아이디만 불러와짐
GPT 대답
🔍 문제 분석
이메일이
"김*희/산학협력팀(**) <wngm****@***.ac.kr>"형식인데도 "wng****"로 표시됨이는 기존 코드에서
"이름"을 추출할 때" "(큰따옴표) 안의 값만 가져오도록 했기 때문일부 이메일에는 큰따옴표 없이 이름이 포함될 수 있음 → 이를 제대로 가져와야 함
따라서 이름이 정상적으로 존재하면 그 값을 가져오고, 없다면 이메일 앞부분을 대체하도록 수정
한국 문자 메시지의 스크린 샷
✅ 해결 방법
📌 이름을 가져오는 방법 개선
기존에는
recipient.match(/"(.+?)"/);를 사용해서 큰따옴표 안의 값을 추출했는데,
→ 큰따옴표 없이도 이름을 가져올 수 있도록 개선 필요<> 내부의 이메일을 제외하고, 남은 문자열을 이름으로 인식하는 방식으로 수정
function getSentEmailContacts() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
sheet.clear(); // 기존 데이터 삭제
// 헤더 설정
sheet.appendRow(["이름", "메일주소", "메일발송횟수"]);
var oneYearAgo = new Date();
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1); // 1년 전 날짜 계산
var query = 'after:' + formatDate(oneYearAgo); // 1년 내 발송된 이메일 검색
var emailCount = {}; // 이메일별 발송 횟수 저장
var start = 0;
var batchSize = 500; // Gmail API의 최대 검색 개수
while (true) {
var threads = GmailApp.search(query, start, batchSize);
if (threads.length === 0) break; // 더 이상 가져올 데이터가 없으면 종료
threads.forEach(thread => {
var messages = thread.getMessages();
messages.forEach(message => {
if (message.getFrom().includes(Session.getActiveUser().getEmail())) { // 내가 보낸 메일인지 확인
var recipients = [].concat(
message.getTo().split(","),
message.getCc() ? message.getCc().split(",") : [],
message.getBcc() ? message.getBcc().split(",") : []
); // TO + CC + BCC 포함
recipients.forEach(recipient => {
var emailMatch = recipient.match(/<(.+?)>/);
var email = emailMatch ? emailMatch[1] : recipient.trim();
// 새로운 이름 추출 방식: <> 내부의 이메일을 제외한 부분을 가져옴
var name = recipient.replace(/<.*?>/, "").trim();
// 이름이 없거나 이메일이 그대로 나올 경우 보정
if (!name || name === email) {
name = email.split("@")[0]; // 이름이 없으면 이메일 앞부분 사용
}
if (!emailCount[email]) {
emailCount[email] = { name: name, count: 0 };
}
emailCount[email].count += 1;
});
}
});
});
start += batchSize; // 다음 500개 검색
}
// 데이터 시트에 저장
Object.keys(emailCount).forEach(email => {
sheet.appendRow([emailCount[email].name, email, emailCount[email].count]);
});
Logger.log("✅ 이메일 데이터 수집 완료!");
}
// 날짜 포맷 함수 (YYYY/MM/DD 형식)
function formatDate(date) {
return date.getFullYear() + '/' + (date.getMonth() + 1) + '/' + date.getDate();
}여기까지 작업한 결과
한국어 텍스트가 장착 된 Google 스프레드 시트
이제 이들에게 메일을 보내는 기능을 추가해달라고 요청했어요.
다만 실제로 메일이 나가면 안 되기 때문에, 다른 사람들에게는 테스트로, 제 메일에만 실제로 메일이 발송되도록 요청했어요.
한국어 문자 메시지의 스크린 샷
한국어로 된 문자 메시지
function getSentEmailContactsAndSendTestEmails() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("이메일 목록");
if (!sheet) {
sheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet("이메일 목록");
}
sheet.clear();
sheet.appendRow(["이름", "메일주소", "메일발송횟수"]);
var logSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("작업 로그");
if (!logSheet) {
logSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet("작업 로그");
}
logSheet.clear();
logSheet.appendRow(["시간", "진행 상황"]);
function logProgress(message) {
logSheet.appendRow([new Date().toLocaleString(), message]);
}
var isTestMode = true; // 기본적으로 모든 메일은 발송 안 함
var testEmail = "[email protected]"; // ✅ 이 이메일에게만 실제 발송
logProgress("✅ 스크립트 실행 시작");
var oneYearAgo = new Date();
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);
var query = 'after:' + formatDate(oneYearAgo);
var emailCount = {};
var start = 0;
var batchSize = 500;
while (true) {
var threads = GmailApp.search(query, start, batchSize);
if (threads.length === 0) break;
logProgress(`🔍 ${threads.length}개의 이메일 스레드 검색됨`);
threads.forEach(thread => {
var messages = thread.getMessages();
messages.forEach(message => {
if (message.getFrom().includes(Session.getActiveUser().getEmail())) {
var recipients = [].concat(
message.getTo().split(","),
message.getCc() ? message.getCc().split(",") : [],
message.getBcc() ? message.getBcc().split(",") : []
);
recipients.forEach(recipient => {
var emailMatch = recipient.match(/<(.+?)>/);
var email = emailMatch ? emailMatch[1] : recipient.trim();
var name = recipient.replace(/<.*?>/, "").trim();
if (!name || name === email) {
name = email.split("@")[0];
}
if (!emailCount[email]) {
emailCount[email] = { name: name, count: 0 };
}
emailCount[email].count += 1;
});
}
});
});
start += batchSize;
}
logProgress("✅ 데이터 처리 완료, 이메일 목록 스프레드시트에 저장 시작");
Object.keys(emailCount).forEach(email => {
sheet.appendRow([emailCount[email].name, email, emailCount[email].count]);
// ✉️ 이메일 제목 및 본문 업데이트
var subject = "설 연휴 잘 보내세요! 🎉";
var message = `안녕하세요,\n\n어느덧 설 연휴가 다가왔습니다. 한 해 동안 함께해 주셔서 감사드리며, 따뜻하고 행복한 명절 보내시길 바랍니다. 새해에는 더 큰 행운과 기쁨이 가득하길 기원합니다. 😊\n\n즐거운 설 연휴 보내세요!\n\n감사합니다.`;
if (email === testEmail) {
// ✅ [email protected]에게만 실제 발송
MailApp.sendEmail({
to: email,
subject: subject,
body: message
});
logProgress(`✅ [실제 발송] ${email}에게 메일이 전송되었습니다.`);
} else {
// ❌ 다른 이메일은 테스트 모드 유지
logProgress(`[테스트 모드] 메일이 전송되지 않음 → 수신자: ${email}`);
}
});
logProgress("✅ 모든 작업 완료!");
}
function formatDate(date) {
return date.getFullYear() + '/' + (date.getMonth() + 1) + '/' + date.getDate();
}아래와 같이 메일 발신/수신 성공!
*메일 문구의 경우 이후에 GPT한테 요청하니 수정도 잘 해줬어요.
한국의 메시지 스크린 샷
다른 메일에 대해서도 작업이 잘 되고 있는건지 궁금..
function getSentEmailContactsAndSendTestEmails() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("이메일 목록");
if (!sheet) {
sheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet("이메일 목록");
}
sheet.clear();
sheet.appendRow(["이름", "메일주소", "메일발송횟수"]);
var logSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("작업 로그");
if (!logSheet) {
logSheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet("작업 로그");
}
logSheet.clear();
logSheet.appendRow(["시간", "진행 상황"]);
function logProgress(message) {
logSheet.appendRow([new Date().toLocaleString(), message]);
}
var isTestMode = true;
logProgress("✅ 스크립트 실행 시작");
var oneYearAgo = new Date();
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);
var query = 'after:' + formatDate(oneYearAgo);
var emailCount = {};
var start = 0;
var batchSize = 500;
while (true) {
var threads = GmailApp.search(query, start, batchSize);
if (threads.length === 0) break;
logProgress(`🔍 ${threads.length}개의 이메일 스레드 검색됨`);
threads.forEach(thread => {
var messages = thread.getMessages();
messages.forEach(message => {
if (message.getFrom().includes(Session.getActiveUser().getEmail())) {
var recipients = [].concat(
message.getTo().split(","),
message.getCc() ? message.getCc().split(",") : [],
message.getBcc() ? message.getBcc().split(",") : []
);
recipients.forEach(recipient => {
var emailMatch = recipient.match(/<(.+?)>/);
var email = emailMatch ? emailMatch[1] : recipient.trim();
var name = recipient.replace(/<.*?>/, "").trim();
if (!name || name === email) {
name = email.split("@")[0];
}
if (!emailCount[email]) {
emailCount[email] = { name: name, count: 0 };
}
emailCount[email].count += 1;
});
}
});
});
start += batchSize;
}
logProgress("✅ 데이터 처리 완료, 이메일 목록 스프레드시트에 저장 시작");
Object.keys(emailCount).forEach(email => {
sheet.appendRow([emailCount[email].name, email, emailCount[email].count]);
var subject = "새해 복 많이 받으세요! 🎉";
var message = `안녕하세요 ${emailCount[email].name}님,\n\n설 명절을 맞이하여 따뜻한 인사를 전합니다! 새해에는 건강과 행복이 가득하시길 바랍니다. 😊\n\n감사합니다.`;
if (isTestMode) {
logProgress(`[테스트 모드] 메일이 전송되지 않음 → 수신자: ${email}`);
} else {
MailApp.sendEmail({
to: email,
subject: subject,
body: message
});
logProgress(`✅ 실제 메일 전송 완료 → ${email}`);
}
});
logProgress("✅ 모든 작업 완료!");
}
function formatDate(date) {
return date.getFullYear() + '/' + (date.getMonth() + 1) + '/' + date.getDate();
}한국어 텍스트가 장착 된 Google 스프레드 시트
결과와 배운 점
배운 점과 나만의 꿀팁을 알려주세요.
-확실히 구글 앱스크립트를 통해 > 구글 지메일로 업무를 연동하니 흐름이 매끄럽다. 내 요청사항을 잘 이해한다는 게 느껴지고, 시스템도 잘 구축되어 있는 느낌.
ex) 코드에 새로운 TASK를 추가하면(ex. 이제는 이 사람들에게 메일까지 전송해줘) 해당 작업에 대한 권한 요청이 온다.
-앱스크립트에서 문제가 생기면 차라리 새로 스프레드시트를 열어서 다시 작업하는 게 편할 수도..!
ex)
한국어로 된 메시지
과정 중에 어떤 시행착오를 겪었나요?
-원래 리멤버에 저장된 명함을 불러오고 싶었는데 크롤링에 어려움 있었음. (리스틀리 활용)
도움이 필요한 부분이 있나요?
-앱스크립트 코드 입력할 때 함수 관련 오류
-'이름' 칼럼 데이터를 깔끔히 불러오는 건 계속 어려웠어요.
앞으로의 계획이 있다면 들려주세요.
-구글 툴을 사용하지 않고, make 같은 자동화 툴과 연결해서 메일 전송 전체 단계를 자동화하고자 합니다.
도움 받은 글 (옵션)
참고한 지피터스 글이나 외부 사례를 알려주세요.
(내용 입력)