ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Django Channels 사용법 - 공식 문서
    개발일지 2022. 6. 21. 23:06

     

    Django Channels 사용법 - 공식문서 내용정리

     


    필요한 패키지 

    django, channels, channels-redis, Docker OR Elastic Cache 


     

    1. Django 

    • Django Cannels 이기 때문에 당연히 기본 베이스인  Django가 필요 
    • 최신 버전이면 상관없다고 들었지만 공식문서상 Django 3.0보다 작은 경우 해당 버전에 대한 문서를 참조!

    2. Channels. 

    • Channels 를 사용하기 위한 패키지 

    3. channels-redis

    • 소켓을 열고 닫을 때 캐시 데이터를 사용하는데 이때 접근할 수있도록 해주는 라이브러리(?)
    • 메세지를 주고 받을 때 필요하다고 Error가 떴음 ( 없어도 Django 실행은 됨)

    4. Docker OR Elastic Cache

    • Docker 인 경우 공식문서에서 사용하고 자기 컴퓨터에서 진행할 때 사용함 
    • 일반 DB 가 아니라 메모리 캐시(?) 인 Redis를 사용
    • 다른 컴퓨터를 사용할 경우 외부 컴퓨터로 URL 접속은 되지만 Docker 와 연결이되질 않음 
    • 그래서 아에 배포를 해서 테스트함 배포시 AWS의 Ealstic Cache를 사용함
    • Docker도 될 것같은데 시도는 안해 봄 

     

    이 4가지는 pip 를 이용해서 설치 

     


     

    기존 Django와 비교하여 수정 및 만들어야 할 곳

     

    1. asgi.py 

    import os
    
    from channels.auth import AuthMiddlewareStack
    from channels.routing import ProtocolTypeRouter, URLRouter
    from django.core.asgi import get_asgi_application
    
    import chat.routing
    
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', '<setting.py 있는 이름>.settings')
    
    application = ProtocolTypeRouter({
        "http": get_asgi_application(),
        # Just HTTP for now. (We can add other protocols later.)
        "websocket": AuthMiddlewareStack(
            URLRouter(
                <앱>.routing.websocket_urlpatterns
            )
        ),
    })

    ASGI가 자세히는 아직도 이해가 안되지만 미들웨어이고 해당 Application으로 실행을 했을 때 웹소켓용 URL를 받기 위한 세팅이라고 이해했다. 

     

     

    2. setting.py 

    # django-chaneels 를 위한 설정
    ASGI_APPLICATION = "Setting.py 있는 폴더.asgi.application"
    
    # Channels Layer
    CHANNEL_LAYERS = {
        "default": {
            "BACKEND": "channels_redis.core.RedisChannelLayer",
            "CONFIG": {
                "hosts": [('Redis 호스트', 6379)],
            },
        },
    }

     

    hosts에는 Docker의 경우 Local에서 작업할 땐  127.0.0.1 을 적어줬고 

    Elastic Cache를 사용헀을 땐 기본 엔드포인트를 적어주었다. - 뒤에 포트 번호 떼는거 잊지 않기!!

     

    여기까지가 Django 에게 Django-channels를 쓰기위한 세팅값을 주는 설정이였다. 

     

     


     

    3. urls.py , view.py 

    해당 파일들은 Django 기본 세팅이니 넘어가고 여기선 해당 View에서 렌더링해주는 HTML의 JavaScript 가 중요함

     

     <script>
            {#const roomName = JSON.parse(document.getElementById('room-name').textContent);#}
    		
            
    		# 웹소켓을 연결할 URL 주소를 보내기 
            const chatSocket = new WebSocket(
                'ws://'
                + window.location.host
                + '/ws/chat/'
                + {{ room_name }}
                + '/'
            );
    		
            # 웹소켓에 메세지가 도착헀을 때 동작
            chatSocket.onmessage = function(e) {
                const data = JSON.parse(e.data);
                document.querySelector('#chat-log').value += (data.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) {
                const messageInputDom = document.querySelector('#chat-message-input');
                const message = messageInputDom.value;
                chatSocket.send(
                    message
                );
                messageInputDom.value = '';
            };
        </script>

     

     

    4. 앱/routing.py

    # chat/routing.py
    from django.urls import re_path
    
    from . import consumers
    from . import custom_consumers
    
    websocket_urlpatterns = [
        re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),
    ]

     

    연결을 위한 웹소켓 URL를 생성, 이후 만들 Consumer.py와 연결하면된다. 

    routing 과 consumers 는 장고의 urls, views 관계와 비슷하다. 

     

    Django urls  - routing.py 
    Django views - consumers.py

     

     

    5. 앱/comsumers.py 

     - 공식문서 참조 

    # chat/consumers.py
    import json
    from asgiref.sync import async_to_sync
    from channels.generic.websocket import WebsocketConsumer
    
    
    class ChatConsumer(WebsocketConsumer):
    	# 연결됬을 때 실행되는 함수 
         def connect(self):
         	# self.scope['url_route'] = /ws/localhost:8000/ws/chat/1 
            self.room_name = self.scope['url_route']['kwargs']['room_name']
            
            # 임의로 그룹명을 만든 과정 -> 수정가능
            self.room_group_name = 'chat_%s' % self.room_name
    
            # Join room group -> group 연결된 소켓을 같은 그룹명에 연결 
            # -> 같은 그룹명이면 같은 메세지 받음
            async_to_sync(self.channel_layer.group_add)(
                self.room_group_name,
                self.channel_name
            )
    
            self.accept()
    
    	# 연결 해제 될때 실행되는 함수
        def disconnect(self, close_code):
            # Leave room group
            async_to_sync(self.channel_layer.group_discard)(
                self.room_group_name,
                self.channel_name
            )
    
        # 메세지를 보낼 때 실행
        def receive(self, text_data):
    
            # Send message to room group
            async_to_sync(self.channel_layer.group_send)(
                self.room_group_name,
                {
                    'type': 'chat_message',
                    'message': text_data
                }
            )
    
        # 메세지를 받을 때 실행
        def chat_message(self, event):
        	# 이벤트가 발생할 때 실행되므로 내부의 message를 꺼내야함
            # Send message to WebSocket
            self.send(text_data=json.dumps({
                'message': event['message']
            }))

     

    여기까지가 실제 접속 및 통신을 할 수 있는 세팅이다. 

     

    이제 실제로 캐시 데이터를 사용하기 위해 

     

    docker 

    $ docker run -p 6379:6379 -d redis:5

     

    AWS 

    Elastic Cache 에서 Redis 생성 

    참고로 보안그룹이나 포트설정 다해줘야하며 로컬과 연결했을때 동작을 안했음 ( 몇번 해보고 안되서 EC2와 연결 )

     

     


    해당 세팅들이 끝났다면 실시간 채팅을 확인해 보면 된다!!

     

    내가 생각 하는 동작 과정은 

     

    1. 브라우저에서 urls.py에서 작성한 url로 이동

    2. 해당 url에 따른 view 파일 실행 

    3. 렌더링 된 HTML 파일 아래 <scriopt> 에서 websocket 연결

    4. websocket 용 URL로 연결 시도 

    5. routing.py 의 URL에 있다면 해당 URL과 연결된 consumers.py 실행 

    6. 첫 연결이면 connect 함수 실행

    7. Django 와 연결된 Channels에 그룹명을 만들어 해당 웹소켓을 넣음 

    8. 같은 그룹명에 속한 웹소켓은 서로 연결되며 연결된 상태를 유지 

    9. 연결 성공!

    10. 메세지를 보내거나 받을 때 웹소켓을 열고 닫으며 공통 메모리 캐시를 읽고 쓰면서 전달 (동시에 일어나는 것 같음 )

    11. 해당 URL - Consumer에 받기, 보내기 함수 실행 

    12. JavaScript로 화면에 표시! 

     

    정확하진 않지만 대략 이렇게 동작하는 것같다. 앞으로 더 많은 테스트와 커스텀을 진행해봐야 알 것 같다. 

    단점은 연결이 된 이전 내용은 다 날라간다는 것

    연결이 된 후부터 내용을 받을 수 있다.

     

    지금까지의 내용은 동기적 방법인 것같고 공식문서 마지막에 비동기 처리를 안내해줌! 

     

    자세한 내용은 참고!

    https://channels.readthedocs.io/en/stable/index.html

     

    Django Channels — Channels 3.0.4 documentation

    Channels is a project that takes Django and extends its abilities beyond HTTP - to handle WebSockets, chat protocols, IoT protocols, and more. It’s built on a Python specification called ASGI. Channels builds upon the native ASGI support available in Dja

    channels.readthedocs.io


    커스텀 성공한 부분 

    • 그룹명을 똑같이 만들어서 URL이 달라도 같은 메세지를 주고 받을 수 있음 
    • URL 수정가능
    • Consumer.py 에서 간단한 로직 가능 - DB 저장을 할 예정 

     

    '개발일지' 카테고리의 다른 글

    DRF - VIEW 종류  (0) 2022.06.20
    DRF - Serializer custom create  (0) 2022.06.19
    Django - OneToOneField, ForeignKey, ManyToManyField  (0) 2022.06.16

    댓글

Designed by Tistory.