Discord.js๋กœ Bot๐Ÿ‘พ ๋งŒ๋“ค๊ธฐ (2)

์ด์ค€์„ยท2021๋…„ 7์›” 24์ผ
3

NodeJs

๋ชฉ๋ก ๋ณด๊ธฐ
8/8
post-thumbnail

์ œ๊ฐ€ ์ œ์ž‘ํ•œ ๋กœ์•„๋ด‡ Repository๋Š” ๋‹ค์Œ ๋งํฌ์—์„œ ํ™•์ธํ•˜์„ธ์š”! (ํ•„์š”ํ•˜์‹  ๋ถ„๋“ค๋งŒ ํ™•์ธํ•ด๋ณด์„ธ์š” ๐Ÿ˜…)

์„œ๋ก 

2๋‹ฌ๋งŒ์— ๋Œ์•„์™”๋‹ค.

๊ทธ ๊ฐ„์— ์‚ฌ์‹ค ๋ณ„๋‹ค๋ฅธ ์ผ์€ ์—†์—ˆ๋Š”๋ฐ, ์ด๊ฒƒ์ €๊ฒƒ ์‹ ๊ฒฝ์“ธ ์ผ๋“ค์ด ์ƒ๊ธฐ๋Š” ๋ฐ”๋žŒ์— ๋ฒจ๋กœ๊ทธ ์ž‘์„ฑ์„ ์†Œํ™€ํžˆํ–ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  Java ๋‚ด์šฉ๋„ ํฌ์ŠคํŒ…ํ•˜๋ฉด์„œ ์•„๋Š” ๋‚ด์šฉ๋„ ๋‹ค์‹œ ์ ์„ ํ•„์š”์„ฑ์„ ์ตœ๊ทผ์— ๋Š๋ผ๊ฒŒ ๋˜์—ˆ๋‹ค. ์ผ๋‹จ ์˜ค๋Š˜์€ ์ด์ „์— ์ž‘์„ฑํ•œ Discord.js ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋งˆ์ง€๋ง‰์œผ๋กœ ์‚ฌ์šฉ๋ฒ•์„ ์ •๋ฆฌํ•˜๋„๋ก ํ•  ์˜ˆ์ •์ด๋‹ค. (์ถ”ํ›„์— ๋” ํ•„์š”ํ•œ ๋‚ด์šฉ์€ ์ž‘์„ฑํ•˜๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.)

๋”ฐ๋ผ์„œ ์˜ค๋Š˜์€ ๋กœ์ŠคํŠธ์•„ํฌ ์ „ํˆฌ์ •๋ณด์‹ค์„ ์ด์šฉํ•˜์—ฌ, ์บ๋ฆญํ„ฐ ์ „ํˆฌ์ •๋ณด๋ฅผ ๋””์Šค์ฝ”๋“œ ์ฑ„ํŒ…์ฐฝ์— ์ถœ๋ ฅํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„์œผ๋กœ ์šฐ์„  ๋งˆ๋ฌด๋ฆฌ๋ฅผ ์ง€์–ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๋ณธ๋ก 

์ด์ „ ํฌ์ŠคํŒ…๊นŒ์ง€๋Š” ๋งˆ๋ฌด๋ฆฌ ๋˜์—ˆ๋‹ค๋Š” ๊ฐ€์ •ํ•˜์— ์ง„ํ–‰๋ฉ๋‹ˆ๋‹ค.
์ด์ „ ํฌ์ŠคํŒ…์„ ์•ˆ๋ณด์…จ์œผ๋ฉด ์—ฌ๊ธฐ๋ฅผ ๋ˆŒ๋Ÿฌ์„œ ์‚ดํŽด๋ณด์‹œ๊ณ  ์˜ค์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

1. ๋ช…๋ น์–ด ์ •ํ•˜๊ธฐ

์ด์ „ ํฌ์ŠคํŒ…์—์„œ ๋””์Šค์ฝ”๋“œ ๋ด‡ ์„ค์ •์„ ํ•˜๊ณ  ๋ฉ”์‹œ์ง€๋ฅผ ์ž…๋ ฅํ•˜๋ฉด ์ฝ˜์†”์ฐฝ์— ์ฐํžˆ๋Š” ๊ฒƒ ๊นŒ์ง€ ์ง„ํ–‰ํ•˜์˜€๋‹ค. ๋””์Šค์ฝ”๋“œ ๋ด‡์„ ์‚ฌ์šฉํ•˜๋‹ค๋ณด๋ฉด !๋ช…๋ น์–ด, #๋ช…๋ น์–ด ์™€ ๊ฐ™์ด ํŠน์ •ํ•œ ๋ฌธ์ž ๋’ค์— ๋ช…๋ น์–ด๋ฅผ ์“ฐ๋Š” ๋ชจ์Šต์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

