2 ๋ถ„ ์†Œ์š”

Django Channels

Channels ๋Š” ์›น ์†Œ์ผ“, ์ฑ„ํŒ… ํ”„๋กœํ† ์ฝœ, IoT ํ”„๋กœํ† ์ฝœ ๋“ฑ์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด Django๋ฅผ HTTP ์ด์ƒ์œผ๋กœ ํ™•์žฅํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋‹ค.

โ€œ์™œ ๊ณผ์ œ๊ฐ€ ์ฑ„ํŒ… ์„œ๋น„์Šค์ผ๊นŒ?โ€์— ๋Œ€ํ•œ ํ•œ ๊ฐ€์ง€ ๋‹ต๋ณ€์ด ๋  ์ˆ˜๋„ ์žˆ๋Š” ๊ฐœ๋…์ธ ๋“ฏ ํ•˜๋‹ค. (IoT ํ”„๋กœํ† ์ฝœ ์ฒ˜๋ฆฌ)

Channels๋Š” ASGI ๋ผ๋Š” ํŒŒ์ด์ฌ ์‚ฌ์–‘์„ ๊ธฐ๋ฐ˜์œผ๋กœ ํ•œ๋‹ค.

Django๋Š” ์—ฌ์ „ํžˆ ๊ธฐ์กด HTTP๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๋ฐ˜๋ฉด, Channels๋Š” ๋™๊ธฐ ๋ฐ ๋น„๋™๊ธฐ ์Šคํƒ€์ผ๋กœ ๋‹ค๋ฅธ ์—ฐ๊ฒฐ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์„ ํƒ๊ถŒ์„ ์ œ๊ณตํ•œ๋‹ค.

ASGI?

ASGI ๋Š” Application Server Gateway Interface์˜ ์•ฝ์ž๋กœ, ๋น„๋™๊ธฐ ๊ฐ€ ๊ฐ€๋Šฅํ•œ ํŒŒ์ด์ฌ ์›น ์„œ๋ฒ„, ํ”„๋ ˆ์ž„์›Œํฌ ๋ฐ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ ๊ฐ„์˜ ํ‘œ์ค€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ œ๊ณตํ•˜๊ธฐ ์œ„ํ•œ WSGI์˜ ํ›„์† ์ œํ’ˆ์ด๋‹ค.

WSGI๊ฐ€ ์•ฑ์— ๋Œ€ํ•œ ํ‘œ์ค€์„ ์ œ๊ณตํ–ˆ๋‹ค๋ฉด, ASGI๋Š” ๋น„๋™๊ธฐ ๋ฐ ๋™๊ธฐ ์•ฑ ๋ชจ๋‘์— ๋Œ€ํ•œ ํ‘œ์ค€์„ ์ œ๊ณตํ•œ๋‹ค.

ASGI๋Š” ๊ณง WSGI์— ๋Œ€ํ•œ ํ˜ธํ™˜์„ฑ์„ ๊ฐ€์ง€๋ฉด์„œ, ๋น„๋™๊ธฐ์ ์ธ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š” ์ธํ„ฐํŽ˜์ด์Šค์ด๋‹ค.

ASGI ์„œ๋ฒ„๋กœ๋Š” ๋ณดํ†ต uvicorn ์„ ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ํ•œ๋‹ค.

์„ธํŒ…์— ํ•„์š”ํ•œ ๊ฒƒ๋“ค์€ ๋ญ์ง€?

Django

  • HTTP ํ†ต์‹ ์„ ์œ„ํ•œ ์›น ํ”„๋ ˆ์ž„์›Œํฌ

Channels

  • ์›น ์†Œ์ผ“ ํ†ต์‹ ์„ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

channels-redis

  • ์†Œ์ผ“์„ ์—ด๊ณ  ๋‹ซ์„ ๋•Œ ์บ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๋ฐ, ์ด ๋•Œ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

Docker (๊ถŒ์žฅ)

Django Channels ๊ตฌ์„ฑ ์š”์†Œ

Scope

scope ๋Š” ํ˜„์žฌ ์š”์ฒญ์˜ ์„ธ๋ถ€ ๋‚ด์—ญ์ด ๋‹ด๊ธด dict์ด๋‹ค.

๋ชจ๋“  ASGI ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ•จ์ˆ˜๋Š” scope, receive, send ์ธ์ž๋ฅผ ๊ฐ–๋Š”๋‹ค.

Consumer

Django์—์„œ HTTP ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์ฃผ์ฒด๊ฐ€ View์ธ ๊ฒƒ์ฒ˜๋Ÿผ, Channels์—์„œ๋Š” Consumer ๊ฐ€ HTTP, ์›น ์†Œ์ผ“ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ์ฃผ์ฒด๊ฐ€ ๋œ๋‹ค.

  • Class ๋งŒ ์ง€์›ํ•œ๋‹ค.
  • Consumer์—์„œ self.scope ์„ ์‚ฌ์šฉํ•ด ํ˜„์žฌ ์š”์ฒญ์˜ ๋ชจ๋“  ๋‚ด์—ญ์„ ์กฐํšŒํ•  ์ˆ˜ ์žˆ๋‹ค.
  • channel_layer๋ฅผ ํ†ตํ•ด ๋ฉ”์‹œ์ง€๋ฅผ ์ฃผ๊ณ  ๋ฐ›๋Š” ๋ถ€๋ถ„๊นŒ์ง€ ๋ชจ๋‘ ์ง€์›ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์— ๋”์šฑ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋‹ค.

