기본 콘텐츠로 건너뛰기

[지메일 팁 1] 지메일에서 예약전송하는 방법

지메일이 좋은 이유는, 지메일에서 제공하는 기본적인 기능외에도 사용자들이 필요로하는 유용한 기능들을 직접 추가할 수 있는 Open API 들을 제공하고 있다는 것입니다. 3rd party 플러그인 모듈들도 다 이런 API 를 이용하여 개발한 것으로 보입니다.

지메일에서 예약 전송을 이용하는 방법에는 두가지가 있습니다.

목차



Google 스크립트와 Google 스프레드시트를 이용한 나만의 예약 전송 기능을 만들어 활용하는 방법


구글 문서도구 스프레드시트의 스크립트 기능을 활용하면 다양하고 유용한 유틸리티를 만들 수 있습니다. 이 문서에서 소개하는 내용은 간단하게 구글문서 도구에서 내 스프레드시트 (예, My Gmail Scheduler) 를 작성하여 공개된 구글 스크립트를 삽입하여 내 지메일을 예약 전송하는 방법에 대해서 소개하고자 합니다.

아래 스프레드시트(My Gmail Scheduler)에서 보는 것과 같이, 지메일에서 작성중인 이메일 (초안 상태)들에 대해서 원하는 시간에 예약 일정을 설정하여 ‘Gmail Scheduler’ 스크립트를 수행하면 예약전송 OK


예약전송 스크립트 스프레드시트 만드는 순서

1. 스프레드시트 ‘My Gmail Scheduler’ 생성
2. 스프데드시트 -> ‘도구’ -> 스크립트 편집기 수행


3. 아래 스크립트 코드 입력

/* This Google Script was written by Amit Agarwal */

/* Web: http://www.labnol.org/?p=24867            */

/* Apps Script Development: http://ctrlq.org      */

/* Please retain this message in your copy        */

function onOpen() {
 var ss = SpreadsheetApp.getActiveSpreadsheet();
 var menu = [
   {name: "Step 1: Authorize", functionName: "authorize_"},
   {name: "Step 2: Fetch Messages", functionName: "initialize_"},
   {name: "Step 3: Schedule Messages", functionName: "setSchedule_"},
   {name: "Cancel Pending Jobs", functionName: "cancelJobs_"}
 ];  
 ss.addMenu("Gmail Scheduler", menu);
}

function authorize_() {  
 deleteTriggers_();
 SpreadsheetApp.getActive().toast("Authorization successful. Please choose Step 2 to fetch the Gmail drafts into the spreadsheet.");
}

function fetchDraftMessages_() {
 var sheet = SpreadsheetApp.getActiveSpreadsheet();
 var drafts = GmailApp.getDraftMessages();
 if (drafts.length > 0) {
   var rows = [];    
   for (var i=0; i
     if (drafts[i].getTo() !== "") {
       rows.push([drafts[i].getId(), drafts[i].getTo(), drafts[i].getSubject(), "", ""]);
     }
   }
   sheet.getActiveSheet().getRange(2,1,rows.length,5).setValues(rows);
   sheet.toast("Excellent. Now please enter the date and time when you would like these messages to be delivered.");
 } else {
   sheet.toast("We could not find any messages in your Gmail drafts folder.");
 }
}

function cancelJobs_() {
 deleteTriggers_();
 var sheet = SpreadsheetApp.getActiveSheet();
 var data  = sheet.getDataRange().getValues();
 for (var row=1; row
   sheet.getRange("E"+(row+1)).setValue("Not Scheduled");
 }
 SpreadsheetApp.getActive().toast("There are no pending messages in queue.");
}

function initialize_() {
 deleteTriggers_();
 clearSheet_();  
 fetchDraftMessages_();
}

function clearSheet_() {
 var sheet = SpreadsheetApp.getActiveSheet();
 sheet.getRange(2, 1, sheet.getLastRow()+1, 5).clearContent();
}

function setSchedule_() {
 deleteTriggers_();
 try {
   var sheet = SpreadsheetApp.getActiveSheet();
   var data  = sheet.getDataRange().getValues();
   var time  = new Date().getTime();
   var code  = [];
   for (var row in data ) {     
     if (row != 0) {
       var schedule = data[row][3];
       if ( schedule !== "" ) {
         if ( schedule.getTime() > time ) {
           ScriptApp.newTrigger("sendMails")
           .timeBased()
           .at(schedule)
           .inTimezone(SpreadsheetApp.getActiveSpreadsheet().getSpreadsheetTimeZone())
           .create();
           code.push("Scheduled");
         } else {
           code.push("Date is in the past");  
         }
       } else {
         code.push("Not Scheduled");
       }
     }
   }
   for (var i=0; i
     sheet.getRange("E" + (i+2)).setValue(code[i]);
   }
   SpreadsheetApp.getActiveSpreadsheet().toast("The mails have been scheduled. You can close this sheet. For help, contact amit@labnol.org", "Success", -1);
 } catch (e) {
   SpreadsheetApp.getActive().toast(e.toString());
 }
}

