2015년 6월 30일 화요일

[Google Apps 소식지 - 뉴스레터 6월호 ]

이번 6월호에도 많은 업데이트 소식 및 유용한 정보들이 담겨 있습니다.
구글문서로 작성된 뉴스레터로 해당 문서아에서 직접 댓글을 달 수 있도록 되어 있어서
의견이나 좋은 댓글들을 뉴스레터에 남겨주셔도 됩니다.


주요 발표: Inbox by Gmail의조기 도입 프로그램






2015년 6월 29일 월요일

[Google 포토]앱에서 얼굴인식 (얼굴 이미지로 검색하기) 기능 활성화 방법

 Google 포토에서의 이미지 검색기능은 탁월하다는 사실은 이미 알려진 사실. WSJ 기사 에서 언급 바와 같이 얼굴인식 기능은 탁월함을 넘어 경의롭기까지 하다. 단순히 얼굴 인식하는 것을 넘어서, 현재의 내 얼굴로 40년전의 어린 아기때의 사진을 찾아 준다고 하니.

그래서 실제 테스트를 해 보았다, 한국에서 설치되는 구글포토에서 얼굴 인식 기능은 막혀 있다. 한국에서 막혀 있는 얼굴 인식 기능을 시도 해 보려면, VPN 앱을 설치 해야 한다. 설치후 이 VPN 앱으로 현재의 위치를 미국으로 변경한 후, Google 포토를 재 설치 하면 포토에서 얼굴 인식 기능을 활성화 시킬 수 있다. VPN앱은 한번만 설치 한 후 Google 포토에서 얼굴검색이 활성화 되면 더이상 사용하지 않아도 됩니다.

안드로이드 설정의 어플리케이션 관리로 들어가셔서 Google포토 앱의 데이터와 캐시를 삭제한 후에 포토 앱을 실행합니다.

아직 내 어릴적 사진을 확보하질 못하였지만, 내 얼굴로 검색을 시도 하면 google 포토에 백업한 모든 사진에 대해서는 정확하게 내 사진을 찾아 주는 것을 확인. 놀라울 정도로 정확하게 찾아 준다.



2015년 6월 27일 토요일

[지메일 팁 2] 메일 수신확인 방법

Gmail 에서 보낸 메일 수신확인 할 수 있는 방법



개인 지메일 또는 기업용 지메일을 사용하는 분들의 불만 사항중에 하나는 보낸 메일에 대한 수신 확인 하는 기능이 없다는 이야기를 많이 듣습니다. 특히, 국내에서 제공하는 이메일 서비스에서는 대부분 제공하는데 왜? 지메일만 제공하지 않는지에 대한 불만이지요.


사실 표준 이메일 시스템들은 본래부터 보낸 메일에 대한 수신 확인 하는 내부 표준 기능은 제공하기 않고 있습니다.  따라서 대부분 수신확인 기능을 제공하는 메일 서비스들은 일부 같은 이메일 시스템 내의 사용자간 이메일 확인만 가능하거나 또는 타 이메일 시스템으로 보낸 메일에 대한 수신확인 제대로 확인이 안되는 경우가 많습니다.


지메일에서는 대부분 수신확인 기능이 제공하지 않는 것으로 알고 있습니다. 사실 개인 지메일 (무료 지메일) 본래 수신확인 기능은 제공하지 않는 것이 맞습니다. 그러나 기업용 지메일(Google Apps)에서는 ‘수신 확인 요청' 기능이 본래 제공하고 있었습니다. (아래 기업용 지메일 캡쳐이미지 참조) 메일 보내기에서 선택적으로 ‘수신 확인 요청' 을 할 수 있게 되어 있습니다.


그러나, 기업용 지메일에서의 ‘수신확인 요청' 은 수신자가 지메일을 사용하는 계정에서만 유효한 기능입니다. 지메일이 아닌 타 이메일 수신자에게 보낸 메일에 대한 수신 확인은 할 수 없는 제약이 있는 서비스 이지요. 어쩃든 지메일이 아주 수신확인 기능이 없는 것은 아니였죠?


