🔍

Django Channels?

브랜치
채팅
생성 일시
2023/09/23 04:32
작성일
2023/09/24
작성자
최종 편집 일시
2023/10/11 04:20

Ⅰ. Django Channels

1. 사용하는 이유

Django는 기본적으로 요청(request)-응답(response) 주기로 동작하는데, 이것은 실시간 애플리케이션에 적합하지 않다.
이러한 한계를 극복하고 실시간 통신비동기 작업을 처리하기 위해 Django Channels 라이브러리를 사용할 수 있다.

2. 주요 기능

1.
WebSocket : 서버와 실시간으로 데이터 주고 받을 수 있다.
예를 들어 실시간 채팅, 게임, 실시간 업데이트 등을 할 수 있음
2.
비동기 작업 : 시간이 오래 걸리는 작업을 블로킹하지 않고 처리할 수 있다.
예를 들어 백그라운드 작업, 큐잉 시스템과의 통합, 실시간 이벤트 처리 등을 할 수 있음
3.
채널 시스템 : 다양한 프로세스 간 통신 지원
이를 통해 여러 서버 인스턴스 또는 다른 백엔드 시스템과 통합할 수 있음

Ⅱ. Django Channels 사용해보기

1. Channels 설치

pip install Django channels

2. settings.py 수정

INSTALLED_APPS에 channel 추가
ASGI_APPLICATION에 ASGI 애플리케이션 지정
ASGI_APPLICATION = 'project_name.routing.application'
ASGI(Asynchronous Server Gateway Interface)란?
웹 서버와 웹 애플리케이션 사이에 비동기 통신을 위한 인터페이스
기본적으로 Django는 WSGI(Web Server Gateway Interface)를 사용하여 동기적인 요청-응답 처리를 하는데, 이러한 동작 방식은 실시간 기능을 구현하는 데 적합하지 않음
CHANNEL_LAYERS에 실시간 통신을 처리할 레이어 지정
CHANNEL_LAYERS = { 'default': { 'BACKEND': 'channels.layers.InMemoryChannelLayer', }, }
Python
복사
channels.layers.InMemoryChannelLayer는 임시 메모리 기반 채널 레이어로, 개발 및 테스트 목적으로 사용해볼 수 있지만, 실제 프로덕션 환경에서는 Redis와 같은 외부 메시지 브로커(?)를 사용하는 것이 권장된다.

3. ChatConsumer 생성

ChatConsumer 클래스 생성하고, WebSocket 통신을 위한 메서드를 정의합니다.
# chat_app/consumers.py import json from channels.generic.websocket import AsyncWebsocketConsumer class ChatConsumer(AsyncWebsocketConsumer): # 클라이언트가 WebSocket 연결을 시도할 때 호출되며, # 연결을 수락하고 그룹에 사용자를 추가합니다. async def connect(self): self.room_name = self.scope['url_route']['kwargs']['room_name'] self.room_group_name = f'chat_{self.room_name}' await self.channel_layer.group_add( self.room_group_name, self.channel_name ) await self.accept() # 클라이언트가 WebSocket 연결을 해제할 떄 호출되며, # 그룹에서 사용자를 제거합니다. async def disconnect(self, close_code): await self.channel_layer.group_discard( self.room_group_name, self.channel_name ) # 클라이언트로부터 메시지를 받을 때 호출되며, # 받은 메시지를 그룹 내의 모든 클라이언트에 전송합니다. async def receive(self, text_data): text_data_json = json.loads(text_data) message = text_data_json['message'] await self.channel_layer.group_send( self.room_group_name, { 'type': 'chat_message', 'message': message } ) # 그룹으로부터 메시지를 받을 때 호출되며, # 받은 메시지를 현재 클라이언트에게 전송합니다. async def chat_message(self, event): message = event['message'] await self.send(text_data=json.dumps({ 'message': message }))
Python
복사
클래스명이 Consumer인 이유는?
클라이언트와 서버 간의 WebSocket 연결을 통한 실시간 통신을 “소비”한다는 의미이다.
Django Channels에서 WebSocket 연결 및 비동기 작업을 처리하는 클래스에 “Consumer”라는 용어를 보편적으로 사용한다.

