안녕하세요, 1년 2개월만에 슬랙봇 만들기 2편을 씁니다.
1편 : 슬랙봇을 만들어봅시다
오랜만에 1편을 다시 읽으니, 쉽게 쓴다고 썼는데 복잡해 보였습니다. 그래서 이번 글에서는 좀 더 낮은 문턱으로 첫 세팅부터 활용까지 적어보려고 합니다. 그리고 제가 아는 범위 내에서 구조에 대해서도 설명을 하려고 합니다. 글의 순서는 아래와 같습니다.
1. 준비 : 슬랙봇 초간단 세팅
1-1. 개발을 위한 에디터 설치
1-2. 슬랙에서 봇 생성 및 Token 받기
1-3. Slack bot library 설치
1-4. 코드 작성 및 실행
2. 활용 : 슬랙봇이 현재 날씨 표시하게 하기
2-1. 날씨정보 제공 서비스 가입 및 API Key 받기
2-2. 날씨를 알고싶은 위치의 좌표 알아내기
2-3. API 요청, 응답을 처리하기 위한 모듈 설치
3. 추가정보 : API와 JSON
3-1. API를 통해 얻은 json 데이터 처리하기
3-2. 다른 API 사용해보기
유의할 점
- 이 글은 Mac환경에서 만드는 것을 설명합니다.
- 이 글을 따라서 만든 슬랙봇은 사용자의 컴퓨터가 켜져있고 슬랙봇이 실행되어있는 경우에만 동작합니다. 그렇지 않고 언제든 동작하게 하기 위해서는 1편의 호스팅 및 git을 통한 배포를 따라해야합니다.
- 저는 개발자가 아닙니다. 용어설명이나 표현이 정확하지 않을 수 있습니다.
처음으로, 코딩을 쉽게 할 수 있도록 에디터를 설치합니다. 아래 사이트에서 OS X용으로 다운받아서 설치하시면 됩니다. 에디터를 사용하는 일은 나중이니, 여기까지만 하고 다음 단계로 넘어갑니다.
슬랙에서 봇을 만들기 위해서는 token을 발급받아야 합니다. 일단 따라 해보시죠. 아래 경로를 따라가시면 슬랙 봇을 생성할 수 있습니다. 웹브라우저에 URL을 직접 입력하거나, 슬랙 앱에서 찾아갈 수도 있습니다.
a. Slack 어플리케이션에서의 경로 : 좌측상단 username 클릭 > Administration > Manage Apps > Custom Integrations > Bots > Add Configuration
b. 웹브라우저에서의 경로 주소 : https://슬랙이름.slack.com/apps/manage/custom-integrations
위에서 Add Configuration을 하면 새로운 봇이 생성됩니다. 표시되는 내용 중에 API token이 있는데, 이를 어딘가에 기록해둡니다. 'xoxb-....'로 시작되는 긴 내용이 token입니다.
이번엔 slack에서 제공하는 bot library를 설치합니다. Mac에서 Terminal을 실행합니다. Terminal은 파인더의 텍스트 버전이라고 생각하시면 됩니다(너무 성의없는 설명 죄송합니다).
Terminal 실행 경로 : Finder > Application > Utilities > Terminal
이제 Terminal을 통해 데스크탑에 example이라는 폴더를 만들고, 해당 폴더로 이동하는 명령어를 입력합니다. 아래의 내용에서 한 줄 적고 엔터, 한 줄 적고 엔터 하시면 됩니다.
mkdir ~/desktop/example
cd ~/desktop/example
그리고 Slack bot library를 설치하는 명령어를 입력합니다. 아래의 내용을 입력하고 엔터를 칩니다.
npm install @slack/client --save
* 혹시 npm이 설치되어있지 않다고 하면, https://www.npmjs.com/get-npm 여기에 가서 다운받아 설치하세요.
이제 준비가 완료되었습니다. 코드를 작성하도록 하죠! 전 언제나 Copy&Paste로 시작하는 것을 좋아합니다. 1-1 에서 설치한 에디터(sublimetext)를 실행하고, 빈 창에 아래의 내용을 붙여넣습니다. 그리고 아래의 빨간색 텍스트, 'xoxb...'로 시작되는 부분은 1-2 에서 생성한 봇의 token으로 대체해주세요.
// 슬랙봇 초기화하기
const { RTMClient } = require('@slack/client');
const token = process.env.SLACK_TOKEN || 'xoxb-190626573414-500782710453-jVbhdun3GVdfcOJdW4z00000';
const rtm = new RTMClient(token);
rtm.start();
// 슬랙봇이 모든 메시지를 받도록 하기
rtm.on('message', (message) => {
var text = message.text
//슬랙봇이 받은 메시지에 '천재'가 포함되어있다면, '감사'라는 답글을 쓰도록 하기
if (text.includes("천재") ) {rtm.sendMessage("감사", message.channel);}
else if(text.includes("바보")){rtm.sendMessage("반사", message.channel);}
});
그리고 나서 저장을 하면 되는데요, Command + S 를 눌러서 저장하기 팝업이 뜨면, 데스크탑의 example폴더에, 파일명은 app.js로 해서 저장합니다. 저장하고 나면 텍스트들에 컬러가 붙었죠? sublimetext가 js라는 확장자를 보고 javascript syntax로 인식한 것입니다.
이제 준비가 끝났습니다! Terminal에서 아래의 내용을 입력하고 엔터를 치면, 슬랙봇이 실행됩니다.
node ~/desktop/example/app.js
슬랙봇이 동작하는지 슬랙에서 확인해볼까요? 슬랙 앱에서, Direct message에서 우리가 만든 봇을 선택합니다. 그리고 '천재'라고 입력하면, 슬랙봇이 '감사'라는 메시지를 보냅니다.
이제 나만의 슬랙봇이 만들어졌습니다. 그리고 슬랙봇에게 특정단어를 이야기하면, 코드에 작성된 내용으로 응답을 할 수 있게 되었습니다. 이제 슬랙봇에게 '날씨'라고 이야기하면, 현재의 날씨정보를 어딘가에서 받아와 응답하도록 만들어 보겠습니다. 여기서 우리는 API를 사용할겁니다. API는 application programming interface의 약자인데, 무엇인지 아직 모르신다면 대충 이렇게 이해하시면 됩니다.
날씨정보를 제공하는 서비스의 API를 통해 '신사동'이라는 지역의 '현재날씨' 정보를 요청하면, 그 서비스에서는 요청한 내용에 맞는 정보를 나에게 보내줍니다.
먼저 '날씨정보를 API로 제공하는 서비스'를 찾아야 합니다. 저는 Darksky를 사용하기로 했습니다. 웹브라우저를 통해 아래의 사이트에 가서 가입을 합니다.
가입을 하고나면 Your Account라는 페이지가 뜨는데, 그 내용 중, Your Secret Key라는 부분에서 Key 값을 복사해 어딘가 저장해둡니다. 6280c9d740000000029d0957e1ff807e 이런 식으로 생긴 값입니다.
위의 설명에서는 '신사동'이라는 지역을 말했지만, darksky에서 특정 지역의 날씨를 알고싶으면, 특정 지역을 주소 명칭이 아닌 GPS 좌표 값으로 요청해야합니다. 이건 구글맵이나 다른 웹서비스를 찾아서 알아내시면 됩니다. 제가 사용할 '가로수길 입구'의 GPS좌표는 '37.518252, 127.023549' 입니다.
nodejs에서 간편하게 API를 요청하고 응답을 처리하기 위해서는 request라는 모듈을 설치해야합니다. Terminal로 돌아가 아래와 같이 입력하고 엔터를 칩니다. 혹시 명령 프롬프트가 표시된 상태가 아니라면(뭔가 입력해도 반응이 없고 왼쪽에 $와 같은 표시가 안뜬다면) 슬랙봇 앱이 실행된 상태이니, Control + C를 통해 종료시키고 입력을 하면 됩니다.
npm install request
이제 코드를 작성할 때가 되었습니다. 에디터를 열고 app.js의 내용을 모두 지우고, 아래의 내용을 Copy&Paste합니다. 보라색 텍스트 부분이 새롭게 추가된 내용이고, 빨간색 내용은 위에서 준비한 GPS좌표, 날씨서비스 API key, 슬랙봇 token 값들로, 여러분들의 값을 입력하면 됩니다. 그리고 파일을 저장합니다.
// 위치값과 API key 정의하기
const lat = 37.518252;
const long = 127.023549;
const DARK_API_KEY = "6280c9d740000000029d0957e1ff807e";
const request = require('request');
// API를 통해 날씨정보를 받아오는 기능 정의하기
function weatherFN(callback) {
request(`https://api.darksky.net/forecast/${DARK_API_KEY}/${lat},${long}?lang=ko&units=si`, { json: true }, (err, res, body) => {
if (err) { return console.log(err); }
var w1 = body.currently.summary
var w2 = body.currently.temperature + "°"
var w3 = body.currently.humidity * 100 + "%"
var weatherValue = "날씨 : " + w1 + "\n기온 : " + w2 + "\n습도 : " + w3
callback (weatherValue);
});
};
// 슬랙봇 초기화하기
const { RTMClient } = require('@slack/client');
const token = process.env.SLACK_TOKEN || 'xoxb-190626573414-500782710453-jVbhdun3GVdfcOJdW4z00000';
const rtm = new RTMClient(token);
rtm.start();
// 슬랙봇이 모든 메시지를 받도록 하기
rtm.on('message', (message) => {
var text = message.text
//슬랙봇이 받은 메시지에 '천재'가 포함되어있다면, '감사'라는 답글을 쓰도록 하기
if (text.includes("천재") ) {rtm.sendMessage("감사", message.channel);}
else if(text.includes("바보")){rtm.sendMessage("반사", message.channel);}
else if(text.includes("날씨")){
weatherFN(function(body) {rtm.sendMessage(body, message.channel);})
}
});
대략 설명을 드리면, 슬랙봇이 '날씨'라는 메시지를 받으면 weatherFN이라는 기능을 실행하고, 해당 기능이 실행된 결과값을 응답 메시지로 보냅니다. 그리고 weatherFN에서는 darksky API로 GPS좌표와 인증을 위한 API key를 요청하고, darksky가 준 결과를 받아 원하는 정보만 정제를 합니다(여기서는 현재 날씨, 현재 기온, 현재 습도). 그리고 정제한 정보를 실행의 결과값으로 돌려줍니다.
이제 완성되었습니다! 다시 Terminal로 돌아가, 작성한 코드를 실행시킵니다.
node app.js
그리고 슬랙앱을 열고, 슬랙봇에게 '날씨'라고 말해보세요. 아래와 같은 응답을 할겁니다.
날씨 : 약간 흐림
기온 : -2.02°
습도 : 0.63
다른 것들은 건들지 않고, weatherFN과 같은 API처리 부분만 살짝 수정하면, 여러분은 다양한 open API를 통해 원하는 정보를 얻어올 수 있습니다. 입력된 내용을 다른 언어로 번역한다거나, 현재의 대기질 수치와 같은 정보들 말이죠. 그리고 많은 경우 open API들을 통한 결과는 JSON이나 xml형태로 받게 됩니다. 이것들은 데이터를 저장하는 형식이라고 보시면 됩니다. 요즘에는 JSON이 더 많이 쓰이고, 대부분의 open API들도 JSON을 지원합니다. 그럼 weatherFN에서 Darksky API를 통해 받은 날씨 데이터는 어떤 형태로 되어있을까요?
{
"latitude":37.518252,
"longitude":127.023549,
"timezone":"Asia/Seoul",
"currently":{
"time":1544604165,
"summary":"맑음",
"icon":"clear-night",
"precipIntensity":0,
"precipProbability":0,
"temperature":-0.23,
"apparentTemperature":-3.55,
"dewPoint":-18.04,
"humidity":0.25,
"pressure":1029.45,
"windSpeed":2.75,
"windGust":5.26,
"windBearing":319,
"cloudCover":0,
"uvIndex":0,
"visibility":15.71,
"ozone":313.26
},
"hourly":{...}
...
}
위의 내용은 weatherFN에서 요청한 내용의 결과 값의 첫 부분만 적은 것입니다. '...'으로 생략된 부분에 훨씬 더 많은 내용이 있습니다. 궁금하시면 아래의 url에서 변수로 처리된 부분에 실제 값을 입력하고 웹브라우저 주소창에 넣으시면 됩니다. ${lat} 부분을 37.518252 로 대체하는 식으로 말이죠.
https://api.darksky.net/forecast/${DARK_API_KEY}/${lat},${long}?lang=ko&units=si
위의 내용을 보면 결과값은 온도, 날씨, 습도 외에도 다양한 수치들을 제공합니다. 슬랙봇에 추가하고 싶은 내용이 있다면 weatherFN에 추가하면 되죠. 예를 들어 바람세기를 추가하고 싶으면 아래의 빨간색 텍스트 부분을 추가하면 됩니다.
// API를 통해 날씨정보를 받아오는 기능 정의하기
function weatherFN(callback) {
request(`https://api.darksky.net/forecast/${DARK_API_KEY}/${lat},${long}?lang=ko&units=si`, { json: true }, (err, res, body) => {
if (err) { return console.log(err); }
var w1 = body.currently.summary
var w2 = body.currently.temperature + "°"
var w3 = body.currently.humidity * 100 + "%"
var w4 = body.currently.windSpeed
var weatherValue = "날씨 : " + w1 + "\n기온 : " + w2 + "\n습도 : " + w3 + "\n바람 : " + w4
callback (weatherValue);
});
};
json의 구조와 weatherFN부분의 코드를 보면 전체 데이터에서 특정 값을 어떻게 읽어들이는지 이해하기가 쉽습니다. 예를 들어 'temperature'값은 json 전체 데이터에서 'currently' 내에 'temperature'라는 부분의 값으로 되어있습니다. 그리고 코드에서는 w2 = body. currently.temperature로 되어있는데, body는 API로 요청한 결과값이 들어있는 데이터입니다. 그래서 해석을 해보면 'w2라는 변수는 json 전체 데이터에서 currently 데이터 하위의 temperature의 값을 가진다' 이고, json 구조 내에서 하위 데이터는 '.'으로 표시한다는 것을 알 수 있습니다. 그럼 앞으로 다른 json 데이터에서 특정 값을 가져오려고 할 때 어떻게 해야할 지 아시겠죠?
그럼 이번에는 대기질 정보를 제공하는 API를 통해 데이터를 받아와보도록 하겠습니다. AQICN.org라는 곳에서 전세계 대기질 정보를 모아서 제공하는데요, 아래의 사이트에 가서 이메일과 정보를 입력합니다.
https://aqicn.org/data-platform/token/#/
이메일 인증을 하고 나면, 발급된 data token을 확인할 수 있습니다. 이 값을 어딘가 기록해둡니다. 아래의 페이지에 가시면 api를 어떻게 사용해야하는지, 어떤 데이터를 얻을 수 있는지 확인할 수 있습니다.
http://aqicn.org/json-api/doc/
내용을 보다보니, 아래와 같이 lat, lng와 token을 입력하여 요청하면 값을 받을 수 있다고 하네요.
https://api.waqi.info/feed/geo::lat;:lng/?token=:token
그렇다면 소스에 AirQualityFN을 만들어서 슬랙봇에 추가해보도록 하겠습니다. 아래의 내용을 작성한 코드 맨 끝에 추가합니다. 빨간색 텍스트 부분은 아까 받은 data token을 입력합니다.
// API를 통해 대기질정보를 받아오는 기능 정의하기
const AQICN_API_KEY = "d3ef6b001bf986c68fc4b79770f2809d94ea0000"
function airQualityFN(callback) {
request(`https://api.waqi.info/feed/geo:${lat};${long}/?token=${AQICN_API_KEY}`, { json: true }, (err, res, body) => {
if (err) { return console.log(err); }
var a1 = body.data.aqi
var a2 = body.data.iaqi.pm10.v
var a3 = body.data.iaqi.pm25.v
var airQualityValue = "AQI : " + a1 + "\npm10 : " + a2 + "\npm2.5 : " + a3
callback (airQualityValue);
});
};
그리고 슬랙봇이 특정단어를 인식하고 그에 맞는 응답을 하는 부분, '날씨'를 추가했던 부분 아래에 '대기질' 부분을 추가합니다.
else if(text.includes("날씨")){
weatherFN(function(body) {
rtm.sendMessage(body, message.channel);
})
}
else if (text.includes("대기질")) {
airQualityFN(function(body) {
rtm.sendMessage(body, message.channel);
})
}
그리고 저장, 이렇게만 하면 끝입니다!
이제 다시 Terminal로 돌아가서, Control + C를 하여 기존에 동작하던 슬랙봇을 중지시키고, 업데이트한 내용을 슬랙봇을 실행합니다.
node app.js
슬랙에서 슬랙봇에게 "대기질" 이라고 해보세요. 아래와 같은 응답을 할 겁니다.
AQI : 85
pm10 : 50
pm2.5 : 85
이렇게 openAPI를 사용하면 다양한 정보들을 얻을 수 있고, 슬랙을 통해 쉽게 표시할 수 있습니다. 수많은 정보와 서비스들이 openAPI를 통해 제공되니, 관심이 있으시면 여러가지를 시도해보시기 바랍니다!
선물 : json을 가독성 좋게 변환해서 표시해주는 사이트