Profile picture

[Python] 디스코드(Discord) 모달(Form)과 콤보박스(Select) 활용법

JaehyoJJAng2024년 09월 21일

개요

모달(Form)과 콤보박스(Select)를 활용하여 사용자와의 상호작용을 조금 더 복잡하게 다루는 방법을 기록해보려고 한다.


구현 목표

  • 1. 알림 등록 버튼 클릭 시 모달(Form)로 물건 이름을 받도록 한다.
  • 2. 입력된 물건 이름을 기반으로 콤보박스(Select)에서 다중 선택할 수 있는 옵션을 제공한다.
  • 3. 선택된 물건들은 버튼 클릭으로 알림 설정에 저장되며, 이 데이터는 프로그램 종료 후에도 유지되도록 해야 한다.

위 구현 목표를 기반으로 이제 코드를 작성하러 가보자.


주요 구현 단계

import 목록은 다음과 같다.

import discord
import json
import os
from discord.ext import commands
from discord import ButtonStyle
from discord.ui import Button, View, Select, Modal

프로젝트에 사용할 JSON 파일을 통해 알림 데이터를 저장하고 관리할 것이기 때문에 아래 기본 코드도 작성해주자.

# 데이터 저장 파일 경로
DATA_FILE = "alert_data.json"

# 데이터 로드 및 저장 함수
def load_data():
    if os.path.exists(DATA_FILE):
        with open(DATA_FILE, "r", encoding="utf-8") as f:
            return json.load(f)
    return {}

def save_data(data):
    with open(DATA_FILE, "w", encoding="utf-8") as f:
        json.dump(data, f, indent=4, ensure_ascii=False)

# 알림 데이터 로드
alert_data = load_data()


1. 버튼 및 모달 동작 정의

AlertButton 클래스

discord.ui.View를 상속받아서 버튼 기능을 구현하자.

사용자가 버튼을 클릭하면 모달이 열리도록 설정할거다.

class AlertView(View):
    def __init__(self) -> None:
        super().__init__(timeout=None)
        self.alert_modal = AlertModal()

    @discord.ui.button(
        label="알림 등록", style=ButtonStyle.primary, custom_id="alert_register"
    )
    async def alert_register(
        self, interaction: discord.Interaction, button: Button
    ) -> None:
        # 모달 생성
        await interaction.response.send_modal(self.alert_modal)

주요 동작 설명

  • AlertView: Discord에서 상호작용 UI를 구현할 때 사용하는 기본 View 클래스이다.
  • interaction.response.send_modal: 모달(Form)을 사용자에게 표시한다.

AlertModal 클래스

모달은 사용자가 데이터를 입력할 수 있도록 돕는 인터페이스이다.

class AlertModal(Modal):
    item_name = discord.ui.TextInput(
        label="물건 이름 입력",
        placeholder="예: 책, 노트북",
        required=True,
    )

    def __init__(self):
        super().__init__(title="알림 등록")

    async def on_submit(self, interaction: discord.Interaction):
        item = self.item_name.value
        # Mock 데이터 (실제로는 DB나 API 호출 가능)
        items = [f"{item} {i+1}" for i in range(5)]
        # 콤보박스 생성
        view = SelectView(interaction.user.id, items)
        embed = discord.Embed(
            title="원하는 물건을 선택하세요",
            description="아래 목록에서 선택 후 등록 버튼을 눌러주세요.",
            color=discord.Color.blurple(),
        )
        await interaction.response.edit_message(embed=embed, view=view)

주요 동작 설명

  • discord.ui.TextInput(): 모달에서 텍스트 입력을 받을 수 있는 필드라고 보면 된다.
  • on_submit: 사용자가 데이터를 입력하고 제출할 때 호출되는 메소드이다.

테스트

discord-example3


2. 콤보박스(Select) 정의

ComboBox 클래스

discord.ui.Select를 상속받아서 콤보박스를 구현하도록 한다.

class ComboBox(Select):
    def __init__(self, items: list[str]):
        options = [discord.SelectOption(label=item) for item in items]
        super().__init__(
            placeholder="아이템", options=options, min_values=1, max_values=len(items)
        )

    async def callback(self, interaction: discord.Interaction) -> None:
        parent_view = self.view  # 현재 Select가 속한 View
        if isinstance(parent_view, SelectView):
            parent_view.selected_items = self.values
            await interaction.response.defer()  # UI 업데이트 수행

주요 동작 설명

  • discord.ui.Select: 유저가 선택 가능한 옵션을 제공하는 콤보박스를 구현한다.
  • options: 콤보박스에 표시될 항목 리스트라고 보면 된다.
  • callback(): 유저가 콤보박스에서 무언가 항목을 선택했을 때 호출된다.
  • Interaction.response.defer(): 콜백 수행 시 UI 업데이트만 수행하도록 하기 위해서 defer()로 처리하였다.

테스트

discord-example4


3. 최종 데이터 처리

SelectView 클래스

선택된 물건들을 저장하고, 버튼을 통해 최종 확인한다.

class SelectView(View):
    def __init__(self, user_id: int, items: list[str]):
        super().__init__(timeout=None)
        self.items = items
        self.user_id = user_id
        self.selected_items = []

        # 콤보박스 추가
        self.add_item(ComboBox(items=items))

    @discord.ui.button(label="알림 등록", style=discord.ButtonStyle.success)
    async def confirm(
        self, interaction: discord.Interaction, button: discord.ui.Button
    ) -> None:
        await interaction.response.send_message(
            f"등록 완료! 알림 설정: {self.selected_items}"
        )

주요 동작 설정

  • add_item: View에 콤보박스를 추가하였다.
  • alert_data: 선택된 항목을 JSON 파일에 저장한다. 해당 변수는 구현 목표에서 따로 처리해둔 변수이다.
  • Interaction.response.send_message: 저장 결과를 사용자에게 전송한다.

테스트

discord-example5


4. 실행

cogs 디렉토리 하위에 dynamic-combobox.py 라는 파일을 생성해주고 지금까지 작성한 코드들을 붙여넣은 후,

마지막에 아래 코드를 추가한다.

class Alert(commands.Cog):
    def __init__(self, bot: commands.Bot):
        super().__init__()
        self.bot = bot

    @commands.Cog.listener()
    async def on_ready(self) -> None:
        print(f"{self.__class__.__name__} Cog is ready.")
        channel_id: int = <YOUR CHANNEL ID>
        channel = self.bot.get_channel(channel_id)
        if channel:
            await channel.send(
                "알림 등록을 시작하려면 아래 버튼을 클릭하세요.", view=AlertView()
            )

# Cog 등록 함수
async def setup(bot: commands.Bot):
    await bot.add_cog(Alert(bot=bot))

bot.py 파일 작성 방법은 다음 게시글을 참고하면 된다.
기본적인 Discord 봇 구조 - jaehyojjang.dev


Loading script...