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

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

sunnnwo님의 프로필 이미지

작성한 질문수

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

채팅방 참여자 목록 노출

유저목록 확인 문제 질문드립니다.

작성

·

67

0



 def room_users(request, room_name):
    # room_name으로 해당 방을 찾기
    room = get_object_or_404(Room, name=room_name)

    # 사용자가 방에 참여한 상태인지 확인
    if not room.is_joined_user(request.user):
        return HttpResponse("Unauthorized user", status=401)

    # 방에 접속 중인 사용자 목록 가져오기
    username_list = room.get_online_usernames()

    return JsonResponse({
        "username_list": username_list,
    })

저는 하나의 채팅방만 사용하기때문에 room_pk가 아니라 room_name을 전달했습니다. 근데 page not found가 뜹니다. 제가 생각기로는 name = room_name 전달이 제대로 안되어서 이렇게 뜨는거같습니다. room_pk에 관한 부분을 제대로 처리하지 못해 접속하거나 나갈때 뜨는 메세지도 제대로 작동하지 않고 있습니다.

class Room(OnlineUserMixin, models.Model):
    owner = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
        related_name="owned_room_set",
    )

    name = models.CharField(max_length=100)

    class Meta:
        ordering = ["-pk"]

    @property
    def chat_group_name(self):
        return self.make_chat_group_name(room=self)

chat/routing.py

from django.urls import path, re_path
from chat import consumers

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

chat/urls.py

 

from django.urls import path

from app.urls import urlpatterns
from chat import views

# 향후 url reverse에 활용
app_name = "chat"

urlpatterns=[
    path("", views.index, name = "index"),
    path("<str:room_name>/chat/", views.room_chat, name = "room_chat" ),
    path("<str:room_name>/users/", views.room_users, name = "room_users" ),
    ]

consumers.py

from asgiref.sync import async_to_sync
from channels.generic.websocket import JsonWebsocketConsumer

from chat.models import Room


# 모든 유저가 고정된 채널 레이어 그룹을 가질것.
class ChatConsumer(JsonWebsocketConsumer):
    SQUARE_GROUP_NAME = "1"
    group_name = [SQUARE_GROUP_NAME]
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def connect(self):
        user = self.scope['user']
        self.group_name = self.SQUARE_GROUP_NAME

        if not user.is_authenticated:
            self.close()
        else:
            is_new_join = self.group_name

            if is_new_join:
                async_to_sync(self.channel_layer.group_send)(
                    self.group_name,
                    {
                        "type": "chat.user.join",
                        "username": user.username,
                    }
                )
            async_to_sync(self.channel_layer.group_add)(
                self.group_name,
                self.channel_name
            )
            self.accept()

    def disconnect(self, code):
        if self.group_name:
            async_to_sync(self.channel_layer.group_discard)(
                self.group_name,
                self.channel_name
            )
        user = self.scope['user']

        # if self.room is not None:
        #     is_last_leave = self.room.user_leave(self.channel_name, user)
        #     if is_last_leave:
        #         async_to_sync(self.channel_layer.group_send)(
        #             self.group_name,
        #             {
        #                 "type": "chat.user.leave",
        #                 "username": user.username,
        #             }
        #         )

    def chat_user_join(self, message_dict):
        self.send_json({
            "type": "chat.user.join",
            "username": message_dict["username"],
        })

    def chat_user_leave(self, message_dict):
        self.send_json({
            "type": "chat.user.leave",
            "username": message_dict["username"],
        })

    def chat_message(self, message_dict):
        self.send_json({
            "type": "chat.message",
            "message": message_dict["message"],
            "sender": message_dict["sender"],
        })



    def receive_json(self, content, **kwargs):
        user = self.scope["user"]
        _type = content["type"]

        if _type == "chat.message":
            message = content["message"]
            sender = user.username

            async_to_sync(self.channel_layer.group_send)(
                self.SQUARE_GROUP_NAME,
                {
                   "type": "chat.message",
                    "message": message,
                    "sender": sender,
                }
            )
        else:
            print(f"Invalid message type : ${_type}")

