데이터 엔지니어의 생일 축하 슬랙 메시지 자동화 개발기 feat. Apps Script

Joshua Kim
IOTRUST : Team Blog
11 min readJul 2, 2024

들어가는 글

회사는 일을 하기 위해 모인 곳입니다. 업무는 물론, 인간 관계와 의사소통 모두 “일을 하기 위한” 목적을 지닐 수밖에 없겠죠. 하지만 사람은 누구나 감정에 의존적인 존재입니다. 이성적인 판단, 논리적으로 빈틈 없는 비판만으로는 사람의 마음을 결코 얻을 수 없을 것입니다. 이성과 논리로만 가득 찬 8시간을 매일 보낸다는 것 역시 누구에게나 힘들고 고되겠죠.

많은 기업에서 각 임직원의 생일, 출산, 입사 기념일 등을 축하해주는 문화를 두고 있습니다. 직장인이라면 가장 많은 시간을 보내는 곳이 회사인 만큼, 경사를 함께 축하하며 감정을 교류하면 “일을 떠난 소통”의 리프레쉬를 통해, 더 매끄러운 협업과 상호 이해에 알게 모르게 도움이 될 것입니다. 그리고 결국 더 좋은 제품을 만들어낼 수 있겠죠.

저희 회사 역시 마찬가지입니다. HR/GA팀을 포함하여 사내의 많은 동료 분들이 캘린더 일정을 확인하고 직접 슬랙 채널에 생일과 입사 기념일 축하 메시지를 남겨주시곤 했습니다.

저의 생일과 입사 기념일 메시지

고민의 발견

이렇게 좋은 문화를 매일 챙겨주시는 HR/GA팀에서는 작지만 깊은 고민이 있었습니다. Day-to-day 업무가 워낙 많은 탓에, 매일 임직원의 생일과 입사 기념일을 확인하여 메시지를 보내는 일조차 버겁게 느낄 수 있고, 또 자칫 날짜를 놓쳐 의도치 않게 축하를 못 받은 동료 분들이 큰 서운함을 느낄 수도 있겠죠. 즉, HR/GA팀에서는 사소한 일인데도 불구하고 큰 부담을 가지고 계셨습니다.

예전에 업무 자동화 개발자로 근무한 적이 있었고 여전히 이 영역에 관심이 많은 저는 “마침 딱 좋은 자동화 영역이구나!”하고 생각했고, HR/GA팀 동료 분과 함께 바로 진행을 시작했습니다. 자동화를 통해 그 누구도 서운함을 느끼지 않은 채, 소중한 경사를 함께 축하해줄 수 있는 완벽무구한 시스템을 만들게 된 것이죠. (사실 엄청 간단하지만.)

생일 축하 슬랙 메시지 자동화 구조

저는 먼저 다음과 같이 큰 흐름을 구상했습니다.

본인 작성

(1) 임직원의 생일 정보 관리용 구글 시트를 만든다.

(2) Apps Script에 sendBirthdayMessage() 함수를 작성하고 매일 특정 시간대에 자동으로 실행할 수 있도록 세팅한다.

(3) 실행 당일에 생일인 임직원이 1명 이상일 경우, 슬랙 Incoming Webhook에 메시지를 요청한다.

(4) 슬랙 Incoming Webhook은 이 메시지를 받아 타겟 채널에 축하 메시지를 보낸다.

자동화 절차

(1) 임직원의 생일 정보를 관리하는 구글 시트 만들기

다음과 같은 양식으로 각 임직원의 성명, 생년월일, 슬랙 Member ID를 작성합니다. (생년월일은 YYYY-MM-DD 포맷으로 작성해주세요.)

한편, 슬랙 Member ID란 각 임직원의 슬랙 계정 고유 ID를 의미하는데요. 메시지를 보낼 때 “멘션”을 하기 위해 이 값을 알아야 합니다. 아래 그림 처럼, 각 임직원의 슬랙 프로필 화면에서 Copy member ID 버튼을 클릭하여 확인할 수 있습니다.

(2) 슬랙 Incoming Webhook 추가하기

슬랙의 Incoming Webhook은 외부 시스템에서 슬랙 앱 상으로 메시지를 보내는 등 어떤 액션을 발생시키기 위한 작은 서버의 역할을 지니고 있다고 생각할 수 있는데요. Slack App Directory에서 Incoming Webhook을 다음과 같은 절차로 추가합니다.

  • xxx.slack.com/apps 에서 Manage 탭 클릭
  • Custom Integrations 탭에서 Incoming WebHooks 클릭
  • Add to Slack 클릭 후 아래와 같이 상세 정보 세팅
본인 작성

빨갛게 칠한 Webhook URL을 복사해둡니다. Apps Script 함수를 작성할 때 사용할 예정이거든요.

(3) Apps Script 열기

다시 구글 시트로 돌아와서, 상단 Extentions 탭 > Apps Script를 클릭합니다.

(4) Apps Script에 sendBirthdayMessage() 함수 작성하기

이제 Editor 탭에서 본격적으로 사용자 정의 함수를 작성해보도록 하겠습니다.

먼저 전체 스크립트를 아래와 같이 보여드린 후, 중요한 부분을 각각 설명해드릴게요.

// 작업 개요: 오늘 생일인 임직원들을 모두 찾아 슬랙 채널에 축하 메시지를 보낸다.