๊ทธ ์ด์œ ๋Š” ํ•ด๋‹น ๋ด‡์ด ์ฐธ์—ฌํ•˜๊ณ  ์žˆ๋Š” ์„œ๋ฒ„์˜ ๋ชจ๋“  ์ฑ„ํŒ… ์ฑ„๋„์˜ ์ฑ„ํŒ… ๋ฐ์ดํ„ฐ๊ฐ€ ์„œ๋ฒ„๋กœ ์ „์†ก๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ๋”ฐ๋ผ์„œ ํŠน์ • ๋ฌธ์ž๊ฐ€ ํฌํ•จ๋˜์—ˆ์„๊ฒฝ์šฐ ์‚ฌ์šฉ์ž๊ฐ€ ๋ด‡์—๊ฒŒ ๋ช…๋ น์„ ๋ณด๋‚ธ๋‹ค๊ณ  ์•”๋ฌต์ ์ธ ์•ฝ์†์„ ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. (์ด๋ ‡๊ฒŒ ํ•˜์ง€ ์•Š์œผ๋ฉด ์‚ฌ์šฉ์ž๊ฐ€ ์›ํ•˜์ง€ ์•Š๋Š” ์ƒํ™ฉ์—์„œ๋„ ๋ช…๋ น์ด ์‹คํ–‰๋˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.)

๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฑ„ํŒ… ์ฑ„๋„์— ์ž…๋ ฅํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์ „๋ถ€ ์„œ๋ฒ„๋กœ ๋“ค์–ด์˜ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
๋”ฐ๋ผ์„œ ์ •ํ™•ํ•œ ๋ด‡ ์‚ฌ์šฉ์„ ์œ„ํ•ด์„œ๋Š” ํŠน์ • ๋ฌธ๊ตฌ์™€ ๋™๋ฐ˜ํ•œ ๋ช…๋ น์–ด ํ˜•ํƒœ ๋กœ ์ž…๋ ฅํ•˜๋„๋ก ํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” !(๋Š๋‚Œํ‘œ) ๋ฅผ ์ด์šฉํ•ด์„œ !<๋ช…๋ น์–ด> ๋‚ด์šฉ ํ˜•์‹์œผ๋กœ ์ž…๋ ฅํ•ด๋ณด๋„๋ก ํ•˜์ž.

2. ์›ํ•˜๋Š” ๋‚ด์šฉ๋งŒ ์ถ”์ถœํ•˜๊ธฐ

์ด์ œ ์–ด๋–ค ๋ช…๋ น์–ด๋ฅผ ์ž…๋ ฅํ–ˆ์„ ๋•Œ ๋ด‡์ด ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ์ง€ ๊ทœ์œจ์„ ์ •ํ–ˆ์œผ๋‹ˆ ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ์ถ”์ถœํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด๋ณด์ž.

์šฐ์„  ๋””์Šค์ฝ”๋“œ ์ฑ„ํŒ…์ฐฝ์ด ์ž…๋ ฅ๋œ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›๋Š” Router๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

client.on('message', message => {
	console.log(message);
});

๋”ฐ๋ผ์„œ ๋ฉ”์‹œ์ง€๋ฅผ ๋กœ๊ทธ ์ฐ์–ด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฝ˜์†”์— ๋ฐ์ดํ„ฐ๊ฐ€ ์ถœ๋ ฅ๋œ๋‹ค.

<ref *2> Message {
  channel: <ref *1> TextChannel {
    type: 'text',
    deleted: false,
    id: '868357534543650857',
    name: 'ํ…Œ์ŠคํŠธ-์ฑ„๋„-2',
    rawPosition: 5,
    parentID: '593065339835908126',
    permissionOverwrites: Collection(0) [Map] {},
    topic: null,
    nsfw: false,
    lastMessageID: '868360251148210246',
    rateLimitPerUser: 0,
    lastPinTimestamp: null,

	----- ์ƒ๋žต ------

    messages: MessageManager {
      cacheType: [class LimitedCollection extends Collection],
      cache: [LimitedCollection [Map]],
      channel: [Circular *1]
    },
    _typing: Map(1) { '316391444765999105' => [Object] }
  },
  deleted: false,
  id: '868360251148210246',
  type: 'DEFAULT',
  system: false,
  content: '๋ฉ”์‹œ์ง€ ํ…Œ์ŠคํŠธ ์ž…๋‹ˆ๋‹ค.',
  author: User {
    id: '316391444765999105',
    system: null,
    locale: null,
    flags: UserFlags { bitfield: 0 },
    username: '์ด์ค€์„',
    bot: false,
    discriminator: '0496',
    avatar: '5eb09554c6c341adfcdbabe0ae8eb091',
    lastMessageID: '868360251148210246',
    lastMessageChannelID: '868357534543650857'
  }
}

(๋„ˆ๋ฌด ๊ธธ์–ด์„œ ์ƒ๋žตํ–ˆ์Šต๋‹ˆ๋‹ค.)

