Discord 커뮤니티를 운영하다 보면 반복적인 관리 작업이나 자동화가 필요한 순간이 많다. 하지만 대부분의 개발자들은 봇을 만들어야 한다는 건 알면서도 어디서부터 시작해야 할지, 정확히 어떤 라이브러리를 써야 할지 모른 채 방황한다. 이번에는 Python의 discord.py 라이브러리를 이용해 Discord 봇을 처음부터 끝까지 만드는 방법을 완벽하게 정리했다. 환경 설정, 기본 개념, 실제 명령어 구현, 흔한 실수까지 한 번에 해결할 수 있다.

 

1단계: Discord 봇의 기초 개념 이해하기
Discord 봇이란 결국 Discord 서버에 접속해서 메시지를 읽고 반응하는 프로그램일 뿐이다. 사용자가 메시지를 보내면 봇은 그 메시지를 '이벤트'로 받아서 처리한 후 응답한다. discord.py는 이런 일련의 과정을 간단하게 만들어주는 Python 라이브러리다.

Discord 봇이 작동하는 기본 흐름은 이렇다:
1. Discord 공식 사이트에서 봇을 등록하고 토큰을 받는다
2. Python 코드에서 그 토큰을 이용해 봇 클라이언트를 만든다
3. 봇이 받을 이벤트(메시지, 반응, 명령어 등)를 정의한다
4. 봇을 실행하면 Discord 서버에 접속해서 대기한다

 

2단계: 개발 환경 설정하기
Step 1: Discord 애플리케이션 생성 및 토큰 받기

먼저 Discord Developer Portal에 접속해서 새 애플리케이션을 만든다. 애플리케이션 이름을 정하고 "Bot" 탭에서 "Add Bot" 버튼을 클릭한다. 그러면 봇 토큰이 생성되는데, 이 토큰은 절대 공개하면 안 된다(깃허브에 올리거나 SNS에 공유하지 말 것). 토큰을 안전하게 저장해두자.

 

Step 2: Python 환경과 discord.py 설치

Python 3.8 이상이 설치되어 있어야 한다. 터미널에서 아래 명령어로 discord.py를 설치한다.

pip install discord.py

버전 확인:

pip show discord.py

 

3단계: 첫 번째 봇 코드 작성하기
✗ 잘못된 코드 - 토큰을 하드코딩한 경우
import discord
from discord.ext import commands

bot = commands.Bot(command_prefix='!')

@bot.event
async def on_ready():
    print(f'{bot.user} has connected to Discord!')

bot.run('YOUR_BOT_TOKEN_HERE')  # ✗ 토큰이 코드에 노출됨

왜 안 되는가: 토큰이 코드에 직접 적혀 있으면 깃허브에 올릴 때나 코드를 공유할 때 토큰이 노출되기 쉽다. 누군가 이 토큰을 얻으면 당신의 봇을 마음대로 조종할 수 있다.

 

✓ 올바른 코드 - 환경변수를 사용한 경우

먼저 프로젝트 폴더 루트에 .env 파일을 만들고 토큰을 저장한다:

DISCORD_TOKEN=your_bot_token_here

그 다음 python-dotenv를 설치한다:

pip install python-dotenv

이제 코드에서 환경변수를 읽는다:

import discord
from discord.ext import commands
import os
from dotenv import load_dotenv

load_dotenv()
token = os.getenv('DISCORD_TOKEN')

bot = commands.Bot(command_prefix='!')

@bot.event
async def on_ready():
    print(f'{bot.user} has connected to Discord!')

bot.run(token)  # ✓ 토큰이 환경변수에서 읽혀서 안전함

왜 이게 맞는가: 토큰이 .env 파일에 저장되고, .env는 .gitignore에 추가하면 깃허브에 올라가지 않는다. 코드에는 토큰을 읽는 로직만 남아서 공유해도 안전하다.

 

4단계: 봇 명령어 구현하기
기본 명령어 구조

Discord 봇이 처리할 명령어를 정의한다. 사용자가 "!ping"이라고 치면 "Pong!"이라고 답하는 간단한 예제다:

import discord
from discord.ext import commands
import os
from dotenv import load_dotenv

load_dotenv()
token = os.getenv('DISCORD_TOKEN')

bot = commands.Bot(command_prefix='!')

@bot.event
async def on_ready():
    print(f'Logged in as {bot.user}')
    await bot.change_presence(activity=discord.Activity(type=discord.ActivityType.watching, name="!help"))

@bot.command(name='ping')
async def ping(ctx):
    await ctx.send(f'Pong! {round(bot.latency * 1000)}ms')

