디스코드 봇 만들기#13역할·채널 자동화로 서버 관리하기! (파이썬 예제)
목차9개 항목
안녕하세요! Mojangcam입니다 😎
지난 #12편에서는 명령어 쿨다운과 권한으로 봇을 안전하게 지키는 법을 배웠어요. 이번 #13편에서는 한 걸음 더 나아가, 서버 관리자의 반복 노동을 봇에게 통째로 넘겨볼게요. 오늘 만들 자동화는 딱 세 가지예요.
- 새 멤버가 들어오면 자동으로 역할 지급 (환영 역할)
- 버튼 한 번으로 원하는 역할 셀프 지급 (역할 버튼 패널)
- 음성 채널에 입장하면 개인 방 자동 생성 (Join to Create)
서버가 커질수록 "새로 온 사람한테 역할 달아주기", "게임할 사람 역할 나눠주기", "회의방 만들어주기" 같은 일이 끝없이 반복돼요. 이걸 사람이 손으로 하면 밤새도 못 끝내죠. 봇에게 맡기면 24시간 알아서 처리해줍니다. 그럼 시작해볼게요! 🙌
📌 준비물
먼저 개발 환경부터 챙길게요.
- Python 3.9 이상 (
discord.py2.7은 3.9+ 필요) discord.py2.x (2026년 7월 기준 최신 안정 버전 2.7.1)
라이브러리 설치는 터미널에서 한 줄이면 끝나요.
pip install -U discord.py그리고 이번 편은 프리빌리지드 인텐트(Privileged Intents) 설정이 꼭 필요해요. 새 멤버 입장을 감지하려면 Server Members Intent를 켜야 하거든요.
- 디스코드 개발자 포털 접속 → 내 봇 선택
- 왼쪽 Bot 탭 → Privileged Gateway Intents
- Server Members Intent 켜기 ✅ (메시지 명령어를 쓸 거면 Message Content Intent도 켜기)
마지막으로 서버에서 봇 역할의 권한도 확인해요. 자동화가 동작하려면 봇에게 아래 권한이 있어야 해요.
- 역할 관리(Manage Roles) — 역할을 붙이고 떼기
- 채널 관리(Manage Channels) — 음성 채널 만들고 지우기
- 멤버 이동(Move Members) — 만든 방으로 멤버 옮기기
💡 가장 중요한 포인트! 봇이 부여할 역할은 반드시 봇 자신의 역할보다 아래에 있어야 해요. 서버 설정 → 역할 목록에서 봇 역할을 위쪽으로 끌어올려 주세요. 이걸 놓치면 뒤에서 볼
Forbidden에러가 납니다.
📌 1단계 - 새 멤버가 오면 역할 자동 지급
먼저 가장 기본이 되는 자동 환영 역할부터 만들어요. 누군가 서버에 들어오는 순간 on_member_join 이벤트가 실행돼요. 여기서 역할을 찾아 붙여주면 끝이에요.
import discord
from discord.ext import commands
intents = discord.Intents.default()
intents.members = True # 멤버 입장 감지 (프리빌리지드 인텐트)
intents.message_content = True # 접두사 명령어용
bot = commands.Bot(command_prefix="!", intents=intents)
AUTO_ROLE_NAME = "새싹" # 입장 시 자동으로 줄 역할 이름
@bot.event
async def on_member_join(member: discord.Member):
role = discord.utils.get(member.guild.roles, name=AUTO_ROLE_NAME)
if role is not None:
await member.add_roles(role)
print(f"👋 {member.name} 님에게 '{role.name}' 역할을 줬어요!")하나씩 뜯어볼게요 💡
intents.members = True: 멤버 입장·퇴장을 감지하는 스위치예요. 이게 꺼져 있으면on_member_join이 아예 실행되지 않아요.on_member_join(member): 새 멤버가 들어올 때마다 자동 호출되는 이벤트예요.member에 들어온 사람 정보가 담겨요.discord.utils.get(member.guild.roles, name=...): 서버 역할 목록에서 이름이 일치하는 역할을 찾아줘요. 없으면None을 돌려주니 꼭 확인해요.await member.add_roles(role): 찾은 역할을 그 멤버에게 붙여줘요. 핵심 한 줄이죠!
이제 "새싹"이라는 역할을 서버에 미리 만들어두면, 새 멤버는 들어오자마자 새싹 역할을 달고 등장해요. 🌱
📌 2단계 - 버튼 하나로 역할 셀프 지급
예전 #5 버튼(Button) 편에서 버튼을 배웠죠? 이번엔 그 버튼으로 멤버가 스스로 역할을 고르는 패널을 만들어요. 관리자가 일일이 역할을 달아줄 필요가 없어져요.
여기서 꼭 알아야 할 개념이 영구 View(Persistent View) 예요. 봇을 재시작하면 보통 버튼이 먹통이 되는데, 아래 두 조건을 지키면 재시작 후에도 버튼이 계속 동작해요.
- View의
timeout=None - 모든 버튼에
custom_id지정
# 버튼 역할 목록 (custom_id, 역할이름, 버튼라벨, 이모지)
BUTTON_ROLES = [
("role_notice", "공지알림", "공지 알림", "🔔"),
("role_game", "게임메이트", "게임 메이트", "🎮"),
("role_study", "스터디원", "스터디원", "📚"),
]
class RoleButton(discord.ui.Button):
def __init__(self, custom_id, role_name, label, emoji):
super().__init__(
label=label, emoji=emoji,
style=discord.ButtonStyle.secondary,
custom_id=custom_id, # 영구 View 는 custom_id 필수!
)
self.role_name = role_name
async def callback(self, interaction: discord.Interaction):
role = discord.utils.get(interaction.guild.roles, name=self.role_name)
if role is None:
await interaction.response.send_message(
f"❌ '{self.role_name}' 역할이 없어요. 먼저 서버에 만들어 주세요!",
ephemeral=True)
return
if role in interaction.user.roles:
await interaction.user.remove_roles(role)
msg = f"'{role.name}' 역할을 뺐어요. 👋"
else:
await interaction.user.add_roles(role)
msg = f"'{role.name}' 역할을 드렸어요! 🎉"
await interaction.response.send_message(msg, ephemeral=True)
class RoleView(discord.ui.View):
def __init__(self):
super().__init__(timeout=None) # 영구 View 는 timeout=None!
for custom_id, role_name, label, emoji in BUTTON_ROLES:
self.add_item(RoleButton(custom_id, role_name, label, emoji))하나씩 뜯어볼게요 💡
custom_id: 버튼마다 붙이는 고유 이름표예요. 재시작 후 봇이 "아, 이 버튼은 그 버튼!" 하고 알아보게 해줘요. 영구 View의 필수 조건이에요.callback(self, interaction): 버튼을 눌렀을 때 실행되는 함수예요.interaction.user가 버튼을 누른 사람이에요.role in interaction.user.roles: 이미 그 역할이 있으면 떼고(remove_roles), 없으면 붙여줘요(add_roles). 버튼 하나로 토글이 되는 거죠.ephemeral=True: "나만 보이는 메시지"예요. 다른 사람 화면을 어지럽히지 않고 조용히 알려줘요.
이제 이 패널을 채팅에 띄우는 명령어와, 재시작 후에도 버튼이 살아있게 하는 코드를 더해요.
class MyBot(commands.Bot):
def __init__(self):
super().__init__(command_prefix="!", intents=intents)
async def setup_hook(self):
self.add_view(RoleView()) # 재시작 후 버튼 되살리기
bot = MyBot()
@bot.command(name="역할판")
@commands.has_permissions(administrator=True)
async def role_panel(ctx: commands.Context):
embed = discord.Embed(
title="🎭 역할 받기",
description="버튼을 누르면 역할을 드려요! 한 번 더 누르면 역할이 빠져요.",
color=0x5865F2,
)
await ctx.send(embed=embed, view=RoleView())setup_hook+add_view: 봇이 켜질 때 영구 View를 다시 등록해요. 이 두 줄 덕분에 봇을 재시작해도 예전에 띄워둔 버튼이 그대로 동작해요.@commands.has_permissions(administrator=True): 관리자만!역할판명령어를 쓸 수 있게 막아줘요.