๋Œ€๋žต ์‚ดํŽด๋ณด๋ฉด ์ฑ„๋„์ด๋ฆ„(name), ๋ฉ”์‹œ์ง€ ๋‚ด์šฉ(content), ๋ฉ”์‹œ์ง€ ์ž‘์„ฑ์ž(author.username) ๋“ฑ ๋‹ค์–‘ํ•œ ๋‚ด์šฉ๋“ค์„ ์ „๋‹ฌํ•ด์ค€๋‹ค. ์šฐ๋ฆฌ๋Š” ์šฐ์„  ๋ฉ”์‹œ์ง€ ๋‚ด์šฉ (content) ์— ์ง‘์ค‘ํ•˜๋„๋ก ํ•˜์ž.
(ํŠน์ • ์ฑ„๋„์—์„œ๋งŒ ํ•ด๋‹น ๋ด‡์ด ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›๊ณ  ์‹ถ๋‹ค๋ฉด ์ฑ„๋„ ์ด๋ฆ„์œผ๋กœ ์กฐ๊ฑด์„ ๊ฑธ์–ด์„œ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฐ›๋„๋ก ํ•˜์…”๋„ ๋ฉ๋‹ˆ๋‹ค.)

์šฐ์„  ๋ฐ์ดํ„ฐ ํ˜•์‹์ด JSON ์ด๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ์ดํ„ฐ ์ ‘๊ทผ ๋ฐฉ์‹์€ .(dot) ์„ ์ด์šฉํ•ด์„œ ํƒ€๊ณ  ํƒ€๊ณ  ๋“ค์–ด๊ฐ€๋ฉด ๋œ๋‹ค.

๊ทธ๋Ÿผ ๋“ค์–ด์˜จ ๋ฉ”์‹œ์ง€์—์„œ ์ฑ„ํŒ… ๋‚ด์šฉ๋งŒ ์ถ”์ถœํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

client.on('message', message => {
	console.log(message.content);
});

๊ทธ๋Ÿผ !์ „ํˆฌ์ •๋ณด <๋‚ด์šฉ> ์œผ๋กœ ์ž…๋ ฅํ–ˆ์„ ๋•Œ ๋‚ด์šฉ์— ์ ํžŒ ๋ฐ์ดํ„ฐ๋งŒ ๋ฐ›์„ ์ˆ˜ ์žˆ๋„๋ก ์กฐ๊ธˆ ๋” ์ฝ”๋“œ๋ฅผ ์ง„ํ–‰ํ•ด๋ณด์ž. (!์ „ํˆฌ์ •๋ณด ๋Š” ์ œ๊ฐ€ ์ž„์˜๋กœ ์ •ํ•œ ๋ช…๋ น์–ด๋ผ ์›ํ•˜์‹œ๋Š” ๋ฌธ๊ตฌ๋กœ ์ˆ˜์ • ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค!!)

ํŒŒ์‹ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ์—ฌ๋Ÿฌ๊ฐ€์ง€๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ๋Š”๋ฐ ๋‹ค์–‘ํ•˜๊ฒŒ ์†Œ๊ฐœํ•ด๋ณด๋„๋ก ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

client.on('message', message => {
	const content = message.content;
	// substring์„ ์ด์šฉํ•ด์„œ ๋งจ ์•ž์ž๋ฆฌ๊ฐ€ !์ „ํˆฌ์ •๋ณด์ธ์ง€ ํ™•์ธ
	// (๋ช…๋ น์–ด๋Š” ํ•ญ์ƒ ๋งจ ์•ž์— ์“ด๋‹ค๋Š” ๊ฐ•์ œ์„ฑ ํ•„์š”!)
	console.log(content.substring(0, 5) === '!์ „ํˆฌ์ •๋ณด');		// ๋ช…๋ น์–ด ํ™•์ธ
	console.log(content.substring(6, content.length).trim());	// <๋‚ด์šฉ>
	// ๋ฉ”์‹œ์ง€ ๋‚ด์šฉ์ค‘ ๋žœ๋คํ•œ ์–ด๋Š ๋ถ€๋ถ„์— !์ „ํˆฌ์ •๋ณด๊ฐ€ ๋“ค์–ด์žˆ๋Š” ๊ฒฝ์šฐ
	console.log(content.includes("!์ „ํˆฌ์ •๋ณด"));			// ๋ช…๋ น์–ด ํ™•์ธ
	console.log(content.replace("!์ „ํˆฌ์ •๋ณด", "").trim());		// <๋‚ด์šฉ>
	// !์ „ํˆฌ์ •๋ณด <๋‚ด์šฉ>์œผ๋กœ ๋ช…๋ น์–ด์™€ ๋‚ด์šฉ ์‚ฌ์ด์— ๊ณต๋ฐฑ์„ ์ด์šฉํ•ด์„œ 
  	// split์„ ํ•œ ํ›„ ๊ณต๋ฐฑ์„ ๊ธฐ์ค€์œผ๋กœ ์•ž ์ชฝ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ !์ „ํˆฌ์ •๋ณด์ธ์ง€ ํ™•์ธ
	// (์‚ฌ์šฉ์ž๊ฐ€ ๊ณต๋ฐฑ์„ ์ž…๋ ฅํ•˜์ง€ ์•Š์œผ๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Œ)
	console.log(content.split(" ")[0] === '!์ „ํˆฌ์ •๋ณด');		// ๋ช…๋ น์–ด ํ™•์ธ
	console.log(content.split(" ")[1]);				// ๋‚ด์šฉ
});
true
์ €๊ฑฐ์ข€
true
์ €๊ฑฐ์ข€
true
์ €๊ฑฐ์ข€