@bot.command(name='hello')
async def hello(ctx):
    await ctx.send(f'Hello {ctx.author.name}!')

bot.run(token)

이 코드를 실행하면 봇이 Discord 서버에 접속한다. "!ping"이라고 치면 "Pong! XXms" 형태로 응답하고, "!hello"라고 치면 "Hello [사용자이름]!"이라고 응답한다.

 

매개변수가 있는 명령어

사용자가 입력한 값을 받아서 처리하려면 매개변수를 받아야 한다:

@bot.command(name='greet')
async def greet(ctx, name):
    await ctx.send(f'Hello {name}! Nice to meet you.')

# 사용법: !greet Alice
# 결과: Hello Alice! Nice to meet you.

 

메시지 이벤트 처리

명령어가 아닌 모든 메시지에 반응하려면 on_message 이벤트를 사용한다:

@bot.event
async def on_message(message):
    # 봇 자신의 메시지는 무시
    if message.author == bot.user:
        return
    
    # 특정 단어가 포함되면 반응
    if 'hello' in message.content.lower():
        await message.channel.send(f'Hi {message.author.name}!')
    
    # 명령어도 처리하기 위해 이 줄은 반드시 마지막에
    await bot.process_commands(message)

 

5단계: 흔한 실수와 대처법
문제 원인 해결법
"Command raised an exception: MissingRequiredArgument" 명령어에 필요한 매개변수가 없음 @bot.command 아래 함수에 필요한 매개변수를 모두 정의하기
"The application did not respond" async 함수가 너무 오래 걸림 시간이 오래 걸리는 작업은 asyncio.create_task()로 백그라운드 실행
봇이 메시지에 반응하지 않음 on_message에서 process_commands 호출 안 함 on_message 맨 마지막에 await bot.process_commands(message) 추가
"Forbidden (status code: 403)" 봇이 특정 채널에 메시지를 보낼 권한 없음 Discord 서버 설정에서 봇 역할에 필요한 권한 부여

 

6단계: 실전 예제 - 사용자 환영 봇

새로운 사용자가 서버에 들어오면 자동으로 환영 메시지를 보내는 봇을 만들어보자:

import discord
from discord.ext import commands
import os
from dotenv import load_dotenv

load_dotenv()
token = os.getenv('DISCORD_TOKEN')

bot = commands.Bot(command_prefix='!')

@bot.event
async def on_ready():
    print(f'Bot is ready! Logged in as {bot.user}')

@bot.event
async def on_member_join(member):
    """새 멤버가 서버에 들어왔을 때"""
    channel = discord.utils.get(member.guild.channels, name='general')
    if channel:
        embed = discord.Embed(
            title=f'Welcome {member.name}!',
            description=f'{member.mention}님이 서버에 입장했습니다.',
            color=discord.Color.green()
        )
        embed.set_thumbnail(url=member.avatar.url)
        await channel.send(embed=embed)

@bot.command(name='userinfo')
async def userinfo(ctx, member: discord.Member = None):
    """사용자 정보 출력"""
    member = member or ctx.author
    embed = discord.Embed(
        title=f'{member.name}의 정보',
        color=discord.Color.blue()
    )
    embed.add_field(name='ID', value=member.id, inline=False)
    embed.add_field(name='가입일', value=member.created_at.strftime('%Y-%m-%d'), inline=False)
    embed.add_field(name='서버 가입', value=member.joined_at.strftime('%Y-%m-%d'), inline=False)
    embed.set_thumbnail(url=member.avatar.url)
    await ctx.send(embed=embed)

bot.run(token)

이 코드는 1) 새 멤버가 들어오면 일반 채널에 환영 메시지를 임베드 형식으로 보내고, 2) "!userinfo [사용자명]" 명령어로 해당 사용자의 정보를 조회할 수 있게 한다.

 

마무리: 봇 배포와 다음 단계

Discord 봇은 결국 메시지를 받고 처리하는 프로그램일 뿐이다. 토큰을 안전하게 관리하고, async/await 문법을 이해하고, 이벤트 시스템을 활용하면 원하는 기능을 거의 모두 만들 수 있다는 걸 잊지 말자. 작은 명령어 하나에서 시작해서 점진적으로 기능을 추가하면, 서버 관리부터 게임 봇까지 만들 수 있다. 이 글의 '환경변수 설정' 부분을 참고해 토큰을 안전하게 보관하고, '실전 예제' 코드를 직접 실행해본 뒤 자신의 서버에 맞게 커스터마이징하면, 완전히 자동화된 Discord 봇을 갖출 수 있을 것이다.