4. routing.py 생성

WebSocket 라우팅을 설정하기 위해 프로젝트 디렉토리 아래에 routing.py 파일 생성한다.
예시 코드
# chat_project/routing.py from django.urls import re_path from . import consumers websocket_urlpatterns = [ re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()), ]
Python
복사
urls.py를 사용하지 않고, routing.py를 따로 만들어주는지?
urls.py : 단방향 통신인 HTTP 요청을 처리하기 위한 규칙을 정의합니다.
routing.py : WebSocket과 같은 실시간 통신, 비동기 작업을 처리하기 위한 라우팅 규칙을 정의합니다.

5. WebSocket URL 설정

WebSocket을 연결할 주소를 설정합니다. (강사님이 제공한 예제 코드에는 빠져있음)
# chat_project/settings.py WebSocket_URL = "ws://localhost:8000"
Python
복사
WebSocket 주소는 아래와 같은 형식을 가질 수 있다.
ws://example.com: 실제 도메인 또는 호스트 주소를 사용하는 경우.
wss://example.com: 안전한 WebSocket 연결을 위해 SSL을 사용하는 경우.
ws://localhost:8000: 로컬 개발 서버에서 WebSocket 연결을 테스트하려는 경우.

6. URL 패턴 설정

WebSocket URL 패턴을 설정합니다.
# chat_app/urls.py from django.urls import path from . import views urlpatterns = [ # 기존 URL 패턴 # ... # WebSocket URL 패턴 path('ws/chat/<str:room_name>/', views.chat_room, name='chat_room'), ]
Python
복사
실시간 채팅은 WebSocket 연결로 실시간 통신을 하는데, HTTP와 같이 단방향 통신을 처리하는 urls.py에도 URL 패턴을 설정해야 하는 이유는?
실시간 채팅 외에도 채팅 목록 조회, 새로운 채팅 생성 등의 HTTP 요청도 처리해야 하기 때문이다.

7. Views 및 Templates 설정

뷰와 템플릿을 생성하고, 클라이언트 측에서 WebSocket을 사용하여 1:1 채팅을 구현한다.
예제 코드
# views.py from django.shortcuts import render def chat_room(request, room_name): # WebSocket 연결을 위한 경로 설정 ws_path = f"/ws/chat/{room_name}/" # 템플릿에 전달할 context 데이터 설정 context = { 'room_name': room_name, 'ws_path': ws_path, } # chat_room.html 템플릿을 렌더링하여 화면 반환 return render(request, 'chat_room.html', context)
Python
복사
// WebSocket 연결을 위한 JavaScript 코드 const socket = new WebSocket("ws://" + window.location.host + "{{ ws_path }}"); // WebSocket 연결 이벤트 리스너 socket.onopen = function () { console.log("WebSocket 연결이 열렸습니다."); }; // WebSocket 연결 종료 이벤트 리스너 socket.onclose = function () { console.log("WebSocket 연결이 종료되었습니다."); }; // WebSocket 메시지 수신 이벤트 리스너 socket.onmessage = function (event) { const messageData = JSON.parse(event.data); // 메시지 데이터를 DOM에 추가하여 채팅 내용을 렌더링 // 예: const chatContainer = document.getElementById('chat-container'); // chatContainer.innerHTML += `<div>${messageData.username}: ${messageData.message}</div>`; }; // 채팅 메시지를 서버로 전송하는 함수 function sendMessage(message) { const messageData = { username: "사용자 이름", // 메시지를 보낸 사용자의 이름 message: message }; socket.send(JSON.stringify(messageData)); }
JavaScript
복사
socket.onmessage : 이 이벤트 리스터에서 서버로부터 수신된 메시지를 파싱하고, DOM에 추가하여 채팅 내용을 실시간으로 렌더링합니다.
sendMessage : 이 함수를 사용하여 새로운 메시지를 서버로 전송합니다.

Ⅲ. 참고자료