수학과의 좌충우돌 프로그래밍

[Django] Channels, 비동기적 채팅 구현하기 - WebSocket (3) 본문

웹프로그래밍/Django

[Django] Channels, 비동기적 채팅 구현하기 - WebSocket (3)

ssung.k 2019. 7. 11. 14:23
  • 소비자 개선

앞선 포스팅 WebSocket (2) 에서 작성했던 소비자는 동기적으로 작성되어 있습니다. 이렇게 함으로서 django 의 I/O 함수를 쉽게 호출할 수 있어 편리했습니다. 소비자를 비동기식으로 작성하게 되면 요청을 처리할 때, 추가적인 쓰레드를 생성하지 않습니다. 즉 성능 개션을 불러올 수 있는 것이죠. 실시간 채팅같은 경우에는 성능이 굉장히 중요한 요소이기 때문에 이 과정이 필요합니다.

앞에서도 한 번 언급했던 sync_to_async 를 통해서 django 의 동기적인 코드를 비동기적으로 수행하도록 할 수 있지만 지금 사용할 async-native 라이브러리보다 성능이 떨어집니다.

# chat/consumers.py

from channels.generic.websocket import AsyncWebsocketConsumer
import json

class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group_name = 'chat_%s' % self.room_name

        # Join room group
        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )
        await self.accept()

    async def disconnect(self, close_code):
        # Leave room group
        await self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )

    # Receive message from WebSocket
    async def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json['message']

        # Send message to room group
        await self.channel_layer.group_send(
            self.room_group_name,
            {
                'type': 'chat_message',
                'message': message
            }
        )

    # Receive message from room group
    async def chat_message(self, event):
        message = event['message']

        # Send message to WebSocket
        await self.send(text_data=json.dumps({
            'message': message
        }))

이전 글과 비교해보면 알 수 있겠지만, ChatConsumer 가 상속받는 것이 WebsocketConsumer 에서 AsyncWebsocketConsumer 으로 바뀌었습니다. 또한 메소드 선언 시, async 가 붙고, 메소드를 호출 하는 경우에도 await 를 붙여서 비동기 처리를 했습니다. 그 대신 앞에서 사용하던 async_to_sync 는 필요가 없어졌습니다.

 

  • Database Access

    Django ORM은 동기식 코드이기 때문에 위와 같이 소비자를 비동기식으로 작성한 상태에서 접근하기 위해서는 해줘야할 작업이 있습니다.

    # 방법 1
    from channels.db import database_sync_to_async
    
    async def connect(self):
        self.username = await database_sync_to_async(self.get_name)()
    
    def get_name(self):
        return User.objects.all()[0].name
    

    첫번째 방법은 database에 접근할 경우 database_sync_to_async 를 함께 호출해주는 것입니다. 위에서 봤던 것 처럼 메소드에 asyncawait도 사용해주었습니다.

     

    # 방법 2
    from channels.db import database_sync_to_async
    
    async def connect(self):
        self.username = await self.get_name()
    
    @database_sync_to_async
    def get_name(self):
        return User.objects.all()[0].name
    

    두번째 방법은 데코레이터로서 database_sync_to_async 를 사용해주었습니다. 나머지는 위와 동일합니다.

Comments