장고(Django) 이용해서 슬랙(Slack)에 출석체크 챗봇 만들기 [1]

     

다음 글 - 2020/12/06 - [분류 전체보기] - 장고(Django) 이용해서 슬랙(Slack)에 출석체크 챗봇 만들기 [2]

 

 

ㅊㅊ 하면 출첵하는 챗봇 만들기 ㅋㅋㅋ

 

대기업은 잘 안 쓰기는 하는데 중소기업이나 스타트업, 심지어 요즘 대학교 과제모임에서도 슬랙(Slack)을 메신저로 활용하는 곳이 늘어나고 있습니다. 저도 첫 회사에서는 회사 메신저로 마이크로소프트에서 만든 링크(Lync)?를 썼었는데 링크가 뭐냐면 스카이프(Skype)를 기업용으로 만든 Skype for business ㅋㅋㅋ 아무튼 엄청 구린 거였음...

 

근데 슬랙은 딱 봐도 뭔가 스타트업들이 쓸만한 세련된 비주얼을 가지고 있고, 개발자들이 좋아할 만한 어둡어둡함을 갖춘 메신저입니다. 

 

게다가 슬랙은 외부에서 슬랙의 서비스를 이용할 수 있도록 다양한 api를 제공해 줍니다. 시스템의 오류 메시지를 슬랙을 통해서 받거나, 지라(Jira), 컨플루언스(Confluence) 등의 협업 툴과도 연계가 잘 되어있어 많이들 사용하는데, 실제로 슬랙 없이 일 못하는 사람들도 많습니다. 

 

아.무.튼

 

저도 슬랙을 이런저런 이유로 쓰고 있는데 #출석체크 채널을 만들고 ㅊㅊ 하는 이상한 그룹(?)이 생겨나면서 출석체크를 기록해주는 기능을 만들라는... 요청이 들어왔습니다. 

 

구글링 해보니까 생각보다 많이 나오고 간단(?)해 보여서 후딱 만들어 보았습니다. 

 

 

일단 Django 프로젝트 만들기

 

일단 Pycharm이든 vscode든 Django 프로젝트를 하나 만듭니다. 

 

저는 3.8루 만듬. 굳이 conda 환경 쓸 필요없이 가볍게 virtualenv로 갑시다

 

슬랙 API를 이용하면 특정 목적 서버에 데이터를 보낼 수 있는데, 우리가 만든 장고 서버로 데이터를 전달할 수 있게 하려면 슬랙 -> 장고로 들어오는 요청을 받을 수 있어야 합니다. 근데 슬랙에서 데이터를 전송하려면 장고를 로컬에 뛰워서는 안되고 특정 서버 역할을 할 수 있게 주소를 잡아줘야 합니다. 예를 들어 AWS에 올린다던지요. 물론 로컬에서도 공인 IP를 사용하고 있는 컴퓨터라면 충분히 받을 수 있습니다.

 

가장 먼저 settings.py에 ALLOWED_HOSTS = ['*']를 수정해주고 run옵션에서 0.0.0.0으로 실행시켜봅시다.

 

runserver를 실행할 때 0.0.0.0으로 해야 외부에서 접속 가능!

그리고 장고를 실행시켜 봅시다.

 

위 화면이 뜬다면 정상

 

외부에서 접속 가능한지 테스트하려면 컴퓨터의 공인 IP주소를 입력해서 들어왔을 때 Django가 뜨는지 확인하면 됩니다. 만약 여러분의 컴퓨터가 통신사 모뎀과 랜선으로 직접 연결 되어있다면 그냥 IP주소를 적으면 되고, 중간에 공유기가 있다면 공유기의 공인IP주소를 입력해야 합니다. 물론 공유기 설정에서 포트 포워딩이나 DMZ 기능을 이용해 공유기의 공인 IP로 들어오는 신호를 PC로 흘러들어오게 세팅되어있어야 합니다.

 

이게 무슨 말인지 이해가 안 가시면 "공유기 공인 IP 설정" 구글링 ㄱㄱ

 

제 공인 IP는 저거네요 지울 필요는 없지만,,,

 