이 문서는 위 기업용 지메일의 수신확인 기능을 설명하려고 하는 것은 아닙니다. 이보다 더 강력한 수신확인 기능을 소개하고자 합니다.


이 수신확인 기능은 크롬브라우저를 사용하는 모든 개인용 지메일 및 기업용 지메일 사용자 모두 사용이 가능 한 것으로. 수신자가 메일을 읽었는지 뿐만아니라, 어떤 기기(데스트탑인지 모바일인지)에서 읽었는지 몇번 읽었는지, 심지어는 어디서 읽었는지 구글 지도로 읽은 위치까지 알수 있는지 확인이 가능한 아주 강력한 수신확인 기능을 제공합니다.
 
이 기능은 크롬 웹스토어에서 제공하는 크롬용 3rd party 무료 익스텐션인 ‘streak’ 라는 기능에 대한 설명입니다.


  1. 크롬 웹 스토어에서 검색 하여 설치 하는 방법 - 크롬 웹스토어에서 ‘streak’ 검색




  1. 해당 ‘Streak’ 를 설치한 후 크롬브라우저에서 본인의 지메일로 로그인을 하면 지메일 메뉴 상단 오른쪽에 ‘Streak’ 라는 메뉴가 생기는지 확인. Streak 는 본래 CRM 도구로 설치 과정중에 여러가지 옵션이 있으나 모두 기본 설정으로 (다른 선택없이) 설치 하시길 바랍니다.


  1. 지메일에서 메일 작성시 보내기 하단에 ‘수신 확인 추적 옵션' (노란색 눈 모양의 아이콘') 을 toggle 하여 ‘수신확인 요청'을 할 것인지 선택이 가능 (default 로는 ‘수신확인 요청' 으로 enable 됨)




  1. 보낸 메일 수신확인 추적하는 방법은 아래 캡쳐 이미지에서와 같이 지메일 목록에서 보낸 메일의 오른쪽에 ‘눈 모양의 아이콘'의 색깔이 초록색인 경우는 상대방이 수신확인 된 상태, 색갈이 회색인 경우는 아직 수신확인이 안된 상태로 표시됨
  1. 해당 보낸 메일을 클릭한 후 지메일의 오른쪽 창에서  보낸 메일의 수신 확인에 대한 자세한 정보가 표시가 됩니다. 아래 예에서와 같이, 메일을 읽은 재생 건수, 얼마나 많은 유일 뷰어로 읽었는지, 어떤 기기에서 몇시에 읽었는지, 심지어는 읽은 곳의 위치가 지도로 표시가 됩니다 (이 지도는 100% 정확한 것은 아니나 대략의 위치는 가늠할 수 있슴)



이외에도 "MailTrack for Gmail" 이라는 앱도 같은 기능을 제공합니다. 

서두에서 언급한바와 같이 기본적으로 이메일시스템들은 ‘수신확인' 기능을 표준으로 제공하지 않고 있기 때문에 3rd party 에서 제공하는 (이 Streak 도 마찬가지) 수신확인 방법은 한가지 기술을 이용하는 것입니다. 메일을 보낼대 본문 내용에 보이지 않는 이미지 (픽셀 수준)를 삽입하여 보내는 방법으로 수신자가 이메일 본문 내용을 읽었을때 자동으로 이 이미지를 엑세스하게 되게 하여 추적하는 방법을 사용합니다.

따라서, 이러한 3rd party 도구들대한 신뢰성 및 안정성에 대한 맹신은 금물입니다. 필요할때 선택적으로 사용하면 유용한 도구로 활용할 수 있을 듯 하여 소개합니다.  

2015년 6월 26일 금요일

[지메일 팁 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’ 라는 버튼이 보입니다. 이 버튼을 클릭하여 예약을 원하는 날짜및 시간을 입력하여 전송 하면 됩니다.

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