[Channels] 공식문서 번역 📘 - 6: 튜토리얼 (3) - 비동기 컨슈머

Eunsung Lim·2021년 1월 15일
0
post-thumbnail

Channels는 Django를 확장해 웹소켓과 같이 HTTP가 아닌 프로토콜을 핸들링할 수 있게 돕고 비동기적인 처리를 가능하게 해주는 ASGI의 구현체로, 장고를 이용한 실시간 채팅 구현 등에 활용할 수 있습니다. 이 글은 채널즈의 공식 문서를 최대한 원어를 살려 번역한 글입니다. 다소 의역하거나 생략한 부분이 있을 수 있음을 너그러이 양해해주시고, 잘못을 자유롭게 지적해주시면 감사하겠습니다.

튜토리얼 파트 3: 채팅 서버, 비동기로 다시 작성하기

튜토리얼 2에서 이어집니다.

이제 성능 향상을 위해 동기적으로 작성된 컨슈머 코드를 비동기적으로 다시 작성해 봅시다.

컨슈머, 비동기로 다시 작성하기

우리가 만든 ChatConsumer는 동기적으로 작동합니다. 동기적인 컨슈머는 장고 모델 접근과 같은 일반적인 동기적 입출력 함수들을 별다른 처리 없이 사용할 수 있어서 편리합니다. 하지만 비동기적인 컨슈머들은 요청을 처리할 때 별도의 쓰레드를 만들 필요가 없어 훨씬 우수한 성능을 자랑합니다.

ChatConsumer는 그 자체로 비동기적인 라이브러리(채널즈와 채널 레이어)만을 사용하고, 특히 장고 모델에 접근하지 않기 때문에 간단하게 비동기적으로 다시 작성할 수 있습니다.

주의사항

ChatConsumer가 장고 모델이나 다른 동기적인 코드에 접근한다 하더라도 비동기적으로 만들 수 있습니다. asgiref.sync.sync_to_asyncchannels.db.database_sync_to_async와 같은 유틸 함수들은 비동기적인 컨슈머에서 동기적인 코드를 실행하기 위해 사용할 수 있습니다. 하지만 비동기적인 컨슈머에서 얻을 수 있는 성능 향상의 정도는 그 자체로 비동기적인 라이브러리만 사용할 때에 비해 다소 줄어듭니다.


ChatConsumer를 비동기적으로 다시 작성해 봅시다. 다음 코드를 chat/consumers.py에 붙여넣습니다:

# chat/consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer

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는 기존의 ChatConsumer와 다음 차이점만 제외하면 거의 비슷해 보입니다:

  • ChatConsumer는 이제 WebsocketConsumer가 아닌 AsyncWebsocketConsumer를 상속받습니다.
  • 모든 메서드들이 그냥 def가 아니라 async def입니다.
  • 입출력 작업을 수행하는 비동기 함수를 호출하기 위해 await가 사용되었습니다.
  • 채널레이어 상에서 메서드를 호출할 때async_to_sync가 더이상 필요하지 않습니다.

이제 /ws/chat/ROOM_NAME/에 해당하는 새 컨슈머가 잘 동작하는지 확인해 봅시다. 채널즈 개발 서버 실행을 위해 다음 커맨드를 입력합니다:

$ python3 manage.py runserver

브라우저를 열고 채팅방 페이지 http://127.0.0.1:8000/chat/lobby/로 이동합니다. 다른 탭을 하나 더 열어 동일한 채팅방 페이지로 이동합니다.

이제 두번째 브라우저 탭에서, “비동기 안녕!”이라고 타이핑하고 엔터를 눌러보세요. 그러면 두번째 탭과 첫번째 탭 모두에서 "비동기 안녕!"이라는 메시지가 채팅 로그에 나타나는 것을 확인하실 수 있습니다.

이제 이 채팅 서버는 완전히 비동기적입니다!


이 튜토리얼은 튜토리얼 4로 이어집니다.

profile
Strong belief in connecting the dots. 찬찬히 배우고 있는 학생 개발자입니다.

0개의 댓글