인프런 영문 브랜드 로고
인프런 영문 브랜드 로고

인프런 커뮤니티 질문&답변

sunnnwo님의 프로필 이미지

작성한 질문수

파이썬/장고로 웹채팅 서비스 만들기 (Feat. Channels) - 기본편

광장 그룹을 통한 단체 채팅

안녕하세요, onopen() 문제로 질문드립니다.

작성

·

64

·

수정됨

0

 똑같이 코딩한거같은데 Uncaught InvalidStateError: Failed to execute 'send' on 'WebSocket': Still in CONNECTING

이런 문제가 생겨 connect()에서 console.log로 this.ws 찍어보니까. readystate가 0에서 바뀌지 않고 onsubmit()에서도 0인걸 확인했습니다. 그래서 웹소켓이 연결되지 않았으니, this.ws.send()에서 걸리는것이라고 생각은하는데, 어디를 고쳐야 될지 모르겠습니다.

{# Chat room #}
{% extends "chat/base.html" %}
{% block extra-style %}
<style>
.chat-message > div {
    background-color: #3b3b3b;
    color: #e1e1e1;
    border-radius: 0.8em;
    padding: 0.4em;
    margin: 0.4em 0;
    display: inline-block;
    white-space: pre-wrap;
    max-width: 80%;
    word-wrap: break-word;
}
</style>
{% endblock %}
{% block content %}

    <h2>채팅방 :{{ room_name }}</h2>
    {# 웹소켓으로 실시간 채팅 기능 구현하기 #}
    <div class="container">
        <div class="row">
            <div class="col-sm-12">
                <div class="card" style="height: 600px;">
                    <div class="card-header">
                        채팅방:{{ room_name }}
                    </div>
                    <div class="card-body overflow-hidden">
                        <div id="chat_messages" class="w-100 h-100 border-0 overflow-hidden overflow-scroll"></div>
                    </div>
                    <div class="card-footer">
                        <form id="message_form">
                            <input type="text" name="message" class="form-control"
                                    autofocus autocomplete="off" />
                        </form>
                    </div>
            </div>
            <hr class="my-3" />
            <a href="{% url 'chat:index' %}" class="btn btn-primary"> 대기실로 이동 </a>
        </div>
    </div>
</div>

{% endblock %}

{% block extra-script %}
    <script>
    const handler = {
        chat_messages_tag: null,
        ws: null,
        retry: 0,

        init(){
            console.log("handler.init() 실행됨");
            this.chat_messages_tag= document.querySelector("#chat_messages");
            document.querySelector("#message_form").addEventListener("submit", this.onsubmit.bind(this));
        },
        connect(ws_url){
            if(this.ws) {
                this.ws.close();
            }
            this.ws = new WebSocket(ws_url || this.ws?.url);
            console.log(this.ws, "연결1.")
            this.ws.onopen = this.onopen.bind(this);
            this.ws.onclose = this.onclose.bind(this);
            this.ws.onerror = this.onerror.bind(this);
            this.ws.onmessage = this.onmessage.bind(this);
        },
        reconnect()
        {
          this.connect();
        },
        onopen(){
          console.log("웹소켓 서버와 접속, : Room_chat");
          this.retry = 0;
        },
        onclose(event){
            if (!event.wasClean) {
                console.error("웹소켓이 죽었거나, 네트워크 에러");
                if (this.retry < 3)
                {
                    this.retry++;
                    setTimeout(() => {
                        this.reconnect();
                        console.log(`[${this.retry}] 접속 재시도 ...`);
                    }, 1000 * this.retry)
                }
                else
                {
                    console.log("웹소켓 서버에 접속할수 없습니다. 메인페이지로 이동합니다.");
                    window.location.href = "{% url 'chat:index' %}";
                }
            }
        },

        onerror(){
            console.log("웹소켓 에러 발생. 메인페이지로 이동합니다.");
            window.location.href = "{% url 'chat:index' %}" ;
        },
        onmessage(event){
            const message_json = event.data;
            console.log("메세지 수신 :", message_json);

            const { type, message } = JSON.parse(message_json);
            switch (type){
                case "chat.message":
                    this.append_message(message);
                    break;
                default:
                    console.error('Invalid message type: &{type}');
            }

        },
        append_message(message){//인자로 받은 채팅 메세지 로그에 추가
            const element = document.createElement("div");
            element.className = "chat-message";

            const wrapper = document.createElement("div");
            wrapper.textContent = message;
            element.appendChild(wrapper);

            this.chat_messages_tag.appendChild(element);
            this.chat_messages_tag.scrollTop = this.chat_messages_tag.scrollHeight;
        },
        onsubmit(event){ //입력된 채팅 메세지 문자열을 획득하고, 메세지 로그에 추가.
            event.preventDefault();

            const form_data = new FormData(event.target); //폼필드의 값을 오브젝트로 변환하여 props로 저장하고, 폼필드는 리셋합니다.
            const props = Object.fromEntries(form_data);
            event.target.reset(); // reset form

            const { message } = props;
            console.log("웹소켓으로 전송할 메세지: ", message);
            console.log(this.ws, "onsubmit");
            {#this.append_message(message);#}
            this.ws.send(JSON.stringify({
                type: "chat.message",
                message: message,
            }))
        }
    };
    handler.init();

    const protocol = location.protocol === 'https:' ? 'ws:' : 'wss:';
    const ws_url = protocol + "//" + location.host + "/ws" + location.pathname;
    handler.connect(ws_url);
    </script>
{% endblock %}
from django.urls import path
from chat import consumers

websocket_urlpatterns = [
    path("ws/chat/<str:room_name>/chat/", consumers.ChatConsumer.as_asgi()),
]

import os

from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
import app.routing
import chat.routing

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')

django_asgi_app = get_asgi_application()

application = ProtocolTypeRouter({
    "http" : django_asgi_app,
    "websocket" : URLRouter(
       chat.routing.websocket_urlpatterns + app.routing.websocket_urlpatterns,

 )
})
from django.shortcuts import render

# Create your views here.
def index(request):
    return render(request, "chat/index.html")

def room_chat(request, room_name):
    return render(request, "chat/room_chat.html",{
        "room_name": room_name,
    })

답변 2

1

이진석님의 프로필 이미지
이진석
지식공유자

안녕하세요.

서버 쪽 로그를 봐야 명확할 듯 싶습니다.

서버 쪽 에러 로그를 스크린샷으로 보여주시겠어요?

그리고 현재 프로젝트를 help@pyhub.kr 메일로 첨부로 보내주시면, 저도 실행해서 동작을 확인해보겠습니다.

sunnnwo님의 프로필 이미지
sunnnwo
질문자

선생님 해결했습니다. 일단 두가지 정도 문제였던것 같습니다.

첫째로 asgiref가 제대로 설치되지 않았던것같고,

둘째로

const protocol = location.protocol === 'http:' ? 'ws:' : 'wss:';
const ws_url = protocol + "//" + location.host + "/ws" + location.pathname;
handler.connect(ws_url);

여기서 'http:' ? 'ws:' : 'wss:';가 아니라 https로 작성해서 그랬던것같습니다. 위와 같은 이유가 맞을까요..?

같이 고민해주셔서 감사합니다.

이진석님의 프로필 이미지
이진석
지식공유자

오. 다행입니다.

장고 서버가 구동되었으니 asgiref 설치 문제는 아닐 듯 하구요.

말씀하신 대로 웹소켓 접속 scheme를 잘못 쓰셔서, 다른 프로토콜로 요청을 해서 서버에서 요청을 못 받은 듯 합니다. :-)

잘 찾으셨습니다. 👍

0

sunnnwo님의 프로필 이미지
sunnnwo
질문자

서버쪽에서는 이렇게 나오는거같습니다. 이메일로 첨부 드렸습니다. 신경써주셔서 감사합니다.

스크린샷 2025-02-10 10.11.40.png

 

sunnnwo님의 프로필 이미지

작성한 질문수

질문하기