디스코드 봇 만들기#15슬래시 명령어 그룹(Group)으로 명령어 정리하기! (파이썬 예제)
목차11개 항목
안녕하세요! Mojangcam입니다 😎
지난 #14편에서는 JSON과 SQLite로 봇에게 기억력을 만들어줬어요. 그런데 시리즈를 따라오면서 기능을 하나씩 붙이다 보니, 어느새 명령어가 열댓 개를 훌쩍 넘지 않았나요? 디스코드에서 /만 입력하면 명령어가 우르르 쏟아져서 정작 필요한 걸 찾기 힘들어지는 시점이 옵니다.
그래서 이번 #15편에서는 슬래시 명령어를 폴더처럼 묶어서 정리하는 슬래시 명령어 그룹(Group)을 배워볼게요. /설정 환영채널, /설정 알림 켜기처럼 유명한 봇들이 쓰는 깔끔한 명령어 구조를 우리 봇에도 달아줍니다!

🧠 왜 그룹이 필요할까요?
명령어가 5개일 때는 아무 문제가 없어요. 그런데 서버 관리 봇을 만들다 보면 /환영채널설정, /인사말설정, /알림켜기, /알림끄기… 이런 식으로 이름이 점점 길고 지저분해져요. 이름이 겹칠까 봐 앞에 접두어를 붙이기 시작하면 이미 신호가 온 거예요.
그룹을 쓰면 관련 명령어가 한 지붕 아래로 들어가요. 유저가 /설정까지만 입력해도 하위 명령어 목록이 자동완성으로 좌르륵 떠서, 봇에 어떤 기능이 있는지 한눈에 보인다는 것도 큰 장점이에요. 음악 봇의 /음악 재생, /음악 정지 같은 구조가 다 이렇게 만들어진 거랍니다.
🧩 그룹 구조, 딱 3가지만 기억하세요
/설정 보기 ← 그룹 + 하위 명령어 (2단)
/설정 알림 켜기 ← 그룹 + 하위 그룹 + 하위 명령어 (3단)| 규칙 | 내용 |
|---|---|
| 그룹 자체는 실행 불가 | /설정만 입력해서 실행하는 건 안 돼요. 그룹은 폴더일 뿐, 실제 동작은 항상 하위 명령어가 해요 |
| 중첩은 1단계까지 | 그룹 → 하위 그룹 → 명령어(3단)가 최대예요. 하위 그룹 안에 또 그룹은 못 넣어요 |
| 하위 명령어 최대 25개 | 그룹 하나에 하위 명령어·하위 그룹을 합쳐 25개까지 넣을 수 있어요 |
이름 규칙도 하나 짚고 갈게요. 슬래시 명령어 이름은 공백 불가·영문은 소문자만 가능해요. 한글 이름은 자유롭게 쓸 수 있어서 /설정 환영채널처럼 만들 수 있답니다.
📌 준비물
- Python 3.8 이상
- discord.py 2.x (최신 2.x 버전이면 돼요)
- 봇 토큰과 서버에 초대된 봇 (시리즈 앞편에서 만든 그대로!)
pip install -U discord.py이번 편은 슬래시 명령어만 쓰기 때문에 Message Content Intent는 켜지 않아도 돼요.
📌 1단계 - 가장 간단한 그룹 만들기
먼저 app_commands.Group 인스턴스를 만들고, 거기에 명령어를 붙이는 방식부터 볼게요.
# discord.py 2.x
import discord
from discord import app_commands
from discord.ext import commands
intents = discord.Intents.default()
bot = commands.Bot(command_prefix="!", intents=intents)
# 1) 그룹(폴더) 만들기
인사 = app_commands.Group(name="인사", description="인사 명령어 모음")
# 2) 그룹에 하위 명령어 붙이기
@인사.command(name="안녕", description="반갑게 인사해요")
async def hello(interaction: discord.Interaction):
await interaction.response.send_message("안녕하세요! 👋")
# 3) 그룹을 봇에 등록하기
bot.tree.add_command(인사)하나씩 뜯어볼게요 💡
- app_commands.Group(name=..., description=...) : 명령어를 담을 폴더를 만들어요.
name이/뒤에 오는 그룹 이름이 됩니다. - @인사.command() : 평소 쓰던
@bot.tree.command()대신 그룹 이름.command()를 쓰면 그 그룹의 하위 명령어가 돼요. - bot.tree.add_command(인사) : 명령어 하나하나가 아니라 그룹을 통째로 트리에 등록해요. 하위 명령어들은 자동으로 따라 들어갑니다.
이제 /인사 안녕으로 실행할 수 있어요. 참 쉽죠? 그런데 명령어가 많아지면 이 방식은 코드가 흩어져서 관리가 어려워요. 그래서 실전에서는 클래스 방식을 씁니다.
📌 2단계 - 클래스로 그룹 만들기 (실전 방식)
app_commands.Group을 상속받은 클래스를 만들면, 관련 명령어를 한 클래스 안에 깔끔하게 모을 수 있어요. 서버 관리 봇의 단골 기능인 설정 그룹을 만들어볼게요.
# discord.py 2.x
class Settings(app_commands.Group, name="설정", description="서버 설정 명령어 모음"):
@app_commands.command(name="환영채널", description="환영 메시지를 보낼 채널을 지정해요")
@app_commands.describe(채널="환영 메시지를 보낼 텍스트 채널")
async def welcome_channel(self, interaction: discord.Interaction, 채널: discord.TextChannel):
config["welcome"] = 채널.id
await interaction.response.send_message(
f"✅ 환영 채널을 {채널.mention} 로 설정했어요!", ephemeral=True
)
bot.tree.add_command(Settings())하나씩 뜯어볼게요 💡
- class Settings(app_commands.Group, name="설정", ...) : 클래스 정의에
name과description을 함께 적어요. 이 클래스가 곧/설정그룹이 됩니다. - @app_commands.command() : 클래스 안에서는 이 데코레이터로 하위 명령어를 만들어요. 메서드라서 첫 번째 인자에 self가 꼭 들어가야 해요! (1단계 방식과 다른 점이에요)
- @app_commands.describe(채널=...) : 명령어 입력창에서 파라미터 옆에 뜨는 설명이에요. 유저 친화도가 확 올라가요.
- ephemeral=True : 응답을 명령어를 친 본인에게만 보여줘요. 설정 명령어에 딱이에요.
- bot.tree.add_command(Settings()) : 클래스를 인스턴스로 만들어서(괄호!) 등록해요.
📌 3단계 - 그룹 속 그룹으로 3단 구조 만들기
/설정 알림 켜기처럼 한 단계 더 들어가는 구조는, 클래스 안에 app_commands.Group을 클래스 속성으로 선언하면 돼요.
# discord.py 2.x
class Settings(app_commands.Group, name="설정", description="서버 설정 명령어 모음"):
# 그룹 속 그룹: /설정 알림 ...
알림 = app_commands.Group(name="알림", description="알림 기능을 켜고 꺼요")
@알림.command(name="켜기", description="알림을 켜요")
async def alarm_on(self, interaction: discord.Interaction):
await interaction.response.send_message("🔔 알림을 켰어요!")
@알림.command(name="끄기", description="알림을 꺼요")
async def alarm_off(self, interaction: discord.Interaction):
await interaction.response.send_message("🔕 알림을 껐어요!")알림 = app_commands.Group(...) 이 한 줄이 핵심이에요. 클래스 속성으로 선언한 그룹은 자동으로 하위 그룹이 되고, @알림.command()로 그 안에 명령어를 넣으면 /설정 알림 켜기 완성! 단, 여기서 또 한 단계 더 들어가는 건 디스코드 규칙상 불가능하다는 것, 기억하시죠?
📌 전체 코드 (복붙 OK)
TOKEN만 바꾸면 바로 돌아가는 완성본이에요. /설정 환영채널, /설정 인사말, /설정 보기, /설정 알림 켜기/끄기까지 서버 설정 봇 하나가 통째로 들어있어요.
# discord.py 2.x / Python 3.8+
import discord
from discord import app_commands
from discord.ext import commands
TOKEN = "여기에_봇_토큰"
intents = discord.Intents.default()
bot = commands.Bot(command_prefix="!", intents=intents)
# 서버별 설정을 담아둘 임시 저장소 (봇을 끄면 사라져요! 저장은 #14편 참고)
config = {}
def get_config(guild_id):
"""서버별 설정 dict를 가져오고, 없으면 기본값으로 만들어요."""
return config.setdefault(
guild_id, {"welcome": None, "greeting": "어서 오세요!", "alarm": False}
)
class Settings(app_commands.Group, name="설정", description="서버 설정 명령어 모음"):
@app_commands.command(name="환영채널", description="환영 메시지를 보낼 채널을 지정해요")
@app_commands.describe(채널="환영 메시지를 보낼 텍스트 채널")
async def welcome_channel(self, interaction: discord.Interaction, 채널: discord.TextChannel):
get_config(interaction.guild_id)["welcome"] = 채널.id
await interaction.response.send_message(
f"✅ 환영 채널을 {채널.mention} 로 설정했어요!", ephemeral=True
)
@app_commands.command(name="인사말", description="환영 인사말 문구를 바꿔요")
@app_commands.describe(문구="새 환영 인사말")
async def greeting(self, interaction: discord.Interaction, 문구: str):
get_config(interaction.guild_id)["greeting"] = 문구
await interaction.response.send_message(
f"✅ 인사말을 바꿨어요: {문구}", ephemeral=True
)
@app_commands.command(name="보기", description="현재 서버 설정을 확인해요")
async def show(self, interaction: discord.Interaction):
c = get_config(interaction.guild_id)
channel = f"<#{c['welcome']}>" if c["welcome"] else "미설정"
embed = discord.Embed(title="⚙️ 현재 서버 설정", color=0x5865F2)
embed.add_field(name="환영 채널", value=channel, inline=True)
embed.add_field(name="인사말", value=c["greeting"], inline=True)
embed.add_field(name="알림", value="켜짐 🔔" if c["alarm"] else "꺼짐 🔕", inline=True)
await interaction.response.send_message(embed=embed, ephemeral=True)
# ── 그룹 속 그룹: /설정 알림 켜기·끄기 ──
알림 = app_commands.Group(name="알림", description="알림 기능을 켜고 꺼요")
@알림.command(name="켜기", description="알림을 켜요")
async def alarm_on(self, interaction: discord.Interaction):
get_config(interaction.guild_id)["alarm"] = True
await interaction.response.send_message("🔔 알림을 켰어요!", ephemeral=True)
@알림.command(name="끄기", description="알림을 꺼요")
async def alarm_off(self, interaction: discord.Interaction):
get_config(interaction.guild_id)["alarm"] = False
await interaction.response.send_message("🔕 알림을 껐어요!", ephemeral=True)
bot.tree.add_command(Settings())
@bot.event
async def on_ready():
synced = await bot.tree.sync()
print(f"✅ {bot.user} 로 로그인! 슬래시 명령어 {len(synced)}개를 동기화했어요.")
bot.run(TOKEN)하나씩 뜯어볼게요 💡
- config + get_config() : 서버(guild)마다 설정을 따로 저장하는 구조예요. 지금은 메모리에만 있어서 봇을 재시작하면 초기화돼요. #14편의 JSON/SQLite 저장을 붙이면 완벽해집니다.
- interaction.guild_id : 명령어가 실행된 서버의 ID예요. 서버별로 설정을 나눠 담는 열쇠로 썼어요.
- bot.tree.sync() : 만든 명령어를 디스코드 서버에 등록(동기화) 하는 단계예요. 이걸 빼먹으면 명령어가 아예 안 보여요!
📌 실행 결과
봇을 켜고 디스코드에서 확인해봐요.
- 채팅창에
/설정까지만 입력 → 하위 명령어(환영채널·인사말·보기·알림)가 자동완성 목록으로 떠요. /설정 환영채널선택 → 채널 선택 UI가 뜨고, 고르면 "✅ 환영 채널을 #일반 로 설정했어요!" (나에게만 보임)/설정 알림 켜기→ "🔔 알림을 켰어요!" — 3단 구조도 자연스럽게 동작해요./설정 보기→ 방금 바꾼 설정이 임베드로 정리되어 나와요.