์ด์ •๋„๋ฅผ ์ด์šฉํ•ด์„œ ๋ช…๋ น์–ด๊ฐ€ ์ž…๋ ฅ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. (trim์€ ๋ฌธ์ž์˜ ์–‘ ์˜†์˜ ๊ณต๋ฐฑ์„ ์ œ๊ฑฐํ•ด์ค๋‹ˆ๋‹ค.)

3. ๋กœ์ŠคํŠธ์•„ํฌ ์ „ํˆฌ์ •๋ณด์‹ค ๋ฐ์ดํ„ฐ ์กฐํšŒ ๋ฐฉ๋ฒ• (Axios)

์œ„์—์„œ ์ฑ„ํŒ… ๋ฉ”์‹œ์ง€๊ฐ€ ๋“ค์–ด์˜ค๋ฉด ๋ช…๋ น์–ด๋ฅผ ํ™•์ธํ•˜๋Š” ๋ฐฉ๋ฒ•๊ณผ <๋‚ด์šฉ>์„ ์ถ”์ถœํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ณด์•˜๋Š”๋ฐ ์ด์ œ๋Š” ๋“ค์–ด์˜จ ๋‚ด์šฉ์„ ๊ฐ€์ง€๊ณ  ์ง์ ‘ ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์‚ดํŽด๋ณด๋„๋ก ํ•  ์˜ˆ์ •์ด๋‹ค.

๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒํ•˜๊ธฐ ์œ„ํ•ด์„œ axios ๋ผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•  ์˜ˆ์ •์ธ๋ฐ ๊ฐ„๋žตํ•˜๊ฒŒ ์„ค๋ช…ํ•˜์ž๋ฉด Node.js ์—์„œ HTTP ์š”์ฒญ์„ ๋ณด๋‹ค ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ๋งŒ๋“ค์–ด์ง„ Promise ๊ธฐ๋ฐ˜์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์—ฌ๊ธฐ๋ฅผ ๋ˆŒ๋Ÿฌ์„œ ํ™•์ธํ•˜์„ธ์š”.

๋”ฐ๋ผ์„œ ํ•ด๋‹น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๋‹ค์šด๋ฐ›์„ ์˜ˆ์ •์ธ๋ฐ ๋‹ค์šด ๋ฐ›๋Š” ๋ช…๋ น์–ด๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

npm install axios --save

๋‹ค์šด์„ ๋ฐ›์•˜์œผ๋ฉด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ฝ”๋“œ์— ์ถ”๊ฐ€ํ•ด์ฃผ์–ด์•ผ ํ•˜๋Š”๋ฐ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

const axios = require('axios');

์—ฌํƒœ๊นŒ์ง€ ์ ์€ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

// require the discord.js module
const Discord = require('discord.js');
const axios = require('axios');		// ์ถ”๊ฐ€

// create a new Discord client
const client = new Discord.Client();

// when the client is ready, run this code
// this event will only trigger one time after logging in
client.once('ready', () => {
	console.log('Ready !!!');
});

// login to Discord with your app's token
client.login('your-login-token');

client.on('message', message => {
	const content = message.content;
	// substring์„ ์ด์šฉํ•ด์„œ ๋งจ ์•ž์ž๋ฆฌ๊ฐ€ !์ „ํˆฌ์ •๋ณด์ธ์ง€ ํ™•์ธ
	// (๋ช…๋ น์–ด๋Š” ํ•ญ์ƒ ๋งจ ์•ž์— ์“ด๋‹ค๋Š” ๊ฐ•์ œ์„ฑ ํ•„์š”!)
	console.log(content.substring(0, 5) === '!์ „ํˆฌ์ •๋ณด');
	console.log(content.substring(6, content.length).trim());
	// ๋ฉ”์‹œ์ง€ ๋‚ด์šฉ์ค‘ ๋žœ๋คํ•œ ์–ด๋Š ๋ถ€๋ถ„์— !์ „ํˆฌ์ •๋ณด๊ฐ€ ๋“ค์–ด์žˆ๋Š” ๊ฒฝ์šฐ
	console.log(content.includes("!์ „ํˆฌ์ •๋ณด"));
	console.log(content.replace("!์ „ํˆฌ์ •๋ณด", "").trim());
	// !์ „ํˆฌ์ •๋ณด <๋‚ด์šฉ>์œผ๋กœ ๋ช…๋ น์–ด์™€ ๋‚ด์šฉ ์‚ฌ์ด์— ๊ณต๋ฐฑ์„ ์ด์šฉํ•ด์„œ split(์ชผ๊ฐœ๋‹ค) ํ•œ ํ›„
	// ๊ณต๋ฐฑ์„ ๊ธฐ์ค€์œผ๋กœ ์•ž ์ชฝ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ !์ „ํˆฌ์ •๋ณด์ธ์ง€ ํ™•์ธ
	// (์‚ฌ์šฉ์ž๊ฐ€ ๊ณต๋ฐฑ์„ ์ž…๋ ฅํ•˜์ง€ ์•Š์œผ๋ฉด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Œ)
	console.log(content.split(" ")[0] === '!์ „ํˆฌ์ •๋ณด');
	console.log(content.split(" ")[1]);
});

