IT/etc

파이어베이스 실시간 데이터베이스로 채팅 구현2 (코드)

토희 2023. 9. 5. 16:04
728x90

구조는 이렇게 구성했다

 

1. 유저 생성 (user_list.html)

현재 프로젝트는 테스트용으로 만들었기 때문에 유저가 개념이 없어서, 페이지에서 유저를 생성해 준다

새 유저 생성 버튼을 클릭하면, 파이어베이스에 새로운 유저가 생성된다

 

// 새로운 사용자 생성
function createNewUser() {
    const userUID = self.crypto.randomUUID();
    // 새로운 사용자를 Firebase 데이터베이스에 저장
    dbRef.child(userUID).set({
        // 유저의 정보 넣기
        userName : "홍길동",
        userEmail :"test@naver.com"
    }).then(() => {
        // 데이터베이스에 사용자를 추가한 후 페이지 다시 로드
        window.location.reload();
    });
}

 

유저 생성 전,

 

유저 생성 후,

 

 

채팅은 나 포함 다른 한명 최소 2명이 필요하기 때문에, 유저를 한명 더 만들어 준다

헷갈릴 수 있으니, createNewUser() 실행전 userEmail과 userName 를 변경하여 만들어주었다

 

생성하고 나면은 유저 전체 리스트에 userUID가 추가된다

 

// 전체 유저 리스트 가져오기
function getAllUsers(callback) {
    const usersList = [];
    dbRef.once("value", snapshot => {
        snapshot.forEach(userSnapshot => {
            const userUID = userSnapshot.key;
            usersList.push(userUID);
        });
        // 전체 사용자 리스트를 가져온 후에 콜백 함수를 호출하여 전달
        callback(usersList);
    });
}

 

// getAllUsers에서 usersList 가져와서 동적으로 추가 & 본인 여부 체크 
// & 상대방과 이미 채팅방 있는지 체크(checkExistingChatRoom()) 해서 채팅방 페이지 이동

async function displayAllUsers(usersList) {
    const userListElement = document.getElementById("userList");
    userListElement.innerHTML = ""; // 기존 리스트 초기화

    usersList.forEach(userUID => {
        const li = document.createElement("li");
        li.innerText = `유저 : ${userUID}`;
        li.addEventListener("click", async () => {
            // 유저 클릭시 채팅방 만들기
            if (userUID === senderUID) {
                alert("본인에게는 채팅을 할수 없습니다.")
            } else {
                const chatRoomNumber = await checkExistingChatRoom(userUID, senderUID);
                if (chatRoomNumber != null) {
                    // 채팅방 있음
                    window.location.href = `chat1?chatRoomNumber=${chatRoomNumber}&userUID=${userUID}&senderUID=${senderUID}`;
                } else {
                    // 채팅방 없음
                    window.location.href = `chat1?userUID=${userUID}&senderUID=${senderUID}`;
                }
            }
        });
        userListElement.appendChild(li);
    });
}
// 채팅방이 이미 존재하는지 확인하는 함수
async function checkExistingChatRoom(userUID, currentUserUID) {
    const chatRoomsRef = firebase.database().ref("chats");
    const snapshot = await chatRoomsRef.once("value");
    const chatSnapshot = snapshot.val();

    for (const chatRoomNumber in chatSnapshot) {
        const participants = chatSnapshot[chatRoomNumber].participants;
        if (participants[userUID] && participants[currentUserUID]) {
            return chatRoomNumber;  // 이미 채팅방이 존재함
        }
    }
    return null; // 채팅방이 없음
}

 

나를 홍길동으로 가정한다고 하고, 유재석에게 채팅메세지를 보내보자

아까 만들어준 유저에서 홍길동의 UID를 넣어준다

현재에서는 로그인 개념이 없기 때문에 이렇게 하는거고, 로그인 가능한 프로젝트면 유저정보에서 채팅UID를 가져오면 된다

// 사용자 UID를 가져오는 가정
const senderUID = "0dad4e6f-add4-447f-aef0-a3a096be4e00"; // 회원의 UID

 

페이지 이동도 파라미터에 정보를 담아주었지만,

보안을 생각해서 안 보이는 방식으로 정보를 가지고 있다가 페이지 이동을 해도 될것 같다

 

홍길동의 UID (0dad4e6f-add4-447f-aef0-a3a096be4e00) 를 클릭할 경우, 이렇게 알림창이 뜨고

 

다른 유저의 UID를 클릭할 경우, 채팅 메세지 보낼수 있는 페이지로 이동한다

파라미터를 확인해보자 