📌 자주 나는 에러
1. 명령어가 디스코드에 안 보여요
원인: tree.sync()를 안 했거나, 글로벌 동기화 반영이 아직 안 됐어요. 글로벌 명령어는 반영까지 몇 분에서 최대 1시간쯤 걸릴 수 있어요.
해결: on_ready에서 sync가 실행됐는지 확인하고, 디스코드 클라이언트를 Ctrl+R로 새로고침해보세요. 테스트 중이라면 아래처럼 내 서버에만 즉시 반영할 수 있어요.
# discord.py 2.x
MY_GUILD = discord.Object(id=내_서버_ID)
@bot.event
async def on_ready():
bot.tree.copy_global_to(guild=MY_GUILD)
await bot.tree.sync(guild=MY_GUILD) # 내 서버에는 즉시 반영!2. CommandAlreadyRegistered 에러
원인: 같은 이름의 그룹이나 명령어를 두 번 등록했어요. add_command를 중복 호출했거나, 그룹 이름이 기존 명령어와 겹치는 경우예요.
해결: bot.tree.add_command(Settings())가 한 번만 실행되는지, 그룹 이름이 다른 명령어와 안 겹치는지 확인하세요.
3. ValueError: groups can only be nested at most one level
원인: 그룹 속 그룹 안에 또 그룹을 넣으려고 했어요. 디스코드 규칙상 중첩은 1단계까지예요.
해결: 3단(/설정 알림 켜기)을 초과하는 구조는 만들 수 없어요. 명령어 설계를 2~3단으로 다시 정리하세요.
4. TypeError: ... missing 1 required positional argument (self 관련)
원인: 1단계(모듈 레벨) 방식과 2단계(클래스) 방식을 섞었어요. 클래스 밖 함수에 self를 넣었거나, 클래스 안 메서드에서 self를 빼먹은 거예요.
해결: 클래스 안 메서드에는 self 필수, 클래스 밖 함수에는 self 금지! 이것만 기억하면 돼요.
💡 보너스 팁
-
그룹 전체에 권한 걸기 : 설정 명령어는 관리자만 쓰게 해야겠죠? 클래스 정의에 한 줄만 추가하면 하위 명령어 전체에 적용돼요. #12편의 권한 관리와 함께 보면 좋아요.
코드 # discord.py 2.x class Settings( app_commands.Group, name="설정", description="서버 설정 명령어 모음", default_permissions=discord.Permissions(manage_guild=True), # 서버 관리 권한 필요 guild_only=True, # DM에서는 못 쓰게 ): -
명령어 이름 짓기 : 그룹 이름은
설정·음악처럼 명사 폴더, 하위 명령어는보기·켜기처럼 짧은 동작으로 지으면 자동완성 목록이 문장처럼 읽혀서 깔끔해요. -
코드 파일이 너무 길어졌다면 : 그룹을 파일별로 나눠 관리하는 Cog(코그)라는 도구도 있어요. 시리즈 후반에 봇 구조 정리 편에서 제대로 다뤄볼게요!
📌 마무리
오늘은 슬래시 명령어를 폴더처럼 정리하는 그룹(Group)을 배웠어요. app_commands.Group 인스턴스로 간단하게, 클래스 상속으로 깔끔하게, 클래스 속성 그룹으로 3단 구조까지 — 이제 명령어가 아무리 많아져도 무섭지 않아요! 그룹 자체는 실행 불가, 중첩은 1단계까지, 하위 명령어는 25개까지라는 규칙 세 가지만 기억해주세요.
다음 편에서는 지정한 시간에 봇이 먼저 말을 거는 리마인더·알림 기능을 만들어볼게요. 기대해주세요! 🙌
📚 디스코드 봇 만들기 시리즈
- #5 - 버튼(Button) 상호작용
- #6 - 드롭다운(Select Menu) 메뉴
- #7 - 모달(Modal)로 입력 받기
- #8 - 임베드(Embed) 꾸미기
- #9 - AI(Claude·ChatGPT API) 연동
- #10 - 다국어(i18n) 지원
- #11 - 플라스크(Flask) 웹 대시보드
- #12 - 명령어 쿨다운·권한 관리
- #13 - 역할·채널 자동화
- #14 - 데이터 저장 (JSON vs SQLite)
- #15 - 슬래시 명령어 그룹(Group) ← 지금 이 글!
연재 · 32편 중 15번째
디스코드 봇 만들기
- #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임시 음성방 확장, 파티방 만들기! 방장 위임·강퇴·초대 전용(비공개) 방 + 전용 채팅방 자동 생성 (파이썬 예제) 🎉
