← 블로그로 돌아가기
#Discord##API#discord.js#커뮤니티#자동화#1인개발

Discord 봇 실전 가이드: 1인 개발자의 커뮤니티 자동화 — discord.js부터 AI 코칭 봇까지

·19분 읽기
TL;DR
  • discord.js 14+ — Node.js용 Discord Bot 공식 라이브러리, 주간 다운로드 800만+
  • Gateway API — 실시간 이벤트 수신 (메시지, 반응, 멤버 참가)
  • Interactions API — 슬래시 커맨드, 버튼, 셀렉트 메뉴
  • API 자체는 무료 — 봇 호스팅 비용(Railway $5/월)만 발생
  • AI + Discord = 자동화된 커뮤니티: 온보딩, 코칭 DM, 스탠드업까지 처리

1인 개발자가 커뮤니티를 운영해야 하는 이유

제품을 만들고 출시했다. 유저가 생겼다. 이제 뭘 해야 할까?

가장 흔한 실수는 제품만 고치는 것이다. 유저들이 서로 연결되지 않으면, 제품이 아무리 좋아도 이탈율은 높다. 커뮤니티가 유저 유지율을 결정한다. 특히 학습/성장 제품에서는 커뮤니티 자체가 핵심 기능이다.

문제는 1인 개발자에게 커뮤니티 관리 시간이 없다는 것이다. 매일 신규 멤버에게 환영 메시지를 보내고, 진행 상황을 확인하고, 넛지를 보내는 건 사람이 할 일이 아니다. 봇이 할 일이다.

Discord가 최적인 이유

Slack은 유료다. 카카오톡은 API가 없다. Facebook 그룹은 알림이 묻힌다. Discord는:

  • 무료 (서버, 멤버 제한 없음)
  • API 완전 개방 (Bot, OAuth, Webhook)
  • 개발자 커뮤니티 표준 — 유저들이 이미 Discord를 쓰고 있다
  • 채널/역할 구조 — 제품 진행 단계별로 접근 제어 가능
  • 모바일 알림 — 이메일보다 열람율이 훨씬 높다

agentic30에서 Discord를 커뮤니티 허브로 쓰는 이유도 같다. 학습자들이 Day를 완료할 때마다 Discord에서 인증받고, AI 코파운더가 DM으로 코칭하고, 스탠드업 봇이 매일 진행 상황을 확인한다.


Discord Bot 기초: 봇 생성부터 토큰까지

1. Discord Developer Portal에서 봇 만들기

  1. discord.com/developers/applications 접속
  2. "New Application" 클릭
  3. "Bot" 탭 → "Add Bot"
  4. Token 복사 (절대 공개 저장소에 올리지 말 것)

2. Intents 설정

Discord Bot은 어떤 이벤트를 받을지 명시해야 한다. "Privileged Gateway Intents"에서 필요한 것만 활성화:

  • Server Members Intent — 멤버 참가/탈퇴 이벤트
  • Message Content Intent — 메시지 내용 읽기
⚠️WARNING

Message Content Intent 없이 메시지를 읽으려 하면 빈 문자열이 반환된다. Developer Portal에서 활성화하고, 코드에서도 GatewayIntentBits.MessageContent를 명시해야 한다. 100개 이상 서버에서 사용하는 봇은 Discord의 심사를 받아야 하지만, 소규모 커뮤니티 봇은 심사 불필요.

3. 봇을 서버에 초대하는 URL 생성

https://discord.com/api/oauth2/authorize?client_id=YOUR_CLIENT_ID&permissions=8&scope=bot%20applications.commands

permissions=8은 Administrator 권한이다. 프로덕션에서는 필요한 권한만 계산해서 사용할 것.


discord.js 핵심: Client, Events, Commands

설치

npm install discord.js
# 또는
bun add discord.js

기본 구조

const { Client, GatewayIntentBits, Events } = require('discord.js');

const client = new Client({
  intents: [
    GatewayIntentBits.Guilds,
    GatewayIntentBits.GuildMembers,
    GatewayIntentBits.GuildMessages,
    GatewayIntentBits.MessageContent,
    GatewayIntentBits.DirectMessages,
  ],
});

client.once(Events.ClientReady, (c) => {
  console.log(`봇 준비 완료: ${c.user.tag}`);
});

client.on(Events.GuildMemberAdd, async (member) => {
  // 신규 멤버 참가 시 처리
  await handleNewMember(member);
});

client.login(process.env.DISCORD_BOT_TOKEN);