userUID : 다른 유저의 UID

senderUID : 본인의 UID ( 메세지 보내는 사람 )

userUID & sendeUID는 각각 마음대로 정하면 된다  

 

 

 

2. 채팅 메세지 보내기 (chat1.html)

내용을 작성해 주세요! 인풋 박스에 내용을 입력하고 전송 버튼을 클릭하면 위의 사진처럼 메세지가 보내진다

메세지를 보내면 파이어베이스에서는 이렇게 저장된다

 

 

chats ( 채팅룸, chattingRoom 이렇게 구조화해도 된다 ) 밑에 참여하고 있는 유저 UID를 넣는다

그리고 messages에 채팅 UID 를 넣고 그 밑에 메세지 UID 생성 그 밑에 메세지 관련 정보를 넣어줬다

( 보내는 사람의 UID, 메세지, 시간, 상대방의 체크여부 등등을 넣으면 된다 ) 

 

// Firebase 실시간 데이터베이스 참조
const dbRef = firebase.database().ref("messages");
const chatsRef = firebase.database().ref("chats"); // 채팅방

const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
const senderUID = urlParams.get('senderUID'); // 본인 UID
const userUID = urlParams.get('userUID'); // 상대방 UID
let chatRoomNumber = urlParams.get('chatRoomNumber');
if(chatRoomNumber == undefined){
    chatRoomNumber = self.crypto.randomUUID();
}


// userUID 추가해서 메시지 보내기
function sendMessage() {
    const messageInput = document.getElementById("messageText");
    const message = messageInput.value;
    if (message.trim() !== "") {
        // 채팅방이 없으면 새로 만들어주기
        if(urlParams.get('chatRoomNumber') == undefined){
            createChatRoom(chatRoomNumber);
        }
        dbRef.child(chatRoomNumber).push().set({
            senderUID: senderUID,
            text: message,
            timestamp: firebase.database.ServerValue.TIMESTAMP,
            checked: false // 상대방이 읽음 여부, 처음에는 디폴트로 false로, 1:1채팅일 경우 이 구조 다대다 일경우는 구조 변경해야함
        });
        messageInput.value = "";
    }
}

function createChatRoom(chatRoomNumber) {
    // 임의의 채팅방 번호 생성
    const participants = {
        [senderUID]: true,
        [userUID] : true
        // 여러 사용자가 있다면 추가로 참여자를 넣어주기, 1:1 채팅이니까 지금은 2명이면 됨
    };

    // 채팅방 생성
    chatsRef.child(chatRoomNumber).set({ participants })
}

// 채팅방 메세지 수신
dbRef.child(chatRoomNumber).on("child_added", snapshot => {
    const message = snapshot.val();
    const messageDiv = document.createElement("div");
    // messageDiv.innerText = message.userName + message.text;
    messageDiv.innerText = message.text;

    if (message.senderUID === senderUID) {
        // 현재 사용자가 보낸 메시지의 경우 'my_message' 클래스 추가
        messageDiv.classList.add("my_message");
    } else {
        // 다른 사용자가 보낸 메시지의 경우 'other_message' 클래스 추가
        messageDiv.classList.add("other_message");
        // 상대방이 보낸 메세지 true 처리, 채팅방에 들어올 때도 true 처리하지만,
        // 채팅방 안에 있을때도 상대방 메세지 보내면 true 처리
        if (!message.checked) {
            const messageRef = dbRef.child(chatRoomNumber).child(snapshot.key);
            messageRef.update({ checked: true });
        }
    }

    document.getElementById("messageAppend").appendChild(messageDiv);
});

파라미터에서 채팅방 번호가 없으면 메세지 보낼때 새로 만들어 주고,

메세지에 대한 정보를 넣어서 저장해준다

 

 

 

3. 내 채팅방 리스트(user_chat_list.html)

// 사용자 UID를 가져오는 가정
const senderUID = "0dad4e6f-add4-447f-aef0-a3a096be4e00"; // 회원의 UID

위에서 미리 말한것 처럼 현재 프로젝트에 로그인 개념이 없기 때문에 senderUID에 내 UID를 넣어준다

 

아까 생성한 채팅방이 리스트로 나와있는거를 확인할 수 있다

 window.onload = () => {
    // getUserChatRooms 함수 호출 시 콜백 함수를 전달하여 데이터를 가져온 후에 displayUserChatRooms 함수 호출
    getUserChatRooms(displayUserChatRooms);
};
// 사용자의 채팅방 리스트 가져오기
function getUserChatRooms(callback) {
    const userChatRooms = [];
    dbRef.once("value", snapshot => {
        snapshot.forEach(chatSnapshot => {
            const chatRoomNumber = chatSnapshot.key;
            const participants = chatSnapshot.val().participants;
            if (participants[senderUID]) {
                userChatRooms.push(chatRoomNumber);
            }
        });
        // 채팅방 리스트를 가져온 후에 콜백 함수를 호출하여 전달
        callback(userChatRooms);
    });
}