๋”ฐ๋ผ์„œ axios ๋ฅผ ์ด์šฉํ•ด์„œ HTTP ์š”์ฒญ์„ ๋ณด๋‚ด๋ณด๋„๋ก ํ•˜์ž. ๊ทธ ์ „์— ๋กœ์ŠคํŠธ์•„ํฌ ์ „ํˆฌ์ •๋ณด์‹ค URL ์„ ํ™•์ธํ•ด์•ผ ๋˜๋Š”๋ฐ ์ œ๊ฐ€ ์•Œ์•„์˜จ ๊ฒฐ๊ณผ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

https://lostark.game.onstove.com/Profile/Character

๊ทธ๋ฆฌ๊ณ  ๊ฒ€์ƒ‰์„ ํ•ด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด URL์ด ๋ณ€๊ฒฝ๋œ๋‹ค.

https://lostark.game.onstove.com/Profile/Character/์ €๊ฑฐ์ข€

๋”ฐ๋ผ์„œ ์—ฌ๊ธฐ์„œ ์–ป์„ ์ˆ˜ ์žˆ๋Š” ํžŒํŠธ๋Š” Character/ ๋‹ค์Œ์— ๋‹‰๋„ค์ž„์„ ์ ์–ด์ฃผ๋ฉด ์ „ํˆฌ์ •๋ณด๊ฐ€ ๊ฒ€์ƒ‰๋œ๋‹ค๋Š” ๊ฒƒ์„ ์–ป์„ ์ˆ˜ ์žˆ๋‹ค.

ํ•ด๋‹น ๋ถ€๋ถ„์„ ์ฝ”๋“œ๋กœ ์ ์–ด๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

client.on('message', async(message) => {
	const content = message.content;
	const contentArr = content.split(" ");
	const command = contentArr[0];
	const nickname = contentArr[1];
	if(command === '!์ „ํˆฌ์ •๋ณด'){
		const encodeNickName = encodeURI(nickname);
		const html = await axios.get(`https://lostark.game.onstove.com/Profile/Character/${encodeNickName}`);
		console.log(html);
	}
});

์„ค๋ช…ํ•˜๊ธฐ์— ์•ž์„œ axios๋Š” Promise ๊ธฐ๋ฐ˜์˜ ๋น„๋™๊ธฐ ํ†ต์‹ ์„ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ๊ฐ€ ๋๋‚  ๋•Œ ๊นŒ์ง€ ๋Œ€๊ธฐํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— async ์™€ await๋ฅผ ์ด์šฉํ•ด์„œ ๋™๊ธฐ ๋ฐฉ์‹๊ณผ ๊ฐ™์ด ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์—ฌ๊ธฐ๋ฅผ ํด๋ฆญ!

  1. ์šฐ์„  ์ฑ„ํŒ… ๋ฉ”์‹œ์ง€๋ฅผ ๊ณต๋ฐฑ์„ ๊ธฐ์ค€์œผ๋กœ ๋‚˜๋ˆˆ๋‹ค.

  2. ๋งจ ์•ž์—๋Š” ๋ช…๋ น์–ด, ๊ทธ ๋‹ค์Œ์€ ๋‚ด์šฉ์ด๋ผ๊ณ  ๊ฐ€์ •ํ•œ๋‹ค.

  3. command๊ฐ€ !์ „ํˆฌ์ •๋ณด ์ผ ๊ฒฝ์šฐ ๋‹ค์Œ์„ ์‹คํ–‰

  4. nickname ์ •๋ณด๊ฐ€ ํ•œ๊ธ€์ผ ๊ฒฝ์šฐ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋œฌ๋‹ค.

    • UnhandledPromiseRejectionWarning: TypeError [ERR_UNESCAPED_CHARACTERS]: Request path contains unescaped characters
    • ๋”ฐ๋ผ์„œ encodeURI ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•ด์„œ ํ•œ๊ธ€์„ UTF-8 ๋กœ ์ธ์ฝ”๋”ฉํ•ด์„œ ์ „๋‹ฌํ•ด์ค„ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค. (์ž์„ธํ•œ ๋‚ด์šฉ์€ ์—ฌ๊ธฐ ์ฐธ๊ณ )
  5. axios ์—์„œ get ๋ฐฉ์‹์„ ์ด์šฉํ•ด์„œ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.

  6. ๋กœ์ŠคํŠธ์•„ํฌ ์ „ํˆฌ์ •๋ณด์‹ค html ํƒœ๊ทธ ์ „์ฒด๊ฐ€ ์ฝ˜์†”์ฐฝ์— ์ฐํžˆ๊ฒŒ ๋œ๋‹ค.

๋Œ€์ถฉ ์ด๋Ÿฐ์‹์œผ๋กœ ์ฐํžˆ๊ฒŒ ๋œ๋‹ค.