function deleteTriggers_() {
 var triggers = ScriptApp.getProjectTriggers();
 for (var i=0; i
   if (triggers[i].getHandlerFunction() === "sendMails") {
     ScriptApp.deleteTrigger(triggers[i]);
   }
 }
}


function sendMails() {
 try {
   var sheet = SpreadsheetApp.getActiveSheet();
   var data  = sheet.getDataRange().getValues();
   var time  = new Date().getTime();
   for (var row=1; row
     if (data[row][4] == "Scheduled") {             
       var schedule = data[row][3];
       if ( schedule != "" ) {
         if ( schedule.getTime() <= time ) {
           var status = dispatchDraft_(data[row][0]);
           sheet.getRange("E"+(row+1)).setValue(status);
         }
       }
     }
   }
 } catch (e) {
   SpreadsheetApp.getActive().toast(e.toString());
 }
}

function dispatchDraft_(id) {
 
 try {
   
   var message = GmailApp.getMessageById(id);
   
   if (message) {
     
     var body = message.getBody();
     var raw  = message.getRawContent();
     
     /* Credit - YetAnotherMailMerge */
     
     var regMessageId = new RegExp(id, "g");
     if (body.match(regMessageId) != null) {
       var inlineImages = {};
       var nbrOfImg = body.match(regMessageId).length;
       var imgVars = body.match(/]+>/g);
       var imgToReplace = [];
       if(imgVars != null){
         for (var i = 0; i < imgVars.length; i++) {
           if (imgVars[i].search(regMessageId) != -1) {
             var id = imgVars[i].match(/realattid=([^&]+)&/);
             if (id != null) {
               id = id[1];
               var temp = raw.split(id)[1];
               temp = temp.substr(temp.lastIndexOf('Content-Type'));
               var imgTitle = temp.match(/name="([^"]+)"/);
               var contentType = temp.match(/Content-Type: ([^;]+);/);
               contentType = (contentType != null) ? contentType[1] : "image/jpeg";
               var b64c1 = raw.lastIndexOf(id) + id.length + 3; // first character in image base64
               var b64cn = raw.substr(b64c1).indexOf("--") - 3; // last character in image base64
               var imgb64 = raw.substring(b64c1, b64c1 + b64cn + 1); // is this fragile or safe enough?
               var imgblob = Utilities.newBlob(Utilities.base64Decode(imgb64), contentType, id); // decode and blob
               if (imgTitle != null) imgToReplace.push([imgTitle[1], imgVars[i], id, imgblob]);
             }
           }
         }
       }
       
       for (var i = 0; i < imgToReplace.length; i++) {
         inlineImages[imgToReplace[i][2]] = imgToReplace[i][3];
         var newImg = imgToReplace[i][1].replace(/src="[^\"]+\"/, "src=\"cid:" + imgToReplace[i][2] + "\"");
         body = body.replace(imgToReplace[i][1], newImg);
       }
     }
     
     var options = {
       cc          : message.getCc(),
       bcc         : message.getBcc(),
       htmlBody    : body,
       replyTo     : message.getReplyTo(),
       inlineImages: inlineImages,
       attachments : message.getAttachments()
     }
     
     GmailApp.sendEmail(message.getTo(), message.getSubject(), body, options);
     message.moveToTrash();
     return "Delivered";
   } else {
     return "Message not found in Drafts";
   }
 } catch (e) {
   return e.toString();
 }
}

4. 스크립트 저장 및 실행
  1). 반드시 위 스크립트 코드를 입력한 후 저장 버튼을 클릭
  2). 저장 완료후, 실행->onOpen 함수를 수행  


5. 스크립트 onOpen 이 정상 수행이 되면, ‘내 지메일 예약 전송' 스프레드시트 메뉴에 ‘Gmail Scheduler’ 가 새롭게 생성이 됩니다.


6. 지메일에서 예약 전송하기 위한 메일 메시지들을 작성 후 전송하지 않고 초안(Draft) 상태 (임시보관함에 저장)로 저장하기