이제 59.15.59.XX:8000으로 접속하면 django 메인 페이지를 볼 수 있습니다. 외부에서도 접속이 잘 되는지 보다 확실하게 체크하기 위해서는 스마트폰에서 접속해보면 됩니다.

 

이 화면이 나온다면 정상. 혹시 모르니 스마트폰에서도 접속해보세욤

 

 

 

 

Slack app 만들기

 

이제 외부에서 호출 가능한 django 서버를 만들었으니, 이 서버를 호출할 Slack app을 만들어 봅시다. 테스트를 위해 슬랙 방 하나를 새로 파겠습니다.

 

플러스 누름
앱추가를 누르고 우측 상단에 더보기 클릭

더보기를 누르면 slack 홈페이지로 이동됩니다.

 

구축 (Build) 클릭
갑자기 영어로 바뀜.. ㅎㅎ Your Apps 클릭
드디어 앱을 만듭니다.
대충 이름 만들고 slack workspace 설정~

 

이제 Slack app이 만들어졌습니다. 이제 대화방에 app을 추가하면 되는데, 추가하려고 하면 permissions 설정을 하라고 안내가 나옵니다. 

 

 

좌측에서 OAuth & Permissions
챗봇이니까 채팅을 할 수 있어야 겠죠? chat:write 권한을 설정합니다.
app Home에서 봇 정보를 입력해야 봇이 생깁니다.

 

 

권한을 설정하고 봇을 만들면 Install App to Workspace가 활성화됩니다.

 

Install App to Workspace

 

허용~
앱에  출석체크봇이 추가되었습니다~!

 

 

Event Sub 만들기

 

Pub/Sub이라고 부르는 구조를 많이 들어보셨을 거예요. 한국어로 표현하면 "발행/구독"입니다. 어느 한 지점에서 데이터를 "발행(Publish)"하면 데이터를 가져가는 쪽은 "구독(Subscribe)"을 걸어놓고 데이터를 가져갑니다. 일반적인 서버 클라이언트 구조가 요청(Request)/응답(Response)으로 이루어져 클라이언트에서 서버로 요청을 보내면 서버가 응답을 내려주는 구조입니다. 하지만 Pub/Sub 구조에서는 클라이언트가 원하는 데이터에 대해 구독을 요청하면(Sub을 걸어놓으면) 서버에서 데이터가 발행될 때(Pub 될 때) 자동으로 데이터가 클라이언트로 이동하게 됩니다.

 

슬랙 봇도 비슷한 구조로 되어있는데 슬랙 채널에 이벤트 단위로 구독(Sub)을 걸어놓고 있다가, 특정 이벤트가 발행(Pub)되면 자동으로 구독을 걸어놓은 곳으로 이벤트가 전송됩니다. 즉 챗봇을 만들기 위해서 봇은 슬랙 채널에 "채팅"이 올라오는 조건으로 Sub을 걸어놓습니다. 채널에서 채팅이 올라올 경우 슬랙이 자동적으로 이벤트를 Pub 해서 Sub을 걸어놓은 봇으로 이벤트를 전달해줍니다.

 

봇생성 -> 채팅 이벤트 sub ------ 채팅 발생 -> 채팅 이벤트 pub -> 봇에서 수신

 

 

슬랙 API 메뉴에 Event Subscriptions 메뉴를 누르면 어떤 이벤트를 Sub 할지 정할 수 있습니다.

 

event sub 

 

 

여기서 먼저 Request URL을 확인해야 합니다. 슬랙에서 이벤트가 발생하면 이 Request URL에 적어놓은 주소로 특정 데이터를 전송하게 됩니다. 즉, 채팅에 대한 이벤트를 Sub 하고 있다면, 채팅이 발생했을 때, URL로 채팅 메시지 데이터를 보내게 됩니다. 

 

일단 Request URL에 Django 서버 주소(지금은 localhost)를 입력해 봅니다. 

 

제 컴퓨터 ip주소는 현재 59.15.59.34입니다.

 