📌 3단계 - 입장하면 개인 음성 채널 자동 생성
마지막은 요즘 서버들이 다 쓰는 Join to Create예요. "➕ 채널 만들기" 같은 음성 채널을 하나 만들어두고, 누가 거기 들어오면 그 사람만의 방을 즉석에서 만들어 옮겨주는 기능이에요. 방이 비면 자동으로 사라져서 서버도 깔끔하게 유지돼요.
음성 채널 이동은 on_voice_state_update 이벤트로 감지해요.
CREATOR_CHANNEL_ID = 123456789012345678 # '➕ 채널 만들기' 음성 채널 ID
temp_channels = set() # 우리가 만든 임시 채널 ID 보관
@bot.event
async def on_voice_state_update(member, before, after):
# (생성) '만들기' 채널에 들어오면 개인 채널을 만들고 이동
if after.channel is not None and after.channel.id == CREATOR_CHANNEL_ID:
new_channel = await member.guild.create_voice_channel(
name=f"🔊 {member.display_name}의 방",
category=after.channel.category,
)
temp_channels.add(new_channel.id)
await member.move_to(new_channel)
# (삭제) 우리가 만든 임시 채널이 비면 제거
if before.channel is not None and before.channel.id in temp_channels:
if len(before.channel.members) == 0:
await before.channel.delete()
temp_channels.discard(before.channel.id)하나씩 뜯어볼게요 💡
on_voice_state_update(member, before, after): 누가 음성 채널을 옮길 때마다 실행돼요.before는 떠난 채널,after는 들어간 채널이에요.after.channel.id == CREATOR_CHANNEL_ID: 방금 들어간 곳이 "만들기" 채널인지 확인해요. 맞으면 새 방을 만들 차례!create_voice_channel(name, category): 개인 음성 채널을 만들어요.category를 원본과 같게 주면 같은 카테고리 아래에 깔끔하게 생겨요.member.move_to(new_channel): 방금 만든 방으로 멤버를 슝 옮겨줘요.len(before.channel.members) == 0: 떠난 방에 아무도 안 남았으면delete()로 지워요.temp_channels로 "우리가 만든 방"만 지우게 안전장치를 뒀어요.
💡
CREATOR_CHANNEL_ID는 디스코드에서 개발자 모드를 켠 뒤(설정 → 고급 → 개발자 모드), 만들기용 음성 채널을 우클릭 → "ID 복사"로 얻을 수 있어요.
📌 전체 코드 (복붙 OK)
아래 코드를 복사해서 TOKEN, AUTO_ROLE_NAME, CREATOR_CHANNEL_ID, BUTTON_ROLES만 내 서버에 맞게 바꾸면 바로 돌아가요.
# discord.py 2.x (2.7.1 기준) / Python 3.9+
import discord
from discord.ext import commands
# ── 설정 ──────────────────────────────
TOKEN = "여기에_봇_토큰"
AUTO_ROLE_NAME = "새싹" # 입장 시 자동으로 줄 역할 이름
CREATOR_CHANNEL_ID = 123456789012345678 # '➕ 채널 만들기' 음성 채널 ID
# 버튼 역할 목록 (custom_id, 역할이름, 버튼라벨, 이모지)
BUTTON_ROLES = [
("role_notice", "공지알림", "공지 알림", "🔔"),
("role_game", "게임메이트", "게임 메이트", "🎮"),
("role_study", "스터디원", "스터디원", "📚"),
]
intents = discord.Intents.default()
intents.members = True # 멤버 입장 감지 (프리빌리지드 인텐트)
intents.message_content = True # 접두사 명령어용
# voice_states 는 default() 에 이미 포함돼 있어요.
# ── 버튼 역할 View ────────────────────
class RoleButton(discord.ui.Button):
def __init__(self, custom_id, role_name, label, emoji):
super().__init__(
label=label, emoji=emoji,
style=discord.ButtonStyle.secondary,
custom_id=custom_id, # 영구 View 는 custom_id 필수!
)
self.role_name = role_name
async def callback(self, interaction: discord.Interaction):
role = discord.utils.get(interaction.guild.roles, name=self.role_name)
if role is None:
await interaction.response.send_message(
f"❌ '{self.role_name}' 역할이 없어요. 먼저 서버에 만들어 주세요!",
ephemeral=True)
return
if role in interaction.user.roles:
await interaction.user.remove_roles(role)
msg = f"'{role.name}' 역할을 뺐어요. 👋"
else:
await interaction.user.add_roles(role)
msg = f"'{role.name}' 역할을 드렸어요! 🎉"
await interaction.response.send_message(msg, ephemeral=True)
class RoleView(discord.ui.View):
def __init__(self):
super().__init__(timeout=None) # 영구 View 는 timeout=None!
for custom_id, role_name, label, emoji in BUTTON_ROLES:
self.add_item(RoleButton(custom_id, role_name, label, emoji))
# ── 봇 본체 ───────────────────────────
class MyBot(commands.Bot):
def __init__(self):
super().__init__(command_prefix="!", intents=intents)
async def setup_hook(self):
# 재시작 후에도 버튼이 동작하도록 영구 View 재등록
self.add_view(RoleView())
bot = MyBot()
temp_channels = set() # 우리가 만든 임시 음성 채널 ID 보관
@bot.event
async def on_ready():
print(f"✅ {bot.user} 로 로그인했어요!")
# 1) 입장하면 자동으로 역할 부여
@bot.event
async def on_member_join(member: discord.Member):
role = discord.utils.get(member.guild.roles, name=AUTO_ROLE_NAME)
if role is not None:
await member.add_roles(role)
print(f"👋 {member.name} → '{role.name}' 역할 부여 완료")
# 2) 역할 버튼 패널 띄우기 (관리자만)
@bot.command(name="역할판")
@commands.has_permissions(administrator=True)
async def role_panel(ctx: commands.Context):
embed = discord.Embed(
title="🎭 역할 받기",
description="버튼을 누르면 역할을 드려요! 한 번 더 누르면 역할이 빠져요.",
color=0x5865F2,
)
await ctx.send(embed=embed, view=RoleView())
# 3) 지정 채널 입장 시 개인 음성 채널 자동 생성 + 비면 삭제
@bot.event
async def on_voice_state_update(member, before, after):
# (생성) '만들기' 채널에 들어오면 개인 채널을 만들고 이동
if after.channel is not None and after.channel.id == CREATOR_CHANNEL_ID:
new_channel = await member.guild.create_voice_channel(
name=f"🔊 {member.display_name}의 방",
category=after.channel.category,
)
temp_channels.add(new_channel.id)
await member.move_to(new_channel)
# (삭제) 우리가 만든 임시 채널이 비면 제거
if before.channel is not None and before.channel.id in temp_channels:
if len(before.channel.members) == 0:
await before.channel.delete()
temp_channels.discard(before.channel.id)
bot.run(TOKEN)📌 실행 결과
봇을 켜고 서버에서 하나씩 확인해봐요.
- 새 계정으로 입장 → 자동으로 "새싹" 역할이 딱 붙어요. 🌱
- 관리자가
!역할판입력 → 버튼 3개가 달린 임베드가 떠요. 버튼을 누르면 "역할을 드렸어요! 🎉", 다시 누르면 "역할을 뺐어요 👋" 하고 나만 보이는 메시지로 답해줘요. - "➕ 채널 만들기" 음성 채널 입장 → 곧바로 "🔊 (내이름)의 방"이 생기고 그리로 옮겨져요. 방에서 나오면 방이 스르륵 사라져요.
📌 자주 나는 에러
1. discord.errors.Forbidden: 403 Missing Permissions
원인: 봇에게 권한이 없거나, 부여하려는 역할이 봇 역할보다 위에 있어요.
해결: 서버 설정 → 역할에서 봇 역할을 위로 올리고, 봇에 역할 관리 / 채널 관리 / 멤버 이동 권한을 켜주세요. 디스코드는 "자기보다 높은 역할"은 절대 못 건드리게 막아둡니다.
2. on_member_join이 아예 실행 안 됨
원인: Server Members Intent가 꺼져 있어요.
해결: 개발자 포털 → Bot → Privileged Gateway Intents에서 Server Members Intent를 켜고, 코드에도 intents.members = True를 넣어요. 둘 다 켜야 동작해요.
3. 봇을 재시작하니 버튼이 "상호작용 실패"
원인: 영구 View 조건을 안 지켰어요.
해결: View는 timeout=None, 모든 버튼은 custom_id를 갖고, setup_hook에서 add_view로 다시 등록했는지 확인하세요. 세 개가 세트예요.
4. 임시 음성 채널이 안 지워짐
원인: 봇에 채널 관리 권한이 없거나, temp_channels에 ID가 안 담겼어요.
해결: 채널 관리 권한을 확인하고, 채널을 만들 때 temp_channels.add(...)로 ID를 저장했는지 점검하세요.
📌 마무리
오늘은 서버 관리를 통째로 봇에게 넘기는 세 가지 자동화를 만들어봤어요. 입장 시 자동 역할, 버튼 역할 셀프 지급, 음성 채널 자동 생성까지 — 이 세 개만 있어도 관리자의 반복 업무가 확 줄어요. 특히 영구 View(timeout=None + custom_id + add_view)는 앞으로 어떤 버튼/메뉴를 만들든 계속 쓰이니 꼭 익혀두세요! 💪
다음 #14편에서는 JSON과 SQLite로 봇 데이터를 저장하는 법을 다뤄볼게요. 역할·설정·기록을 껐다 켜도 잃지 않게 만드는 거죠. 기대해주세요! 🙌
📚 디스코드 봇 만들기 시리즈
- #5 - 버튼(Button) 상호작용
- #6 - 드롭다운(Select Menu) 메뉴
- #7 - 모달(Modal)로 입력 받기
- #8 - 임베드(Embed) 꾸미기
- #9 - AI(Claude·ChatGPT API) 연동
- #10 - 다국어(i18n) 지원
- #11 - 플라스크(Flask) 웹 대시보드
- #12 - 명령어 쿨다운·권한 관리
- #13 - 역할·채널 자동화 ← 지금 이 글!
연재 · 32편 중 13번째
디스코드 봇 만들기
- #1디스코드 봇 만들기 - Slash Command 완전 정복!
- #2Slash Command에 옵션(Arguments) 추가하기!
- #3Slash Command에 서브 커맨드(Subcommand) 넣기!
- #4Slash Command로 임베드 전송하기!
- #5버튼(Button) 상호작용 구현하기!
- #6드롭다운(Select Menu) 메뉴 구현하기!
- #7모달(Modal)로 입력 받기 구현하기 (파이썬 예제)
- #8임베드(Embed) 이미지·필드로 꾸미기 (파이썬 예제)
- #9AI(Claude·ChatGPT API) 연동하기! (파이썬 예제)
- #10다국어(i18n) 지원하기! (파이썬 예제)
- #11플라스크(Flask)로 웹 대시보드 만들기! (파이썬 예제)
- #12명령어 쿨다운(Cooldown)·권한(Permission) 관리하기! (파이썬 예제)
- #13역할·채널 자동화로 서버 관리하기! (파이썬 예제)지금 읽는 글
- #14데이터 저장하기! JSON vs SQLite (파이썬 예제)
- #15슬래시 명령어 그룹(Group)으로 명령어 정리하기! (파이썬 예제)
- #16리마인더·알림 기능 만들기! 봇이 먼저 말 걸게 하기 (파이썬 예제)
- #17RAG로 문서 기반 답변봇 만들기! 우리 서버 전용 FAQ 봇 (파이썬 예제)
- #18투표(Poll) 기능 만들기! 다수결로 정하는 서버 규칙 (파이썬 예제)
- #19Cog로 코드 쪼개기! 1000줄 봇도 안 더러워지는 구조 (파이썬 예제)
- #20자동 환영 메시지·온보딩 만들기! 규칙 동의 버튼으로 자동 역할 부여 (파이썬 예제)
- #21키워드 자동응답·금지어 필터 만들기! 경고 누적으로 자동 제재까지 (파이썬 예제)
- #22슬래시 명령어 자동완성(Autocomplete) 만들기! 금지어를 골라서 추가·삭제 (파이썬 예제)
- #23aiosqlite로 비동기 DB 제대로 설계하기! JSON 탈출 (파이썬 예제)
- #24채널 난독화(Channel Obfuscation) 대응하기! 11/16부터 안 보이는 채널이 생겨요 (파이썬 예제)
- #25봇이 죽지 않게! 에러 핸들링·로깅 체계 잡기 (파이썬 예제)
- #26티켓(Ticket) 문의 시스템 만들기! 버튼 하나로 1:1 상담 채널 자동 생성 (파이썬 예제)
- #27티켓 담당자 배정·우선순위 만들기! 스태프가 여러 명이어도 안 꼬이게 (파이썬 예제)
- #28레벨·경험치(XP) 시스템 만들기! 채팅할수록 레벨업, 랭킹판까지 (파이썬 예제)
- #29화폐·상점(Economy) 시스템 만들기! 코인 벌어서 아이템 사기 (파이썬 예제)
- #30투표·설문(Poll) 봇 만들기! 네이티브 투표 + 버튼으로 실시간 집계까지 (파이썬 예제) 🗳️
- #32서버 로그·감사(Audit Log) 채널 만들기! 메시지 삭제·수정·입퇴장·추방까지 자동 기록 (파이썬 예제) 🕵️
- #33임시 음성방 확장, 파티방 만들기! 방장 위임·강퇴·초대 전용(비공개) 방 + 전용 채팅방 자동 생성 (파이썬 예제) 🎉
