Channels는 Django를 확장해 웹소켓과 같이 HTTP가 아닌 프로토콜을 핸들링할 수 있게 돕고 비동기적인 처리를 가능하게 해주는 ASGI의 구현체로, 장고를 이용한 실시간 채팅 구현 등에 활용할 수 있습니다. 이 글은 채널즈의 공식 문서를 최대한 원어를 살려 번역한 글입니다. 다소 의역하거나 생략한 부분이 있을 수 있음을 너그러이 양해해주시고, 잘못을 자유롭게 지적해주시면 감사하겠습니다.
튜토리얼에서 우리는 다음의 두 페이지로 구성된 간단한 채팅 서버를 만들어보려 합니다.
채팅방 화면에서 웹소켓을 이용해 장고 서버와 통신하고 게시된 메시지를 받아올 것입니다.
아래 내용은 장고 프로젝트를 구성하는데 필요한 기본 지식을 갖추고 있다고 가정하고 진행됩니다. 그렇지 않다면 장고 튜토리얼을 먼저 완료하고 돌아와주세요.
먼저 장고를 설치해주세요. 쉘 프롬프트에서 다음 커맨드를 실행해 장고가 설치되었는지 확인하고 그 버전을 알 수 있습니다.
$ python3 -m django --version
아직 채널즈를 설치하지 않았다면, 다음 커맨드로 설치합니다.
$ python -m pip install -U channels
채널즈가 설치되었는지 확인하려면 다음 커맨드를 실행하세요:
$ python3 -c 'import channels; print(channels.__version__)'
이 튜토리얼은 파이썬 3.6 이상과 장고 2.2 이상에서 호환되는 채널즈 3.0을 기준으로 작성되었습니다.
이 튜토리얼에서는 또한 Redis
를 설치하고 실행하기 위해 Docker
를 사용합니다. 레디스는 채널 레이어의 저장소로 활용됩니다. 도커를 공식 홈페이지의 설치법에 따라 설치해주세요.
주의사항
기본적인 장고
runserver
를 실행하는 데에는 도커가 필요하지는 않지만, 뒷 파트에서 사용할 채널즈의 기능들을 위해선Redis
가 필요합니다. 우리는Redis
를 실행하기 위한 가장 쉬운 방법으로 도커를 권장합니다.
아직 장고 프로젝트가 없다면 새 프로젝트를 만들어야 합니다.
커맨드 라인에서 프로젝트를 생성할 디렉토리로 이동한 후 다음 커맨드를 실행하세요:
$ django-admin startproject mysite
이 커맨드는 다음과 같이 구성된 mysite
디렉토리를 생성합니다:
mysite/
manage.py
mysite/
__init__.py
asgi.py
settings.py
urls.py
wsgi.py
주의사항
장고 2.2는 asgi.py 파일을 생성하지 않습니다. 하지만 일 분도 걸리지 않아 만들 수 있으니 걱정 마세요.
chat
앱을 만들어 그 안에서 채팅서버 코드를 작성할 겁니다.
manage.py
가 있는 디렉토리에서 다음 커맨드를 실행하세요:
$ python3 manage.py startapp chat
이 커맨드는 다음과 같이 구성된 chat
디렉토리를 생성할 것입니다:
chat/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
views.py
이 튜토리얼의 목표를 달성하기 위해선 chat/views.py
와 chat/__init__.py
만 필요하니 다른 파일들은 지우셔도 좋습니다.
불필요한 파일을 지우고 나면 chat
디렉토리는 다음과 같이 구성될 것입니다:
chat/
__init__.py
views.py
이제 프로젝트에 chat
앱이 생성됐음을 알려야 합니다. mysite/settings.py
파일에서 INSTALLED_APPS
값에 다음과 같이 'chat'을 추가하세요.
# mysite/settings.py
INSTALLED_APPS = [
'chat',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
이제 입장할 채팅방 이름을 입력할 인덱스 뷰를 만들어 봅시다.
chat
디렉토리 안에 templates
폴더를 만들고, 그 안에 chat
폴더를 생성합니다. 그리고 그 안에 인덱스 뷰의 템플릿을 담을 index.html
파일을 만듭니다.
그러면 이제 chat
디렉토리는 다음과 같습니다:
chat/
__init__.py
templates/
chat/
index.html
views.py
이제 다음 코드를 chat/templates/chat/index.html
에 붙여 넣으세요:
<!-- 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>
그 다음 인덱스 뷰를 위한 뷰 함수를 만듭니다. 아래 코드를 chat/views.py
에 붙여 넣으세요:
# chat/views.py
from django.shortcuts import render
def index(request):
return render(request, 'chat/index.html')
뷰를 호출하려면 뷰를 URL에 매핑하기 위해 URLconf
가 필요합니다.
urls.py
라는 이름으로 chat
디렉토리에 파일을 생성하세요. 이제 앱 디렉토리는 다음과 같습니다:
chat/
__init__.py
templates/
chat/
index.html
urls.py
views.py
chat/urls.py
에 다음 코드를 붙여넣으세요.
# chat/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
이제 최상단(root) URLconf가 방금 만든 chat.urls
모듈을 가리키게 해야합니다. mysite/urls.py
에서 django.conf.urls.include
를 import
하고, urlpatterns
리스트에 include()
를 추가하세요. 그 결과는 다음과 같습니다:
# mysite/urls.py
from django.conf.urls import include
from django.urls import path
from django.contrib import admin
urlpatterns = [
path('chat/', include('chat.urls')),
path('admin/', admin.site.urls),
]
이제 인덱스 뷰가 작동하는지 확인해 봅시다. 다음 커맨드를 실행하세요:
$ python3 manage.py runserver
그러면 커맨드라인에 다음과 같은 문구가 출력됩니다:
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
October 21, 2020 - 18:49:39
Django version 3.1.2, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
브라우저를 열고 http://127.0.0.1:8000/chat/
주소로 이동하면 “What chat room would you like to enter?”와 함께 입장할 채팅방 이름을 입력하는 입력창이 나타납니다.
방 이름으로 "lobby"를 입력하고 엔터를 눌러보세요. 그러면 http://127.0.0.1:8000/chat/lobby/
주소의 room 뷰로 이동하지만, 아직 우리가 room 뷰를 작성하지 않았기 때문에 “Page not found” 에러 페이지가 나타날 겁니다.
터미널로 돌아가 Control-C
를 눌러 서버를 중지하세요.
지금까지는 평범한 장고 앱 하나를 만들었습니다; 아직 채널즈 라이브러리는 전혀 사용하지 않았습니다. 이제 사용해 볼 때입니다.
먼저 채널즈를 위한 루트 라우팅 설정을 만듭니다. 채널즈 라우팅 설정은 채널즈 서버가 HTTP 요청을 받았을 때 어떤 코드를 실행할지 결정한다는 점에서, 장고의 URLConf
와 유사한 ASGI 애플리케이션입니다.
다음 코드를 mysite/asgi.py
파일에 붙여 넣어 시작해봅시다.
# mysite/asgi.py
import os
from channels.routing import ProtocolTypeRouter
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
application = ProtocolTypeRouter({
"http": get_asgi_application(),
# Just HTTP for now. (We can add other protocols later.)
})
주의사항
장고 2.2에서는 ASGI를 자체 지원하지 않기 때문에 채널즈의 대체 시스템을 활용해야 합니다.
mysite/asgi.py
파일을 다음과 같이 만드세요:# mysite/asgi.py import os import django from channels.http import AsgiHandler from channels.routing import ProtocolTypeRouter os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings') django.setup() application = ProtocolTypeRouter({ "http": AsgiHandler(), # 지금은 HTTP만 등록합니다. (나중에 다른 프로토콜들도 등록할 거예요!) })
이제mysite/settings.py
파일에서 INSTALLED_APPS
리스트에 다음과 같이 채널즈를 추가합니다:
# mysite/settings.py
INSTALLED_APPS = [
'channels',
'chat',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
또한 채널즈가 루트 라우팅 설정을 가리키도록 해야합니다. mysite/settings.py
파일 끝에 다음 코드를 붙여 넣으세요:
# mysite/settings.py
# Channels
ASGI_APPLICATION = 'mysite.asgi.application'
채널즈가 INSTALLED_APPS
리스트에 추가되면, 기존의 장고 개발 서버를 채널즈 개발 서버로 대체하도록 runserver
커맨드를 제어합니다.
주의사항
다른 서드파티 앱이
runserver
커맨드를 오버로드하거나 대체할 경우에 주의하세요. 채널즈는 별도의runserver
커맨드를 제공하기 때문에 해당 앱과 충돌할 수 있습니다. 그러한 대표적인 사례로whitenoise
앱의runserver_nostatic
이 있습니다. 이를 해결하기 위해선 채널즈를INSTALLED_APPS
설정값의 최상단으로 이동하거나, 충돌하는 다른 앱을 제거해주세요.
채널즈 개발 서버가 올바르게 작동하는지 확인하기 위해 다음 커맨드를 실행합니다:
$ python3 manage.py runserver
그러면 다음과 같은 문구가 커맨드 라인에 출력됩니다:
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
October 21, 2020 - 19:08:48
Django version 3.1.2, using settings 'mysite.settings'
Starting ASGI/Channels version 3.0.0 development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Note
적용되지 않은 DB 마이그레이션에 대한 warning
은 무시하세요. 이 튜토리얼에서는 DB를 사용하지 않습니다.
다음과 같은 문구로 시작하는 라인에 주목하세요: Starting ASGI/Channels version 3.0.0 development server at http://127.0.0.1:8000/
. 이 줄은 채널즈 개발 서버가 장고 개발 서버를 대체하고 있음을 알려줍니다.
이제 다시 브라우저를 열고 http://127.0.0.1:8000/chat/
로 가면 아까 만든 초기 화면이 여전히 잘 나타날 겁니다. 터미널로 돌아가 Control-C
로 서버를 중지하세요.
이 튜토리얼은 튜토리얼 2: 채팅 서버 만들기로 이어집니다.
안녕하세요. 좋은 글 남겨주셔서 감사합니다.
작성해주신 튜토리얼대로 했는데 서버를 실행하면 튜토리얼에 있는 결과의 예시처럼 나오지 않습니다.
채널즈 개발 서버가 장고 개발 서버를 대체하지 못한건가요?