실시간 메시지 저장 및 WebSocket 최적화 (Redis 활용)
Django와 WebSocket을 사용하여 실시간 메시지를 저장하고, Redis를 활용하여 성능을 최적화하며, JWT 인증을 통해 보안을 강화하는 방법을 다룹니다.
1. WebSocket 메시지를 데이터베이스에 저장하기
이제 Django에서 WebSocket을 통해 받은 메시지를 데이터베이스에 저장하여 채팅 기록을 유지할 수 있도록 만듭니다.
1.1. Message
모델 생성
chat/models.py
에서 메시지를 저장할 모델을 정의합니다.
1
2
3
4
5
6
7
8
9
10
11
# chat/models.py
from django.db import models
from django.contrib.auth.models import User
class Message(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
content = models.TextField()
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return f"{self.user.username}: {self.content}"
모델 필드 설명
user
: 메시지를 보낸 사용자content
: 메시지 내용timestamp
: 메시지가 전송된 시간
1.2. 마이그레이션 적용
1
2
python manage.py makemigrations chat
python manage.py migrate
이제 메시지를 저장할 데이터베이스 테이블이 생성되었습니다.
2. WebSocket 메시지를 DB에 저장하기
2.1. ChatConsumer
수정
이제 메시지를 받으면 데이터베이스에 저장하도록 consumers.py
를 수정합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# chat/consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer
from django.contrib.auth.models import User
from .models import Message
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.user = self.scope["user"]
await self.accept()
async def disconnect(self, close_code):
pass
async def receive(self, text_data):
data = json.loads(text_data)
message = data['message']
if self.user.is_authenticated:
new_message = Message.objects.create(user=self.user, content=message)
await self.send(text_data=json.dumps({
'user': self.user.username,
'message': message,
'timestamp': str(new_message.timestamp)
}))
이제 메시지가 데이터베이스에 저장되며, 저장된 메시지와 함께 클라이언트에 응답이 전달됩니다.
3. Redis를 활용한 WebSocket 성능 최적화
3.1. Redis를 WebSocket 메시지 브로커로 사용
Django Channels는 기본적으로 InMemoryChannelLayer를 사용하지만, 실제 서비스에서는 Redis를 메시지 브로커로 사용해야 합니다.
settings.py
에서 Redis 설정을 추가합니다.
1
2
3
4
5
6
7
8
9
# settings.py
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": ["redis://localhost:6379"],
},
},
}
3.2. Redis 실행
Redis가 실행 중인지 확인하고, 실행되지 않았다면 아래 명령어로 실행합니다.
1
redis-server
이제 Django Channels는 Redis를 사용하여 WebSocket 메시지를 더 빠르고 안정적으로 처리할 수 있습니다.
4. JWT 인증을 통한 WebSocket 보안 강화
4.1. WebSocket에서 JWT 토큰 검증하기
웹소켓은 기본적으로 Django의 session
을 사용하지 않기 때문에 JWT 토큰을 검증하는 방식을 적용해야 합니다.
consumers.py
에서 WebSocket이 연결될 때 JWT 토큰을 검증하는 로직을 추가합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# chat/consumers.py
from channels.db import database_sync_to_async
from rest_framework_simplejwt.tokens import AccessToken
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
token = self.scope["query_string"].decode().split("=")[1]
user = await self.get_user_from_token(token)
if user:
self.user = user
await self.accept()
else:
await self.close()
@database_sync_to_async
def get_user_from_token(self, token):
try:
access_token = AccessToken(token)
return User.objects.get(id=access_token["user_id"])
except Exception:
return None
이제 WebSocket 연결 시, JWT 토큰을 검증하여 인증된 사용자만 접속할 수 있도록 보안이 강화됩니다.
5. 테스트 및 실행
5.1. Django 서버 실행
1
python manage.py runserver
5.2. Redis 실행 (이미 실행되지 않았다면)
1
redis-server
5.3. React에서 WebSocket 연결 시 JWT 포함하기
React 클라이언트에서 WebSocket 연결할 때 JWT 토큰을 포함해야 합니다.
1
2
const token = localStorage.getItem("access_token");
const socket = new WebSocket(`ws://localhost:8000/ws/chat/?token=${token}`);
6. 다음 단계
이제 Django에서 WebSocket 메시지를 데이터베이스에 저장하고, Redis를 활용하여 성능을 최적화하며, JWT 인증을 통해 보안을 강화하였습니다.
프로젝트를 실서비스로 배포하는 방법은 다음과 같습니다다:
- Gunicorn + Daphne + Redis를 사용한 WebSocket 배포
- Nginx 리버스 프록시 설정 및 HTTPS 적용
- Docker를 활용한 WebSocket 서비스 배포
정리 Django에서 WebSocket 메시지를 데이터베이스에 저장 완료
Redis를 활용한 WebSocket 성능 최적화 완료
JWT 인증을 통한 WebSocket 보안 강화 완료