๋”ฐ๋ผ์„œ html ๋กœ ๋“ค์–ด์˜จ ๋ฐ์ดํ„ฐ ์ค‘์—์„œ ์šฐ๋ฆฌ์—๊ฒŒ ํ•„์š”ํ•œ ์ •๋ณด๋งŒ์„ ํŒŒ์‹ฑํ•ด๋ณด๋„๋ก ํ•˜์ž.

4. HTML ๋ฐ์ดํ„ฐ ํŒŒ์‹ฑํ•˜๊ธฐ (Cheerio)

๋‹คํ–‰์ด๋„ ์•„์ฃผ ๊ณ ๋ง™๊ฒŒ html ๋ฐ์ดํ„ฐ๋ฅผ ํŒŒ์‹ฑํ•  ์ˆ˜ ์žˆ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์กด์žฌํ•œ๋‹ค.
์šฐ๋ฆฌ๋Š” ํ•ด๋‹น ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ด์šฉํ•  ์˜ˆ์ •์ธ๋ฐ ๋‹ค์šด๋กœ๋“œ๋Š” ๋‹ค์Œ ๋ช…๋ น์–ด๋กœ ํ•  ์ˆ˜ ์žˆ๋‹ค.

npm install cheerio --save

Cheerio ๊ฐ€ html ๋งŒ ํŒŒ์‹ฑํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ Markup ํ˜•ํƒœ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ํŒŒ์‹ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ Markup ์–ธ์–ด์ค‘์—์„œ ๋‹จ์—ฐ ์ œ์ผ ์œ ๋ช…ํ•œ ์–ธ์–ด๊ฐ€ html ์ด์–ด์„œ html ํŒŒ์‹ฑ์— ๋งŽ์ด ์‚ฌ์šฉ๋˜๊ณ  ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋  ๊ฒƒ ๊ฐ™๋‹ค.

๋˜ํ•œ Cheerio ์˜ ์„ค๋ช…์„ ๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ ํ˜€์žˆ๋‹ค.

Cheerio's selector implementation is nearly identical to jQuery's, so the API is very similar.

Cheerio ์˜ ์…€๋ ‰ํ„ฐ ๊ตฌํ˜„์ด JQuery ์™€ ์œ ์‚ฌํ•˜๋‹ค๊ณ  ๋‚˜์™€์žˆ๋‹ค. ๋”ฐ๋ผ์„œ JQuery ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฌธ๋ฒ•๋“ค๋กœ ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์„ ๋•Œ $๋กœ ๋ฐ›๋Š” ์ด์œ ๋„ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ด์œ ์—์„œ ์ธ ๊ฒƒ ๊ฐ™๋‹ค.

์ข€ ๋” ์ž์„ธํ•œ ๋‚ด์šฉ์„ ๋ณด๊ณ  ์‹ถ์œผ์‹œ๋ฉด ์—ฌ๊ธฐ๋ฅผ ์ฐธ๊ณ ํ•˜์‹œ๋ฉด ๋  ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๊ฐ์„คํ•˜๊ณ , ๋ณธ๊ฒฉ์ ์œผ๋กœ ํŒŒ์‹ฑ์„ ์‹œ์ž‘ํ•ด๋ณด๋ฉด axios ๋กœ ๋ฐ›์€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€์ˆ˜์— ์ €์žฅํ•˜๊ณ  Cheerio ์— ์ „๋‹ฌํ•ด์ฃผ๋„๋ก ํ•˜์ž.

client.on('message', async(message) => {
	const content = message.content;
	const contentArr = content.split(" ");
	const command = contentArr[0];
	const nickname = contentArr[1];
	if(command === '!์ „ํˆฌ์ •๋ณด'){
		const encodeNickName = encodeURI(nickname);
		const html = await axios.get(`https://lostark.game.onstove.com/Profile/Character/${encodeNickName}`);
		const $ = cheerio.load(html.data);
	}
});

axios๋กœ ํ˜ธ์ถœํ•œ ๋ฆฌํ„ด๊ฐ’์— ์šฐ๋ฆฌ๊ฐ€ ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋Š” data ๋ผ๋Š” ํ‚ค ๋ฐ‘์— ๋“ค์–ด์žˆ๋‹ค. (https://jsonplaceholder.typicode.com/users ํ…Œ์ŠคํŠธ ์š”์ฒญํ•œ ๊ฒฐ๊ณผ๊ฐ’)

{
  status: 200,
  statusText: 'OK',
 
    ----- ์ƒ๋žต ------
  
  },
  data: [
    {
      id: 1,
      name: 'Leanne Graham',
      username: 'Bret',
      email: 'Sincere@april.biz',
      address: [Object],
      phone: '1-770-736-8031 x56442',
      website: 'hildegard.org',
      company: [Object]
    },
    {
      id: 2,
      name: 'Ervin Howell',
      username: 'Antonette',
      email: 'Shanna@melissa.tv',
      address: [Object],
      phone: '010-692-6593 x09125',
      website: 'anastasia.net',
      company: [Object]
    }
  ]
}