// 채팅방 li에 추가
async function displayUserChatRooms(chatRooms) {
    const chatRoomList = document.getElementById("chatRoomList");

    for (const chatRoomNumber of chatRooms) {
        const li = document.createElement("li");
        const unreadCount = await countUnreadMessages(chatRoomNumber, senderUID);

        li.innerText = `채팅방: ${chatRoomNumber} (읽지 않은 메시지: ${unreadCount}개)`;
        li.addEventListener("click", async () => {
            // 클릭한 채팅방 번호를 chat.html로 전달하면서 페이지 이동
            await messageRead(chatRoomNumber, senderUID); // await를 사용하여 messageRead가 완료될 때까지 기다림
            window.location.href = `chat1?chatRoomNumber=${chatRoomNumber}&senderUID=${senderUID}`;
        });

        chatRoomList.appendChild(li);
    }
}

// 안 읽은 메세지 카운트
async function countUnreadMessages(chatRoomNumber, userUID) {
    const messagesRef = firebase.database().ref(`messages/${chatRoomNumber}`);

    let unreadCount = 0;

    // child_added 이벤트 리스너 추가
    messagesRef.on("child_added", snapshot => {
        const message = snapshot.val();
        if (message.senderUID !== userUID && !message.checked) {
            unreadCount++;
        }
        // 새 메시지가 추가될 때마다 unreadCount가 갱신됩니다.
        console.log(unreadCount);

        // 채팅방 리스트의 내용을 업데이트
        updateChatRoomList(chatRoomNumber, unreadCount);
    });

    // 최초에 메시지를 읽지 않은 메시지들을 카운트하여 반환합니다.
    return unreadCount;

}

// 메시지를 읽었을 때 호출되는 함수
async function messageRead(chatRoomNumber, userUID) {
    return new Promise((resolve, reject) => {
        const messagesRef = firebase.database().ref(`messages/${chatRoomNumber}`);
        messagesRef.once("value", snapshot => {
            snapshot.forEach(messageSnapshot => {
                const messageID = messageSnapshot.key;
                const message = messageSnapshot.val();
                if (message.senderUID !== userUID && !message.checked) {
                    const messageRef = messagesRef.child(messageID);
                    messageRef.update({ checked: true });
                }
            });
            resolve(); // 작업이 끝나면 resolve 호출
        });
    });
}

// 메세지가 추가되면 안 읽은 메시지 count 업데이트, 실시간으로 변동됨
function updateChatRoomList(chatRoomNumber, unreadCount) {
    const chatRoomList = document.getElementById("chatRoomList");
    const chatRoomItems = chatRoomList.getElementsByTagName("li");

    for (let i = 0; i < chatRoomItems.length; i++) {
        const item = chatRoomItems[i];
        if (item.innerText.includes(`채팅방: ${chatRoomNumber}`)) {
            // 채팅방 리스트에서 해당 채팅방을 찾아 업데이트
            item.innerText = `채팅방: ${chatRoomNumber} (읽지 않은 메시지: ${unreadCount}개)`;
            break; // 해당 채팅방을 찾았으면 루프 종료
        }
    }
}

위의 코드는

채팅방 리스트 불러오기,

채팅방에 읽지 않은 메세지 count,

채팅방 클릭시 상대방이 보낸 메세지 읽음 처리, 

상대방이 메세지 보냈을경우,

읽지 않은 메세지 실시간 count 변경해주기

 

채팅방을 클릭하면, 아까 보낸 메세지가 보여지고

파라미터를 보면 이번에는 chatRoomNumber도 보여지는거를 확인할수 있다.

http://localhost:8080/chatting/chat1?chatRoomNumber=03b90872-38d3-4687-9f3b-fb0e3b112176&senderUID=0dad4e6f-add4-447f-aef0-a3a096be4e00

 

 

실시간으로 채팅이 되는지 확인하기 위해 

senderUID=0dad4e6f-add4-447f-aef0-a3a096be4e00에 

다른 사람의 UID를 넣어줘서 창을 2개 열어줄거이다

 

 

http://localhost:8080/chatting/chat1?chatRoomNumber=03b90872-38d3-4687-9f3b-fb0e3b112176&senderUID=1c862baa-b109-4218-af02-c8f39343920d

 

