Requirements
- 일대일 채팅, 그룹 채팅 모두 가능해야 함.
- 현재 열람중인 채팅방에서 발생하는 채팅 내용들을 실시간으로 확인해야 함.
- 현재 열람 중인 채팅방이 아니더라도 좌측 목록에서 다른 채팅방으로부터의 알림을 실시간으로 받아야 함.
- 채팅 내역은 데이터베이스에 저장되어, 브라우저를 새로 열고 로그인을 다시 해도 이전의 기록들을 보존&열람할 수 있어야 함.
- 인증받은 사람만 채팅이 가능해야 함.
Scenario
1. A 가 채팅방을 연다.
- 서버로부터 기존 채팅 내역들을 불러온다 (일반적인 HTTP 요청-응답)
- 클라이언트와 서버가 웹소켓으로 연결된다.
2. A가 채팅방에 메세지를 보낸다.
- A의 클라이언트가 웹소켓을 통해 서버로 메세지를 전송한다.
- 서버는 A가 연결된 웹소켓으로부터 데이터를 받고 핸들러를 호출한다.
- 메세지를 DB에 저장한다.
3. 서버는 해당 채팅방에 참여한 다른 유저에게 웹소켓을 통해 메세지를 전송한다 (B, C 도 서버와 웹소켓으로 연결되어 있다고 가정)
- 해당 메세지가 전송된 채팅방의 유저 목록을 DB 에서 가져온다.
- 해당 유저들과 연결된 웹소켓이 있다면 메세지를 전파한다.
4. B, C 각각의 클라이언트는 웹소켓을 통해 메세지를 전달 받고 프론트 UI를 리렌더하는 등의 핸들러 동작을 수행한다.
- B, C의 클라이언트는 서버와 연결된 웹소켓으로부터 데이터를 받고 핸들러를 호출한다.
- 핸들러는 받아온 데이터를 바탕으로 필요한 작업을 수행한다.
채널 레이어 설계
하나의 웹소켓 채널은 하나의 서버 - 하나의 클라이언트를 연결한다.
그러나 채팅이 발생할 경우 해당 채팅방에 참여한 다수의 클라이언트 모두에게 데이터를 전송해야 한다.
위에서 보았던 Channel Layer 는 여러 클라이언트를 하나의 그룹으로 묶어 일괄적으로 데이터를 전송할 수 있게 해준다.
Django의 Consumer 클래스
소켓 연결 허용 + 소켓 연결 해제 + 정보 송수신 에 대한 핸들링을 쉽게 구현할 수 있도록 함
=> JsonWebsocketConsumer 클래스를 상속
미들웨어 장고 인증
임의의 클라이언트가 특정 채팅방에 마음대로 채팅을 보내거나 받도록 두면 안됨
- HTTP 연결 시에는 장고가 token 을 보내거나 받아 유저의 인증 정보를 체크
- 마찬가지로 웹소켓 연결 시에도 토큰을 통해 유저의 인증 정보를 체크하도록 구현
- 이 절차는 서버가 데이터를 수신한 후, 그리고 수신한 데이터를 처리하기 이전에 수행
- 웹소켓 연결 시의 헤더 교환을 통해 브라우저에 저장해두었던 토큰을 전달할 수 있음
=> 이 Layer를 미들웨어라고 한다
프론트 단에서 해야 할 것은?
직접적으로 서버와 연결하기
1. 최초의 웹소켓 연결 요청
2. 데이터 전송
통신 과정에서 클라이언트가 각종 상태 관리 하기
a. 서버와의 웹소켓 연결 후 각종 상태 초기화
b. 서버로부터 수신된 데이터 처리
c. 웹소켓 연결 종료 처리
=> react-use-websocket 라이브러리를 활용
=> useWebSocket은 웹소켓 연결을 쉽게 관리할 수 있도록 도와주는 React 커스텀 훅
=> 라이브러리가 웹소켓 연결과 데이터 송수신, 재연결 등 여러 복잡한 작업(1번, 2번)을 알아서 해주기 때문에, 우리는 데이터를 주고받는 로직(a, b, c)에만 집중
import React, { useState, useCallback, useEffect } from 'react';
import useWebSocket from 'react-use-websocket';
export const WebSocketDemo = () => {
const [socketUrl, setSocketUrl] = useState('wss://echo.websocket.org');
const options = {
onOpen: () => console.log('연결이 성공적으로 열렸습니다.'),
onClose: () => console.log('연결이 닫혔습니다.'),
onError: (error) => console.error('웹소켓 에러:', error),
onMessage: (message) => console.log('수신된 메시지:', message.data),
};
const { sendJsonMessage } = useWebSocket(socketUrl, options, reconnectInterval: 1000,share: true,);
백엔드 단에서 구현해야 할 것은?
WebSocket 등 비동기 통신 프로토콜을 지원하는 기능을 추가하는 라이브러리 = 장고의 Channel 라이브러리
Channels는 ASGI 웹서버( 예: Uvicorn 등 )에서 작동하며, Consumer라는 Django의 기존 동기식 View와 유사하지만 비동기 처리를 지원하는 구조를 차용함
wsgi.py 대신 asgi.py 를 작성해서 사용한다
- Uvicorn이 Django Application의 ASGI Instance 를 정의하는 부분
- asgi.py는 ASGI 사용시 Django Application 으로의 진입점
- 프로토콜을 구분해서 HTTP 요청은 get_asgi_application()으로, WebSocket 요청은 URLRouter로 전달
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'likesaju.settings')
django_application = get_asgi_application() # init django application
from . import urls
from channels.routing import ProtocolTypeRouter, URLRouter
from webchat.middleware import JWTAuthMiddleWare
application = ProtocolTypeRouter(
{
"http" : django_application,
"websocket": URLRouter(urls.websocket_urlpatterns),
}
)
더 이상 python manage.py runserver 로 장고 내장 서버를 실행하지 말고
uvicorn likesaju.asgi:application --port 8000 --workers 4 --log-level debug --reload
uvicorn 서버 실행 커맨드를 사용하자
구현 디테일은 멋사 세미나 자료에...
(디테일 야무짐)
'web > snulion' 카테고리의 다른 글
클라우드 컴퓨팅과 네트워크의 핵심 개념 이해하기 (0) | 2024.11.30 |
---|---|
[8주차 과제] interactive web (0) | 2024.11.16 |
WAS, WSGI, ASGI에 대해 araboza (0) | 2024.11.02 |
websocket에 대해 araboza (0) | 2024.11.02 |
카카오페이를 이용한 간편결제에 대해 araboza (6) | 2024.10.05 |