슬래시 커맨드 등록

discord.js 14부터 슬래시 커맨드가 표준이다. 메시지 기반 커맨드(!start)는 Message Content Intent가 필요하지만, 슬래시 커맨드는 불필요하다.

const { REST, Routes, SlashCommandBuilder } = require('discord.js');

const commands = [
  new SlashCommandBuilder()
    .setName('status')
    .setDescription('현재 진행 상황 확인')
    .toJSON(),
];

const rest = new REST().setToken(process.env.DISCORD_BOT_TOKEN);

// 커맨드 등록 (서버별 등록은 즉시 반영, 글로벌은 최대 1시간)
await rest.put(
  Routes.applicationGuildCommands(CLIENT_ID, GUILD_ID),
  { body: commands }
);

실전 봇 기능: 온보딩 자동화

신규 멤버 웰컴 + 역할 자동 부여

client.on(Events.GuildMemberAdd, async (member) => {
  // 1. 웰컴 채널에 공개 메시지
  const welcomeChannel = member.guild.channels.cache.find(
    ch => ch.name === 'welcome'
  );

  if (welcomeChannel) {
    await welcomeChannel.send(
      `👋 ${member} 님, 환영합니다! #시작하기 채널을 읽어보세요.`
    );
  }

  // 2. DM으로 온보딩 안내
  try {
    await member.send({
      content: `안녕하세요! 커뮤니티에 오신 걸 환영합니다.\n\n` +
               `시작 전에 #rules 채널의 규칙을 확인해주세요.\n` +
               `질문은 언제든 #general에서 해주세요. 🚀`
    });
  } catch (e) {
    // DM 차단한 유저는 무시
    console.log(`DM 전송 실패: ${member.user.tag}`);
  }

  // 3. 기본 역할 부여
  const newcomerRole = member.guild.roles.cache.find(
    r => r.name === '신규 멤버'
  );
  if (newcomerRole) {
    await member.roles.add(newcomerRole);
  }
});

반응 역할 (Reaction Role)

특정 이모지에 반응하면 역할을 부여하는 패턴. 온보딩 채널에서 규칙에 동의하면 역할을 해제하는 방식으로 자주 쓴다.

client.on(Events.MessageReactionAdd, async (reaction, user) => {
  if (user.bot) return;

  const member = await reaction.message.guild.members.fetch(user.id);

  if (
    reaction.message.id === ONBOARDING_MESSAGE_ID &&
    reaction.emoji.name === '✅'
  ) {
    const verifiedRole = reaction.message.guild.roles.cache.find(
      r => r.name === '인증 완료'
    );
    await member.roles.add(verifiedRole);

    // DM으로 다음 단계 안내
    await user.send('인증 완료! 이제 모든 채널에 접근할 수 있어요. 🎉');
  }
});
💡TIP

파셜 반응(캐시되지 않은 메시지의 반응)을 처리하려면 reaction.fetch()를 먼저 호출해야 한다. 봇 재시작 이후의 반응 이벤트가 누락되지 않으려면 이 패턴이 필수다.


AI 코칭 봇: agentic30 코파운더 봇 사례

agentic30의 코파운더 봇은 Discord를 학습 코칭 채널로 사용한다. 단순 알림 봇이 아니라 Claude AI를 연결해서 실제 코칭을 한다.

아키텍처

Discord Gateway (이벤트 수신)
    ↓
Node.js Express 서버 (Railway)
    ↓
Claude Agent SDK (AI 추론)
    ↓
Discord API (DM 발송)

DM 코칭: 트리거 기반 발송

유저가 특정 행동을 하면(Day 완료, GitHub 커밋, 체크인) 코파운더 봇이 분석하고 DM을 보낸다.

async function sendCoachingDM(userId, triggerType, context) {
  const user = await client.users.fetch(userId);

  // Claude로 코칭 메시지 생성
  const coachingMessage = await generateCoachingMessage({
    triggerType,  // 'day_completion', 'github_commit', 'checkin'
    context,      // 유저의 진행 상황, GitHub 활동 등
    userName: user.username,
  });

  // DM 발송 (임베드 형식)
  await user.send({
    embeds: [{
      title: '🤖 코파운더 피드백',
      description: coachingMessage,
      color: 0xFF6B35, // 아이덴티티 색상
      footer: { text: 'Agentic30 코파운더 봇' }
    }]
  });
}

데일리 스탠드업 봇

