1번째 줄:
1번째 줄:
== 개요 ==
== 개요 ==
+
장고 채널 기능의 활용을 위해 간단한 채팅 앱 만들어보기.
−
== 기초준비 ==
+
=== 사전 준비 ===
+
{| class="wikitable"
+
!과정
+
!설명
+
!방법
+
|-
+
|장고
+
|장고가 설치되어 있다고 가정한다.
+
|[http://id8436.iptime.org:2786/mediawiki/index.php/1.%20%EC%9E%A5%EA%B3%A0%20%EA%B0%9C%EC%9A%94 1. 장고 개요]
+
|-
+
|채널 설치
+
|채널이 설치되어 있다고 가정한다.
+
|[http://id8436.iptime.org:2786/mediawiki/index.php/%EC%9E%A5%EA%B3%A0%20channels 장고 channels]
+
|}
+
= 기초준비 =
−
=== 채팅어플 만들기 ===
+
== 채팅어플 만들기 ==
패키지설치부터 진행하기엔 중간점검이 어려워 틀 만들기를 먼저 수행한다.
패키지설치부터 진행하기엔 중간점검이 어려워 틀 만들기를 먼저 수행한다.
{| class="wikitable"
{| class="wikitable"
12번째 줄:
27번째 줄:
|어플리케이션생성
|어플리케이션생성
|채팅을 위한 앱을 생성한다.
|채팅을 위한 앱을 생성한다.
−
앱을 생성하고 __init__.py와 views.py를 제외한 모든 것들을 지운다.
|django-admin startapp chat
|django-admin startapp chat
|-
|-
19번째 줄:
33번째 줄:
|<syntaxhighlight lang="python">
|<syntaxhighlight lang="python">
INSTALLED_APPS = [
INSTALLED_APPS = [
+
...
'chat',
'chat',
</syntaxhighlight>
</syntaxhighlight>
|-
|-
−
|URL매핑
+
|폴더 정리
−
|기본이 되는 urls.py 안에서 해당 앱으로 매핑을 시켜준다.
+
| - 앱을 생성하고 생성된 __init__.py와 views.py를 제외한 모든 것들을 지운다.
−
|path('chat/', include('chat.urls')),
+
(채팅기능만을 위해선 나머지는 필요 없어, 지워도 된다. 근데 그냥 두자.)
+
+
- 탬플릿을 담기 위해 templates/chat 디렉토리를 생성해준다.
+
|
|-
|-
−
|URL매핑2
+
|urls.py 생성 및 매핑
−
|그리고 앱 안의 urls.py 작성.
+
|앱 내에서 사용되는 url을 다루기 위해 urls.py를 생성한다.
−
|<syntaxhighlight lang="python">
+
|urls.py의 내용.<syntaxhighlight lang="python">
−
from dfango.urls import path
+
from django.urls import path
from . import views
from . import views
+
app_name = 'chat' # 보통 앱이름을 써서 url을 구분하지만,
+
+
urlpatterns = [
+
path('', views.index, name='index'),
+
]
+
</syntaxhighlight>기초 urls.py 수정<syntaxhighlight lang="python">
+
from django.contrib import admin
+
from django.urls import include, path
urlpatterns = [
urlpatterns = [
−
path('/', views.index, name='index'),
+
path('chat/', include('chat.urls')),
+
path('admin/', admin.site.urls),
]
]
</syntaxhighlight>
</syntaxhighlight>
−
|-
+
|}
−
|view 작성
+
여기까지 하고 runserver 후 제대로 작동하는지 확인하자.
−
|
−
|<syntaxhighlight lang="python">
−
from django.shortcuts import render
−
def index(request):
+
== 채팅룸 구현 ==
−
return render(request, 'chat/index.html', {})
+
=== 채팅 인덱스 구현 ===
−
</syntaxhighlight>
+
들어갈 채팅룸을 입력하는 공간.
+
{| class="wikitable"
+
!과정
+
!설명
+
!방법
|-
|-
−
|탬플릿 준비
+
|채팅 인덱스 탬플릿 작성
−
|앱 하위에 templates>chat 디렉터리까지 만든다.
+
|채팅룸을 입력하기 위한 인덱스 탬플릿.
−
index.html 이라는 이름으로 만들자.
+
templates/chat/index.html로 작성하자.
|<syntaxhighlight lang="html+django">
|<syntaxhighlight lang="html+django">
−
<!-- chat/templates/chat/index.html -->
<!DOCTYPE html>
<!DOCTYPE html>
<html>
<html>
57번째 줄:
84번째 줄:
<title>Chat Rooms</title>
<title>Chat Rooms</title>
</head>
</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">
−
<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>
<script>
document.querySelector('#room-name-input').focus();
document.querySelector('#room-name-input').focus();
78번째 줄:
104번째 줄:
</body>
</body>
</html>
</html>
+
</syntaxhighlight>
+
|-
+
|뷰 작성
+
|
+
|<syntaxhighlight lang="python">
+
from django.shortcuts import render
+
+
def index(request):
+
return render(request, 'chat/index.html')
</syntaxhighlight>
</syntaxhighlight>
|}
|}
−
여기까지 하고 runserver 후 제대로 작동하는지 확인하자.
=== 채팅룸 구현 ===
=== 채팅룸 구현 ===
87번째 줄:
121번째 줄:
!설명
!설명
!방법
!방법
−
|-
−
|URL매핑
−
|앱 내의 urls.py에 추가.
−
|<syntaxhighlight lang="python">
−
urlpatterns = [
−
...
−
path('<str:room_name>/', views.room, name='room'),
−
...
−
]
−
</syntaxhighlight>
−
|-
−
|뷰 작성
−
|room 뷰 작성
−
mark_safe와 json을 가져온다.
−
|<syntaxhighlight lang="python">
−
from django.shortcuts import render
−
from django.utils.safestring import mark_safe
−
import json
−
−
def room(request, room_name):
−
return render(request, 'chat/room.html', {
−
'room_name_json': mark_safe(json.dumps(room_name))
−
})
−
</syntaxhighlight>
|-
|-
|탬플릿 작성
|탬플릿 작성
−
|/char/room.html을 만든다.
+
|templates/chat/room.html
|<syntaxhighlight lang="html+django">
|<syntaxhighlight lang="html+django">
<!DOCTYPE html>
<!DOCTYPE html>
121번째 줄:
131번째 줄:
<title>Chat Room</title>
<title>Chat Room</title>
</head>
</head>
−
<body>
<body>
−
<textarea id="chat-log" cols="100" rows="20"></textarea><br/>
+
<textarea id="chat-log" cols="100" rows="20"></textarea><br>
−
<input id="chat-message-input" type="text" size="100"/><br/>
+
<input id="chat-message-input" type="text" size="100"><br>
−
<input id="chat-message-submit" type="button" value="Send"/>
+
<input id="chat-message-submit" type="button" value="Send">
−
</body>
+
{{ room_name|json_script:"room-name" }}
+
<script>
+
const roomName = JSON.parse(document.getElementById('room-name').textContent);
−
<script>
+
const chatSocket = new WebSocket(
−
var roomName = {{ room_name_json }};
+
'ws://'
+
+ window.location.host
+
+ '/ws/chat/'
+
+ roomName
+
+ '/'
+
);
−
var chatSocket = new WebSocket(
+
chatSocket.onmessage = function(e) {
−
'ws://' + window.location.host +
+
const data = JSON.parse(e.data);
−
'/ws/chat/' + roomName + '/');
+
document.querySelector('#chat-log').value += (data.message + '\n');
+
};
−
chatSocket.onmessage = function(e) {
+
chatSocket.onclose = function(e) {
−
var data = JSON.parse(e.data);
+
console.error('Chat socket closed unexpectedly');
−
var message = data['message'];
+
};
−
document.querySelector('#chat-log').value += (message + '\n');
−
};
−
chatSocket.onclose = function(e) {
+
document.querySelector('#chat-message-input').focus();
−
console.error('Chat socket closed unexpectedly');
+
document.querySelector('#chat-message-input').onkeyup = function(e) {
−
};
+
if (e.keyCode === 13) { // enter, return
−
+
document.querySelector('#chat-message-submit').click();
−
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>
+
document.querySelector('#chat-message-submit').onclick = function(e) {
+
const messageInputDom = document.querySelector('#chat-message-input');
+
const message = messageInputDom.value;
+
chatSocket.send(JSON.stringify({
+
'message': message
+
}));
+
messageInputDom.value = '';
+
};
+
</script>
+
</body>
</html>
</html>
+
</syntaxhighlight>
+
|-
+
|뷰 작성
+
|뷰에 다음 함수를 추가한다.
+
|<syntaxhighlight lang="python">
+
def room(request, room_name):
+
return render(request, 'chat/room.html', {
+
'room_name': room_name
+
})
+
</syntaxhighlight>
+
|-
+
|URL
+
|다음의 내용을 urls.py에 추가한다.
+
|<syntaxhighlight lang="python">
+
path('<str:room_name>/', views.room, name='room'),
</syntaxhighlight>
</syntaxhighlight>
|}
|}
−
=== 참가자 구현 ===
=== 참가자 구현 ===
{| class="wikitable"
{| class="wikitable"
179번째 줄:
203번째 줄:
클라이언트로부터 메시지를 받아서 그대로 전달.
클라이언트로부터 메시지를 받아서 그대로 전달.
|<syntaxhighlight lang="python">
|<syntaxhighlight lang="python">
+
import json
from channels.generic.websocket import WebsocketConsumer
from channels.generic.websocket import WebsocketConsumer
−
import json
class ChatConsumer(WebsocketConsumer):
class ChatConsumer(WebsocketConsumer):
228번째 줄:
252번째 줄:
})
})
</syntaxhighlight>
</syntaxhighlight>
−
|}
+
|}여기까지 하고 채팅을 쳐 보면... 채팅이 나와야 정상.
−
여기까지 하고 채팅을 쳐 보면... 채팅이 나와야 정상.
+
=== 체널레이어 구현 ===
=== 체널레이어 구현 ===
255번째 줄:
279번째 줄:
</syntaxhighlight>
</syntaxhighlight>
|}
|}
+
+
== 개요 ==
== 개요 ==
레디스 서버를 이용한 실시간 채팅 구현
레디스 서버를 이용한 실시간 채팅 구현