일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- es6
- java
- 알고리즘
- javascript
- react
- MAC
- django widget
- 알고리즘 문제
- 백준
- form
- AWS
- 파이썬 알고리즘
- 파이썬
- PYTHON
- django ORM
- web
- django rest framework
- js
- API
- 장고
- DRF
- c++
- HTML
- 알고리즘 연습
- 알고리즘 풀이
- Baekjoon
- Git
- Algorithm
- Django
- CSS
- Today
- Total
수학과의 좌충우돌 프로그래밍
[Django] Channels, 비동기적 채팅 구현하기 - WebSocket (1) 본문
비동기적, 즉 실시간 채팅을 구현하기 위해서 WebSocket
을 사용하고자 하였고, django
에서 이를 가능하게 해주는 Channels
라이브러리를 알아보았습니다. 전체적인 내용은 공식 문서 를 참고하였습니다.
WebSocket 이란?
WebSocket
은 프로토콜로서, 실시간으로 데이터를 양방향 통신 할 수 있게 해주는 기술 입니다. Socket
이란 쉽게 생각해서 통신을 위한 통로라고 생각하면 됩니다. 이런 통로를 Web 에도 도입하게 되었는데 기존의 Web이 정보를 어떻게 주고 받는지 생각해보면 socket이 필요한 이유를 알 수 있습니다. Web 환경은 필요한 정보를 HTTP 기반으로 Request/Response
로 연결하여 데이터를 주고 받아 네트워크의 연결을 유지하지 않는 특징을 가지고 있습니다. 그렇기 때문에 연결을 유지하며 실시간으로 데이터를 주고 받기에는 새로운 개념, WebSocket
이 필요하게 된 것 입니다.
WebSocket
이 Client 와 Server 간의 접속을 유지하는 방법은 HandShake 입니다. 이 과정을 통해 연결을 유지함으로서 방화벽과 같은 환경에서도 연결을 유지할 수 있습니다. HandShakIng 과정을 살펴보면 Client는 랜덤한 키 값을 전송하고 Server는 키 값으로 생성된 토큰을 Client에게 Response 보냅니다.
Channels 이란?
Channels
은 django 에서 HTTP 프로토콜이 아닌 다른 프로토콜을 사용할 수 있게 해줍니다. 위에서 알아봤듯이 WebSocket
은 프로토콜의 일종으로서 이를 사용하기 위해서 Channels 을 이용하게 됩니다. 아래 그림을 통해 일반적으로 django 가 Request/Response
을 다루는 경우와, Channels 을 사용했을 때의 차이를 알아볼 수 있습니다.
Channel layer 가 생겨 일반적인 HTTP message 외에도 Websocket message 를 처리할 수 있게 됩니다.
ASGI 란?
ASGI( Asynchronous Server Gateway Interface )
는WSGI(Web Server Gateway Interface)
를 계승한 것으로 비동기 방식으로 실행되며, 웹 서버와 python 응용프로그램 간의 표준 인터페이스를 제공하기 위해 Django Channels와 배포에 사용되는 Daphne 서버에서 정의한 사양으로서 HTTP, HTTP/2 및 WebSocket와 같은 프로토콜을 지원합니다.ASGI
는 비동기 요청인 웹 소켓을 처리하는 이벤트로서 connect, send, receive, disconnect가 있습니다.
실습
Django에 위에서 알아봤던 개념들을 적용시켜보도록 하겠습니다.
-
준비 단계
$ django-admin startproject mysite $ python manage.py startapp chat
앱을 생성했으므로 앱을 추가해주겠습니다.
# mysite/settings.py INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'chat', ]
다음으로는 어느 채팅방으로 입장할 지 결정하는
index.html
입니다.input에 키워드를 입력하고 enter key 또는 button 을 누를 시, 키워드에 해당하는 채팅방으로 접속합니다.
<!-- chat/templates/chat/index.html --> <!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <title>Chat Rooms</title> </head> <body> What chat room would you like to enter?<br/> <input id="room-name-input" type="text" size="100"/><br/> <input id="room-name-submit" type="button" value="Enter"/> <script> document.querySelector('#room-name-input').focus(); document.querySelector('#room-name-input').onkeyup = function(e) { if (e.keyCode === 13) { // enter, return document.querySelector('#room-name-submit').click(); } }; document.querySelector('#room-name-submit').onclick = function(e) { var roomName = document.querySelector('#room-name-input').value; window.location.pathname = '/chat/' + roomName + '/'; }; </script> </body> </html>
index
에 해당하는views
입니다.# chat/view.py from django.shortcuts import render def index(request): return render(request, 'chat/index.html', {})
이에 대해서 매핑해주는
urls
입니다.# chat/urls.py from django.conf.urls import url from . import views urlpatterns = [ url(r'^$', views.index, name='index'), ]
# mysite/urls.py from django.conf.urls import url, include from django.contrib import admin urlpatterns = [ url(r'^chat/', include('chat.urls')), url(r'^admin/', admin.site.urls), ]
-
Channels 라이브러리
우선
channels
라이브러리를 설치하도록 하겠습니다.$ pip install -U channels
다음으로는 라우팅 config 를 작성하겠습니다. 새로운 파일을 만들어주고 아래 내용을 추가해줍니다.
# mysite/routing.py from channels.routing import ProtocolTypeRouter application = ProtocolTypeRouter({ # (http->django views is added by default) })
이제
settings
에 channels 앱을 등록하고, ASGI_APPLICATION 을 추가해줍니다. 여기서 주의할 점은 channels 개발 서버가 다른 앱과 충돌할 수 있으므로, 가장 위에 추가해주도록 합시다.INSTALLED_APPS = [ 'channels', 'chat', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] # Channels ASGI_APPLICATION = 'mysite.routing.application'
이제
channels
은 runserver 명령을 제어하고 django 개발 서버를 Channel 개발 서버로 대처하게 됩니다. 서버를 돌리게 되면 기존과 다른 방식으로 서버가 돌아가는 걸 확인할 수 있습니다.Django version 2.2.3, using settings 'mysite.settings' Starting ASGI/Channels version 2.2.0 development server at http://127.0.0.1:8000/
-
채팅해보기
현재는 채팅방에 접속을 해도 채팅방이 존재하지 않으므로 에러가 발생합니다. 채팅방에 해당하는
room.html
을 만들어봅시다.<!-- chat/templates/chat/room.html --> <!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <title>Chat Room</title> </head> <body> <textarea id="chat-log" cols="100" rows="20"></textarea><br/> <input id="chat-message-input" type="text" size="100"/><br/> <input id="chat-message-submit" type="button" value="Send"/> </body> <script> var roomName = {{ room_name_json }}; var chatSocket = new WebSocket( 'ws://' + window.location.host + '/ws/chat/' + roomName + '/'); chatSocket.onmessage = function(e) { var data = JSON.parse(e.data); var message = data['message']; document.querySelector('#chat-log').value += (message + '\n'); }; chatSocket.onclose = function(e) { console.error('Chat socket closed unexpectedly'); }; document.querySelector('#chat-message-input').focus(); document.querySelector('#chat-message-input').onkeyup = function(e) { if (e.keyCode === 13) { // enter, return document.querySelector('#chat-message-submit').click(); } }; document.querySelector('#chat-message-submit').onclick = function(e) { var messageInputDom = document.querySelector('#chat-message-input'); var message = messageInputDom.value; chatSocket.send(JSON.stringify({ 'message': message })); messageInputDom.value = ''; }; </script> </html>
room
에 해당하는view
입니다.# chat/views.py from django.shortcuts import render from django.utils.safestring import mark_safe import json def index(request): return render(request, 'chat/index.html', {}) def room(request, room_name): return render(request, 'chat/room.html', { 'room_name_json': mark_safe(json.dumps(room_name)) })
이를 매핑해주는
urls
도 추가해줍니다.# chat/urls.py from django.conf.urls import url from . import views urlpatterns = [ url(r'^$', views.index, name='index'), url(r'^(?P<room_name>[^/]+)/$', views.room, name='room'), ]
현재까지 상황을 정리해보면, 원하는 채팅방으로 접속이 가능하고
채팅방에서 메세지를 작성함으로 대화를 할 수 있습니다.
하지만 글을 입력하고 전송을 하면, 에러가 발생합니다. 아직 웹소캣 소비자가 만들어지지 않았기 때문에 에러가 발생하는 것이 정상입니다.
이에 대해서는 다음 포스팅에서 이어서 알아보도록 하겠습니다.
'웹프로그래밍 > Django' 카테고리의 다른 글
[Django] Channels, 비동기적 채팅 구현하기 - WebSocket (3) (2) | 2019.07.11 |
---|---|
[Django] Channels, 비동기적 채팅 구현하기 - WebSocket (2) (4) | 2019.07.11 |
[Django] ChoiceField 사용하기 (4) | 2019.07.06 |
[Django]message framework 알아보기 (0) | 2019.07.06 |
[Django]Date와 Time을 나타내는 template filter (0) | 2019.07.05 |