1. websocket → 비동기

  2. wsgi와 asgi

  3. asgi.py

    import os
    
    from django.core.asgi import get_asgi_application
    
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "gaggamagga.settings")
    django_asgi_app = get_asgi_application()
    
    from channels.auth import AuthMiddlewareStack
    from channels.routing import ProtocolTypeRouter, URLRouter
    
    from notification import routing
    
    application = ProtocolTypeRouter(
        {
            "http": django_asgi_app,
            "websocket" : AuthMiddlewareStack(
                URLRouter(
                    routing.websocket_urlpatterns
                )
            )
        }
    )
    
  4. routing.pyurls.py

    from django.urls import re_path
    
    from . import consumers
    
    websocket_urlpatterns = [
        re_path(r"ws/notification/(?P<room_name>\\w+)/$",consumers.NotificationConsumer.as_asgi()),
    ]
    
  5. consumers.pyviews.py

from channels.generic.websocket import AsyncWebsocketConsumer
from channels.db import database_sync_to_async

import json

from .models import Notification

class NotificationConsumer(AsyncWebsocketConsumer):
    @database_sync_to_async
    def create_notification(self, message, author):
        return Notification.objects.create(content=message, user_id=author)
    
    async def connect(self) :
        self.room_name = self.scope["url_route"]["kwargs"]["room_name"]

        await self.channel_layer.group_add(self.room_name, self.channel_name)
        await self.accept()
    async def disconnect(self, code):
        
        # 그룹 떠남
        await self.channel_layer.group_discard(self.room_name, self.channel_name)

    async def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message = text_data_json["message"]
        author = text_data_json["author"]
        user_id = text_data_json["user_id"]
        
        event = {
            'type': 'send_message',
            'message': message,
            "author" : author,
            "user_id" : user_id
        }
        
        # DB 저장
        if int(author) != user_id :
            save_message = await self.create_notification(message=message, author=author)
            
        # 그룹에 메시지 보내기
        await self.channel_layer.group_send(self.room_name, event)         

    # 그룹에서 메시지 받기
    async def send_message(self, event):
        message = event["message"]
        author = event["author"]
        user_id = event["user_id"]

        # 웹소켓에 메시지 보내기
        await self.send(text_data=json.dumps({'message':message, 'author':author, "user_id":user_id}))
// 알람 
const notificationSocket = new WebSocket(
    'wss://'
    + "www.back-gaggamagga.tk"
    + '/ws/notification/'
    + author_id
    + '/'
);

notificationSocket.onmessage = async function (e) {
    const data = JSON.parse(e.data);
    const alarmBox = document.querySelector('.alarm')
    if (payload_parse.user_id == author_id) {
        const alarmContent = document.createElement('div')
        alarmContent.style.display = "flex"
        alarmContent.style.height = "10vh"
        alarmContent.innerHTML = data.message
        alarmBox.appendChild(alarmContent)

        const response = await fetch(`${backendBaseUrl}/notification/${payload_parse.user_id}/`, {
            headers: {
                "authorization": "Bearer " + localStorage.getItem("access")
            },
            method: 'GET'
        })
        .then(response => response.json())
        const notificationButton = document.createElement('button')
        const notificationButtonText = document.createTextNode('확인')
        notificationButton.appendChild(notificationButtonText)
        notificationButton.onclick = async function () {
            await fetch(`${backendBaseUrl}/notification/alarm/${response[0].id}/`, {
                headers: {
                    'content-type': 'application/json',
                    "authorization": "Bearer " + localStorage.getItem("access")
                },
                method: 'PUT',
                body: ''
            })
            alarmBox.innerHTML = ""
            getNotification()
        }
        alarmContent.appendChild(notificationButton)
    }
};

notificationSocket.onclose = function (e) {
    console.error('소켓이 닫혔어요 ㅜㅜ');
};

