랜덤 런치, 슬랙 봇으로 자동화하기

Jay·2024년 11월 10일
2
post-thumbnail

현재 제가 속한 팀에서는 매주 랜덤 런치를 진행하고 있습니다. 인원 수가 많아 소그룹을 나눠서 진행하였는데요, 지금까지는 매번 그룹을 수동으로 나누는 방식을 사용해 왔습니다.

기존 방식은 다음과 같았습니다:
1. 매주 목요일 오전, 콘솔 창에서 랜덤 그룹을 생성하는 코드를 실행한다.
2. 결과를 캡쳐해 슬랙 채널에 공유한다.
3. 휴가자나 참석 불가 인원이 있다면, 해당 인원을 제외하고 다시 그룹을 생성한다.

나쁘지 않은 방식이지만, 매번 누군가가 자발적으로 처리해야 했고, 투명성 문제도 있었습니다. 자연스럽게 '자동화 할 수 있는 방법이 없을까?'라는 생각이 들었고, 슬랙 마켓플레이스에서 적절한 무료앱을 찾아보았지만 원하는 기능을 충족하는 앱을 찾지 못했습니다. 그래서 직접 슬랙 앱을 만들어보기로 했습니다.

랜덤 런치 자동화에 필요한 요구사항:
1. 매주 목요일 알림 기능
2. 무작위 그룹 생성 기능
3. 슬랙 채널에 메세지를 전송하는 기능

검토한 결과, 간단하면서도 강력하게 이 기능들을 구현할 수 있는 서버리스 솔루션으로 Google Apps Script가 적합하다고 판단했습니다. Google Apps Script는 구글 워크스페이스에 기본적으로 포함되어 있으며, 브라우저에서 바로 코드를 작성하고 웹 애플리케이션 형태로 쉽게 배포할 수 있다는 장점이 있습니다.

랜덤 런치 슬랙 앱 구축을 위한 핵심 과정:

  • 슬랙으로 메시지를 전송하는 스크립트 작성: 팀의 구성원을 무작위로 나눠 슬랙 채널에 메세지로 전송하는 기능을 구현합니다.
  • Google Sheets를 데이터 저장소로 활용: 명단을 간편하게 관리하고 저장할 수 있습니다.
  • 일정에 따라 스크립트를 자동 실행하도록 예약: 트리거 설정을 통해 매주 목요일 지정된 시간에 자동으로 실행되도록 합니다.
  • 슬랙의 슬래시 커맨드 기능을 통해 유저 요청 시 새로운 그룹을 만들어 줍니다.

이제, 랜덤 런치 슬랙 앱을 직접 만들어 자동화하는 과정을 소개하겠습니다.

Slack 앱 생성

  1. https://api.slack.com/apps 접속
  2. Create an App 버튼 클릭
  3. From scratch 선택
  4. App Name 입력, workspace 선택
  5. Create App 버튼 클릭

Arcade 데모

Slack 앱 Bot User 생성

  1. https://api.slack.com/apps 에서 생성한 앱 이름 클릭
  2. Incoming Webhooks 메뉴에서 Activate Incoming Webhooks 스위치 버튼 ON
  3. App Home 메뉴에서 App Display Name Edit 버튼 클릭
  4. Display Name, Default username 입력
  5. Add 버튼 클릭

Arcade 데모

Slack 앱 수신 웹훅 생성

  1. Incoming Webhooks 메뉴에서 Add New Webhook to Workspace 버튼 클릭
  2. 앱으로 게시할 채널 선택
  3. 허용 버튼 클릭

Arcade 데모

Google Sheets 생성

  1. Google Sheets 생성
  2. 시트 명단 작성
  3. 시트 이름 지정
  4. 확장 프로그램 버튼 클릭
  5. Apps Script 선택

Google Apps Script

스크립트 속성에 Slack 웹훅 URL 등록

  1. https://api.slack.com/apps 을 통해 생성한 앱의 설정 페이지로 다시 접속
  2. Incoming Webhooks 메뉴에서 생성한 수신 웹훅 URL 주소를 복사

  1. Apps Script 프로젝트 설정 메뉴에서 스크립트 속성 추가 버튼 클릭
  2. Slack 웹훅 URL 키 이름과 값 입력
  3. 스크립트 속성 저장 버튼 클릭

스크립트 속성에 대한 자세한 내용은 아래 링크를 참고 해주세요.
Properties Service

Google Apps Script 스크립트 작성

아래는 제가 작성한 스크립트 코드 전체입니다. 글이 길어져 로직 설명은 주석으로 대신하였으며, 설정이 동일하게 적용되었다면 그대로 사용하시거나 필요에 따라 수정하셔도 무방합니다.

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);
} 

Google Apps Script 배포

  1. 배포 버튼 클릭
  2. 새 배포 선택

  1. 액세스 권한이 있는 사용자를 모든 사용자로 변경
  2. 배포 버튼 클릭

(+ 배포한 웹 앱 URL은 추후 설정에 사용됩니다.)

Google Apps Script 트리거 생성

  1. Apps Script 트리거 메뉴 선택
  2. 트리거 추가 버튼 클릭

이후 설정은 트리거 추가 모달 이미지로 대신합니다.
이제 매주 목요일 오전 8시 ~ 오전 9시 사이 랜덤 런치 그룹 메세지가 슬랙 채널로 전송 됩니다.

Slack 앱 슬래시 커맨드 설정

  1. https://api.slack.com/apps 에서 생성한 앱 이름 클릭
  2. Slash Commands 메뉴 선택
  3. Create New Command 버튼 클릭

  1. Command 입력 (+ 다른 명령어를 사용하려면 스크립트 수정이 필요합니다.)
  2. 배포한 Apps Script URL 입력
  3. 명령어 설명 입력
  4. 명령어 힌트 입력
  5. Save 버튼 클릭

권한이 변경되어 앱 재설치가 필요합니다. 링크를 누르고 안내에 따라 진행해 주세요.
슬랙앱이 갖고 있는 권한이 궁금하시다면 OAuth & Permissions 메뉴를 선택하여 권한을 확인하실 수 있습니다.

테스트

로직 테스트

  1. Apps Script 접속
  2. 실행할 함수 선택
  3. 실행 버튼 클릭

만약 잘 동작하지 않는다면 실행 로그로 디버깅 해보세요!

슬래시 커맨드 테스트

봇을 생성한 채널에서 슬래시 커맨드 명령어를 입력

마무리

지금까지 랜덤 런치 슬랙앱 만드는 과정이었습니다. 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

0개의 댓글