매일 정해진 시간에 유저에게 스탠드업 DM을 보내고, 응답을 수집해서 AI가 리뷰한다.

// node-cron으로 스케줄 설정
const cron = require('node-cron');

// 매일 09:00 KST (00:00 UTC)
cron.schedule('0 0 * * *', async () => {
  const activeUsers = await getActiveUsers(); // DB에서 활성 유저 조회

  for (const user of activeUsers) {
    await sendStandupDM(user);
  }
});

async function sendStandupDM(user) {
  const discordUser = await client.users.fetch(user.discordId);

  const standupMessage = await discordUser.send({
    content: '☀️ 오늘의 스탠드업 시간입니다!\n\n' +
             '어제 뭘 했고, 오늘은 뭘 할 계획인가요?\n' +
             '막히는 부분이 있나요?'
  });

  // 응답 대기 (CollectorFilter로 해당 유저만 수집)
  const collector = standupMessage.channel.createMessageCollector({
    filter: (m) => m.author.id === user.discordId,
    time: 30 * 60 * 1000, // 30분 대기
    max: 1,
  });

  collector.on('collect', async (response) => {
    // AI로 스탠드업 응답 분석 + 피드백
    const feedback = await analyzeStandup(response.content, user);
    await discordUser.send(feedback);
  });
}

넛지 시스템

3일 이상 활동이 없는 유저에게 자동으로 넛지를 보낸다.

// 매일 18:00 KST 비활성 유저 체크
cron.schedule('0 9 * * *', async () => {
  const inactiveUsers = await getInactiveUsers(3); // 3일 비활성

  for (const user of inactiveUsers) {
    const lastActivity = await getLastActivity(user.id);

    const nudgeMessage = await generateNudge({
      userName: user.name,
      daysSinceActivity: user.daysSinceActivity,
      lastCompletedDay: user.lastCompletedDay,
      blockingIssue: lastActivity?.blockingIssue,
    });

    const discordUser = await client.users.fetch(user.discordId);
    await discordUser.send(nudgeMessage);
  }
});

이 패턴의 핵심은 Discord를 UI로, Claude를 두뇌로 사용한다는 것이다. 봇 코드는 이벤트 라우팅과 메시지 포매팅만 담당하고, 실제 코칭 로직은 AI에게 위임한다.


Webhook 활용: 외부 서비스 → Discord 알림

봇 없이도 Discord에 알림을 보낼 수 있다. Webhook URL을 생성하면 어떤 서비스에서도 POST 요청 하나로 메시지를 보낼 수 있다.

Webhook 생성

Discord 채널 설정 → 연동 → 웹후크 → 새 웹후크 → URL 복사

GitHub 커밋 알림

// GitHub Actions 또는 서버에서
async function notifyDiscord(event) {
  const webhookUrl = process.env.DISCORD_WEBHOOK_URL;

  await fetch(webhookUrl, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      embeds: [{
        title: `🔨 새 커밋: ${event.repository.name}`,
        description: event.head_commit.message,
        url: event.head_commit.url,
        color: 0x24292e,
        fields: [
          { name: '작성자', value: event.head_commit.author.name, inline: true },
          { name: '브랜치', value: event.ref.replace('refs/heads/', ''), inline: true }
        ],
        timestamp: new Date().toISOString()
      }]
    })
  });
}

결제 완료 알림

TossPayments, Stripe 등 결제 웹훅에서 Discord로 알림:

// /api/payment/webhook 라우트에서
app.post('/api/payment/webhook', async (req, res) => {
  const { event, data } = req.body;

  if (event === 'payment.completed') {
    await fetch(process.env.DISCORD_PAYMENT_WEBHOOK, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        content: `💰 결제 완료!\n` +
                 `금액: ${data.amount.toLocaleString()}원\n` +
                 `구독자: ${data.customerEmail}`
      })
    });
  }

  res.json({ ok: true });
});

에러 모니터링 알림

프로덕션 에러가 발생하면 즉시 Discord로:

// 에러 핸들러에서
async function alertError(error, context) {
  await fetch(process.env.DISCORD_ERROR_WEBHOOK, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      embeds: [{
        title: '🚨 에러 발생',
        description: `\`\`\`${error.message}\`\`\``,
        color: 0xFF0000,
        fields: [
          { name: '환경', value: process.env.NODE_ENV, inline: true },
          { name: '컨텍스트', value: JSON.stringify(context), inline: false }
        ],
        timestamp: new Date().toISOString()
      }]
    })
  });
}