질문 받아주셔서 감사합니다.

 

선생님의 파이썬 장고 가이드를 다 보고 이 강의 질문하는게 맞다고 생각합니다. , 제 팀프로젝트 데드라인이 임박하여 파이썬 장고 가이드를 다 보지 못하고, 제대로 공부하지 못하고 채널스를 공부하게 되어 이런 질문을 드리게되는거같아 죄송합니다.

프로젝트가 끝나도 장고 놓지 않고 제대로 이해할수있게 공부하겠습니다. 감사합니다ㅠ

답변 2

1

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

안녕하세요.

장고 View 단에서 page not found가 뜨는 이유는 여러 가지 입니다.

  1. urlpatterns 리스트에서 매칭된 URL Pattern 을 찾지 못했을 때.

  2. 두번째는 view 내에서 get_object_or_404 메서드 내에서 지정 name의 Room을 찾지 못했을 때, get_object_or_404 메서드 내부에서 Http404 예외가 발생하고 이것이 page not found 응답으로 이어갑니다.

어떤 상황인지 파악하셔야 합니다. 단지 page not found 라고만 인지하시면 문제 해결이 어렵습니다. 현재 어떤 URL로 요청을 했고, 어떤 응답화면을 보고 계신지 확인을 하시면 좋습니다. settings.DEBUG = True 일 때에는 에러응답에 디버깅에 필요한 정보를 주거든요.

요청 URL과 응답화면 스크린샷, 그리고 찾을려고 하시는 Room 레코드의 존재유무를 확인해서 보여주시겠어요?

화이팅입니다. :-)

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

스크린샷 2025-02-14 09.42.20.png.webp

감사합니다 선생님.

room 레코드의 존재유무를 확인하는 걸 잘 모르겠는데, 05-06 모델을 통한 데이터베이스 조회 강의를 들어보면 될까요?

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

No room matches the given query. 메시지는 지정 Room조회에 실패한 것입니다. 해당 name의 레코드가 없다는 오류구요. 이는 다른 강의를 보는 것으로 해결되지 않습니다. 현재 db에 없다는 것이니 db 데이터를 확인해보셔야합니다. name 컬럼 값과 조회를 시도하는 값이 미묘하게 달라서 조회에 실패하실 수도 있습니다.

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

넵 감사합니다. 선생님 코드로 공부해보면서 만들어보겠습니다. 감사합니다.

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

update_user_list() {
		const html = [...this.username_set]
			.map(username => `
				<li>
					${username}
					<button class="btn btn-primary btn-sm" onclick="inviteUser('${username}')">초대</button>
					<a href="{% url 'accounts:profile' %}" class="btn btn-secondary btn-sm" onclick="viewProfile('${username}')">프로필</a>
				</li>
			`)
			.join('');
		document.querySelector("#user_list").innerHTML = html;
	
		document.querySelector("#user_count").textContent = `(${this.username_set.size}명)`;
	},

이렇게 작성하고 나니, 본인의 프로필만 볼 수 있고, 남의 프로필은 접속이 안되는거같습니다. 어떨뗀 CSRF 에러도 발생한 경우도 있고요. 남의 프로필을 보려면 권한을 따로 줘야하는건가요?

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

애플리케이션의 동작이 원하는 대로 동작하지 않을 때에는, 디버거를 물려서 확인하시는 것이 가장 정확합니다. 제 장고/리액트 강의에서도 처음 개발환경 섹션에서 vscode 혹은 파이참 프로페셔널을 통한 디버거를 사용하는 방법이 있으니 (미리보기로 제공되기에 강의 수강없이도 확인하실 수 있습니다.), 이를 참고하여 디버거를 통해 어떤 요청이 있을 때 해당 루틴이 수행되는지를 확인해보시는 것이 가장 정확합니다.