왼쪽 창은 senderUID = 0dad4e6f-add4-447f-aef0-a3a096be4e00

오른쪽은 senderUID = 1c862baa-b109-4218-af02-c8f39343920d 이다

 

메세지를 보내면 상대방 창에도 실시간 메세지가 날라온다

내가 보낸 메세지가 오른쪽에 있도록 ui를 표시했다

 

 

 

다시 메세지를 보내보고, 읽지 않은 메세지 카운트를 봐본다

메세지를 보내고 user_chat_list 페이지로 가면, 내가 읽지 않은 메세지 0개로 표시된다

채팅방에 있으면 상대방이 메세지를 보낼때, 바로 true 처리를 해준다

 

 

아래 영상은 내가 user_chat_list 페이지에 있고 상대방이 채팅을 보낼경우, 읽지 않은 메세지가 실시간 올라가는 거를 보여준다

 

 

채팅방을 들어갔다가 나오면은 읽지 않은 메세지 0개로 변경

 

 

 

 

위의 로직으로도 간단한 채팅은 구현할 수 있다

( 1:1 채팅일 경우 채팅방의 participants 아니면, 메세지 보내는 거를 막는다는가의 유효성은 추가해야할것 같다 )

 

 

 

++ 추가 기능을 애기해보자면

 

1) 새 채팅방 생성(user_chat_list)

 

 // 새로운 채팅방 생성
function createChatRoom() {
    // 임의의 채팅방 번호 생성
    const chatRoomNumber = self.crypto.randomUUID();
    const participants = {
        [senderUID]: true,
        // 여러 사용자가 있다면 추가로 참여자를 넣어주기
    };

    // 채팅방 생성
    dbRef.child(chatRoomNumber).set({ participants })
    // chatRoomList 비우고 새로 추가
    const chatRoomList = document.getElementById("chatRoomList");
    chatRoomList.innerHTML = "";
    getUserChatRooms(displayUserChatRooms);
}

 

2) 전체 채팅방 리스트 가져오기 (all_chat_list.html)

아까까지는 03b90872-38d3-4687-9f3b-fb0e3b112176 로 채팅을 구현했고,

새로 만들어진 ebd7b7e7-a60e-49fa-b2bb-5e85dd173b13 를 리스트로 가져왔다

ebd7b7e7-a60e-49fa-b2bb-5e85dd173b13 에서도 메세지를 보낼수 있지만,

다대다 채팅일 경우, 메세지 읽음 여부 checked는 구조를 변경해줘야 한다 (주의!)

 

 

 

 

이상 간단하게 파이어베이스의 실시간 데이터로 채팅을 구현하는 방법을 알아보았다

 

 

 

전체코드

user_list.html

<html lang="ko">
<head>
    <script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-database.js"></script>

    <script>
        const firebaseConfig = {
            apiKey: "",
            authDomain: "",
            databaseURL: "",
            projectId: "",
            storageBucket: "",
            messagingSenderId: "",
            appId: ""
        };

        firebase.initializeApp(firebaseConfig);
        // const app = initializeApp(firebaseConfig); // 이거는 9버전 문법

        // Firebase 실시간 데이터베이스 참조
        const dbRef = firebase.database().ref("users"); // 유저

        // 사용자 UID를 가져오는 가정
        const senderUID = "0dad4e6f-add4-447f-aef0-a3a096be4e00"; // 회원의 UID

        // 새로운 사용자 생성
        function createNewUser() {
            const userUID = self.crypto.randomUUID();
            // 새로운 사용자를 Firebase 데이터베이스에 저장
            dbRef.child(userUID).set({
                // 유저의 정보 넣기
                userName : "유재석",
                userEmail :"user@naver.com"
            }).then(() => {
                // 데이터베이스에 사용자를 추가한 후 페이지 다시 로드
                window.location.reload();
            });
        }

        // 전체 유저 리스트 가져오기
        function getAllUsers(callback) {
            const usersList = [];
            dbRef.once("value", snapshot => {
                snapshot.forEach(userSnapshot => {
                    const userUID = userSnapshot.key;
                    usersList.push(userUID);
                });
                // 전체 사용자 리스트를 가져온 후에 콜백 함수를 호출하여 전달
                callback(usersList);
            });
        }

        // 유저 리스트 가져와서 동적으로 추가
        async function displayAllUsers(usersList) {
            const userListElement = document.getElementById("userList");
            userListElement.innerHTML = ""; // 기존 리스트 초기화

            usersList.forEach(userUID => {
                const li = document.createElement("li");
                li.innerText = `유저 : ${userUID}`;
                li.addEventListener("click", async () => {
                    // 유저 클릭시 채팅방 만들기
                    if (userUID === senderUID) {
                        alert("본인에게는 채팅을 할수 없습니다.")
                    } else {
                        const chatRoomNumber = await checkExistingChatRoom(userUID, senderUID);
                        if (chatRoomNumber != null) {
                            // 채팅방 있음
                            window.location.href = `chat1?chatRoomNumber=${chatRoomNumber}&userUID=${userUID}&senderUID=${senderUID}`;
                        } else {
                            // 채팅방 없음
                            window.location.href = `chat1?userUID=${userUID}&senderUID=${senderUID}`;
                        }
                    }
                });
                userListElement.appendChild(li);
            });
        }

        // 채팅방이 이미 존재하는지 확인하는 함수
        async function checkExistingChatRoom(userUID, currentUserUID) {
            const chatRoomsRef = firebase.database().ref("chats");
            const snapshot = await chatRoomsRef.once("value");
            const chatSnapshot = snapshot.val();

            for (const chatRoomNumber in chatSnapshot) {
                const participants = chatSnapshot[chatRoomNumber].participants;
                if (participants[userUID] && participants[currentUserUID]) {
                    return chatRoomNumber;  // 이미 채팅방이 존재함
                }
            }
            return null; // 채팅방이 없음
        }


        window.onload = () => {
            // 전체 채팅방 리스트 가져오기
            getAllUsers(displayAllUsers);
        };


    </script>
    <style>
        #userList li {
            cursor: pointer;
        }
    </style>
