[개인 프로젝트]🤖 Slack Sheet Bot

이창훈·2025년 9월 1일

회고

목록 보기
10/13

📌 Slack Sheet Bot

1. 들어가며

슬랙봇 개발을 경험해보고 싶었던 배경:

사내 인하우스 서비스 기획 회의 중, 슬랙봇을 활용하면 별도의 회원가입이나 복잡한 절차 없이 간단히 서비스를 구축할 수 있다는 점을 알게 되었습니다. 특히, 슬랙봇이 제공하는 유려한 UI와 직관적인 사용자 경험에 관심이 생겨 간단한 서비스를 구현하게 되었습니다.

서비스 구상 배경:

제가 하루에 “큰일”을 자주 본다고 했을 때 친구가 믿지 않았습니다. 그래서 실제로 증명하기 위해, 큰일을 볼 때는 카톡으로 ‘ㄸ’, 작은 일을 볼 때는 ‘ㅇ’을 친구에게 보냈습니다. 그 친구는 이 기록을 흥미롭게 여겨 주변 사람들의 배출 기록까지 모아 DB에 저장하기 시작했습니다. 이 과정이 저에게 영감을 주었고, 이를 기반으로 서비스를 만들어보고자 하였습니다.

2. 앱 소개

Slack Sheet Bot / 구글 시트

NestJS 기반의 Slack 봇으로, Google Sheets와 연동하여 개인 활동 데이터를 추적하고 기록하는 애플리케이션입니다. Slack 슬래시 커맨드와 모달 인터페이스를 통해 데이터를 입력하고, Google Sheets에 실시간으로 저장됩니다.

🚀 주요 기능

  • /record 명령어를 통한 데이터 기록
  • /history 명령어를 통한 기록 조회 및 통계
  • 일일/누적 데이터 자동 관리
  • 실시간 순위 시스템
  • 한국 표준시(KST) 기준 시간 관리

🛠️ 사용한 기술 스택

Backend & Framework

  • NestJS (Node.js 프레임워크)
  • TypeScript
  • Socket Mode 통신

API & 연동

  • @slack/bolt (Slack 봇 개발 프레임워크)
  • @slack/webhook (웹훅 알림)
  • googleapis & google-spreadsheet (Google Sheets API)
  • google-auth-library (구글 인증)

개발 도구 & 품질 관리

  • ESLint & Prettier (코드 품질 관리)
  • Commitlint (커밋 메시지 규칙)
  • TypeScript (타입 안정성)

3. 개발 과정

🏗️ 설계: 아키텍처, 폴더 구조, 디자인 패턴 적용 여부

아키텍처 설계

  • 모듈화된 NestJS 아키텍처 적용
  • 의존성 주입(DI) 패턴을 통한 느슨한 결합
  • 서비스 레이어 분리로 관심사 분리

폴더 구조

src/
├── google-sheets/          # Google Sheets 관련 로직
│   ├── google-api.service.ts    # Google API 인증 및 기본 설정
│   ├── google-sheets.service.ts # Sheets 데이터 CRUD 로직
│   └── google-sheets.module.ts  # Google Sheets 모듈
├── slack/                  # Slack 봇 관련 로직
│   ├── slack.service.ts         # Slack 이벤트 처리 및 봇 로직
│   └── slack.module.ts          # Slack 모듈
├── utils/                  # 공통 유틸리티
│   └── date.utils.ts           # KST 시간 처리 유틸리티
├── app.module.ts           # 루트 모듈
└── main.ts                # 애플리케이션 진입점

적용된 디자인 패턴

  1. 모듈 패턴: NestJS의 모듈 시스템을 활용한 기능별 모듈화
  2. 의존성 주입: @Injectable 데코레이터를 통한 서비스 주입
  3. 팩토리 패턴: Google API 인증 객체 생성
  4. 전략 패턴: 다양한 Slack 이벤트에 대한 핸들러 분리

🔧 주요 기능 구현

1. Slack Bot 통신 (Socket Mode)

// Socket Mode를 통한 실시간 양방향 통신 설정
this.app = new App({
  token: process.env.SLACK_BOT_TOKEN,
  appToken: process.env.SLACK_APP_TOKEN,
  socketMode: true,
});

구현 특징:

  • HTTP 폴링이 아닌 WebSocket 기반 실시간 통신
  • Slash Command와 Modal 인터페이스 연동
  • 이벤트 기반 비동기 처리

2. Google Sheets 연동

// Service Account 인증을 통한 Sheets API 접근
private async getSheetsApi() {
  const auth = new google.auth.GoogleAuth({
    keyFile: './service_account.json',
    scopes: this.scopes,
  });
  
  return google.sheets({ version: 'v4', auth });
}

구현 특징:

  • Service Account를 통한 서버-투-서버 인증
  • 동적 행 탐색 및 업데이트 로직
  • 일일/누적 데이터 동시 관리

3. 한국 표준시(KST) 처리

// UTC를 KST로 변환하는 유틸리티
export const getKstString = (): string => {
  const now = new Date();
  const utc = now.getTime() + now.getTimezoneOffset() * 60000;
  const kst = new Date(utc + 9 * 3600000);
  return formatDate(kst);
};

구현 특징:

  • 서버 환경에 상관없이 일관된 KST 시간 처리
  • Google Sheets의 날짜 형식과 호환

4. 모달 기반 데이터 입력

  • 드롭다운을 통한 직관적인 UI
  • 입력값 검증 및 에러 핸들링
  • 실시간 피드백 제공

🚨 트러블 슈팅

1. Slack Modal 중복 응답 문제

문제 상황: Modal 제출 시 동일한 요청이 여러 번 처리됨

시도한 해결책:
1. await ack() 호출 순서 변경
2. 이벤트 리스너 중복 등록 확인

최종 해결 방법:

// 즉시 응답 후 비동기 처리
this.app.view('record_modal', async ({ ack, body, view }) => {
  await ack(); // 먼저 응답
  
  // 그 다음 비즈니스 로직 처리
  try {
    await this.sheetsService.appendPoopRow(data, this.webhook);
  } catch (error) {
    // 에러 핸들링
  }
});

2. 시간대 처리 이슈

문제 상황: 서버와 Google Sheets 간의 시간대 불일치

시도한 해결책:
1. moment.js 라이브러리 사용 고려
2. Google Sheets API의 시간대 설정 변경

최종 해결 방법:

  • 커스텀 KST 유틸리티 함수 구현
  • 서버 환경에 독립적인 시간 처리 로직 적용

4. 배운 점

🆕 새로운 기술/도구 사용 소감

Slack Bolt Framework

  • 장점: Slack API의 복잡함을 추상화한 직관적인 인터페이스
  • 소감: Socket Mode를 통한 실시간 통신이 생각보다 간단함

Google Sheets API

  • 장점: 별도 DB 없이도 구조화된 데이터 관리 가능
  • 소감: REST API로 스프레드시트를 조작하는 것의 신선함

profile
실패를 두려워하지 않고 배우고 기록하여 내일의 밑거름 삼아 다음 단계로 성장하겠습니다.

0개의 댓글