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> |
| |} | | |} |
| + | |
| + | |
| == 개요 == | | == 개요 == |
| 레디스 서버를 이용한 실시간 채팅 구현 | | 레디스 서버를 이용한 실시간 채팅 구현 |