현재 제가 속한 팀에서는 매주 랜덤 런치를 진행하고 있습니다. 인원 수가 많아 소그룹을 나눠서 진행하였는데요, 지금까지는 매번 그룹을 수동으로 나누는 방식을 사용해 왔습니다.
기존 방식은 다음과 같았습니다:
1. 매주 목요일 오전, 콘솔 창에서 랜덤 그룹을 생성하는 코드를 실행한다.
2. 결과를 캡쳐해 슬랙 채널에 공유한다.
3. 휴가자나 참석 불가 인원이 있다면, 해당 인원을 제외하고 다시 그룹을 생성한다.
나쁘지 않은 방식이지만, 매번 누군가가 자발적으로 처리해야 했고, 투명성 문제도 있었습니다. 자연스럽게 '자동화 할 수 있는 방법이 없을까?'라는 생각이 들었고, 슬랙 마켓플레이스에서 적절한 무료앱을 찾아보았지만 원하는 기능을 충족하는 앱을 찾지 못했습니다. 그래서 직접 슬랙 앱을 만들어보기로 했습니다.
랜덤 런치 자동화에 필요한 요구사항:
1. 매주 목요일 알림 기능
2. 무작위 그룹 생성 기능
3. 슬랙 채널에 메세지를 전송하는 기능
검토한 결과, 간단하면서도 강력하게 이 기능들을 구현할 수 있는 서버리스 솔루션으로 Google Apps Script가 적합하다고 판단했습니다. Google Apps Script는 구글 워크스페이스에 기본적으로 포함되어 있으며, 브라우저에서 바로 코드를 작성하고 웹 애플리케이션 형태로 쉽게 배포할 수 있다는 장점이 있습니다.
랜덤 런치 슬랙 앱 구축을 위한 핵심 과정:
이제, 랜덤 런치 슬랙 앱을 직접 만들어 자동화하는 과정을 소개하겠습니다.
스크립트 속성에 대한 자세한 내용은 아래 링크를 참고 해주세요.
Properties Service
아래는 제가 작성한 스크립트 코드 전체입니다. 글이 길어져 로직 설명은 주석으로 대신하였으며, 설정이 동일하게 적용되었다면 그대로 사용하시거나 필요에 따라 수정하셔도 무방합니다.
function doPost(e) {
let data = {};
const contents = e.postData.contents;
// URL 인코딩된 데이터 파싱
const params = contents.split('&');
params.forEach(param => {
const [key, value] = param.split('=');
data[decodeURIComponent(key)] = decodeURIComponent(value || '');
});
if(data.command === "/groupgroup") {
const groups = createGroupExcept(data.text, "프론트엔드 명단", 3)
const message = getLunchGroupNotificationMessage(groups)
sendSlackMessage({ text: message })
}
return ContentService.createTextOutput(
JSON.stringify({"text": "OK"})
).setMimeType(ContentService.MimeType.JSON);
}
function createGroupExcept(excludedMembersText, sheetName, numberOfGroups) {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const data = sheet.getDataRange().getValues();
const members = data.slice(1)
const excludedMembers = excludedMembersText.split(',').map(name => name.trim()).filter(name => name)
const filteredMembers = members.filter(member => {
return !excludedMembers.includes(member[3]);
});
const shuffled = shuffle(filteredMembers)
const groups = divideGroups(shuffled, numberOfGroups)
return groups
}
function createRandomLunchGroup() {
const numberOfGroups = 3
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("프론트엔드 명단");
const data = sheet.getDataRange().getValues();
const members = data.slice(1);
const shuffled = shuffle(members)
const groups = divideGroups(shuffled, numberOfGroups)
const message = getLunchGroupNotificationMessage(groups)
sendSlackMessage({ text: message })
}
function getLunchGroupNotificationMessage(groups) {
// 그룹 메시지 생성
let message = "🍽️ 오늘의 점심 멤버는? 🍽️\n\n";
groups.forEach((group, index) => {
message += `🌟 그룹 ${index + 1} 🌟\n`;
group.forEach(member => {
const name = member[3]; // 스프레드 시트에서 이름은 네 번째 열
message += `@${name} `;
});
message += "\n\n"; // 각 그룹 끝에 줄바꿈 추가
});
return message
}
function shuffle(members) {
// 랜덤으로 섞기 (Fisher-Yates 알고리즘)
for (let i = members.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[members[i], members[j]] = [members[j], members[i]];
}
return members
}
function divideGroups(members, numberOfGroups) {
// 그룹 나누기
const baseGroupSize = Math.floor(members.length / numberOfGroups);
let remainder = members.length % numberOfGroups;
const groups = Array.from({ length: numberOfGroups }, () => []);
// 멤버를 그룹에 배분
let index = 0;
for (let i = 0; i < numberOfGroups; i++) {
const currentGroupSize = baseGroupSize + (remainder > 0 ? 1 : 0);
for (let j = 0; j < currentGroupSize; j++) {
groups[i].push(members[index++]);
}
if (remainder > 0) remainder--;
}
return groups
}
function sendSlackMessage(response) {
// Slack에 메시지 보내기
const scriptProperties = PropertiesService.getScriptProperties()
const webhookUrl = scriptProperties.getProperty('SLACK_WEBHOOK_URL');
const payload = JSON.stringify(response);
const options = {
method: "post",
contentType: "application/json",
payload: payload
};
UrlFetchApp.fetch(webhookUrl, options);
}
(+ 배포한 웹 앱 URL은 추후 설정에 사용됩니다.)
이후 설정은 트리거 추가 모달 이미지로 대신합니다.
이제 매주 목요일 오전 8시 ~ 오전 9시 사이 랜덤 런치 그룹 메세지가 슬랙 채널로 전송 됩니다.
권한이 변경되어 앱 재설치가 필요합니다. 링크를 누르고 안내에 따라 진행해 주세요.
슬랙앱이 갖고 있는 권한이 궁금하시다면 OAuth & Permissions 메뉴를 선택하여 권한을 확인하실 수 있습니다.
만약 잘 동작하지 않는다면 실행 로그로 디버깅 해보세요!
봇을 생성한 채널에서 슬래시 커맨드 명령어를 입력
지금까지 랜덤 런치 슬랙앱 만드는 과정이었습니다. Arcade로 데모를 열심히 만들었는데 벨로그가 iframe을 지원하지 않아 캡처 이미지로 대체하게 되었네요.🥲
구글 앱스 스크립트와 구글 스프레드시트를 활용해 작고 빠르게 시도할 수 있었고, 현재 팀 내에서 잘 사용되고 있습니다.
필요하다면 여러 방면으로 고도화할 수 있을 것 같습니다. 예를 들어, Slack User List API를 활용하여 구글 스프레드시트 관리 문제를 개선하거나, 멘션 기능을 추가할 수 있습니다. 메세지에 Block element를 추가해 맛집 리스트를 공유할 수도 있고, Socket Mode를 활용해 챗봇으로 고도화할 수도 있습니다.
(참고로, Google Apps Script는 웹소켓을 지원하지 않습니다!)
개발자로서 매우 즐거운 경험이었고 색다른 도전이었습니다. 이 앱은 저희 조직에 처음 도입된 사내 슬랙앱인데요. 각 팀에서 겪고 있는 문제나 자동화가 필요한 부분에 대해, 추후 다른 슬랙 앱을 추가로 만들어 조직에 기여할 수도 있을 것 같습니다.
필요한 기능을 슬랙에서 찾을 수 없다면, 나만의 슬랙 앱을 만들어 보세요. 이 글이 나만의 슬랙 앱을 만드는 데 도움이 되었기를 바랍니다.
감사합니다.
레퍼런스
Slack API Docs
Google Apps Script Docs
How to Make a Slackbot Using Google Scripts