mir.pe (일반/밝은 화면)
최근 수정 시각 : 2024-03-28 20:07:36

discord.js

discord.js
파일:discordjs.png
<colbgcolor=#fff,#1f2023><colcolor=#4b50c9> 필요한 Node.js 버전 v16.9 이상
최신 버전 v14.14.1
지원되는 가장
오래된 버전
v13.9
파일:홈페이지 아이콘.svg | 파일:GitHub 아이콘.svg 파일:GitHub 아이콘 화이트.svg | 파일:npm, lnc. n-logo.png | 파일:디스코드 아이콘.svg
1. 개요2. 개발 시 주의사항3. 설치4. 예제
4.1. 예제 14.2. 예제 2
5. Commando 방식
5.1. 예제
6. discord RPC
6.1. 예제6.2. 사용 불가
7. 여담

[clearfix]

1. 개요

디스코드 JS 패키지 문서
영문 개발 가이드

디스코드 API를 사용하여 을 만드는 Node.js 라이브러리이다.

2024년 기준 자바스크립트로 디스코드 봇을 개발할 때 가장 많이 쓰이고 있으며, discord RPC를 지원하고 Commando라는 명령어를 알아서 핸들링해주는 공식 라이브러리가 있다.

2. 개발 시 주의사항

우선 이 예제를 따라하기 전에, discord.Collection() 객체를 완벽히 사용할 수 있어야 한다. Collection 객체는 거의 모든 종류의 cache에서[1] 리턴하는 값이기 때문에 .filter, .map 등의 메서드를 사용하는 데 익숙하지 않다면 해당 기능들을 활용하기 어렵다.

3. 설치

discord.js는 npm또는 yarn에서 설치할 수 있다.
#!syntax sh
npm i discord.js

yarn에서는
#!syntax sh
yarn add discord.js

참고로 구버전이 필요한 경우에는 npm의 경우 npm install discord.js@<버전번호>[2]로 설치할 수 있다.

4. 예제

4.1. 예제 1

#!syntax javascript
const Discord = require('discord.js');
const { GatewayIntentBits } = require('discord.js')

const client = new Discord.Client({
  intents: [ // Intent를 설정합니다. 설정하지 않으면 CLIENT_MISSING_INTENTS 오류가 발생합니다.
    GatewayIntentBits.Guilds,
    GatewayIntentBits.GuildMessages
  ]
}); // Discord.Client 객체를 생성합니다.

client.on('ready', () => { // ready 이벤트시 실행할 함수
  console.log(`Logged in as ${client.user.tag}!`); // client.user 는 자신의 유저 객체이고 tag 는 유저 객체의 프로퍼티 입니다.
});

client.on('messageCreate', msg => { // message 이벤트시 msg (Discord.Message) 매개변수를 받고 실행할 함수
  if (msg.content === 'ping') { // Discord.Message 객체의 content 프로퍼티가 'ping' 일 때
    msg.reply('Pong!'); // reply 는 멘션 + , msg 로 출력됩니다.
  }
});

client.login('token'); // 토큰을 입력합니다. 올바르지 않은 토큰일 시 에러가 발생합니다.


이 코드는 if 문으로 명령어를 판단한다. 하지만 이런 식으로 한 파일에 if 문을 늘려가다 보면 어느샌가 코드가 300줄, 500줄, 심하면 3000줄까지 불어나는 문제가 발생한다. 해당 문제는 스파게티 코드를 발생시킬수 있어 후에 유지 보수가 매우 어려워지기 때문에 많은 유명한 discord.js 봇 들은 거의 다 명령어 또는 이벤트 파일을 분리하고, 명령어 추가를 자동화하고, 가독성을 중요시한다. 따라서 예제 1은 별로 좋지 않은 예시이다. 이 문제점은 discord.py의 Client 클래스를 사용해 개발하는 사람들에게서도 많이 나타난다[3].

v12 이하를 사용한다면 client.on에서 messageCreate 대신 message를 사용해야 한다.

4.2. 예제 2

index.js
#!syntax javascript
const Discord = require('discord.js');
const client = new Discord.Client({
  intents: [ // Intent를 설정합니다. 설정하지 않으면 CLIENT_MISSING_INTENTS 오류가 발생합니다.
    GatewayIntentBits.Guilds,
    GatewayIntentBits.GuildMessages
  ]
});
const fs = require('fs');
const prefix = '!';