function sendBirthdayMessage() {

// 변수 선언: 시트, 데이터 영역
var active_sh = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var data_rng = active_sh.getDataRange().getValues();

// 변수 선언: 오늘 날짜 (MM-DD 형식)
var today = Utilities.formatDate(
new Date(),
Session.getScriptTimeZone(),
"MM-dd"
)

// 변수 선언: Slack Incoming Webhook URL
var webhookUrl = "YOUR INCOMING WEBHOOK URL";

// 변수 선언: 임시 리스트
var mentions = []; // 생일인 임직원들의 Slack Member ID 리스트
var years = []; // 생일인 임직원들의 생년 리스트

// Loop 시작: 구글 시트 데이터 영역의 각 Row를 순회하기
data_rng.forEach(
function(row) {

var member_id = row[3];
var mention = "<@" + member_id + ">";
new Date(row[1]);
var birthday = Utilities.formatDate(
Session.getScriptTimeZone(),
"MM-dd" // (MM-DD 형식)
);
var year = Utilities.formatDate(
new Date(row[1]),
Session.getScriptTimeZone(),
"YYYY"
);

// 만약 오늘이 생일이라면, Slack Member ID와 생년을 각각 memtions와 years에 담아주기
if (birthday === today) {
mentions.push(mention);
years.push(year);
}

}
);

// 오늘 생일인 사람들이 1명 이상 존재할 경우, Slack Incoming Webhook에 메시지 요청하기
if (mentions.length > 0) {

// mentions 리스트의 각 항목을 임직원 나이 순으로 내림차순 정렬 for 경로 우대
var mentions_sorted = mentions.slice()
mentions_sorted.sort(function(a, b) {return years[mentions.indexOf(a)] - years[mentions.indexOf(b)];});

// 이제 리스트의 각 항목을 String 타입으로 Concat하기
mentions_joined = mentions_sorted.join("님 ")

// 슬랙 Markdown을 적용하여 보낼 메시지 생성하기
var message = ">>> 오늘 생일의 주인공은 누구일까요? (두구두구두구) \n";
message += ":drum_with_drumsticks:".repeat(13) + " \n";
message += ":partying_face: " + mentions_joined + " 님의 생일입니다! \n";
message += ":birthday: 생일 축하드립니다! 행복한 하루 보내세요! :tada:";

// 요청할 쿼리 Object 만들기
var payload = JSON.stringify({text: message});
var query = {
method: "post",
contentType: "application/json",
payload: payload
};

// 슬랙 Incoming Webhook에 요청하기
UrlFetchApp.fetch(webhookUrl, query);

}

}
  • Apps Script에서 구글 시트의 각 요소를 Object로 활용할 수 있습니다. 마치 Excel VBA에서 Workbook, Worksheet, Range, Cell 등을 Object로 사용할 수 있는 것처럼요.
  // 변수 선언: 시트, 데이터 영역
var active_sh = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var data_rng = active_sh.getDataRange().getValues();
  • 방금 전 복사해둔 슬랙 Incoming Webhook URL을 이곳에 작성해줍니다. 이 함수가 해당 Incoming Webhook에 요청해야 하기 때문입니다.
  // 변수 선언: Slack Incoming Webhook URL
var webhookUrl = "YOUR INCOMING WEBHOOK URL";
  • 슬랙의 Markdown을 작성할 때, “멘션”을 하려면 아래와 같은 포맷이 이루어져야 합니다. (이 문서에서 자세한 포맷을 확인하실 수 있어요.)
      var mention = "<@" + member_id + ">";

(5) 함수가 매일 특정 시간대에 자동으로 실행될 수 있도록 세팅하기

스케줄러를 활성화하기 위해 Triggers 탭으로 이동합니다.

Add Trigger 버튼을 클릭하고, 다음과 같이 sendBirthdayMessage 함수 실행 스케줄링을 설정합니다.

위 설정은 다음과 같은 의미를 지닙니다.

sendBirthdayMessage 함수의 Head(가장 최근 업데이트) 버전을 매일 오전 8시-9시 사이에 실행하고, 만약 함수 실행이 실패할 경우 매일 나에게 알려줘!

(6) 생일 메시지 개봉박두

아래와 같이, 매일 아침 8시-9시 사이에 임직원의 생일을 축하하는 메시지가 나타납니다.

나가는 글

지금까지 소개해드린 자동화 사례는 사실 정말 간단합니다. Apps Script는 JavaScript와 매우 유사하기 때문에, 특히 프론트엔드 개발자에게는 식은 죽 먹기죠. 하지만 업무 자동화는 많은 기업에서 누구의 본업도 아닌, 그레이 영역에 해당하는 경우가 많습니다. 그래서 본업 만으로도 바쁜 프론트엔드 개발자가 시간 내어 다른 팀의 업무 자동화를 만들어주기 쉽지 않죠. (그래서 보통 외주 개발을 맡기나 봅니다.)

하지만 “간단한 데이터라도 이걸로 회사에 어떤 가치를 만들어낼 수 있을까”의 고민을 하며 살아가는 제게 이 일은 매력적으로 느껴졌습니다. 뒤에서 조용히 “사람 챙겨주는 것”을 좋아하는 성향으로서, 누군가의 경사를 축하해주고 있다는 인간적인 뿌듯함도 느낄 수 있었죠.

물론 이 일이 데이터 분석가나 데이터 엔지니어의 본업이 될 수는 없습니다. 데이터 파이프라인을 매끄럽게 구축하고 매출과 프로덕트 성장을 위한 고민만으로도 벅차거든요. 본업만으로도 바쁜 요즘이지만, 앞으로도 가끔씩 기회가 되면 회사 내에 업무 자동화가 필요한 영역을 찾아 회사에도 가치를 만들어내고 스스로도 공부할 수 있도록 노력해보겠습니다.

--

--