Webhook은 별도 봇 없이 바로 사용 가능하다. 단순 알림은 Webhook, 양방향 상호작용은 봇으로 구분하면 된다.


Railway에 봇 배포하기

Discord 봇은 24/7 실행되어야 한다. 로컬에서 키고 끌 수 없다. 가장 간단한 옵션은 Railway다.

Dockerfile 기반 배포

FROM node:22-slim

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .

# Discord 봇은 non-root 유저로 실행 권장
RUN useradd -m botuser
USER botuser

CMD ["node", "src/index.js"]

railway.toml

[build]
builder = "dockerfile"

[deploy]
healthcheckPath = "/health"
healthcheckTimeout = 30
restartPolicyType = "on-failure"
💡TIP

Discord 봇은 HTTP 서버가 필요 없지만, Railway의 헬스체크를 위해 최소한의 Express 엔드포인트(GET /health → 200 OK)를 추가하면 배포 안정성이 높아진다. agentic30의 코파운더 봇도 이 패턴을 사용한다.

환경변수 설정

Railway Dashboard → Variables에서 설정:

DISCORD_BOT_TOKEN=your_bot_token
DISCORD_GUILD_ID=your_server_id
ANTHROPIC_API_KEY=your_api_key  # AI 코칭 봇의 경우
DATABASE_URL=your_db_url

비용 계산

  • Discord API: 완전 무료
  • Railway 봇 호스팅: $5/월 (Starter 플랜, 512MB RAM, 항상 켜짐)
  • Anthropic API: 사용량 기반 (Claude Sonnet 기준 월 $1-10 수준, 트래픽 따라 다름)

커뮤니티 100명 이하 기준, 월 $5-15면 봇 운영이 가능하다.


실전 팁: 운영하면서 배운 것들

1. DM 차단 유저 처리

모든 Discord DM 발송은 try-catch로 감싸야 한다. 유저가 DM을 차단하면 DiscordAPIError: Cannot send messages to this user 에러가 발생한다. 이를 조용히 무시하거나 DB에 dm_disabled 플래그를 저장해서 반복 시도를 막아야 한다.

2. Rate Limit 관리

Discord API는 엔드포인트별 Rate Limit이 있다. discord.js가 자동으로 큐잉하지만, 대량 DM 발송 시 setInterval로 분산시키는 게 좋다. 초당 50개 이상 요청은 글로벌 Rate Limit에 걸린다.

3. 봇 재시작 후 상태 복원

봇이 재시작되면 인메모리 상태가 날아간다. 대화 상태(스탠드업 진행 중 등)는 DB에 저장해야 한다. standup_sessions 테이블처럼 상태를 영속화하는 패턴이 필요하다.

4. 테스트 서버 분리

개발용 Discord 서버를 별도로 만들어서 테스트한다. 프로덕션 서버에서 봇을 테스트하다 실수로 전체 멤버에게 DM을 보내는 사고가 생각보다 자주 난다. 슬래시 커맨드도 Guild Command(서버별, 즉시 반영)로 등록하면 테스트 서버에만 적용할 수 있다.


결론: AI 에이전트 + Discord = 커뮤니티 자동화

Discord Bot은 1인 개발자에게 최고의 커뮤니티 자동화 도구다. 이유는 세 가지다:

1. API가 완전히 열려있다. 실시간 이벤트, 메시지, DM, 역할 관리 — 원하는 모든 걸 자동화할 수 있다.

2. 비용이 거의 없다. Discord API 자체는 무료이고, Railway 봇 호스팅은 월 $5다. 유저 100명짜리 커뮤니티 운영 비용이 커피 두 잔이다.

3. AI와 결합하면 강력해진다. discord.js로 이벤트를 잡고, Claude로 분석하고, 다시 Discord로 응답하는 루프가 완성되면 — 코치가 24시간 상주하는 커뮤니티를 1인이 운영할 수 있다.

agentic30의 코파운더 봇이 정확히 이 패턴이다. Day 완료 감지 → 활동 분석 → 개인화된 코칭 DM → 다음 액션 제안. 이 루프가 자동으로 돌아가면서 학습자 이탈율을 낮춘다.

커뮤니티를 만들었다면, 지금 바로 웰컴 봇부터 시작하자. npm install discord.js, 토큰 발급, Railway 배포 — 반나절이면 된다.

도구를 알았으면 이제 만들 차례

AI 코파운더와 함께 30일 안에 아이디어를 검증하고 첫 유저를 만나보세요.

무료로 시작하기