function alarm() {
    if (payload_parse.user_id != author_id) {
        const message = `<img src="<https://cdn-icons-png.flaticon.com/512/1827/1827422.png>" class="modal-icon"><a style="cursor:pointer;margin:auto; text-decoration:none;" href="review_detail.html?id=${review_id}&place=${place_id}&author=${author_id}">
        <p class="alarm-content">후기에 덧글이 달렸습니다.</p></a>`
        notificationSocket.onopen = () => notificationSocket.send(JSON.stringify({
            'message': message,
            "author": author_id,
            "user_id": payload_parse.user_id
        }
        ))
    }
}

번외

  1. urls.py

    from django.urls import path
    
    from . import views
    
    urlpatterns = [
        # Notification
        path('<int:user_id>/', views.NotificationView.as_view(), name='notification'),
        path('alarm/<int:notification_id>/', views.NotificationDetailView.as_view(), name="notification_detail"),
    ]
    
  2. views.py

    from django.urls import path
    
    from . import views
    
    urlpatterns = [
        # Notification
        path('<int:user_id>/', views.NotificationView.as_view(), name='notification'),
        path('alarm/<int:notification_id>/', views.NotificationDetailView.as_view(), name="notification_detail"),
    ]
    

class NotificationConsumer(AsyncWebsocketConsumer):
    @database_sync_to_async
    def create_notification(self, message, author):
        return Notification.objects.create(content=message, user_id=author)

window.onload = () => {
    getNotification() //알람
}

notificationSocket.onmessage = async function (e) {
    const data = JSON.parse(e.data);
    const alarmBox = document.querySelector('.alarm')
    if (payload_parse.user_id == author_id) {
        const alarmContent = document.createElement('div')
        alarmContent.style.display = "flex"
        alarmContent.style.height = "10vh"
        alarmContent.innerHTML = data.message
        alarmBox.appendChild(alarmContent)

        const response = await fetch(`${backendBaseUrl}/notification/${payload_parse.user_id}/`, {
            headers: {
                "authorization": "Bearer " + localStorage.getItem("access")
            },
            method: 'GET'
        })
        .then(response => response.json())
        const notificationButton = document.createElement('button')
        const notificationButtonText = document.createTextNode('확인')
        notificationButton.appendChild(notificationButtonText)
        notificationButton.onclick = async function () {
            await fetch(`${backendBaseUrl}/notification/alarm/${response[0].id}/`, {
                headers: {
                    'content-type': 'application/json',
                    "authorization": "Bearer " + localStorage.getItem("access")
                },
                method: 'PUT',
                body: ''
            })
            alarmBox.innerHTML = ""
            getNotification()
        }
        alarmContent.appendChild(notificationButton)
    }
};

async function getNotification() {

    const response = await fetch(`${backendBaseUrl}/notification/${payload_parse.user_id}/`, {
        headers: {
            "authorization": "Bearer " + localStorage.getItem("access")
        },
        method: 'GET'
    })
        .then(response => response.json())
    response.forEach(notification => {
        const alarmBox = document.querySelector('.alarm')

        let alarmContent = document.createElement('div')
        alarmContent.setAttribute("id", `alarm${notification.id}`)
        alarmContent.innerHTML = notification.content
        alarmContent.style.display = "flex"
        alarmContent.style.height = "10vh"
        alarmBox.appendChild(alarmContent)

        const notificationButton = document.createElement('button')
        const notificationButtonText = document.createTextNode('확인')
        notificationButton.appendChild(notificationButtonText)
        notificationButton.onclick = async function () {
            await fetch(`${backendBaseUrl}/notification/alarm/${notification.id}/`, {
                headers: {
                    'content-type': 'application/json',
                    "authorization": "Bearer " + localStorage.getItem("access")
                },
                method: 'PUT',
                body: ''
            })
            alarmBox.innerHTML = ""
            getNotification()

        }

        alarmContent.appendChild(notificationButton)
    })
}