<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Slack-Bot on Chanyeol Dev</title>
    <link>https://chanyeols.com/tags/slack-bot/</link>
    <description>Recent content in Slack-Bot on Chanyeol Dev</description>
    <generator>Hugo</generator>
    <language>ko-kr</language>
    <lastBuildDate>Sun, 05 Apr 2026 10:00:00 +0900</lastBuildDate>
    <atom:link href="https://chanyeols.com/tags/slack-bot/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>사내 Slack 봇 만들기 - 기획 배경 &#43; 전체 아키텍처 (1편)</title>
      <link>https://chanyeols.com/posts/slack-01-intro/</link>
      <pubDate>Sun, 05 Apr 2026 10:00:00 +0900</pubDate>
      <guid>https://chanyeols.com/posts/slack-01-intro/</guid>
      <description>조직 개편으로 생긴 불편함을 해결하기 위해 휴가·회의실·출근 알림 Slack 봇을 만든 배경과 전체 아키텍처를 소개합니다.</description>
      <content:encoded><![CDATA[<h2 id="왜-만들게-됐나">왜 만들게 됐나</h2>
<p>조직 개편 전에는 팀원들이 한 채널에 모여 있어서 누가 휴가인지, 어떤 회의실이 예약됐는지 슬쩍 보면 알 수 있었다.</p>
<p>개편 이후 팀이 분리되면서 연계 채널이 새로 생겼다. 그런데 서로 다른 팀 채널에 있다 보니 상대 팀의 휴가·회의실 정보를 알기가 불편해졌다. 매번 그룹웨어에 들어가서 확인하는 게 번거로웠다.</p>
<p>그래서 만들었다. 매일 아침 Slack으로 당일 휴가자와 회의실 예약 현황을 자동으로 보내주는 봇.</p>
<p>만들다 보니 기능이 붙었다. 출근 미등록자 DM 알림, 슬래시 커맨드로 날짜별 조회, 관리자 웹 페이지까지.</p>
<hr>
<h2 id="기술-스택">기술 스택</h2>
<table>
  <thead>
      <tr>
          <th>영역</th>
          <th>기술</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>Runtime</td>
          <td>Node.js 20</td>
      </tr>
      <tr>
          <td>Framework</td>
          <td>Express.js</td>
      </tr>
      <tr>
          <td>스케줄러</td>
          <td>node-cron</td>
      </tr>
      <tr>
          <td>HTTP 클라이언트</td>
          <td>axios</td>
      </tr>
      <tr>
          <td>배포</td>
          <td>Docker Compose</td>
      </tr>
      <tr>
          <td>인프라</td>
          <td>OCI 서버 (홈서버에서 OCI로 이전)</td>
      </tr>
      <tr>
          <td>리버스 프록시</td>
          <td>Nginx</td>
      </tr>
      <tr>
          <td>데이터 저장</td>
          <td>JSON 파일 (DB 없음)</td>
      </tr>
  </tbody>
</table>
<p>DB 없이 JSON 파일로 상태를 관리한 이유는 단순하다. 저장해야 할 데이터가 &ldquo;마지막으로 확인한 휴가/회의실 목록&rdquo; 하나뿐이라 PostgreSQL까지 쓸 이유가 없었다.</p>
<hr>
<h2 id="전체-아키텍처">전체 아키텍처</h2>
<pre tabindex="0"><code>[그룹웨어 API]
      │
      │ SSO 인증 → 휴가 / 회의실 / 출근 데이터 수집
      ▼
[Node.js 봇 서버]  ←→  [JSON 파일]
      │                  teams.json     (팀·Webhook 설정)
      │                  members.json   (출근 알림 대상)
      │                  settings.json  (Slack 토큰, 크론 설정)
      │                  snapshot.json  (마지막 상태 저장)
      │
      ├─ 크론 스케줄 → Slack Webhook 알림
      ├─ 슬래시 커맨드 → Slack API 응답
      └─ /admin 관리자 페이지
</code></pre><p>외부에서 <code>/admin</code>은 Tailscale VPN으로만 접근하고, 슬래시 커맨드 엔드포인트(<code>/slack/command</code>)만 Nginx를 통해 외부에 열어둔다.</p>
<hr>
<h2 id="전체-워크플로우">전체 워크플로우</h2>
<p><img alt="전체 워크플로우 다이어그램" loading="lazy" src="/images/slack-01-workflow.png"></p>
<p>크게 세 가지 흐름으로 동작한다.</p>
<p><strong>1. 서버 시작 시</strong></p>
<ul>
<li><code>snapshot.json</code> 로드 (재시작 후에도 이전 상태 복구)</li>
<li>그룹웨어 SSO 로그인 → 세션 취득</li>
<li>마지막 스냅샷이 10분 초과됐으면 즉시 동기화 실행</li>
</ul>
<p><strong>2. 매 10분 정기 동기화 (<code>sync</code> 크론)</strong></p>
<ul>
<li>휴가·회의실 데이터를 API에서 새로 가져옴</li>
<li>이전 스냅샷과 비교해서 추가/취소된 건 감지</li>
<li>변동이 있으면 Slack Webhook으로 즉시 알림 발송</li>
<li>스냅샷 갱신 후 <code>snapshot.json</code> 저장</li>
</ul>
<p><strong>3. 오전 9시 정기 발송</strong></p>
<ul>
<li>월요일: 이번 주 전체 휴가자 + 당일 회의실 예약</li>
<li>화~금: 당일 휴가자 + 당일 회의실 예약</li>
<li>출근 미등록자 → 개인 Slack DM 발송</li>
</ul>
<hr>
<h2 id="알림-종류">알림 종류</h2>
<h3 id="휴가-변동-알림">휴가 변동 알림</h3>
<p>휴가가 새로 등록되거나 취소되면 즉시 알림이 온다.</p>
<p><img alt="Slack 휴가 변동 알림" loading="lazy" src="/images/slack-01-vacation-change.png"></p>
<h3 id="오전-정기-알림">오전 정기 알림</h3>
<p>매일 아침 9시에 당일 휴가자와 회의실 예약 현황을 한번에 보내준다.</p>
<p><img alt="Slack 오전 정기 알림 - 휴가자 + 회의실" loading="lazy" src="/images/slack-01-daily.png"></p>
<h3 id="출근-미등록-dm">출근 미등록 DM</h3>
<p>출근 미등록자에게는 채널 알림 대신 개인 DM으로 조용히 보낸다.</p>
<p><img alt="출근 미등록 Slack DM" loading="lazy" src="/images/slack-01-commute-dm.png"></p>
<h3 id="슬래시-커맨드">슬래시 커맨드</h3>
<p>Slack에서 직접 조회도 된다.</p>
<p><img alt="/휴가 슬래시 커맨드 결과" loading="lazy" src="/images/slack-01-slash.png"></p>
<hr>
<h2 id="파일-구조">파일 구조</h2>
<pre tabindex="0"><code>~/slackbot/
├── Dockerfile
├── docker-compose.yml
├── .env
├── server.js
├── package.json
└── data/                ← 볼륨 마운트 (재시작 시 유지)
    ├── teams.json        (팀·Webhook 설정)
    ├── members.json      (출근 알림 대상 멤버)
    ├── settings.json     (Slack Bot Token, 크론 설정)
    ├── snapshot.json     (마지막 상태 저장)
    └── server.log
</code></pre><p><code>data/</code> 폴더를 볼륨으로 마운트해서 컨테이너를 재시작해도 설정과 상태가 유지된다.</p>
<hr>
<h2 id="크론-스케줄-전체-목록">크론 스케줄 전체 목록</h2>
<table>
  <thead>
      <tr>
          <th>크론 ID</th>
          <th>시간</th>
          <th>내용</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>sync</code></td>
          <td>10분마다</td>
          <td>휴가/회의실 변동 감지 → 알림</td>
      </tr>
      <tr>
          <td><code>vacationWeekly</code></td>
          <td>월요일 09시</td>
          <td>이번 주 전체 휴가자</td>
      </tr>
      <tr>
          <td><code>vacationDaily</code></td>
          <td>화~금 09시</td>
          <td>당일 휴가자</td>
      </tr>
      <tr>
          <td><code>roomDaily</code></td>
          <td>월~금 09시</td>
          <td>당일 회의실 예약 현황</td>
      </tr>
      <tr>
          <td><code>commute</code></td>
          <td>월~금 09시</td>
          <td>출근 미등록자 DM</td>
      </tr>
      <tr>
          <td><code>sessionKeep</code></td>
          <td>30분마다</td>
          <td>세션 유지 ping</td>
      </tr>
      <tr>
          <td><code>logReset</code></td>
          <td>월요일 00시</td>
          <td>로그 파일 초기화</td>
      </tr>
  </tbody>
</table>
<p>모든 크론은 관리자 페이지에서 활성화/비활성화하고 스케줄 표현식도 수정할 수 있다.</p>
<hr>
<p>다음 편에서는 그룹웨어 SSO 로그인 세션을 처리하는 방법을 다룬다. 쿠키 기반 SSO를 axios로 처리할 때 빠지기 쉬운 함정들을 정리한다.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
