해당 글은, 제가 작성한 Discord.js 보일러플레이트를 기반으로 합니다. 해당 보일러픝레이트는 다음에라도 봇을 빠르게 만들고 싶으실 때 사용하실 수 있습니다. Discord.js TypeScript Boilerplate
안녕하세요! 지난 시간에는 이벤트 핸들링을 통해 봇이 서버의 다양한 상황에 능동적으로 반응하도록 만들었습니다. 덕분에 우리 봇은 이제 단순한 명령어를 넘어, 서버와 좀 더 긴밀하게 상호작용할 수 있게 되었죠.
이번에는 봇과 서버의 질서를 유지하는 데 아주 중요한 역할(Role) 관리와 권한(Permission) 체크에 대해 알아보겠습니다. 모든 사용자가 모든 명령어를 사용하거나, 봇이 모든 기능을 아무에게나 제공한다면 서버가 혼란스러워질 수 있겠죠? 특정 명령어는 관리자만 사용하도록 하거나, 특정 역할을 가진 유저에게만 특별한 기능을 제공하는 방법을 배워봅시다.
왜 역할과 권한 체크가 필요할까요?
서버를 운영하다 보면 다양한 등급의 사용자가 생기기 마련입니다. 예를 들어, 일반 멤버, 모더레이터, 관리자 등이 있겠죠. 각 그룹마다 접근할 수 있는 기능이나 정보가 달라야 할 필요가 있습니다.
- 보안: 서버 관리 명령어 (예: 멤버 추방, 채널 삭제)는 아무나 사용해서는 안 됩니다.
- 기능 제한: 특정 이벤트 공지 명령어는 이벤트 담당 역할에게만 허용하고 싶을 수 있습니다.
- 사용자 경험: 사용자에게 필요 없는 기능이나 권한이 없는 기능을 애초에 시도하지 못하게 안내하는 것이 좋습니다.
Discord.js는 사용자의 역할과 권한을 쉽게 확인하고 이를 바탕으로 명령어 접근을 제어할 수 있는 강력한 기능을 제공합니다.
사용자의 역할(Role) 확인하기
디스코드에서 역할은 사용자에게 특정 권한을 부여하거나 그룹을 나타내는 데 사용됩니다. 봇 명령어 실행 시, 해당 명령어를 실행한 사용자가 어떤 역할을 가지고 있는지 확인하는 것은 매우 일반적인 작업입니다.
ChatInputCommandInteraction
객체에서 member
속성을 통해 명령어 사용자(GuildMember
) 정보에 접근할 수 있고, 이 GuildMember
객체는 roles
라는 속성을 가집니다. member.roles
는 GuildMemberRoleManager
타입으로, 사용자가 가진 역할들을 관리하는 여러 메서드와 속성을 제공합니다.
가장 흔하게 사용되는 것은 member.roles.cache
로, 사용자가 가진 역할들을 Collection
형태로 가지고 있습니다. 이 Collection
을 이용해 특정 역할이 있는지 확인할 수 있습니다.
특정 역할이 있는지 확인하는 방법
- 역할 ID로 확인: 가장 정확하고 권장되는 방법입니다. 역할 이름은 변경될 수 있지만, ID는 고유합니다.
- 역할 이름으로 확인: 편리하지만, 이름이 변경되거나 중복될 가능성이 있어 주의해야 합니다.
// 예시: 명령어 실행 부분에서 역할 확인
// import { ChatInputCommandInteraction, GuildMemberRoleManager } from 'discord.js';
export async function execute(interaction: ChatInputCommandInteraction) {
if (!interaction.inGuild()) {
await interaction.reply({
content: "이 명령어는 서버에서만 사용할 수 있습니다.",
ephemeral: true,
});
return;
}
// interaction.member가 GuildMember 타입임을 확신할 수 있습니다.
const memberRoles = interaction.member.roles as GuildMemberRoleManager;
const adminRoleId = "YOUR_ADMIN_ROLE_ID"; // 실제 관리자 역할 ID로 변경
const moderatorRoleName = "Moderator"; // 예시 역할 이름
// 1. 역할 ID로 확인
if (memberRoles.cache.has(adminRoleId)) {
console.log("이 사용자는 관리자 역할을 가지고 있습니다.");
// 관리자 전용 로직 수행
} else {
console.log("이 사용자는 관리자 역할이 없습니다.");
}
// 2. 역할 이름으로 확인 (대소문자 구분)
if (memberRoles.cache.some((role) => role.name === moderatorRoleName)) {
console.log("이 사용자는 Moderator 역할을 가지고 있습니다.");
// 모더레이터 관련 로직 수행
} else {
console.log("이 사용자는 Moderator 역할이 없습니다.");
}
// ... (명령어 기본 로직)
await interaction.reply({
content: "역할 확인 테스트 완료!",
ephemeral: true,
});
}
팁: 역할 ID는 디스코드 클라이언트에서 개발자 모드를 활성화한 후, 서버 설정 > 역할 메뉴에서 해당 역할을 우클릭하여 "ID 복사하기"를 통해 얻을 수 있습니다.
사용자의 권한(Permission) 확인하기
역할이 사용자 그룹핑과 특정 권한 묶음을 제공한다면, 권한은 더 세부적인 개별 행동에 대한 허용 여부를 나타냅니다. 예를 들어, "메시지 관리", "멤버 추방", "채널 관리" 등이 각각의 권한입니다.
GuildMember
객체의 permissions
속성(PermissionsBitField
)을 통해 사용자가 가진 권한을 확인할 수 있습니다. permissions.has()
메서드에 확인하고자 하는 권한 플래그를 전달하여 해당 권한이 있는지 여부를 boolean
값으로 얻을 수 있습니다.
PermissionsBitField
에서 사용할 수 있는 주요 권한 플래그들은 다음과 같습니다 (전체 목록은 공식 문서 참고):
PermissionsBitField.Flags.Administrator
PermissionsBitField.Flags.KickMembers
PermissionsBitField.Flags.BanMembers
PermissionsBitField.Flags.ManageChannels
PermissionsBitField.Flags.ManageGuild
PermissionsBitField.Flags.ManageMessages
PermissionsBitField.Flags.SendMessages
PermissionsBitField.Flags.ViewChannel
특정 권한이 있는지 확인하는 방법
// 예시: 명령어 실행 부분에서 권한 확인
// import { ChatInputCommandInteraction, PermissionsBitField, GuildMember } from 'discord.js';
export async function execute(interaction: ChatInputCommandInteraction) {
if (!interaction.inGuild()) {
await interaction.reply({
content: "이 명령어는 서버에서만 사용할 수 있습니다.",
ephemeral: true,
});
return;
}
const member = interaction.member as GuildMember; // 타입 단언
// 관리자 권한 확인
if (member.permissions.has(PermissionsBitField.Flags.Administrator)) {
console.log("이 사용자는 서버 관리자 권한을 가지고 있습니다.");
// 관리자 전용 로직
} else {
console.log("이 사용자는 서버 관리자 권한이 없습니다.");
}
// 여러 권한 중 하나라도 있는지 확인 (배열 전달)
if (
member.permissions.has([
PermissionsBitField.Flags.KickMembers,
PermissionsBitField.Flags.BanMembers,
])
) {
console.log(
"이 사용자는 멤버 추방 또는 차단 권한 중 하나 이상을 가지고 있습니다."
);
} else {
console.log("이 사용자는 멤버 추방 및 차단 권한이 모두 없습니다.");
}
// 특정 채널에서의 권한 확인 (더 복잡한 시나리오)
// const channel = interaction.channel;
// if (channel && member.permissionsIn(channel).has(PermissionsBitField.Flags.SendMessages)) {
// console.log('이 사용자는 현재 채널에 메시지를 보낼 수 있습니다.');
// }
// ... (명령어 기본 로직)
await interaction.reply({
content: "권한 확인 테스트 완료!",
ephemeral: true,
});
}
명령어 접근 제어하기
이제 역할과 권한을 확인하는 방법을 알았으니, 이를 활용해 명령어 접근을 제어해 봅시다.
1. 명령어 실행 로직 내에서 직접 확인
가장 유연한 방법은 각 명령어의 execute
함수 시작 부분에서 필요한 역할이나 권한을 확인하고, 조건에 맞지 않으면 사용자에게 알리고 명령 실행을 중단하는 것입니다.
// src/commands/adminOnlyCommand.ts
import {
SlashCommandBuilder,
ChatInputCommandInteraction,
GuildMember,
PermissionsBitField,
} from "discord.js";
export const data = new SlashCommandBuilder()
.setName("관리자전용")
.setDescription("서버 관리자만 사용할 수 있는 명령어입니다.");
export async function execute(interaction: ChatInputCommandInteraction) {
if (!interaction.inGuild()) {
return interaction.reply({
content: "이 명령어는 서버에서만 사용할 수 있습니다.",
ephemeral: true,
});
}
const member = interaction.member as GuildMember;
// 관리자 권한이 없으면 실행 거부
if (!member.permissions.has(PermissionsBitField.Flags.Administrator)) {
return interaction.reply({
content: "이 명령어를 사용할 권한이 없습니다. (관리자 권한 필요)",
ephemeral: true,
});
}
// 관리자만 실행할 수 있는 로직
await interaction.reply({
content: "관리자 전용 명령어가 성공적으로 실행되었습니다!",
ephemeral: true,
});
}
2. default_member_permissions
사용하기
SlashCommandBuilder
는 setDefaultMemberPermissions()
메서드를 제공합니다. 여기에 필요한 권한을 설정하면, 디스코드 클라이언트 자체에서 해당 권한이 없는 사용자에게는 명령어가 회색으로 비활성화되어 보이거나, 실행 시 디스코드 시스템 메시지로 권한 없음을 알려줍니다. 즉, 봇의 InteractionCreate
이벤트까지 도달하기 전에 디스코드가 1차적으로 필터링해줍니다.
// src/commands/kick.ts
import {
SlashCommandBuilder,
ChatInputCommandInteraction,
GuildMember,
PermissionsBitField,
} from "discord.js";
export const data = new SlashCommandBuilder()
.setName("추방")
.setDescription("서버에서 멤버를 추방합니다. (추방 권한 필요)")
.addUserOption((option) =>
option
.setName("대상")
.setDescription("추방할 멤버를 선택하세요.")
.setRequired(true)
)
.addStringOption((option) =>
option.setName("사유").setDescription("추방 사유 (선택 사항)")
)
.setDefaultMemberPermissions(PermissionsBitField.Flags.KickMembers); // 멤버 추방 권한 필요
export async function execute(interaction: ChatInputCommandInteraction) {
if (!interaction.inGuild()) {
return interaction.reply({
content: "이 명령어는 서버에서만 사용할 수 있습니다.",
ephemeral: true,
});
}
// setDefaultMemberPermissions를 사용했더라도, 추가적인 안전장치로 한번 더 확인하는 것이 좋습니다.
// 또는 더 복잡한 역할 기반 로직을 여기에 추가할 수 있습니다.
const member = interaction.member as GuildMember;
if (!member.permissions.has(PermissionsBitField.Flags.KickMembers)) {
return interaction.reply({
content: "이 명령어를 사용할 권한이 없습니다. (멤버 추방 권한 필요)",
ephemeral: true,
});
}
const targetUser = interaction.options.getUser("대상", true);
const reason = interaction.options.getString("사유") || "사유 없음";
const targetMember = await interaction
.guild!.members.fetch(targetUser.id)
.catch(() => null);
if (!targetMember) {
return interaction.reply({
content: "추방할 멤버를 서버에서 찾을 수 없습니다.",
ephemeral: true,
});
}
if (!targetMember.kickable) {
return interaction.reply({
content: "봇이 이 멤버를 추방할 권한이 없습니다. (역할 순서 등 확인)",
ephemeral: true,
});
}
try {
await targetMember.kick(reason);
await interaction.reply({
content: `${targetUser.tag}님을 성공적으로 추방했습니다. 사유: ${reason}`,
ephemeral: false,
});
} catch (error) {
console.error("멤버 추방 중 오류 발생:", error);
await interaction.reply({
content: "멤버를 추방하는 중 오류가 발생했습니다.",
ephemeral: true,
});
}
}
setDefaultMemberPermissions
와 수동 체크의 차이점 및 사용 시기:
setDefaultMemberPermissions
: 명령어 등록 시점에 디스코드에 "이 명령어는 이 권한이 필요해"라고 알려주는 것과 같습니다. 사용 편의성이 좋고, 봇의 부하를 줄일 수 있습니다. 기본적인 권한 제한에 적합합니다.- 수동 체크 (
execute
내부): 더 복잡한 로직 (예: 특정 역할 조합, 특정 채널에서의 권한, 커스텀 조건)을 구현할 때 필요합니다.setDefaultMemberPermissions
를 사용했더라도 중요한 명령어는 이중으로 체크하는 것이 안전합니다.
역할 관리 (간단 소개)
봇을 사용하여 사용자에게 역할을 부여하거나 제거하는 것도 가능합니다. GuildMember
객체의 roles
매니저를 통해 add()
, remove()
등의 메서드를 사용할 수 있습니다.
// 예시: 특정 사용자에게 역할 부여
// const roleToGive = interaction.guild.roles.cache.get('YOUR_ROLE_ID_TO_GIVE');
// if (targetMember && roleToGive) {
// try {
// await targetMember.roles.add(roleToGive);
// console.log(`${targetMember.user.tag}에게 ${roleToGive.name} 역할을 부여했습니다.`);
// } catch (error) {
// console.error('역할 부여 실패:', error);
// }
// }
역할 관리는 멤버의 상태 변경, 특정 조건 달성 시 보상 등 다양한 자동화 기능을 구현하는 데 활용될 수 있습니다. 이 부분은 더 심화된 주제이므로, 필요에 따라 공식 문서를 참고하여 탐색해 보세요.
마무리하며
이번 시간에는 사용자의 역할과 권한을 확인하고, 이를 바탕으로 명령어 접근을 제어하는 방법에 대해 배웠습니다. member.roles.cache
를 통해 역할을 확인하고, member.permissions.has()
로 권한을 체크하며, setDefaultMemberPermissions
로 명령어의 기본 접근 권한을 설정하는 방법을 익혔습니다.
이러한 기능들을 활용하면 여러분의 봇은 훨씬 더 체계적이고 안전하게 서버 기능을 제공할 수 있게 됩니다. 관리자 전용 명령어, 특정 역할 전용 기능 등을 구현하여 서버 운영의 효율성을 높여보세요.
다음 시간에는 드디어 데이터베이스 연동에 대해 알아볼 차례입니다! Prisma ORM을 사용하여 SQLite나 MySQL 같은 데이터베이스에 봇의 데이터를 저장하고 불러오는 방법을 배우게 됩니다. 사용자 정보, 서버 설정, 경고 횟수 등 다양한 정보를 영구적으로 관리할 수 있게 될 거예요. 기대하셔도 좋습니다!
'DiscordJS 개발 튜토리얼' 카테고리의 다른 글
[DiscordJS 봇 개발 튜토리얼] 6. 이벤트 핸들링 마스터하기: 봇을 살아 움직이게 만드는 비법 (1) | 2025.06.08 |
---|---|
[DiscordJS 봇 개발 튜토리얼] 5. 임베드 메시지와 버튼 만들기: 봇과의 소통을 더 풍부하게! (1) | 2025.06.06 |
[DiscordJS 봇 개발 튜토리얼] 4. 명령어 쿨타임과 안정적인 오류 처리 (1) | 2025.06.05 |
[DiscordJS 봇 개발 튜토리얼] 3. 슬래시 명령어: 옵션과 서브커맨드로 더욱 강력하게! (1) | 2025.06.04 |
[DiscordJS 봇 개발 튜토리얼] 2. 명령어 구조 만들기: 슬래시 명령어를 위한 첫걸음 (1) | 2025.06.03 |