Django가 실행 중인 컴퓨터의 ip주소를 입력하면 테스트로 api호출을 한번 해보는데 아마 위와 같은 메시지가 나올 겁니다. 제일 위에 주황색으로 적힌 글 "Your URL didn't respond with the value of the challenge parameter."을 보면  슬랙에서 API를 호출하면 서버 응답으로 challenge라는 parameter가 꼭 필요한 것 같습니다. 

 

그 아래 있는 Our Request항목을 보면 슬랙에서 Django API로 어떤 값을 어떻게 던지는지 나옵니다. 일단 POST방식이고, body안에 type, token, challenge 3가지 parameter를 보낸 것 같습니다. 요청에 challenge parameter가 있는 걸 봐선 이 필드의 값을 그대로 응답 필드에 넣으면 될 것 같습니다.

 

주소 옆에 Retry버튼을 누르면 다시 한번 Django서버로 API를 호출합니다. 

 

Retry 누를 때마다 콘솔에 로그가 찍힙니다.

 

그럼 이제 Django에서 요청을 받아 응답을 주는 API를 만들어 보겠습니다. 일단 프로젝트에 슬랙용 앱을 만들고 POST를 받아줄 API를 만듭니다.

 

python manage.py startapp slackbot

 

아 그리고 restfulAPI를 만들기 위해서 DRF(django-REST-framework) 패키지를 설치해야 합니다.

공식 홈페이지는 아래↓

Home - Django REST framework (django-rest-framework.org)

 

Home - Django REST framework

 

www.django-rest-framework.org

 

패키지 설치는 한 줄이면 됩니다.

pip install djangorestframework

djangorestframework 설치
settings.py INSTALLED_APPS에 'rest_framework' 추가

 

이제 DRF를 이용해 restfulAPI를 받을 준비가 끝났습니다. 상세 API는 아까 만든 app 폴더에 있는 view.py에서 구현합니다.

 

# ~/slackbot/views.py
from rest_framework.views import APIView
from rest_framework.response import Response


# Create your views here.
class Attend(APIView):
    def post(self, request):
        """
        슬랙에서 채팅 이벤트가 있을 때 호출하는 API
        :param request:
        :return:
        """

        # 요청이 어떻게 들어오나 찍어보기
        print(request.body)

        # 그냥 응답 성공으로 줘보기
        return Response(status=200)

 

views.py에 있는 Attend 클래스를 호출하기 위한 url도 생성해줍니다. 

 

# ~/untitled4/urls.py
# untitled4는 프로젝트명 입니다. 

from django.contrib import admin
from django.urls import path
from slackbot import views # 새로만든 앱 'slackbot'에서 views.py를 import합니다.

urlpatterns = [
    path('admin/', admin.site.urls), # 이건 원래 있던거
    path('attend', views.Attend.as_view()),
]

 

각 소스의 경로가 다른 걸 확인해주세요. views.py는 새로 만든 앱인 slackbot폴더에 있고, urls.py는 루트 경로인 untitled4(프로젝트명)에 있습니다. 

 

 

이제 ~/attend URL을 호출할 수 있습니다. 시험 삼아 브라우저에서 호출해보겠습니다.

 

이렇게 나오면 성공

 

아까 views.py에서 class안에 def가 post만 있던 게 기억나시나요? attend는 현재 post 방식의 호출만 받아주고 그 외의 방식을 받아주지 않습니다. 따라서 브라우져에서 단순 url호출인 get방식의 경우 지원하지 않는다고 페이지에 표시가 되네요. Method "GET" not allowed 

 

print로 request의 body를 찍어놨으니 다시 슬랙 API 페이지에서 Retry를 해보겠습니다.

 

Retry

 

URL 주소에 attend를 붙이는 걸 잊지 마세요. attend까지 붙여서 Retry 하게 되면 콘솔 로그에 request의 body가 찍히는 것을 볼 수 있습니다. 이제 들어온 challenge 값을 그대로 응답으로 주도록 해보겠습니다.

 

# ~/slackbot/views.py
from django.shortcuts import render
from rest_framework.views import APIView
from rest_framework.response import Response