7. 스프레드시트에서 예약 전송 메시지 가져오기
  - 메뉴 -> Gmail Scheduler-> Step2: Fetch Messages 수행

8. 지메일에 예약전송 대상이 되는 메시지의 header 정보가 표시가 되고, 시트의 ‘D’ 컬럼에서 예약 전송하고자 하는 메시지를 선택 원하는 예약 일자를 입력 (예, 2014-08-01 08:00:00) 반드시 날짜 (YYYY-MM-DD) 와 시간 (HH:MM:SS) 을 입력해야 함


8번 과정을 통해서 지메일 예약이 정상 설정이 되면 스프레드시트는 닫기를 한 후, 나중에 다시 오픈하여 ‘E’ 컬럼을 확인하여 전송 상태 유무를 확인 할 수 있습니다. 정상으로 전송이 되면 ‘E’ 컬럼에 Delivered 표시가 됩니다.

크롬스토어에서 3rd party 앱을 설치하여 사용하는 방법이 있습니다.


크롬스토어에서 무료로 설치할 수 있는 예약전송의 대표적인 앱  Boomerang 을 크롬브라우저에서 설치하시면 됩니다.

크롬 브라우저에서 지메일 로그인 후, Boomerang 앱이 정상 설치 되면 아래 이미지에서와 같이 지메일 상단 오른쪽에 Boomerang 아이콘이 보여야 합니다.


편지쓰기시 하단에 ‘Send Later’ 라는 버튼이 보입니다. 이 버튼을 클릭하여 예약을 원하는 날짜및 시간을 입력하여 전송 하면 됩니다.

자세한 사항은 구글문서 참조

댓글

이 블로그의 인기 게시물

초파리 뇌가 게임하고 로봇을 조종한다? 커넥톰 AI의 원리와 현실

CONNECTOME × AI 초파리 뇌가 게임하고 로봇을 조종한다? 커넥톰 AI가 실제로 하는 일과 하지 못하는 일을, Doom과 드론 사례로 구분해 봤습니다. 결론부터 말하면 완전히 거짓은 아닙니다. 다만 "살아 있는 뇌를 컴퓨터에 업로드했다"는 설명은 틀립니다. 139,255 2024년 공개된 성체 암컷 초파리 뇌의 뉴런 수 5,450만 같은 커넥톰에 기록된 화학 시냅스 수 1억 2,500만 2026년 수컷 중추신경계 지도에 포함된 시냅스 연결 먼저, 무엇이 공개된 것인가 2024년 FlyWire 연구진은 성체 암컷 초파리 한 마리의 뇌에서 139,255개 뉴런과 약 5,450만 개 화학 시냅스를 재구성했습니다. 세포 유형과 뇌 영역, 신경전달물질 예측도 함께 공개했습니다. [1] 2026년에는 수컷 초파리의 뇌와 복측신경삭까지 연결한 지도가 나왔습니다. 16만6천 개가 넘는 뉴런과 1억2,500만 개 시냅스 연결을 담아, 감각이 움직임으로 이어지는 경로까지 연구할 수 있게 됐습니다. [2] 연구진은 전자현미경 사진에서 AI로 뉴런 경계를 분리하고 3차원으로 이어 붙입니다. AI가 잘못 합치거나 끊은 부분은 사람이 검수합니다. 커넥톰은 도로 지도에 가깝습니다. 도로가 어디로 이어지는지는 보여주지만, 지금 차가 어디를 달리는지, 신호등이 어떻게 바뀌는지, 운전자가 어디로 가려는지까지 알려주지는 않습니다. 커넥톰은 죽은 조직에서 얻은 정적 구조입니다. 순간의 막전위와 발화 패턴, 정확한 시냅스 강도, 호르몬 상태, 배고픔 같은 내부 상태는 온전히 담겨 있지 않습니다. 정적인 배선도에 어떻게 행동을 넣나 1. 그래프로 바꾼다 뉴런을 노드, 시냅스를 방향이 있는 연결로 표현합니다. 시냅스 수와 신경전달물질 예측은 가중치와 흥분·억제 부호의 출발점이 됩니다. 2. 시간 규칙을 넣는다 각 뉴런에 입력이 쌓이고 임계값을 넘으면 발화하는 수학 모델을 붙입니다. 대표적인 방...

CLARITY 법안 표결 실패, 비트코인과 XRP는 왜 급락했나