</head>
<body>
    <button onclick="createNewUser()">새 유저 생성</button>

    <h2>유저 전체 리스트</h2>
    <ul id="userList">
        <!-- 여기에 유저 리스트를 동적으로 추가하는 JavaScript 코드 넣기 -->
    </ul>
</body>
</html>

 

chat1.html

<html lang="ko">
<head>
    <script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-database.js"></script>

    <script>
        const firebaseConfig = {
            apiKey: "",
            authDomain: "",
            databaseURL: "",
            projectId: "",
            storageBucket: "",
            messagingSenderId: "",
            appId: ""
        };

        firebase.initializeApp(firebaseConfig);
        // const app = initializeApp(firebaseConfig); // 이거는 9버전 문법

        // Firebase 실시간 데이터베이스 참조
        const dbRef = firebase.database().ref("messages");
        const chatsRef = firebase.database().ref("chats"); // 채팅방

        const queryString = window.location.search;
        const urlParams = new URLSearchParams(queryString);
        const senderUID = urlParams.get('senderUID'); // 본인 UID
        const userUID = urlParams.get('userUID'); // 상대방 UID
        let chatRoomNumber = urlParams.get('chatRoomNumber');
        if(chatRoomNumber == undefined){
            chatRoomNumber = self.crypto.randomUUID();
        }

        // userUID 추가해서 메시지 보내기
        function sendMessage() {
            const messageInput = document.getElementById("messageText");
            const message = messageInput.value;
            if (message.trim() !== "") {
                // 채팅방이 없으면 새로 만들어주기
                if(urlParams.get('chatRoomNumber') == undefined){
                    createChatRoom(chatRoomNumber);
                }
                dbRef.child(chatRoomNumber).push().set({
                    senderUID: senderUID,
                    text: message,
                    timestamp: firebase.database.ServerValue.TIMESTAMP,
                    checked: false // 상대방이 읽음 여부, 처음에는 디폴트로 false로, 1:1채팅일 경우 이 구조 다대다 일경우는 구조 변경해야함
                });
                messageInput.value = "";
            }
        }

        function createChatRoom(chatRoomNumber) {
            // 임의의 채팅방 번호 생성
            const participants = {
                [senderUID]: true,
                [userUID] : true
                // 여러 사용자가 있다면 추가로 참여자를 넣어주기, 1:1 채팅이니까 지금은 2명이면 됨
            };

            // 채팅방 생성
            chatsRef.child(chatRoomNumber).set({ participants })
            // TODO 채팅방 생기면 채탕방 리스트 가져오게 온로드 해야함
        }

        // 채팅방 메세지 수신
        dbRef.child(chatRoomNumber).on("child_added", snapshot => {
            const message = snapshot.val();
            const messageDiv = document.createElement("div");
            // messageDiv.innerText = message.userName + message.text;
            messageDiv.innerText = message.text;

            if (message.senderUID === senderUID) {
                // 현재 사용자가 보낸 메시지의 경우 'my_message' 클래스 추가
                messageDiv.classList.add("my_message");
            } else {
                // 다른 사용자가 보낸 메시지의 경우 'other_message' 클래스 추가
                messageDiv.classList.add("other_message");
                // 상대방이 보낸 메세지 true 처리, 채팅방에 들어올 때도 true 처리하지만,
                // 채팅방 안에 있을때도 상대방 메세지 보내면 true 처리
                if (!message.checked) {
                    const messageRef = dbRef.child(chatRoomNumber).child(snapshot.key);
                    messageRef.update({ checked: true });
                }
            }

            document.getElementById("messageAppend").appendChild(messageDiv);
        });

    </script>

    <style>
        .side_bar_wrap{
            display: flex;
            padding-top: 45px;
            padding-bottom: 206px;
        }
        .side_bar_wrap .side_bar{
            margin-left: 30px;
            flex: none;
            width: 270px;
        }
        .side_bar_wrap .side_bar .side_bar_con .top{
            background: #FFFDF3;
            border-radius: 5px;
            padding: 39px 21px;
            box-sizing: border-box;
            height: 550px;
        }
        .side_bar_wrap .side_bar .side_bar_con .top h1{
            text-align: center;
            font-size: 18px;
            font-weight: 500;
            position: relative;
        }
        .side_bar_wrap .side_bar .side_bar_con .top h1 i{
            position: absolute;
            top: -12px;
        }
        .side_bar_wrap .side_bar .side_bar_con .top h5{
            margin-top: 29px;
            text-align: center;
            font-size: 14px;
        }
        .side_bar_wrap .side_bar .side_bar_con .top a{
            display: flex;
            align-items: center;
            justify-content: center;
            margin: 0 auto;
            width: 80px;
            height: 35px;
            line-height: 35px;
            text-align: center;
            background: #FFFFFF;
            border: 1px solid #FFB84D;
            border-radius: 5px;
            margin-top: 13px;
            margin-bottom: 34px;
        }
        .side_bar_wrap .side_bar .side_bar_con .top p{
            font-size: 12px;
            color:  #929292;
            line-height: 20px;
        }
        .side_bar_wrap .side_bar .side_bar_con.on .top{
            padding: 39px 0px;
        }
        .side_bar_wrap .side_bar .side_bar_con.on .top h1{
            text-align: left;
            padding-left: 23px;
        }

        .side_bar_wrap .side_bar .side_bar_con.on .top h1 i{
            position: unset;
            position: relative;
            top: 3px;
        }
        .side_bar_wrap .side_bar .side_bar_con.on .top .comment_box{
            font-size: 11px;
            overflow-y: scroll;
            height: 472px;
            margin-top: 15px;
        }
        .side_bar_wrap .side_bar .side_bar_con.on .top .comment_box .teacher{
            background: #fff;
            border: 1px solid #EBEBEB;
            border-radius: 5px;
            padding: 15px 14px;
            width: 240px;
            box-sizing: border-box;
            margin-left: 20px;
            position: relative;
        }
        .side_bar_wrap .side_bar .side_bar_con.on .top .comment_box > div:nth-child(n + 2){
            margin-top: 9px;
        }
        .side_bar_wrap .side_bar .side_bar_con.on .top .comment_box > div:last-child{
            margin-bottom: 30px;
        }
        .side_bar_wrap .side_bar .side_bar_con .bottom{
            position: relative;
            background: #FBFBFB;
            border: 1px solid #ECECEC;
            border-radius: 0px 0px 5px 5px;
            text-align: center;
            height: 130px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 13px;
            padding: 0 20px;
        }
        .side_bar_wrap .side_bar .side_bar_con.on .bottom{
            background: #fff;
        }
        .side_bar_wrap .side_bar .bottom .tarsfer button{
            width: 50px;
            height: 25px;
            background: #F1F1F1;
            border-radius: 5px;
            position: absolute;
            right: 14px;
            bottom: 12px;
            font-size: 12px;
        }
        .side_bar_wrap .side_bar .bottom textarea{
            height: 67px;
            border: none;
            resize: none;
            position: relative;
            top: -13px;
            font-size: 12px;
            width: 100%;
        }
        .side_bar_wrap .side_bar .bottom textarea::placeholder{
            text-align: center;
            background: #fff;
            border: none;
        }

        #messageAppend .my_message {
            /* 내가 보낸 메시지의 스타일 지정 */
            background-color: #f5f5f5;
            border-radius: 8px;
            padding: 8px;
            text-align: right;
        }

        #messageAppend .other_message {
            /* 다른 사용자가 보낸 메시지의 스타일 지정 */
            background-color: #eaeaea;
            border-radius: 8px;
            padding: 8px;
            text-align: left;
        }
    </style>
