-
-
Save stresszero/9a4f203e42fcd318df6c39f865df1ecc to your computer and use it in GitHub Desktop.
socketio.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import socketio | |
import django | |
# Django 기본생성 파일이 아닌 경우 밑에서 Django models를 임포트하면 에러 발생함 | |
# Django models 임포트 하기 전에 django.setup() 해줘야 에러 안 남 | |
# Django 환경설정 파일 로드 | |
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "togedog_dj.settings") | |
django.setup() | |
from django.conf import settings | |
from .mongodb import save_message | |
from cores.models import UserStatus | |
from cores.utils import censor_text | |
from users.models import User | |
# 소켓.io 서버 인스턴스 생성 | |
# async_mode: 비동기처리 방법 설정, 따로 설정하지 않으면 eventlet부터 통신 시도 | |
# cors_allowed_origins: '*'로 설정하면 cors 모두 허용 | |
# logger=True로 설정하면 서버에서 발생하는 이벤트 로그를 터미널에 출력함, fatal error는 False로 설정해도 출력됨 | |
sio = socketio.Server( | |
async_mode="eventlet", | |
cors_allowed_origins=settings.CORS_ALLOWED_ORIGINS, | |
# cors_allowed_origins='*', | |
# logger=True | |
) | |
# sio.event: connect와 disconnect는 클라이언트가 서버에 접속하거나 접속을 끊을 때 발생 | |
# 이벤트 명으로 connect, message, disconnect 등은 명시적으로 정의된 이벤트로, python-socketio에서 정해진 대로만 사용 가능 | |
# @sio.event는 밑에서 데코레이터 받은 함수명(def 함수명)을 이벤트로 받으며 @sio.on('이벤트명')는 인자 값으로 이벤트명을 받음 | |
# 따라서 @sio.on으로는 함수명으로 쓰지 못하는 한글이나 공백이 들어간 문자열을 이벤트명으로 사용 할 수 있음 | |
# sio.emit안의 인자인 to와 room은 완전히 똑같음, 아무거나 써도 되고 특정 room으로만 메시지를 보낼 때 사용 | |
@sio.event | |
def connect(sid, environ, auth): | |
""" | |
socket.io 클라이언트가 연결되면 아래 코드를 실행한다, connect는 미리 정의된 이벤트이며 | |
파라미터인 sid, environ, auth 들도 python-socketio 패키지에서 미리 정해놓은 것들이다. | |
- environ: http 헤더를 포함한 http 리퀘스트 데이터를 담는 WSGI 표준 딕셔너리 | |
- auth: 클라이언트에서 넘겨준 인증 데이터, 데이터가 없으면 None이 된다 | |
- 클라이언트가 보낸 auth의 유저id값으로 해당 유저가 존재하지 않거나 | |
- 인증이 불가한 유저는 return False해서 채팅방에 입장 못하게 처리 | |
- return False 하는 대신 raise ConnectionRefusedError으로 연결 거부 메시지를 전송할 수도 있음 | |
""" | |
user = User.objects.filter(id=auth["userId"]) | |
if not user.exists() or user.get().status == UserStatus.BANNED.value: | |
# 잘못된 사용자가 연결 시도하는 경우 바로 연결해제 처리 | |
# 다음과 같이 emit하면 다른 사용자도 튕기는 문제가 발생함 | |
# sio.emit("connect_error", {"message": "invalid user"}) | |
# 클라이언트 코드: | |
# useEffect(() => { | |
# socket.on('connect_error', data => { | |
# if (data.message === 'invalid user') { | |
# socket.close(); | |
# navigate('/'); | |
# } | |
# }); | |
# return () => { | |
# socket.close(); | |
# }; | |
# }, []); | |
# 따라서 emit없이 False 반환만 하면 권한없는 사용자가 채팅방에 접속할 수 없고 바로 연결 해제됨 | |
return False | |
@sio.on('join') | |
def handle_join(sid, data): | |
""" | |
유저가 채팅방에 입장하면 아래 코드를 실행한다 | |
- 브라우저에서 채팅방 입장시 connect 이벤트가 먼저 발생하고 이어서 join 이벤트가 발생됨 | |
- data를 자바스크립트 객체로 받으면 파이썬 딕셔너리로 사용 가능 | |
- data에는 유저 닉네임과 방 번호가 포함됨(key: nickname, room) | |
- save_session(sid, data): 채팅방에 접속한 클라이언트의 sid를 기준으로 data를 세션 데이터로 저장 | |
""" | |
sio.save_session(sid, data) | |
sio.enter_room(sid, room=data['room']) | |
sio.emit( | |
'add_message', | |
{ | |
"user_nickname": '함께하개 관리자', | |
"text": f"{data['nickname']}님이 들어왔어요." | |
}, | |
to=data['room'] | |
) | |
@sio.on('send_message') | |
def handle_send_message(sid, message, nickname, room, currentTime, userMbti, userImage, userId): | |
""" | |
send_message 이벤트로 받은 데이터를 MongoDB에 저장하고 add_message로 emit한다 | |
- censor_text(): 욕설 필터링 함수, 채팅 메시지에 욕설이 있으면 | |
- 그 부분만 *으로 수정한 문자열을 반환함 | |
- save_message(): MongoDB에 메시지 데이터를 저장하고 고유한 ObjectId(24자리 문자열)반환 | |
- 필요한 메시지 처리가 완료되면 data를 add_message로 emit | |
""" | |
data = { | |
"user_nickname": nickname, | |
"user_id" : userId, | |
"user_mbti" : userMbti, | |
"user_image" : userImage, | |
"text" : censor_text(message), | |
"message_id" : save_message(message, nickname, userId, room), | |
"time" : currentTime, | |
} | |
sio.emit('add_message', data, to=room) | |
@sio.event | |
def disconnect(sid): | |
""" | |
socket.io 클라이언트가 연결 해제되면 아래 코드를 실행한다 | |
- get_session(sid): 위에서 save_session으로 저장한 sid별 세션 데이터 딕셔너리를 반환함 | |
- disconnected_username: 연결 해제된 sid의 유저 닉네임 | |
- room_number: 연결 해제된 sid가 있던 방 번호 | |
- 퇴장한 유저 정보를 담아 add_message로 emit | |
""" | |
disconnected_username = sio.get_session(sid).get("nickname", "") | |
room_number = sio.get_session(sid).get("room") | |
sio.emit( | |
"add_message", | |
{ | |
"user_nickname": "함께하개 관리자", | |
"text" : f"{disconnected_username}님이 퇴장하셨어요." | |
}, | |
to=room_number, | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment