본문 바로가기

Side Project

[PROJECT] Socket.io 를 이용한 채팅 애플리케이션 (6) - 채팅 기능 구현 (채팅방에 들어가보자 - 클라이언트)

[PROJECT] Socket.io 채팅 애플리케이션 (6) - 채팅 기능 구현 (채팅방에 들어가기 - 클라이언트)

Socket.io 채팅 애플리케이션 (6) - 채팅 기능 구현 (채팅방에 들어가보자 - 클라이언트)

지난 시간에

지난 시간에는 채팅방에 들어가는 기능중에서 서버측 코드를 작성해 보았다.

이번 차시에서는 클라이언트 측에서 채팅방에 들어가보자.

 

클라이언트측 구현

지난 시간에 채팅방에 들어가는 플로우를 기억해보자.

  1. (c) 채팅방 목록중에 아무거나 클릭하면 join room 이벤트 발생
  2. (s) join room 이벤트 발생시 기존에 있던 방에서 나가고 새로운 방에 입장 socket.join()
  3. (s) 이제 방에 새로운 멤버가 들어왔으니 member list 업데이트 이벤트 발생 userlist
  4. (c) userlist 이벤트 발생시 memberWrap 에 있는 데이터를 서버한테 받은 데이터로 갱신
  5. (s) 유저가 disconnect 할때나 로그아웃할 때도 userlist update 이벤트 발생
  6. (c) 유저가 들어오거나 떠날 때 공지해주기

이중 클라이언트 측 구현은 1, 4, 6번 이였다.

 

1. (c) 채팅방 목록중에 아무거나 클릭하면 join room 이벤트 발생

# /public/js/script.js
$(function () {
    var socket = io.connect();
    var $userWrap = $('#userWrap');
    var $contentWrap = $('#contentWrap');
    var $loginForm = $('#loginForm');
    var $joinForm = $('#joinForm');
    var $chatForm = $('#chatForm');
    var $roomSelect = $('#roomSelect');
    var $memberSelect = $('#memberSelect');
    var $chatLog = $('#chatLog');
    var roomId = 1;
    var socketId = "";
    ----------
    if (res.result) {
        alert(res.data);
        socketId = socket.id;
        roomId = 1;
        id.val("");
        pw.val("");
        $userWrap.hide();
        $contentWrap.show();
        $chatLog.html(""); //<-- add
        $('#chatHeader').html("Everyone"); //<-- add
    }
    ----------
    $roomSelect.on("click", "div", function () {
        if (roomId !== $(this).data('id')) {
            roomId = $(this).data('id');
        }
        $(this).parents().children().removeClass("active");
        $(this).addClass("active");
        $chatLog.html(""); // 채팅 기록을 지우는 코드
        $('#chatHeader').html(`${$(this).html()}`);
        socket.emit('join room', {
            roomId
        });
    });
})

추가 시켜야 할 코드가 많으니 잘 따라오길 바란다.

일단 맨 위의 코드들은 코드의 가시성을 위해서 jQuery selector 들을 변수에 담아 놓은 것이다.

두번째 단락의 코드는 loginsubmit 하는 코드에 추가 시키는 것인데 default로 접속되는 방인 EveryOne

으로 채팅창의 header를 변경하고 채팅 기록을 지우는 코드이다.

세번째 단락의 코드는 logoutBtn 클릭이벤트 뒤에 작성한 코드로 (실제로 위치는 상관없음) roomEl 을 클릭했을때 해당 방의 목록의 엘리먼트에 active 클래스를 추가시켜주고, #chatHeader 의 내용을 클릭한 방의 이름으로 바꿔주고, 마지막으로 방을 들어갔다는 join room 이벤트를 서버에게 보내는 코드이다.

 

4. (c) userlist 이벤트 발생시 memberWrap 에 있는 데이터를 서버한테 받은 데이터로 갱신

# /public/js/script.js
----------
socket.on('userlist', function (data) {
    let html = "";
    data.forEach((el) => {
        if (el.socketId === socketId) {
            html += `<div class="memberEl">${el.name} (me)</div>`
        } else {
            html += `<div class="memberEl">${el.name}</div>`
        }
    });
    $memberSelect.html(html);
});

서버로 부터 받은 userlist 정보를 $memberSelect 의 엘리먼트에 넣는 작업이다. 그리고 만약 자신의 socketId 와 동일하다면 <div class="memberEl">${el.name} (me)</div> 를 추가시켜 사용자가 자신인지 판별한다.

 

6. (c) 유저가 들어오거나 떠날 때 공지해주기

# /public/js/script/js
----------
socket.on('lefted room', function (data) {
	$chatLog.append(`<div class="notice"><strong>${data}</strong> lefted the room</div>`)
});
socket.on('joined room', function (data) {
	$chatLog.append(`<div class="notice"><strong>${data}</strong> joined the room</div>`)
});

유저가 채팅방에 들어오거나 나갔을 때 공지해주는 코드이다. 이 마크업에 스타일을 조금 입혀주자.

# /public/css/style.css
.notice {
    text-align: center;
    color: gray;
    font-size: 13px;
}

 

 

이제 우리가 해야할 일은 테스트이다. 기존에 작성했던 테스트 코드를 지워보자.

# /chat.html
----------
<div id="chatLog">
    <div class="anotherMsg"> // 삭제
        <span class="anotherName">Jo</span> // 삭제
        <span class="msg">Hello, Nice to meet you.</span> // 삭제
    </div> // 삭제
    <div class="myMsg"> // 삭제
        <span class="msg">Nice to meet you, too.</span> // 삭제
    </div> // 삭제
</div>
----------
<div id="memberSelect">
    <div class="memberEl">test</div> // 삭제
    <div class="memberEl">test1</div> // 삭제
    <div class="memberEl">test2</div> // 삭제
    <div class="memberEl">test3</div> // 삭제
</div>

그다음 테스트 계정으로 로그인 해보자



자신의 계정 옆에 (me) 라는 단어가 나온다면 잘한 것이다.

그다음 브라우저 새 탭을 열어 다른 계정을 만들어 로그인해보자.


 

그러면 두개의 계정이 Everyone 채팅방에 있는 것을 확인할 수 있다.

방을 옮기면 옮긴대로 사용자가 이동하고 다른 사용자의 userlist는 업데이트 된다.



그리고 로그아웃 버튼을 눌렀을때 성공적으로 로그아웃 되는 것을 확인할 수 있다.


 

이번에 작성한 전체 코드

# /public/js/script.js

$(function () {
    var socket = io.connect();
    var $userWrap = $('#userWrap');
    var $contentWrap = $('#contentWrap');
    var $loginForm = $('#loginForm');
    var $joinForm = $('#joinForm');
    var $chatForm = $('#chatForm');
    var $roomSelect = $('#roomSelect');
    var $memberSelect = $('#memberSelect');
    var $chatLog = $('#chatLog');
    var roomId = 1;
    var socketId = "";

    $("#loginBtn").click(function (e) {
        e.preventDefault();
        $loginForm.show();
        $joinForm.hide();
    });

    $("#joinBtn").click(function (e) {
        e.preventDefault();
        $joinForm.show();
        $loginForm.hide();
    });
    $("#logoutBtn").click(function (e) {
        e.preventDefault();
        socket.emit('logout');
        socketId = "";
        alert("로그아웃되었습니다.");
        $userWrap.show();
        $contentWrap.hide();
    });

    $roomSelect.on("click", "div", function () {
        if (roomId !== $(this).data('id')) {
            roomId = $(this).data('id');
        }
        $(this).parents().children().removeClass("active");
        $(this).addClass("active");
        $chatLog.html("");
        $('#chatHeader').html(`${$(this).html()}`);
        socket.emit('join room', {
            roomId
        });
    });

    socket.on('userlist', function (data) {
        let html = "";
        data.forEach((el) => {
            if (el.socketId === socketId) {
                html += `<div class="memberEl">${el.name} (me)</div>`
            } else {
                html += `<div class="memberEl">${el.name}</div>`
            }
        });
        $memberSelect.html(html);
    });

    socket.on('lefted room', function (data) {
        $chatLog.append(`<div class="notice"><strong>${data}</strong> lefted the room</div>`)
    });
    socket.on('joined room', function (data) {
        $chatLog.append(`<div class="notice"><strong>${data}</strong> joined the room</div>`)
    });

    $loginForm.submit(function (e) {
        e.preventDefault();
        let id = $("#loginId");
        let pw = $("#loginPw");
        if (id.val() === "" || pw.val() === "") {
            alert("check validation");
            return false;
        } else {
            socket.emit('login user', {
                id: id.val(),
                pw: pw.val()
            }, function (res) {
                if (res.result) {
                    alert(res.data);
                    socketId = socket.id;
                    roomId = 1;
                    id.val("");
                    pw.val("");
                    $userWrap.hide();
                    $contentWrap.show();
                    $('#chatHeader').html("Everyone");
                } else {
                    alert(res.data);
                    id.val("");
                    pw.val("");
                    $("#joinBtn").click();
                }
            });
        }
    });

    $joinForm.submit(function (e) {
        e.preventDefault();
        let id = $("#joinId");
        let pw = $("#joinPw");
        if (id.val() === "" || pw.val() === "") {
            alert("check validation");
            return false;
        } else {
            socket.emit('join user', {
                id: id.val(),
                pw: pw.val()
            }, function (res) {
                if (res.result) {
                    alert(res.data);
                    id.val("");
                    pw.val("");
                    $("#loginBtn").click();
                } else {
                    alert(res.data);
                    return false;
                }
            });
        }
    });

});

지난 번에 말했듯이 위에 코드를 이해 했으면 그냥 복붙해도 상관없다.

 

다음 시간에

이제 거의 다 끝났다. 다음시간에는 대망의 채팅을 구현할 것이다. 이번에 한 채팅방보다 구현이 쉽고 간단하다.