client.commands = new Discord.Collection() 
// 명령어 캐시 컬렉션을 클라이언트 내에 선언한다. 해당 방법으로 명령어 파일 내에서도 client.commands로 다른 명령어들에 접근할수 있다.

client.commands.load = dir => {
    for (const file of fs.readdirSync(dir)) {
      const cmd = require(`${dir}/${file}`);
      client.commands.set(cmd.name, cmd);
    }
    console.log(client.commands.map(c => c.name).join(', ') + ' 명령어가 로드됨.');
}

client.commands.load(__dirname + "/commands");
//해당 파일이 위치한 디렉터리에서 "/commands" 경로를 추가

client.on('ready', () => console.log(`${client.user.tag} 에 로그인됨`));

client.on('messageCreate', msg => {
    if (msg.author.bot) return;
    if (!msg.content.startsWith(prefix)) return;
    if (msg.content.slice(0, prefix.length) !== prefix) return;

    const args = msg.content.slice(prefix.length).trim().split(/ +/g);
    const command = args.shift().toLowerCase();

    let cmd = client.commands.get(command);
    //get는 컬렉션 내에 해당 key 값을 가진 데이터가 없으면 false 값을 반환하므로 부분적으로 Collection#has처럼 사용할수 있습니다.

    if(cmd) cmd.run(client, msg, args);
})

client.login('token');


commands/ping.js
#!syntax javascript
//run이라는 메소드(function)을 export(수출)
exports.run = (client, msg, args) => {
    msg.reply(`${client.ws.ping}ms`);
};

exports = {
    name: 'ping'
};


이런 식으로 파일을 분리하고 가져오는 것을 자동화하면 앞의 문제를 바로 해결할 수 있다.

5. Commando 방식

이는 discord.js Client가 아닌 discord.js CommandoClient를 사용하여 봇을 만드는 것이다.

위의 파일 핸들링의 방식을 이 라이브러리로 대체할 수 있으며, 또 sqlite3를 지원하여 데이터베이스 저장도 쉽다.

CommandoClient는 단순히 discord.js Client의 확장이므로 Client에 있던 메서드, 클래스 등은 여기서도 사용할 수 있다.

5.1. 예제

#!syntax javascript
const Commando = require('discord.js-commando')

const client = new Commando.Client({
    owner: '소유자 아이디'
})

const path = require('path')

client.registry
    // 명령어들의 그룹들을 등록합니다.
    .registerGroups([
        ['fun', 'Fun commands'],
        ['some', 'Some group'],
        ['other', 'Some other group']
    ])

    // 기본 명령어, 그룹 등을 등록합니다.
    .registerDefaults()

    // 다른 폴더 (여기서는 commands) 에 있는 명령어 파일 들을 불러오고 등록합니다.
    .registerCommandsIn(path.join(__dirname, 'commands'));

const sqlite = require('sqlite');
// Commando에는 길드 별 접두사, 명령어 활성화 또는 비활성화 등의 기능이 있지만, 이를 저장해 놓으려면 데이터베이스가 필요하기 때문에 sqlite를 이용합니다.
client.setProvider(
    sqlite.open(path.join(__dirname, 'settings.sqlite3')).then(db => new Commando.SQLiteProvider(db))
).catch(console.error);

client.login('token goes here'); // 마지막으로 discord.js Client 처럼 로그인합니다.


이러면 모든 준비는 끝이다.
명령어를 commands 폴더에 저장하고 코드를 짜는건 여기에서 확인하면 된다.

6. discord RPC

6.1. 예제

#!syntax javascript
const clientId = '187406016902594560';
const scopes = ['rpc', 'rpc.api', 'messages.read'];

const client = new RPC.Client({ transport: 'websocket' });

client.on('ready', () => {
  console.log('Logged in as', client.application.name);
  console.log('Authenticated as user: ' + client.user.username);

  client.selectVoiceChannel('81384788862181376');
});

client.login({ clientId, scopes });

6.2. 사용 불가

Discord Developer Portal에, RPC에 대한 신청을 더 이상 받지 않는다고 나와있다.

따라서 만들고 싶어도 만들지 못하며, 거의 필요가 없어진 라이브러리이다.

7. 여담


[1] 유저, 길드 등 [2] 삼각괄호는 빼야한다. [3] 현재는 Cog 시스템으로 인해 많이 줄어든 편이나 discord.Client를 사용하는 오래된 강의들로 인해 아직 적지 않다