이번시간에는 간단한 실습으로, 초간단한 Echo 웹 소켓을 구현해 보자. 지난 시간에 장고 프로젝트를 만들었으니 프로젝트 생성에 대한 설명은 생략하고 이번엔 바로 애플리케이션을 만들어보자.
app 생성
먼저 위의 명령어를 실행하여 새로운 장고 applicaion을 생성하자. 생성하고 나면 위의 우측 이미지에서 확인할 수 있듯이 새롭게 만든 app 다이렉토리가 생성된 것을 확인할 수 있다.
그리고 위의 이미지에 나온 순서처럼 settings.py와 app/urls.py 그리고 프로젝트 폴더 아래 있는 urls.py에 빨간색으로 표시된 부분의 코드를 추가해 주면 된다. 이때, app/urls.py 같은 경우는 처음에 app 폴더 아래에 urls.py가 없을 것이다. 따라서, urls.py를 새로 생성해 주고 코드를 추가해 주면 된다.
EchoConsumer 클래스
EchoConsumer 클래스는 웹 소켓 구현을 위해 WebsocketCOnsumer 클래스를 상속받는다. 웹 소켓 클라이언트로부터의 연결/통신에 대한 처리는 WebsocketConsumer 클래스에 모두 구현되어 있으니, 그대로 사용하면 되겠다. 그리고 연결된 웹 소켓 클라이언트로부터 text frame을 받으면 recieve 메서드가 호출되며 text_data 파라미터에 그 text frame 문자열이 담겨있다. 메시지를 받는 즉시, 수신한 문자열 앞에 "You said : " 문자열을 붙여 응답해 보도록 하자. 이때는 send 메서드를 활용한다.
app 폴더 아래에 consumer.py 파일을 새로 생성하고 위와 같이 코드를 작성해 주자. WebsocketConsumer 클래스를 상속받은 EchoConsumer 클래스를 정의하고 있다. 그리고 웹 소켓 수신 메시지를 처리하기 위해 receive 메서드를 재정의한다. 그럼 receive 메서드는 새로운 text/bytes frame을 받을 때마다 호출되게 된다. 그리고 정의되어 있는 것처럼 receive 메서드 내에서 수신한 텍스트 메시지 앞에 "You said : " 문자열을 붙여 그대로 응답하도록 작성하였다.
routing.py
웹 소켓 URL 라우팅을 정의하기 위해 위의 사진과 같이 app/routing.py 파일을 새롭게 생성하고 위와 같은 코드를 적어주자. url.py 파일의 urlpatterns 리스트처럼 websocket_urlpatterns 리스트를 정의한다. Consumer 클래스를 path에 등록할 때는 as_asgi() 호출을 통해 ASGI application 함수를 만들어서 등록한다.
다음으로는 asgi.py 파일에서 ProtocolTypeRouter에 "websocket" 타입을 추가해 주고 URLRouter를 통해서 요청을 분기할 수 있도록, websocket_urlpatterns를 파라미터로 넘긴다.
html 템플릿을 app/templates/app/echo_page.html 경로에 새로 생성하자. 위의 코드를 살펴보면 먼저 WebSocket 자바스크립트 객체를 생성한다. WebSocket 객체의 첫 번째 파라미터로 연결한 웹 소켓 주소를 지정한다. 요청 프로토콜은 ws://이고 호스트명은 localhost:8000, 그리고 요청 경로는 /ws/echo/로 설정하였다. 해당 주소로 웹 소켓을 연결하면 EchoConsumer 클래스의 인스턴스에 의해서 처리될 것이다. 다음은 open, message, error, close 이벤트에 대한 이벤트 핸들러를 정의하였다.
message 이벤트와 close 이벤트에서는 event 객체를 활용할 것이기에 event 파라미터를 받을 것이다. 먼저 onmessage 이벤트를 살펴보면 연결될 웹 소켓 서버로부터 메시지를 받으면 event.data 속성에 수신 데이터가 저장되어 있다.(참고로 EchoConsumer 클래스에서는 문자열 응답을 주므로, event.data도 문자열이다). 위의 예제에서는 수신한 문자열을 단순히 console에 출력하고 있다. 그다음으로는 onerror 이벤트를 살펴보자, 웹 소켓 연결 시에 에러가 발생하면 콘솔에 에러 메시지를 출력하게 정의하였다. 그리고 onclose는 연결된 웹 소켓이 끊어졌을 때 발생하는 이벤트이다. 서버 혹은 클라이언트 단에서 의도적으로 연결을 끊었을 때에는 event.wasClean 속성값이 참이 되며, 네트워크 단절이나 서버 오류로 연결이 끊어졌을 때는 event.wasClean 속성값이 거짓이 된다. 마지막으로 onopen 이벤트를 살펴보면, 지정 주소의 서버에 웹 소켓 연결에 성공하면 발생하는 이벤트이다. 예제에서는 단순히 연결에 성공하면 console에 출력하도록 정의하였다.
웹 브라우저에서 확인하기
python .\manage.py runserver
위의 명령어를 터미널창에 입력하여 서버를 실행하고 http://localhost:8000/echo/ 주소를 입력하면 아래와 같은 화면을 볼 수 있을 것이다(혹시라도 경로를 필자와 다르게 하였다면 본인이 정의한 경로를 입력하면 된다).
그리고 위의 이미지처럼 개발자 도구(F12)를 열어 콘솔창으로 이동한 뒤, ws를 입력하면 웹 소켓이 정상적으로 연결된 걸 확인할 수 있고 ws.send(메시지 내용)을 통해 서버로 "Hello, world!" 문자열을 전송하면 그 즉시 서버로부터 "You said : Hello, world!"라는 문자열을 수신했음을 확인할 수 있다. 그리고 터미널에서 Ctrl + C를 눌러 서버를 중단시키면 브라우저 웹 소켓에서 close 이벤트가 발생됨 또한 확인할 수 있다.