그리고 장고에서 csrf 검증 오류는, 장고는 모든 POST 요청에서 보안을 위해 csrf token을 확인합니다. <form> 태그를 통한 요청이시라면 {% csrf_token %} 템플릿 태그를 통해 추가하실 수 있구요. 자바스크립트 fetch/axios 등을 통한 요청이라면 매 요청에서 csrf token 값을 넣어주셔야 합니다. 이와 관련해서는 아래 공식문서에서도 확인하실 수 있습니다.

https://docs.djangoproject.com/en/5.1/howto/csrf/

그리고, 다른 이의 프로필을 보지 못하는 부분은 "안된다"라고 하시기보다, accounts 앱 profile 뷰 구현이 어떻게 되어있는 지 먼저 확인해보시고, 그 구현 기반에서 질문을 해주시는 것이 우선일 것입니다. 제 실습코드라면, profile 뷰는 로그인 유저, 본인의 프로필 만을 응답토록 되어있구요. 다른 유저의 프로필도 응답토록 하실려면 profile 뷰를 그에 맞게 수정하셔야될 것입니다.

아직 웹과 장고의 여러 개념들이 정리되지 않으신 상황이신 듯 보입니다. 살펴보시고, 설명이 필요하시면 Google Meet이나 Zoom을 통해 제가 질답과 코드를 직접 봐드려도 될까요?

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

감사합니다. 위에 말씀 주신 사항 확인해보고 해결이 안된다면 염치없지만 부탁드리겠습니다. 감사합니다 선생님.

0

인프런 AI 인턴님의 프로필 이미지

안녕하세요, 인프런 AI 인턴입니다. sunnnwo님.

채팅방에 참여하는 유저 목록을 확인하는 과정에서 발생한 문제에 대해 몇 가지 점검할 사항과 힌트를 드리겠습니다.

  1. room_users 함수의 get_object_or_404 사용:

    • get_object_or_404(Room, name=room_name)을 사용하는데, room_name이 데이터베이스에 저장된 Roomname 필드와 정확히 일치하는지 확인이 필요합니다. 만약 일치하지 않으면 404 오류가 발생할 수 있습니다.
  2. URL 경로 설정 확인:

    • chat/urls.py에서 정의된 URL 패턴이 views.room_users에 올바르게 매핑되는지 확인하세요:
      
      path("<str:room_name>/users/", views.room_users, name="room_users"),
      

    • URL이 올바르게 경로 붙임에 따라 나와야 하며, 이에 대한 잘못된 경로 설정이 있는지 확인해야 합니다.
  3. Room 모델의 is_joined_user 메서드:

    • 추가적인 디버깅을 위해 Room 모델의 is_joined_user 메서드가 제대로 동작하는지 확인 바랍니다. 이 메서드가 현재 사용자가 방에 참석하고 있는지를 명확하게 반환하고 있는지 확인이 필요합니다.
  4. 로그를 통해 서버 에러 확인:

    • 가능한 서버 로그를 확인하여 추가 에러 메시지를 통해 더 많은 정보를 얻을 수 있습니다. 특히, View 함수 호출 시 다른 예외나 오류가 있는지 확인합니다.
  5. Django 프로젝트 설정:

    • 프로젝트 전체의 Django 설정 파일에서 ALLOWED_HOSTS, DEBUG, URL Configuration 등을 점검하여 프로젝트 전반의 서버 설정과 URL 매칭 문제를 파악해도 좋습니다.

이러한 오류들은 보통 URL 설정에서의 오타나, 데이터베이스의 잘못된 참조로 인해 발생하는 경우가 많습니다.

아래의 링크는 Django 채널스 관련하여 유사한 설정 문제를 다룬 게시물입니다:
- 게시물: 하나의 채팅방만 만들어보려고 하는데 잘 안되고 있습니다.

해당 해결책이 도움이 되길 바랍니다. 문제를 해결하면서 이해하는 방법이 굉장히 효과적입니다. 유명한 프로젝트와 프레임워크를 공부하며 경험을 쌓아 가시길 응원합니다.

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

sunnnwo님의 프로필 이미지

작성한 질문수

질문하기