369번째 줄: |
369번째 줄: |
| |서버에서 실행 | | |서버에서 실행 |
| |서비스를 위해선 백그라운드로 진행해주어야 한다. | | |서비스를 위해선 백그라운드로 진행해주어야 한다. |
| + | 실행 여부는 <code>ps -ef | grep daphne</code>로 확인해보자. 실행이 안되면 nohup 떼서 에러메시지 확인. |
| |<code>nohup daphne -b 0.0.0.0 -p 8001 config.asgi:application &</code> | | |<code>nohup daphne -b 0.0.0.0 -p 8001 config.asgi:application &</code> |
| |- | | |- |
385번째 줄: |
386번째 줄: |
| </syntaxhighlight> | | </syntaxhighlight> |
| |} | | |} |
− | ,
| + | 웹소켓에서도 https 인증을 적용할 수 있다.([https://victorydntmd.tistory.com/265 추후에 반영해보자.]) |
| | | |
− | 웹소켓에서도 https 인증을 적용할 수 있다.([https://victorydntmd.tistory.com/265 추후에 반영해보자.])
| + | == 관련에러 == |
| + | |
| + | === The app module <module '앱이름' (<_frozen_importlib_external._NamespaceLoader object at 0x7f2faed9a8f0>)> has multiple filesystem locations (['/./앱경로', '/앱경로']); you must configure this app with an AppConfig subclass with a 'path' class attribute. === |
| + | 생각지도 못한 문제점이었다... 앱은 하나의 모듈처리가 되어 디렉토리 안에 __init__.py가 담기는데, 이게 없는 경우 경로를 제대로 찾지 못해 발생하는 에러이다. 앱 디렉토리 안에 __init__.py를 제대로 넣어주면 해결됨. |
| | | |
| = 마무리. 각 요소들이 어떤 역할을 하는지. = | | = 마무리. 각 요소들이 어떤 역할을 하는지. = |
417번째 줄: |
421번째 줄: |
| </syntaxhighlight> | | </syntaxhighlight> |
| | | |
− | === Consumer.py === | + | === [컨슈머는 컨슈머 문서로 옮김] === |
− | 컨슈머는 클래스로 구현되어 connect, disconnect 등으로 구성된다. | |
− | {| class="wikitable"
| |
− | !항목
| |
− | !설명
| |
− | !예시
| |
− | |-
| |
− | | 컨슈머 정의
| |
− | |클래스로 구현한다.
| |
− | |<syntaxhighlight lang="python">
| |
− | from channels.generic.websocket import WebsocketConsumer
| |
− | | |
− | class 컨슈머이름(WebsocketConsumer):
| |
− | </syntaxhighlight>비동기식으로 구성한 경우<syntaxhighlight lang="python">
| |
− | from channels.generic.websocket import AsyncWebsocketConsumer
| |
− | | |
− | class 컨슈머이름(AsyncWebsocketConsumer):
| |
− | </syntaxhighlight>
| |
− | |}
| |
− | | |
− | ==== connect ====
| |
− | {| class="wikitable"
| |
− | !항목
| |
− | !설명
| |
− | !예시
| |
− | |-
| |
− | | 사전 설정
| |
− | |각종 변수들의 조작을 먼저 한다.
| |
− | 스코프로 받아서 변수들을 조작하는데, 그 구조는 다음과 같다.
| |
− | {| class="wikitable"
| |
− | !항목
| |
− | !설명
| |
− | |-
| |
− | | self.scope["url_route"]
| |
− | |args와 kargs를 키로 갖는 사전.
| |
− | |-
| |
− | |self.scope["url_route"]["kargs"]
| |
− | |routing에서 정규표현식으로 사용한 변수.
| |
− | |-
| |
− | |self.scope["url_route"]["kargs"]["변수키"]
| |
− | |변수의 값을 얻어온다.
| |
− | |}
| |
− | |self.group_name = self.scope["url_route"]["kwargs"]["room_name"]
| |
− | |-
| |
− | |그룹 참여
| |
− | |그룹을 지정하여 참여시킨다.
| |
− | |<syntaxhighlight lang="python">
| |
− | async_to_sync(self.channel_layer.group_add)(
| |
− | self.group_name, # 위에서 설정하여 넣어준다.
| |
− | self.channel_name # 자동으로 고유하게 지정된다.
| |
− | )
| |
− | </syntaxhighlight>비동기식으로 구성한 경우<syntaxhighlight lang="python">
| |
− | await self.channel_layer.group_add(self.group_name, self.channel_name)
| |
− | </syntaxhighlight>
| |
− | |-
| |
− | |연결
| |
− | |위 설정을 토대로 연결한다.
| |
− | 연결 이후 필요한 명령을 넣기도 한다.
| |
− | | |
− | | |
− | 연결과 동시에 다음과 같이 신호를 보내기도 한다.<syntaxhighlight lang="python">
| |
− | notifications = get_notification()
| |
− | if notifications:
| |
− | async_to_sync(self.channel_layer.group_send)(
| |
− | "center_name", {
| |
− | "type": "notify",
| |
− | "data": notifications
| |
− | }
| |
− | )
| |
− | </syntaxhighlight>
| |
− | |<syntaxhighlight lang="python">
| |
− | self.accept()
| |
− | </syntaxhighlight>비동기식으로 구성한 경우<syntaxhighlight lang="python">
| |
− | await self.accept()
| |
− | </syntaxhighlight>
| |
− | |}
| |
− | | |
− | ==== disconnect ====
| |
− | {| class="wikitable"
| |
− | !항목
| |
− | !설명
| |
− | !예시
| |
− | |-
| |
− | |정의
| |
− | |연결을 끊기만 할 테니, 기본 설정은 간단하다.
| |
− | 적당히 끊어질 때의 명령을 넣기도 한다.
| |
− | |<syntaxhighlight lang="python">
| |
− | def disconnect(self, close_code):
| |
− | async_to_sync(self.channel_layer.group_discard)(
| |
− | self.group_name,
| |
− | self.channel_name
| |
− | )
| |
− | </syntaxhighlight>비동기식으로 구성한 경우<syntaxhighlight lang="python">
| |
− | async def disconnect(self, close_code):
| |
− | await self.channel_layer.group_discard(self.group_name, self.channel_name)
| |
− | </syntaxhighlight>
| |
− | |}
| |
− | | |
− | ==== receive ====
| |
− | 타인으로부터 메시지를 받는 게 아니라, 자바스크립트로부터 받는 메시지와 관련된 함수이다.(웹소켓에서 메시지를 받을 때.)
| |
− | {| class="wikitable"
| |
− | !항목
| |
− | !설명
| |
− | !예시
| |
− | |-
| |
− | |정의
| |
− | |본인의 자바스크립트에서 웹소켓으로 메시지를 보낸 경우.
| |
− | 자바스크립트에서 데이터가 넘어올 땐 json으로 변환되어 오기 때문에 우측과 같이 구성해야 한다.
| |
− | | |
− | | |
− | | |
− | 메시지를 보낼 때 type는 받는 함수를 지정할 때 쓰인다.(없어도 되긴 함.)
| |
− | | |
− | type의 이름이 chat.message 라면 chat_message로 변경되어 처리된다.
| |
− | |<syntaxhighlight lang="python">
| |
− | def receive(self, text_data):
| |
− | text_data_json = json.loads(text_data)
| |
− | message = text_data_json['message']
| |
− |
| |
− | # 나 뿐 아니라 다른 사람들도 보게끔.
| |
− | self.send(text_data=json.dumps({
| |
− | 'message': message
| |
− | }))
| |
− | </syntaxhighlight>비동기식으로 구성한 경우<syntaxhighlight lang="python">
| |
− | 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}
| |
− | )
| |
− | </syntaxhighlight>
| |
− | |}
| |
− | | |
− | ==== 이벤트 ====
| |
− | 다른 사용자들로부터 메시지를 받는 등의 이벤트가 나타났을 때.
| |
− | {| class="wikitable"
| |
− | !항목
| |
− | !설명
| |
− | !예시
| |
− | |-
| |
− | |정의
| |
− | |함수의 이름은 메시지를 보낼 때의 type와 동일하게 짓는다.
| |
− | type에 따라 다른 함수가 실행된다.
| |
− | | |
− | (event가 들어오면 자동으로 실행되는 듯하다.)
| |
− | | |
− | | |
− | 웹소켓(자바스크립트)으로 보낼 땐 json으로 변환하여 보내주어야 한다.
| |
− | |<syntaxhighlight lang="python">
| |
− | def chat_message(self, event):
| |
− | message = event['message']
| |
− | | |
− | # Send message to WebSocket
| |
− | self.send(text_data=json.dumps({
| |
− | 'message': message
| |
− | }))
| |
− | </syntaxhighlight>비동기식으로 구성한 경우<syntaxhighlight lang="python">
| |
− | async def chat_message(self, event):
| |
− | message = event["message"]
| |
− | | |
− | # Send message to WebSocket
| |
− | await self.send(text_data=json.dumps({"message": message}))
| |
− | </syntaxhighlight>
| |
− | |-
| |
− | |
| |
− | |보내는 함수의 차이.
| |
− | |
| |
− | |}
| |
− | | |
− | ==== 자바스크립트에서 ====
| |
− | {| class="wikitable"
| |
− | !항목
| |
− | !설명
| |
− | !예시
| |
− | |-
| |
− | |웹소켓 생성
| |
− | |스크립트의 초반에 우측과 같이 주소를 지정하여 소켓을 생성한다.
| |
− | roomname과 같은 변수는 위에서 받아 넘겨주어야 한다.
| |
− | | |
− | | |
− | 웹소켓이 만들어지면 아래 4개의 이벤트 사용이 가능해진다.
| |
− | | |
− | * open – 커넥션이 제대로 만들어졌을 때 발생함
| |
− | * message – 데이터를 수신하였을 때 발생함
| |
− | * error – 에러가 생겼을 때 발생함
| |
− | * close – 커넥션이 종료되었을 때 발생
| |
− | | |
− | 이벤트의 활용은 아래 예시를 참고하자.
| |
− | |<syntaxhighlight lang="python">
| |
− | const chatSocket = new WebSocket(
| |
− | 'ws://' + window.location.host
| |
− | + '/ws/chat/'
| |
− | + roomName
| |
− | + '/'
| |
− | );
| |
− | </syntaxhighlight>
| |
− | |-
| |
− | |message
| |
− | |웹소켓이 데이터를 수신할 때 발생
| |
− | |<syntaxhighlight lang="python">
| |
− | chatSocket.onmessage = function(e) {
| |
− | const data = JSON.parse(e.data);
| |
− | document.querySelector('#chat-log').value += (data.message + '\n');
| |
− | };
| |
− | </syntaxhighlight>
| |
− | |-
| |
− | |close
| |
− | |
| |
− | |<syntaxhighlight lang="python">
| |
− | chatSocket.onclose = function(e) {
| |
− | console.error('Chat socket closed unexpectedly');
| |
− | };
| |
− | </syntaxhighlight>
| |
− | |-
| |
− | |메시지 보내기
| |
− | |만든 소켓으로 메시지 보낼 때.
| |
− | JavaScript 값이나 객체를 JSON 문자열로 변환해야만 한다.
| |
− | |<syntaxhighlight lang="python">
| |
− | chatSocket.send(JSON.stringify({
| |
− | 'message': message
| |
− | }));
| |
− | </syntaxhighlight>
| |
− | |}
| |
| [[분류:장고 웹소켓]] | | [[분류:장고 웹소켓]] |