긴급 시장 분석 CLARITY 법안 표결 실패, 비트코인과 XRP는 왜 급락했나 49 대 50으로 막힌 미국 암호화폐 시장구조 법안. 표결의 정확한 의미와 BTC·XRP 가격 충격, 앞으로 확인할 가격대를 함께 분석합니다. 작성 기준: 2026년 9월 16일 오전 7시 25분(KST) · 가격은 작성 시점 기준 상원 표결 찬성 49 · 반대 50 비트코인 24시간 -4.32% XRP 24시간 -11.36% 목차 무엇이 부결됐나 왜 60표를 얻지 못했나 비트코인·XRP 가격 반응 비트코인 기술적 구간 XRP 기술적 구간 향후 시나리오와 관찰 포인트 먼저 결론부터 미국 상원이 부결한 것은 CLARITY 법안의 최종 통과안이 아닙니다. 상원 본회의에서 법안을 다루기 위해 토론 종결 절차를 밟고 심의로 넘어갈 것인지를 묻는 클로처 표결 이었습니다. 필요한 60표를 얻지 못하면서 법안은 본격 토론 단계에 진입하지 못했습니다. 정확한 표현: “CLARITY 법안이 최종 폐기됐다”가 아니라 “법안을 본회의에서 다루기 위한 절차표결이 실패했다”가 맞습니다. 시장은 이 결과를 미국 암호화폐 규제 명확화의 지연으로 받아들였습니다. 비트코인은 약 4% 하락했지만 XRP는 11% 넘게 떨어졌습니다. 규제 기대에 민감한 알트코인이 정치적 불확실성에 더 크게 반응한 모습입니다. 1. 상원에서 실제로 무엇이 부결됐나 미국 상원 공식 기록에 따르면 2026년 9월 15일 오후 3시, H.R. 3633인 Digital Asset Market Clarity Act 에 대한 ‘motion to proceed’의 클로처 동의가 찬성 49표, 반대 50표 로 부결됐습니다. 클...

Jev는 더 빠른 챗봇이 아니다: System One Model의 역할과 기업 활용 가능성

한 줄 요약: Jev는 글을 생성하는 챗봇이 아니라, 소프트웨어가 직접 사용할 판단값과 확률을 100ms 내외로 빠르게 반환하는 System One 의사결정 전용 모델 이다. 최근 AI 개발자 커뮤니티에서 Jev 가 빠르게 주목받고 있다. 항공권을 약 7초 만에 찾고, 광고 수백 개를 수십 초 안에 분석하고, 논문 1,018편을 0.08달러에 분류했다는 사례들이 등장했다. 숫자만 보면 새로운 초고속 생성형 AI처럼 보인다. 하지만 Jev를 ChatGPT나 Claude의 더 빠르고 저렴한 대체품으로 이해하면 핵심을 놓친다. Jev는 글을 생성하는 AI가 아니라, 소프트웨어가 사용할 판단값을 빠르게 반환하는 의사결정 전용 모델이다. TypeSafe AI는 이를 새로운 모델 계열인 System One Model 이라고 부른다.[1] 이 글에서는 TypeSafe의 공식 발표와 개발 문서, 초기 활용 사례를 바탕으로 다음 질문에 답한다. Jev는 정확히 무엇인가? 기존 LLM과 무엇이 다른가? 어떤 업무에 유용한가? Hermes 같은 AI 에이전트와 어떻게 연결할 수 있는가? 기업 데이터와 개인정보를 외부 API로 보내는 보안 문제는 어떻게 봐야 하는가? 1. 기존 LLM이 자동화에서 부딪히는 문제 ChatGPT, Claude, Gemini와 같은 LLM은 기본적으로 사람이 읽는 문자열을 생성하는 모델 이다. 보고서 작성, 설명, 대화, 코드 생성에는 매우 강력하다. 그러나 기업의 업무 시스템이 필요한 것은 종종 긴 설명문이 아니다. 예를 들어 고객 문의가 들어왔을 때 프로그램이 알고 싶은 것은 다음과 같다. 어느 부서가 처리해야 하는가? 긴급도는 얼마인가? 환불 요청인가? 사람이 확인해야 하는가? 자동 처리해도 되는가? 일반 LLM에 이런 질문을 하면 자연스러운 문장이나 JSON을 생성하도록 프롬프트를 작성한 뒤, 결과를 다시 파싱하고 검증해야 한다...

Muse와 OpenClaw: 같은 에이전트인가, 새로운 개인비서인가

