회사에서 업무의 효율성을 위해 고객들의 문의를 Slack으로 알려줘 쉽게 확인할 수 있으면 좋겠다는 얘기가 나왔고, 내가 맡게 되었다.
이러한 방법을 webhook
이라고 한다.
우선 webhook이 뭔지부터 알아야겠다;;
Webhook이란?
Webhook(웹훅)이란, 서버에서 어떠한 작업이 수행 되었을 때 해당 작업이 수행되었음을 HTTP POST
로 알리는 개념을 말한다. Webhook을 구현한 웹 애플리케이션은, 특정 작업이 수행될 때 URL
에 대해 POST
방식으로 요청을 생성한다. 이 때, url(콜백 url)은 웹 애플리케이션을 사용하는 유저가 자신의 URL
을 지정할 수 있다.
유저의 입장에서는 지속적으로 데이터를 폴링(polling)하여 대부분의 경우 불필요한 정보를 받는 대신, webhook을 활용하여 중요한 이벤트가 발생했을 때에만 정보를 수신할 수 있다. 이를 활용하여 유저의 커스텀 기능이나 다른 애플리케이션과 통합하거나 기능을 확장할 수 있다.
슬랙 웹 훅 URL 발급받기
- 웹훅으로 뿌려줄 채널을 선택한다.
- 톱니바퀴 모양의 설정버튼 -> Add an app -> Incoming WebHooks 를 선택해서 추가
- 추가하고 나면 웹훅 URL이 생성되는데 기억해 놓자.
https://hooks.slack.com/services/~~~~ 이런방식으로 생성된다
터미널에서 테스트 하는 방법
curl -s -d "payload={\"text\":\"hi\"} "https://hooks.slack.com/services/~~~"
보내는 방식
-
payload 파라미터는 JSON string으로 보내야 한다.
-
method는 POST로 전송해야 한다.
var payload = {
// bot 이름을 바꿀 수 있다.
"username" : "",
// bot 아이콘을 바꿀 수 있다.
"icon_url" : "",
// bot 아이콘을 이모티콘으로 사용할 수 있다. 위의 icon_url 중 하나만 사용하면 된다.
"icon_emoji" : "",
// 본문 내용을 입력한다. (필수)
"text" : "",
// 채널을 override 시킬 수 있다.
"channel" : ""
}
Slack message template
username : 웹훅 앱 이름 변경
icon_emoji : 웹훅 앱 아이콘 변경 -> 이모지에 있는 것들
애초에 웹훅을 만들 때 바로 페이지 하단에서
Customize Name, Customize Icon으로 바꿀 수 있음.
\"username\": \"webhookbot\",
\"icon_emoji\": \":ghost:\"}"
아래 데이터를 어떻게 넣냐에 따라 슬랙 메세지가 바뀐다.
공식문서 를 참고해 마음에 드는 템플릿을 고르면 된다.
{
"attachments": [
{
"fallback": "New ticket from Andrea Lee - Ticket #1943: Can't reset my password - https://groove.hq/path/to/ticket/1943",
"pretext": "New ticket from Andrea Lee",
"title": "Ticket #1943: Can't reset my password",
"title_link": "https://groove.hq/path/to/ticket/1943",
"text": "Help! I tried to reset my password but nothing happened!",
"color": "#7CD197"
}
]
};
포스트맨 테스트 결과 날라온 메세지
오오.. 재밌어
메세지가 날라오니깐 위 코드로 개발하면 되겠다고 생각했다.
하지만 나는 api를 호출해 본 적이 없다.
post방식으로 값을 전달하라그래서 '이렇게 해도 되겠지' 하고 단순하게
type, url, data에 위 코드를 넣고, contentType:json으로 해서 ajax 방식으로 값을 보내봤다.
그런데 자꾸 아래와 같은 에러가 발생했다.
해당 오류에 대해서는 따로 정리를 했다.
그래서 구글에 api 호출
을 검색했더니 전혀 다른 방식으로 호출을 하고 있었다.
검색해서 찾은 api 호출 방식대로 동작 시켰더니 맨 처음 postman으로 값을 보냈을 때 처럼 메시지가 성공적으로 왔다..!
원래 계획은 Q&A가 접수되면 스케줄러가 돌다가 접수된 Q&A를 읽어서 메시지를 보내주는 것이었지만, 해당 레거시 프로젝트가 JSP페이지에 모든 코드가 들어가있는 초 울트라 레거시 프로젝트
라서 스프링 배치는 사용해보지 못했다(아쉽 ㅠㅠ).
그래서 방식 또한 문의하기 버튼을 눌러 DB에 insert 될 때, slack api를 호출해서 insert한 데이터를 전달하기로 했다.
insert한 데이터를 select해서 api 호출 코드에 필요한 데이터만 넣어준다.
private static String sendPost(ArrayList qnaUserList) {
//qnaUserList{content,reg_dt,reg_id,MAX(TO_NUMBER(SEQ))}
String slackContent = qnaUserList.get(0).getProperty("CONTENT","");
String slackSeq = qnaUserList.get(0).getProperty("MAX(TO_NUMBER(SEQ))","");
String slackRegId = qnaUserList.get(0).getProperty("REG_ID","");
String input = null;
StringBuffer outResult = new StringBuffer();
try {
//URL 클래스 생성
URL url = new URL("발급받은 api url");
//url클래스의 openConnection()메소드로 HttpURLConnection 접속객체를 생성해
// 주어진 URL에 접속
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setDoOutput(true); //쓰기모드 지정, DoInput은 읽기모드
con.setRequestMethod("POST"); //POST 방식으로 요청한다. default=GET
//요청 헤더 정의
con.setRequestProperty("Content-Type", "application/json");
con.setRequestProperty("Accept-Charset", "UTF-8");
//타임 아웃 정의
con.setConnectTimeout(10000);
con.setReadTimeout(10000);
//slack에 요청 할 데이터
String param ="{\n" +
" \"attachments\": [\n" +
" {\n" +
" \"fallback\": \"문의가 접수되었습니다.\",\n" +
" \"pretext\": \""+slackRegId+"님의 문의가 접수되었습니다.\",\n" +
" \"title\": \""+slackContent+"\",\n" +
" \"title_link\": \"https:해당사이트주소? seq="+slackSeq+"\",\n" +
" \"text\": \"답변을 작성해 주세요.\",\n" +
" \"color\": \"#7CD197\"\n" +
" }\n" +
" ]\n" +
"}";
//새로운 OuputStream에 요청할 OutputStream을 넣는다.
OutputStream os = con.getOutputStream();
//write 메소드로 작성된 파라미터 정보를 byte 단위로 인코딩해 요청
os.write(param.getBytes("UTF-8"));
//스트림 버퍼 비우기
os.flush();
if(con.getResponseCode() == HttpURLConnection.HTTP_OK) {
//응답받은 메시지를 버퍼를 생성해 읽어들이고, "UTF-8"로 디코딩해서 읽음.
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
//한 라인씩 출력
while ((input = in.readLine()) != null) {
outResult.append(input);
}
}else{
System.out.println(con.getResponseMessage());
}
//연결종료
con.disconnect();
}catch(Exception e){
e.printStackTrace();
}
return outResult.toString();
}
자바가 1.6버전이라 try with resources도 사용하지 못했다 ㅠ
실제 슬랙방에서는 좀 더 다듬어서 배포를 해였다. 프로필 사진은 나문희 선생님으로~~
결과
테스트를 누르면 해당 글의 시퀀스를 태워보냈기 때문에 바로 Q&A글을 확인할 수 있다.
댓글