๋”ฐ๋ผ์„œ cheerio ์— ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•ด์ค„๋•Œ html.data ๋กœ ์ „๋‹ฌํ•ด์ฃผ๋ฉด ๋œ๋‹ค.
(html.data ์— ์ „ํˆฌ์ •๋ณด์‹ค html ์ •๋ณด๊ฐ€ ๋“ค์–ด์žˆ์„ ๊ฒƒ์ด๋‹ค.)

์ด์ œ๋ถ€ํ„ฐ ์ง„์งœ ์ง„์งœ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ํŒŒ์‹ฑํ•ด์•ผ๋˜๋Š”๋ฐ ๋จผ์ € element ์˜ ๊ตฌ์กฐ๋ฅผ ์‚ดํŽด๋ณด์•„์•ผ ํ•œ๋‹ค.

๋‹‰๋„ค์ž„์˜ ๊ฒฝ์šฐ span ํƒœ๊ทธ์ธ๋ฐ className์ด profile-character-info__name์ธ ๊ณณ์— ๋“ค์–ด์žˆ๋‹ค. ์™œ ์•Œ์•„์•ผํ•˜๋ƒ? Cheerio ์—์„œ ํ•ด๋‹น ๊ฒฝ๋กœ๋กœ ์ฐพ์•„์„œ ๋“ค์–ด๊ฐ€์•ผ์ง€ ์ •๋ณด๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ๋‹ค. ๋ฐ”๋กœ ์ฝ”๋“œ๋กœ ์„ค๋ช…ํ•˜๋„๋ก ํ•˜๊ฒ ๋‹ค.

const encodeNickName = encodeURI(nickName);
const html = await axios.get(`https://lostark.game.onstove.com/Profile/Character/${encodeNickName}`);
const $ = cheerio.load(html.data);
const userName = $("span.profile-character-info__name").text();
console.log(`๋กœ์ŠคํŠธ์•„ํฌ ์ „ํˆฌ์ •๋ณด์‹ค์—์„œ ๊ฐ€์ ธ์˜จ ๋‹‰๋„ค์ž„ ์ด๋ฆ„์€ <${userName}> ์ž…๋‹ˆ๋‹ค.`);
์ถœ๋ ฅ๋‚ด์šฉ : ๋กœ์ŠคํŠธ์•„ํฌ ์ „ํˆฌ์ •๋ณด์‹ค์—์„œ ๊ฐ€์ ธ์˜จ ๋‹‰๋„ค์ž„ ์ด๋ฆ„์€ <์ €๊ฑฐ์ข€> ์ž…๋‹ˆ๋‹ค.
  • cheerio.load(html.data) : cheerio ์— html ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ๋“œํ•œ๋‹ค.
  • $("span.profile-character-info__name").text(); : className ์ด profile-character-info__name ์ธ ํƒœ๊ทธ์˜ innerText ๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.

๊ทธ๋ž˜์„œ ๋ฐ์ดํ„ฐ๊ฐ€ ์–ด๋–ค ํƒœ๊ทธ์— ์ €์žฅ๋˜์–ด์žˆ๋Š”์ง€ ์ •๋„๋Š” ํ™•์ธ์„ ์†์ˆ˜ ํ•ด์•ผ์ง€๋งŒ ์›ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋‹ค.

์œ„์—์„œ text() ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๊ณ  ์žˆ๋Š”๋ฐ ํƒœ๊ทธ๋ฅผ ์‚ดํŽด๋ณด๋ฉด title ์†์„ฑ์—๋„ ๋‹‰๋„ค์ž„ ์ •๋ณด๊ฐ€ ์ €์žฅ๋˜์–ด์žˆ๋Š”๋ฐ ์†์„ฑ ๊ฐ’์„ ์ถ”์ถœํ•  ๋•Œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

const userName = $("span.profile-character-info__name").attr("title");

์ด์™ธ์—๋„ ๊ฐ„๋‹จํ•˜๊ฒŒ ์ง์—…, ๋ ˆ๋ฒจ๋“ค์„ ๊ฐ€์ ธ์˜ค๋Š” ์ฝ”๋“œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

const level = $("span.profile-character-info__lv").text();
const job = $("img.profile-character-info__img").attr("alt");

๋ณธ์ธ์˜ ์ฝ”๋“œ๋Š” ์ฐธ๊ณ ์šฉ์ด์ง€ ์ •๋‹ต์€ ์•„๋‹™๋‹ˆ๋‹ค! ๋” ์ข‹์€ ์ฝ”๋“œ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!

5. Discord์— ์ฑ„ํŒ… ๋ณด๋‚ด๊ธฐ

๋งˆ์ง€๋ง‰์ด๋‹ค.

์ด์ œ ๊ฐ€์ ธ์˜จ ๋ฐ์ดํ„ฐ๋ฅผ ๋””์Šค์ฝ”๋“œ ์ฑ„๋„์— ์ „์†กํ•˜๋Š” ๋ถ€๋ถ„์„ ์•Œ์•„๋ณด๋„๋ก ํ•˜์ž.