개인 AI 에이전트 비교 · 2026년 9월 25일 Muse와 OpenClaw: 같은 에이전트인가, 새로운 개인비서인가 Muse가 OpenClaw 코드를 그대로 실행하는 클론이라는 증거는 없습니다. 다만 에이전트의 기본 구조와 제품 철학은 크게 닮았고, Muse의 새로움은 개념보다 배포 방식·보안 모델·대중화에 있습니다. 에이전트 개념은 OpenClaw ≈ Muse입니다. 구현은 다릅니다. OpenClaw가 사용자가 직접 운영하는 개방형 하니스라면, Muse는 Meta가 호스팅하는 소비자용 서비스입니다. 1. 왜 ‘일반인용 OpenClaw’라는 말이 나왔나 Muse 출시 뒤 초기 사용자들은 두 제품의 내부 파일 체계를 비교했습니다. SOUL.md , IDENTITY.md , USER.md , MEMORY.md , AGENTS.md , TOOLS.md 같은 이름뿐 아니라, 에이전트의 성격과 행동 경계를 정하는 문장까지 비슷하다는 지적이 나왔습니다. The Verge도 두 제품이 핵심 파일 이름과 “Be genuinely helpful, not performatively helpful” 같은 문구를 공유한다고 보도했습니다. [2] 파일 규약 OpenClaw: 성격·사용자·기억·도구를 텍스트 파일로 관리합니다. Muse: 이름과 역할이 유사한 파일 체계가 관찰됐습니다. 제품 형태 공통점: 질문에 답하는 챗봇을 넘어 브라우저와 도구를 사용해 실제 일을 수행하는 개인 에이전트를 지향합니다. 논란의 핵심은 화면이 비슷하다는 정도가 아닙니다. 에이전트 하니스, 즉 모델이 기억·도구·규칙을 활용하도록 둘러싼 실행 구조와 성격 문서까지 닮았다는 점입니다. 2. Meta의 공식 설명 Meta Superintelligence Labs 제품 책임자 Nat Friedman은 Muse를 “처음부터 새로 만들었지만, 제품으로서는...

[알아두면 쓸모 있는 구글 문서 팁] 문서 공유시- 사용자 이름 대신에 익명의 동물이 표시 되는 이유와 동물 종류

구글 드라이브에는 다른 유사 서비스에서는 제공하지 않는 구글 만의 유니크한 기능들이 있다 구글 문서를  불특정 다수에게 전체 공개로 공유할 수 있습니다. 불특정인이 구글 문서에 접속한 경우 익명의 동물로 표시됩니다.  ' 웹에 공개' 또는 '링크가 있는 사용자' 공유 설정을 선택하면 인식할 수 없는 이름이나 익명의 동물이 표시될 수 있습니다. 파일에서 인식할 수 없는 이름을 볼 수 있는 몇 가지 이유는 다음과 같습니다. 메일링 리스트와 파일을 공유합니다. Google 계정이 없는 사용자와 파일을 공유하며, 그 사용자가 다른 사용자에게 공유 초대를 전달했습니다. 내 파일을 수정할 수 있는 누군가가 파일을 다른 사용자와 공유했습니다. 다른 사용자가 자신의 Google 계정 이름을 변경했습니다. 공유 설정 페이지에서 해당 사용자 이름 위로 마우스를 이동하여 이메일 주소를 확인하세요. 익명의 동물 다른 사용자에게 개별적으로 보기 또는 수정 권한을 부여하거나 메일링 리스트에 속해 있는 경우에만 사용자 이름이 표시됩니다. 파일 권한을 '링크가 있는 사용자'로 설정하면 파일을 보고 있는 사용자의 이름이 표시되지 않습니다. 대신 다른 사용자가 익명으로 라벨이 지정되어 표시되고 각 익명 사용자는 다양한 익명의 동물로 나열됩니다. 파일 권한을 '링크가 있는 사용자'로 설정했지만 특정 사용자와 파일을 공유하는 경우 파일을 공유한 사용자의 이름이 표시됩니다. 그 외 다른 사용자가 파일을 볼 때는 익명으로 나타납니다. 비공개 파일의 익명 동물 파일 권한을 '링크가 있는 사용자'로 설정한 다음 이를 '특정 사용자'로 변경하면 다음과 같은 경우 여러 익명의 동물이 표시될 수 있습니다. 누군가 파일을 여러 번 여는 경우에는 익명의 동물 목록에서 오래되고 연결이 끊긴 세션을 강제 종료하는 데 조금 시간이 걸릴 수 있습니다. 누군가 온...