</head>
<body>
    <div class="side_bar_wrap">
        <!-- sidebar -->
        <div class="side_bar">
            <div class="side_bar_con on">
                <div class="top ">
                    <h1>채팅<i><img src="/assets/images/i_comment.png" alt=""></i></h1>
                    <div class="comment_box scrollbar" id="messageAppend"></div>
                </div>
                <div class="bottom">
                <textarea class="scrollbar" name="" id="messageText" cols="30" rows="10" placeholder="내용을 작성해 주세요!"></textarea>
                    <div class="tarsfer">
                        <button type="button" onclick="sendMessage()">전송</button>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>
</html>

 

user_chat_list.html

<html lang="ko">
<head>
    <script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-database.js"></script>

    <script>
        const firebaseConfig = {
            apiKey: "",
            authDomain: "",
            databaseURL: "",
            projectId: "",
            storageBucket: "",
            messagingSenderId: "",
            appId: ""
        };

        firebase.initializeApp(firebaseConfig);
        // const app = initializeApp(firebaseConfig); // 이거는 9버전 문법

        // Firebase 실시간 데이터베이스 참조
        const dbRef = firebase.database().ref("chats"); // 채팅방

        // 사용자 UID를 가져오는 가정
        const senderUID = "0dad4e6f-add4-447f-aef0-a3a096be4e00"; // 회원의 UID

        // 새로운 채팅방 생성
        function createChatRoom() {
            // 임의의 채팅방 번호 생성
            const chatRoomNumber = self.crypto.randomUUID();
            const participants = {
                [senderUID]: true,
                // 여러 사용자가 있다면 추가로 참여자를 넣어주기
            };

            // 채팅방 생성
            dbRef.child(chatRoomNumber).set({ participants })
            // chatRoomList 비우고 새로 추가
            const chatRoomList = document.getElementById("chatRoomList");
            chatRoomList.innerHTML = "";
            getUserChatRooms(displayUserChatRooms);
        }

        // 사용자의 채팅방 리스트 가져오기
        function getUserChatRooms(callback) {
            const userChatRooms = [];
            dbRef.once("value", snapshot => {
                snapshot.forEach(chatSnapshot => {
                    const chatRoomNumber = chatSnapshot.key;
                    const participants = chatSnapshot.val().participants;
                    if (participants[senderUID]) {
                        userChatRooms.push(chatRoomNumber);
                    }
                });
                // 채팅방 리스트를 가져온 후에 콜백 함수를 호출하여 전달
                callback(userChatRooms);
            });
        }

        // 채팅방 li에 추가
        async function displayUserChatRooms(chatRooms) {
            const chatRoomList = document.getElementById("chatRoomList");

            for (const chatRoomNumber of chatRooms) {
                const li = document.createElement("li");
                const unreadCount = await countUnreadMessages(chatRoomNumber, senderUID);

                li.innerText = `채팅방: ${chatRoomNumber} (읽지 않은 메시지: ${unreadCount}개)`;
                li.addEventListener("click", async () => {
                    // 클릭한 채팅방 번호를 chat.html로 전달하면서 페이지 이동
                    await messageRead(chatRoomNumber, senderUID); // await를 사용하여 messageRead가 완료될 때까지 기다림
                    window.location.href = `chat1?chatRoomNumber=${chatRoomNumber}&senderUID=${senderUID}`;
                });

                chatRoomList.appendChild(li);
            }
        }

        // 안 읽은 메세지 카운트
        async function countUnreadMessages(chatRoomNumber, userUID) {
            const messagesRef = firebase.database().ref(`messages/${chatRoomNumber}`);

            let unreadCount = 0;

            // child_added 이벤트 리스너 추가
            messagesRef.on("child_added", snapshot => {
                const message = snapshot.val();
                if (message.senderUID !== userUID && !message.checked) {
                    unreadCount++;
                }
                // 새 메시지가 추가될 때마다 unreadCount가 갱신됩니다.
                console.log(unreadCount);

                // 채팅방 리스트의 내용을 업데이트
                updateChatRoomList(chatRoomNumber, unreadCount);
            });

            // 최초에 메시지를 읽지 않은 메시지들을 카운트하여 반환합니다.
            return unreadCount;

        }

        // 메시지를 읽었을 때 호출되는 함수
        async function messageRead(chatRoomNumber, userUID) {
            return new Promise((resolve, reject) => {
                const messagesRef = firebase.database().ref(`messages/${chatRoomNumber}`);
                messagesRef.once("value", snapshot => {
                    snapshot.forEach(messageSnapshot => {
                        const messageID = messageSnapshot.key;
                        const message = messageSnapshot.val();
                        if (message.senderUID !== userUID && !message.checked) {
                            const messageRef = messagesRef.child(messageID);
                            messageRef.update({ checked: true });
                        }
                    });
                    resolve(); // 작업이 끝나면 resolve 호출
                });
            });
        }

        // 메세지가 추가되면 안 읽은 메시지 count 업데이트
        function updateChatRoomList(chatRoomNumber, unreadCount) {
            const chatRoomList = document.getElementById("chatRoomList");
            const chatRoomItems = chatRoomList.getElementsByTagName("li");

            for (let i = 0; i < chatRoomItems.length; i++) {
                const item = chatRoomItems[i];
                if (item.innerText.includes(`채팅방: ${chatRoomNumber}`)) {
                    // 채팅방 리스트에서 해당 채팅방을 찾아 업데이트
                    item.innerText = `채팅방: ${chatRoomNumber} (읽지 않은 메시지: ${unreadCount}개)`;
                    break; // 해당 채팅방을 찾았으면 루프 종료
                }
            }
        }

        window.onload = () => {
            // getUserChatRooms 함수 호출 시 콜백 함수를 전달하여 데이터를 가져온 후에 displayUserChatRooms 함수 호출
            getUserChatRooms(displayUserChatRooms);
        };


    </script>
    <style>
        #chatRoomList li {
            cursor: pointer;
        }
    </style>