์ผ๋‹จ ์ฑ„๋„๋กœ ๋‹ค์‹œ ๋ฉ”์‹œ์ง€๋ฅผ ์ „์†กํ•˜๋Š” ๋ถ€๋ถ„์€ ๊ต‰์žฅํžˆ ์‰ฝ๋‹ค. ์ฝ”๋“œ๋กœ ๋ณด๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค. (๊ณต์‹๋ฌธ์„œ ์ฐธ๊ณ ๋Š” ์—ฌ๊ธฐ์„œ ํ•˜์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.)

const $ = cheerio.load(html.data);
const userName = $("span.profile-character-info__name").attr("title");
const level = $("span.profile-character-info__lv").text();
const job = $("img.profile-character-info__img").attr("alt");
await message.channel.send(`${userName}์˜ ๋ ˆ๋ฒจ์€ ${level}์ด๊ณ  ์ง์—…์€ ${job}์ž…๋‹ˆ๋‹ค.`);

๊ทธ๋Ÿฌ๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ถœ๋ ฅ๋ฉ๋‹ˆ๋‹ค.

๋งˆ์ง€๋ง‰ ์ค„์˜ ์˜๋ฏธ๊ฐ€ message ๊ฐ์ฒด์— ์ €์žฅ๋œ channel ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์ „์†กํ•œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋  ๊ฒƒ ๊ฐ™๋‹ค.

์ฐธ๊ณ ์‚ฌํ•ญ

ํ•˜์ง€๋งŒ ์ด๋ ‡๊ฒŒ ๋ณด๋‚ด๋ฉด ๋ฐ‹๋ฐ‹ํ•ด์„œ ์žฌ๋ฏธ๊ฐ€ ์—†๋‹ค. ๋”ฐ๋ผ์„œ MessageEmbed ๋ฅผ ์ด์šฉํ•ด์„œ ์ข€ ๋” ๋‹ค์ฑ„๋กญ๊ฒŒ ๋ฉ”์‹œ์ง€๋ฅผ ๋ณด๋‚ผ ์ˆ˜ ์žˆ๋Š”๋ฐ ์˜ˆ์‹œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • ํ•ด๋‹น ๋‚ด์šฉ์€ ๊ณต์‹๋งํฌ๋กœ ๋Œ€์ฒดํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๐Ÿ˜…๐Ÿ˜…
  • ๊ทธ๋ฆฌ๊ณ  ๊ฒ€์ƒ‰ํ•˜๋‹ค๊ฐ€ ์ฐพ์€ ์˜ˆ์ œ ์‚ฌ์ดํŠธ์ธ๋ฐ ํ•ด๋‹น ์‚ฌ์ดํŠธ๋„ ์ฐธ๊ณ ํ•˜๊ธฐ ์ข‹์€ ๊ฒƒ ๊ฐ™์•„์„œ ๋งํฌ ๊ฑธ์–ด๋‘๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

์˜ค๋žœ๋งŒ์— ๋ฒจ๋กœ๊ทธ ํฌ์ŠคํŒ…์„ ํ–ˆ๋Š”๋ฐ ์‹œ๊ฐ„์ด ๊ฝค๋‚˜ ๊ฑธ๋ ธ๋‹ค. ํ•˜์ง€๋งŒ ์ด๊ฑฐ๋งŒํผ ์ข‹์€ ํฌ์Šคํด๋ฆฌ์˜ค๋Š” ์—†๋‹ค๊ณ  ์ƒ๊ฐํ•œ๋‹ค. ์•ž์œผ๋กœ๋Š” ์ง„์งœ ์ง„์งœ๋กœ ๊พธ์ค€ํžˆ ํฌ์ŠคํŒ…ํ•ด์„œ ๊ฒŒ์‹œ๊ธ€ ๊ฐœ์ˆ˜์ข€ ๋Š˜๋ฆฌ๊ณ  ์กฐํšŒ์ˆ˜๊ฐ€ ๋†’์€ ๋ฒจ๋กœ๊ทธ๋กœ ๋ฐœ๋‹์›€ ํ•˜๊ณ ์‹ถ๋‹ค.

๊ทธ๋Ÿผ ์˜ค๋Š˜์€ ์ด๋ ‡๊ฒŒ ๋งˆ๋ฌด๋ฆฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ธด ๊ธ€ ์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

profile
ํ˜ธ์ฃผ ์›Œํ™€์ค‘

7๊ฐœ์˜ ๋Œ“๊ธ€

comment-user-thumbnail
2021๋…„ 7์›” 30์ผ

ใ…œใ…œใ…œ์˜คํƒ€๋ฅผ ์ฐพ์•„๋ฒ„๋ ธ๋‹ค..ใ… ใ… 

1๊ฐœ์˜ ๋‹ต๊ธ€
comment-user-thumbnail
2021๋…„ 9์›” 27์ผ

์ข‹์€ ์ •๋ณด๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค~ ๊ทธ๋Ÿฐ๋ฐ ์ˆ˜์ง‘ํ˜• ํฌ์ธํŠธ ์ •๋ณด๋Š” ๊ฐ€์ ธ์˜ค๋Š” ๋ฐฉ๋ฒ•์ด ๋”ฐ๋กœ์žˆ๋‚˜์š”?

1๊ฐœ์˜ ๋‹ต๊ธ€