view์—์„œ๋Š” url ๋งคํ•‘์„ ํ†ตํ•ด ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๋“ฏ์ด, Channels์—์„œ๋Š” 3๊ฐ€์ง€ ๊ธฐ์ค€์œผ๋กœ ํ˜„์žฌ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•  Consumer Instance๋ฅผ ๊ฒฐ์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

๊ธฐ์ค€ 1: ํ”„๋กœํ† ์ฝœ ํƒ€์ž…

HTTP ์š”์ฒญ์™€ ์›น ์†Œ์ผ“ ํ”„๋กœํ† ์ฝœ ์š”์ฒญ ํƒ€์ž…์„ ๊ตฌ๋ถ„ํ•œ๋‹ค.

ProtocolTypeRouter ๋ฅผ ํ™œ์šฉํ•œ๋‹ค.

๊ธฐ์ค€ 2: ์š”์ฒญ URL ๋ฌธ์ž์—ด๋กœ ๋ถ„๊ธฐ

URLRouter ๋ฅผ ํ™œ์šฉํ•ด ๋Œ€๋‹ค์ˆ˜์˜ Consumer ํด๋ž˜์Šค๊ฐ€ ๊ฐ–๋Š” ๊ฐœ๋ณ„ URL์„ URLRouter์— ๋“ฑ๋กํ•œ๋‹ค.

๊ธฐ์ค€ 3: ์ฑ„๋„๋ช…์— ์˜ํ•œ ๋ผ์šฐํŒ…

channels worker ์—์„œ ์‚ฌ์šฉํ•œ๋‹ค.

Channels์—์„œ๋„ ์ฟ ํ‚ค/์„ธ์…˜ ์ธ์ฆ์ด ๊ฐ€๋Šฅํ•˜๋‹ค?

Django ์›น ํŽ˜์ด์ง€์—์„œ์˜ ์ฟ ํ‚ค/์„ธ์…˜์„ ๊ทธ๋Œ€๋กœ Consumer Instance์—์„œ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

LoginView๋ฅผ ํ†ตํ•ด ์ธ์ฆ๋œ ์œ ์ €๊ฐ€ ์›น ์†Œ์ผ“์œผ๋กœ ์ ‘์†ํ•˜๋ฉด, ์ธ์ฆ๋œ ์œ ์ €์˜ User Instance๋ฅผ scope[โ€userโ€]๋กœ ์กฐํšŒ๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

์ด๋ฅผ ํ†ตํ•ด ์ธ์ฆ ์—ฌ๋ถ€๋ฅผ ์•Œ ์ˆ˜ ์žˆ๊ณ , ์›น์†Œ์ผ“ ๋‚ด์—์„œ์˜ ๋กœ๊ทธ์ธ/๋กœ๊ทธ์•„์›ƒ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

๊ธฐ๋ณธ ์„ธํŒ… ์‹œ์ž‘

mysite/asgi.py

import os

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator
from django.core.asgi import get_asgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')

django_asgi_app = get_asgi_application()

import chat.routing  

application = ProtocolTypeRouter({
    "http": django_asgi_app,
    "websocket": AllowedHostsOriginValidator(
        AuthMiddlewareStack(
            URLRouter(
                chat.routing.websocket_urlpatterns
            )
        )
    ),
})
  • ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ์‹œ, ์›น ์†Œ์ผ“ ์šฉ URL์„ ๋งคํ•‘
  • AllowedHostsOriginValidator : ์›น ์†Œ์ผ“ ์—ด๊ฒฐ์˜ Origin ํ—ค๋”๋ฅผ ์‚ฌ์šฉํ•ด ํ—ˆ์šฉ๋œ ํ˜ธ์ŠคํŠธ์—์„œ ์˜ค๋Š” ์š”์ฒญ๋งŒ ์ฒ˜๋ฆฌ
  • AuthMiddlewareStack : ์›น ์†Œ์ผ“ ์š”์ฒญ์— ๋Œ€ํ•ด Django์˜ ๋ฏธ๋“ค์›จ์–ด๋ฅผ ์‚ฌ์šฉํ•ด ์‚ฌ์šฉ์ž ์ธ์ฆ

mysite/settings.py

...

ASGI_APPLICATION = "mysite.asgi.application"

# Channels Layer
CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [('Redis ํ˜ธ์ŠคํŠธ', 6379)],
        },
    },
}

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()),
]

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']
        }))

์ฐธ๊ณ  ์ž๋ฃŒ

Django Channels๋ž€

CGI, WSGI, ASGI ์ •๋ฆฌ

Django Channels โ€” Channels 4.0.0 documentation

ํƒœ๊ทธ: , ,

์นดํ…Œ๊ณ ๋ฆฌ:

์—…๋ฐ์ดํŠธ:

๋Œ“๊ธ€๋‚จ๊ธฐ๊ธฐ