</head>
<body class="notosanskr">
    <button onclick="createChatRoom()">새 채팅방 생성</button>

    <!-- 채팅방 리스트 표시 -->
    <h2>내 채팅방 리스트</h2>
    <ul id="chatRoomList">
        <!-- 채팅방 리스트를 동적으로 추가 -->
    </ul>

</body>
</html>

 

all_chat_list.html

<html lang="ko">
<head>
    <script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-app.js"></script>
    <script src="https://www.gstatic.com/firebasejs/8.10.1/firebase-database.js"></script>

    <script>
        const firebaseConfig = {
            apiKey: "",
            authDomain: "",
            databaseURL: "",
            projectId: "",
            storageBucket: "",
            messagingSenderId: "",
            appId: ""
        };

        firebase.initializeApp(firebaseConfig);
        // const app = initializeApp(firebaseConfig); // 이거는 9버전 문법

        // 사용자 UID를 가져오는 가정
        const senderUID = "0dad4e6f-add4-447f-aef0-a3a096be4e00"; // 회원의 UID

        // Firebase 실시간 데이터베이스 참조
        const dbRef = firebase.database().ref("chats"); // 채팅방

        // 전체 채팅방 리스트 가져오기
        function getAllChatRooms(callback) {
            const chatRooms = [];
            dbRef.once("value", snapshot => {
                snapshot.forEach(chatSnapshot => {
                    const chatRoomNumber = chatSnapshot.key;
                    chatRooms.push(chatRoomNumber);
                });
                // 전체 채팅방 리스트를 가져온 후에 콜백 함수를 호출하여 전달
                callback(chatRooms);
            });
        }

        // 전체 채팅방 리스트 가져와서 동적으로 추가
        function displayAllChatRooms(chatRooms) {
            const chatRoomList = document.getElementById("chatRoomAllList");
            chatRooms.forEach(chatRoomNumber => {
                const li = document.createElement("li");
                li.innerText = `채팅방: ${chatRoomNumber}`;
                // li 클릭 이벤트
                li.addEventListener("click", () => {
                    // const userUID = self.crypto.randomUUID(); // 회원의 uid가 기존에 있을때지만 지금은 random값으로 만들어줘
                    dbRef.child(chatRoomNumber).child("participants").child(senderUID).set(true);
                    window.location.href = `chat1?chatRoomNumber=${chatRoomNumber}&senderUID=${senderUID}`;

                });
                chatRoomList.appendChild(li);
            });
        }

        window.onload = () => {
            // 전체 채팅방 리스트 가져오기
            getAllChatRooms(displayAllChatRooms);
        };
        

    </script>
    <style>
        #chatRoomAllList li {
            cursor: pointer;
        }
    </style>
</head>
<body>
    <h2>채팅방 리스트</h2>
    <ul id="chatRoomAllList">
        <!-- 채팅방 리스트를 동적으로 추가 -->
    </ul>
</body>
</html>

 

 

 

끝!!!👏

728x90