# Create your views here.
class Attend(APIView):
    def post(self, request):
        """
        슬랙에서 채팅 이벤트가 있을 때 호출하는 API
        :param request:
        :return:
        """

        # 요청이 어떻게 들어오나 찍어보기
        print(request.body)

        # body에서 challenge 필드만 빼오기
        challenge = request.data.get('challenge')

        # 응답 데이터로 { challenge : challenge } 주기
        return Response(status=200, data=dict(challenge=challenge))

 

이제 Request에 들어오는 challenge값 그대~~~~~로 Response에 넣어줍니다. 

 

다시 한번 Retry!!!

 

Verified

 

이제 테스트를 통과했으니, 실제로 채팅이 발생했을 때 메시지를 서버로 넘겨주도록 해보겠습니다. 

 

URL 적는 화면 아래쪽을 보면 Subscribe to ~~~ 라는 메뉴들이 있습니다. 여기서 Subscribe to events on behalf of users를 선택해서 message.channels라는 이벤트를 추가합니다. 채널에 메시지가 새로 생길 때마다 이벤트를 받도록 설정하는 내용입니다.

 

message.channels를 추가하면 슬랙 채널에 메시지가 생길 때 마다 이벤트가 발생됨

 

아마 이벤트를 등록하고 저장을 누르면 슬랙 앱을 재설치하라고 팝업이 뜰 겁니다. 재설치를 해주고 테스트 삼아서 채널에 채팅을 써봅시다.

 

 

봇을 초대하고 나서 채팅해야 합니다.

 

봇을 슬랙 특정 채널에 초대하는 방법은 초대하고 싶은 채널에 들어가서 @봇 이름으로 태그를 걸면 됩니다. 그럼 슬랙에서 봇을 추가하겠냐고 묻게 되고 예를 누르면 봇이 채널에 들어오게 됩니다. 그 상태에서 아무 메시지나 처봅니다.

 

body가 늘어났다!!!!

ㅎㅇ라고 쳤을 때, 아까 테스트 요청으로 들어왔던 Request body와 다른 데이터가 들어왔습니다. 아마도 메시지 이벤트는 데이터 형태가 조금 다른 모양입니다. "text" 필드가 아마도 채팅 내용인 것 같은데 출력된 값을 보니 인코딩이 뭔가 다른 것 같네요. 따로 값으로 빼와서 출력해보도록 하겠습니다.

 

# ~/slackbot/views.py
from rest_framework.views import APIView
from rest_framework.response import Response


# Create your views here.
class Attend(APIView):
    def post(self, request):
        """
        슬랙에서 채팅 이벤트가 있을 때 호출하는 API
        :param request:
        :return:
        """

        # 요청이 어떻게 들어오나 찍어보기
        print(request.body)

        # body에서 challenge 필드만 빼오기
        challenge = request.data.get('challenge')

        user = request.data.get('event').get('user')
        text = request.data.get('event').get('text')
        print("사용자 :", user, "| 메시지 :", text)


        # 응답 데이터로 { challenge : challenge } 주기
        return Response(status=200, data=dict(challenge=challenge))

ㅎㅇㅎㅇ

text와 user 필드 값을 빼와서 print로 찍었더니 잘 나오네요. 하지만 user는 실제 아이디가 아니라 뭔가 고유한 특정 값으로 찍힙니다. 실제로 슬랙 채널에서 사용하는 제 ID(tkdlek11112)는 아니지만 유저를 구분할 수는 있는 토큰 값으로 생각하시면 됩니다. 

 

 

 

 

마무리

포스팅 하나로 끝내려고 했는데 생각보다 길어졌네요 ㅎㅎㅎㅎ 2편으로 만들어야 겠습니다.

 

여기서는 채널에서 메시지가 생성될 때 슬랙 앱에서 이벤트를 발생시키고, Django 서버로 API를 호출하는 것까지 완성했습니다. 

 

다음 포스팅에서는 특정 유저, 특정 메시지에서 로직이 돌아가도록 만들고, 다시 Django 서버에서 채널로 메시지를 보낼 수 있도록 만들어 보겠습니다.

 

 

 

 

다음 글 - 2020/12/06 - [분류 전체보기] - 장고(Django) 이용해서 슬랙(Slack)에 출석체크 챗봇 만들기 [2]

 

 

 

 

반응형

댓글

Designed by JB FACTORY