바뀜

편집 요약 없음
191번째 줄: 191번째 줄:  
</syntaxhighlight>
 
</syntaxhighlight>
 
|}
 
|}
=== 참가자 구현 ===
+
=== 참가자(컨슈머) 구현 ===
 
{| class="wikitable"
 
{| class="wikitable"
 
!과정
 
!과정
261번째 줄: 261번째 줄:     
= 채널 레이어 구현 =
 
= 채널 레이어 구현 =
  −
=== 체널레이어 구현 ===
   
consumer 인스턴스는 자동으로 유일한 channel name을 구성하기 때문에 서로 소통하려면 layer가 필요하다.
 
consumer 인스턴스는 자동으로 유일한 channel name을 구성하기 때문에 서로 소통하려면 layer가 필요하다.
{| class="wikitable"
  −
!과정
  −
!설명
  −
!방법
  −
|-
  −
|패키지 설치
  −
|Channels가 Redis인터페이스를 인식하도록.
  −
|pip install channels_redis
  −
|-
  −
|settings.py 설정
  −
|ASGI 아래 추가하자.
  −
|<syntaxhighlight lang="python">
  −
ASGI_APPLICATION = 'routing.application'  # routing.py 파일의 application을 불러온다.
  −
CHANNEL_LAYERS = {
  −
    'default': {
  −
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
  −
        'CONFIG': {
  −
            "hosts": [('127.0.0.1', 6379)],
  −
        },
  −
    },
  −
}
  −
</syntaxhighlight>
  −
|}
  −
     −
== 개요 ==
+
== 레디스 ==
 
레디스 서버를 이용한 실시간 채팅 구현.
 
레디스 서버를 이용한 실시간 채팅 구현.
   304번째 줄: 278번째 줄:     
네이티브로 설치하는 게 아니라 도커를 사용할 거라면 아래로 넘어가자.
 
네이티브로 설치하는 게 아니라 도커를 사용할 거라면 아래로 넘어가자.
 +
 +
단순 apt-get install redis로 설치해 5 이상의 버전이 되어도 작동한다.
 
|[http://id8436.iptime.org:8080/mediawiki/index.php/Radis Redis] 문서 참조.
 
|[http://id8436.iptime.org:8080/mediawiki/index.php/Radis Redis] 문서 참조.
 
|-
 
|-
369번째 줄: 345번째 줄:  
         await self.send(text_data=json.dumps({"message": message}))
 
         await self.send(text_data=json.dumps({"message": message}))
 
</syntaxhighlight>
 
</syntaxhighlight>
==모델작성==
  −
chat 앱의 models.py 작성<syntaxhighlight lang="python">
  −
from django.db import models
  −
from django.conf import settings
     −
class Room(models.Model):
+
= 서버에 반영 =
    room_name = models.CharField(max_length=100, blank=True)
+
튜토리얼엔 나오지 않지만(공식문서 한참 뒤에 나온다..), 서버에 반영하게 되면 ws/ 경로에 대하여 새로운 설정을 해주어야 한다.(필자는 이걸 몰라서 이것저것 건드리다가 sql 날려먹었다.. 젠장)
    users = models.ManyToManyfield(
  −
        settings.AUTH_USER_MODEL,  # 유저모델과 연결한다.
  −
        blank=True,
  −
        related_name = 'rooms')  # 룸이라는 인덱스 지정.
  −
   
  −
    def __str__(self):
  −
        return self.room_name
  −
       
  −
class Message(models.Model):
  −
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, default=1)
  −
    room = models.ForeignKey(Room, related_name='messages', default=1, on_delete=models.CASCADE)
  −
    content = models.TextField(blank=True)
  −
    created_at = models.DateTimeField(auto_now_add=True)
  −
   
  −
    def __str__(self):
  −
        return self.content
  −
</syntaxhighlight>chat앱의 admin.py 작성<syntaxhighlight lang="python">
  −
from django.contrib import admin
  −
from .models import Room, Message
     −
@admin.register(Room)
+
일반적으로 WSGI 통신을 gunicorn으로 처리한다면, ASGI통신은 daphne으로 처리한다.(daphne에서도 wsgi통신이 가능하지만, 보안상 권장되진 않는다.)
class RoomAdmin(admin.ModelAdmin):
+
{| class="wikitable"
    list_display = ['id', 'room_name']
+
!과정
    list_display_links = ['room_name']
+
!설명
   
+
!방법
@admin.register(Room)
+
|-
class MessageAdmin(admin.ModelAdmin):
+
| 설치
    list_display = ['user', 'room', 'content', 'created_at']
+
|daphne은 channels를 설치하면 자동으로 설치가 된다.
    list_display_links = ['user', 'room', 'content', 'created_at']
+
|
</syntaxhighlight>대화모델 작성(보통 친구모델 등에 작성된다.)(근데 그냥 chat 앱 안에 작성하면 안되나...?)<syntaxhighlight lang="python">
+
|-
...
+
|확인
from .chat.models import Room, Message
+
|실행 명령은 우측과 같다.(asgi의 경로를 지정해주면 된다.)
   −
class chat_connection(models.Model):
+
포트번호는 필요에 따라 지정하면 된다.
    target = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, on_delete=models.CASCADE)  # 상대방
  −
    room = models.ForeignKey(Room, blank=True, on_delete=models.SET_NULL, null=True)  # null옵션이 있어야 하나..? 없어도 될듯, 강의에선 친구모델 위에 더하느라 붙인듯.
  −
    user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, on_delete=models.CASCADE)  # 나
  −
    created_at = models.DateField(auto_now_add=True)
  −
   
  −
    def __str__(self):
  −
        return self.user.username + "채팅"  # 사용자명을 반환한다.
  −
</syntaxhighlight>
  −
==뷰 작성==
  −
<syntaxhighlight lang="python">
  −
from django.shortcuts import render, get_object_or_404
  −
forom django.contrib.auth import get_user_model
  −
from chat.models import *
  −
import logging
     −
def start_chat(request):
+
해당 포트로 접속해보면 사이트와 채팅이 잘 작동한다.
    from_user =  # 본인의 유저모델 가져오기
+
|<code>daphne -b 0.0.0.0 -p 8001 config.asgi:application</code>
    to_user = 유저모델.objects.get(pk=target_request_id)  # 상대의 유저모델 가져오기(이름이나 pk로 불러오게 하면 될듯)
+
|-
   
+
|서버에서 실행
    room_name = "{},{}".format(from_user.username, to_user.username)
+
|서비스를 위해선 백그라운드로 진행해주어야 한다.
    room = Room.objects.create(room_name=room_name)  # 룸을 만든다.
+
실행 여부는 <code>ps -ef | grep daphne</code>로 확인해보자. 실행이 안되면 nohup 떼서 에러메시지 확인.
   
+
|<code>nohup daphne -b 0.0.0.0 -p 8001 config.asgi:application &</code>
def chat_list(request):
+
|-
    user = request.user
+
|웹서버에 반영
    user_profile = user.user_profile
+
|/static을 반영하듯, /ws로 시작되는 주소는 다핀으로 넘겨준다.
    friends = user.friends.all()  # 모든 친구 불러오기...(채팅자 목록이라고 보면 됨.)
+
이후 관련 서비스들을 재시작하면 설정들이 반영된다.
   
  −
    context = {'user_profile': user_profile,
  −
                'friends':friends,
  −
    }
  −
   
  −
    return render(request, 'chat/chat_list.html', context)
  −
   
  −
def room(request, room_id):
  −
    user = request.user
  −
    user_profile = user.profile
  −
    friends = user.friends.all()  # 모든 친구 불러오기...(채팅자 목록이라고 보면 됨.)
  −
   
  −
    room = Room.objects.get(pk=room_id)  # 룸 모델에서 해당 pk에 맞는 룸 불러오기
  −
    friends_uer = room.users.all().exclude(pk=user.id).first()
  −
   
  −
    context = {'current_user': user,
  −
        'user_profile': user_profile,
  −
        'friends':friends,
  −
        'room':room,
  −
        'friends_user': friends_user,
  −
    }
  −
   
  −
    return render(request, 'chat/room.html', context)
     −
   
+
채팅도 물론 성공적...!
 +
|<syntaxhighlight lang="bash">
 +
location /ws {
 +
                proxy_pass http://localhost:8001;
 +
                proxy_http_version 1.1;
 +
                proxy_set_header Upgrade $http_upgrade;
 +
                proxy_set_header Connection "upgrade";
 +
        }
 
</syntaxhighlight>
 
</syntaxhighlight>
==url 작성==
+
|}
config의 url을 넘김 처리하고..
+
웹소켓에서도 https 인증을 적용할 수 있다.([https://victorydntmd.tistory.com/265 추후에 반영해보자.])   
 +
 
 +
== 관련에러 ==
   −
chat앱 안의 urls.py를 작성한다.<syntaxhighlight lang="python">
+
=== The app module <module '앱이름' (<_frozen_importlib_external._NamespaceLoader object at 0x7f2faed9a8f0>)> has multiple filesystem locations (['/./앱경로', '/앱경로']); you must configure this app with an AppConfig subclass with a 'path' class attribute. ===
from .views import *
+
생각지도 못한 문제점이었다... 앱은 하나의 모듈처리가 되어 디렉토리 안에 __init__.py가 담기는데, 이게 없는 경우 경로를 제대로 찾지 못해 발생하는 에러이다. 앱 디렉토리 안에 __init__.py를 제대로 넣어주면 해결됨.
   −
app_name= 'chat'
+
= 마무리. 각 요소들이 어떤 역할을 하는지. =
 +
순서대로 어떤 경로를 거쳐 작동하는지 살펴보자.
   −
urlpatterns = [
+
=== asgi.py ===
    path('', chat_list, name='chat_list'),
+
{| class="wikitable"
    paht('<str:room_id', room, name='room'),
+
!항목
]
+
!설명
</syntaxhighlight>
+
|-
==탬플릿 작성 ==
+
| ProtocolTypeRouter
chat 앱 안의 templates 폴더를 만든 다음 넣는다.<syntaxhighlight lang="html+django">
+
|연결의 종류를 파악하여 처리. http로 넘길지, 웹소켓 처리로 넘길지.
<!--확장코드-->
     −
<!--들어가야 할 것들-->
+
* http 연결일 경우 보통 django_asgi_app<code>(get_asgi_application())</code> 가 실행된다.
<script src="{% static 'js/messenger.js' %}"></script>
+
* websocket 연결(ws:// 혹은 wss://)일 경우 <code>AuthMiddlewareStack</code>으로 앱별로 <code>routing.py</code>에 적어놓은 코드로 라우팅 한다.
 +
|-
 +
|
 +
|
 +
*
 +
|}
   −
<div id="user_list">
+
=== routing.py ===
    {% for friend in friends %}
+
들어온 경로와 컨슈머를 연결해준다. urls.py와 view.py의 관계.
        {% if friend.room %}
  −
        <li name="{{ friend.room.id }}"><a href="/chat/{{ friend.room.id }}/"
  −
        <div>{{ friend.user }}</div>
  −
</div>
     −
<div id="time_line">
+
다음과 같은 형태로 컨슈머를 연결한다.<syntaxhighlight lang="python">
    <div id="main_section">
+
websocket_urlpatterns = [
        <div id="feed">
+
    re_path(r'ws/notification/(?P<center_name>\w+)/$', consumers.NotificationConsumer.as_asgi()),
            <div id="text_field">
+
]
                <input type="text" id="txt">
  −
                <button type="submit" id="btn">전송</button>
  −
            </div>
  −
        </div>
  −
    </div>
  −
</div>
   
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
=== [컨슈머는 컨슈머 문서로 옮김] ===
 
[[분류:장고 웹소켓]]
 
[[분류:장고 웹소켓]]