반응형

이런 모양의 채팅방을 만들어보자. 

 

지난 시간에 기본적인 php 웹소켓을 이용해서 채팅 서버를 만들어봤다. 이번엔 그 채팅서버를 이용해서 좀더 그럴듯한 채팅방을 만들어보려고 한다. 

 

MyApp/Chat.php

 

<?php

namespace MyApp;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;

class Chat implements MessageComponentInterface {
    protected $clients;

    public function __construct() {
        $this->clients = new \SplObjectStorage;
$this->subscriptions = [];
        $this->users = [];
$this->usernames = [];
$this->channelcnt = [];
    }

    public function onOpen(ConnectionInterface $conn)
    {
        $this->clients->attach($conn);
        $this->users[$conn->resourceId] = $conn;
echo "New connection! ({$conn->resourceId})\n";
    }

public function onMessage(ConnectionInterface $conn, $msg)
    {
$numRecv = count($this->clients) - 1;
        echo sprintf('Connection %d sending message "%s" to %d other connection%s' . "\n"
            , $conn->resourceId, $msg, $numRecv, $numRecv == 1 ? '' : 's');

        $data = json_decode($msg);
$nowtime = date("Y-m-d H:i:s");
        switch ($data->command) {
            case "subscribe":
                $this->subscriptions[$conn->resourceId] = $data->channel;
$this->usernames[$conn->resourceId] = $data->uname;
$this->channelcnt = array_count_values($this->subscriptions);
$target = $this->subscriptions[$conn->resourceId];
foreach ($this->subscriptions as $id=>$channel) {
                        if ($channel == $target && $id != $conn->resourceId) {
$sendmessage='{"nowtime" : "'.$nowtime.'","uname" : "SERVER","msg" : "'.$data->uname.'님이 대화방에 들어오셨습니다. 현재 인원은 '.$this->channelcnt[$data->channel].'명 입니다."}';
                            $this->users[$id]->send($sendmessage);
                        }
if ($channel == $target && $id == $conn->resourceId) {
$sendmessage='{"nowtime" : "'.$nowtime.'","uname" : "SERVER","msg" : "'.$channel.' 대화방에 입장하셨습니다. 현재 인원은 '.$this->channelcnt[$data->channel].'명 입니다."}';
                            $this->users[$id]->send($sendmessage);
                        }
                    }
                break;
            case "message":
$sendmessage='{"nowtime" : "'.$nowtime.'","uname" : "'.$data->uname.'","msg" : "'.$data->message.'"}';
                if (isset($this->subscriptions[$conn->resourceId])) {
                    $target = $this->subscriptions[$conn->resourceId];
                    foreach ($this->subscriptions as $id=>$channel) {
if ($channel == $target) {//같은 채널에 모두에게 보냄
                            $this->users[$id]->send($sendmessage);
                        }
                    }
                }
        }
    }

public function onClose(ConnectionInterface $conn)
    {
$nowtime = date("Y-m-d H:i:s");
if (isset($this->subscriptions[$conn->resourceId])) {
                    $target = $this->subscriptions[$conn->resourceId];
                    foreach ($this->subscriptions as $id=>$channel) {
                        if ($channel == $target && $id != $conn->resourceId) { //나 말고 다른 상대에게만 보냄
$sendmessage='{"nowtime" : "'.$nowtime.'","uname" : "SERVER","msg" : "'.$this->usernames[$conn->resourceId].'님이 대화방에서 나가셨습니다. 현재 인원은 '.($this->channelcnt[$target]-1).'명 입니다."}';
                            $this->users[$id]->send($sendmessage);
                        }
                    }
         }

        $this->clients->detach($conn);
        unset($this->users[$conn->resourceId]);
        unset($this->subscriptions[$conn->resourceId]);
echo "Connection {$conn->resourceId} has disconnected\n";
    }

    public function onError(ConnectionInterface $conn, \Exception $e)
    {
        echo "An error has occurred: {$e->getMessage()}\n";
        $conn->close();
    }

}

?>

 

날짜와 시간을 넣었다. 

 

사용자가 접근하는 페이지를 chatroom.php로 다시 만들었다.

 

<?php session_start();
$uname=$_GET["uname"]??"익명";
?>
<!doctype html>
<html lang="kr">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Bootstrap demo</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
</head>

<style>
body{margin-top:20px;}

.chat-online {
    color: #34ce57
}

.chat-offline {
    color: #e4606d
}

.chat-messages {
    display: flex;
    flex-direction: column;
    max-height: 800px;
    overflow-y: scroll
}

.chat-message-left,
.chat-message-right {
    display: flex;
    flex-shrink: 0
}

.chat-message-left {
    margin-right: auto
}

.chat-message-right {
    flex-direction: row-reverse;
    margin-left: auto
}
.py-3 {
    padding-top: 1rem!important;
    padding-bottom: 1rem!important;
}
.px-4 {
    padding-right: 1.5rem!important;
    padding-left: 1.5rem!important;
}
.flex-grow-0 {
    flex-grow: 0!important;
}
.border-top {
    border-top: 1px solid #dee2e6!important;
}
</style>



<main class="content">
    <div class="container p-0">

<h1 class="h3 mb-3">Messages</h1>


<div class="col-12 col-lg-7 col-xl-9">

<div class="position-relative">
<div class="chat-messages p-4" id="chat">

</div>
</div>

<div class="flex-grow-0 py-3 px-4 border-top">
<div class="input-group">
<input type="hidden" id="uname" value="<?php echo $uname;?>">
<input type="text" class="form-control" placeholder="Type your message" id="message">
<button class="btn btn-primary" id="send">Send</button>
</div>
</div>

</div>

</div>
</main>

<script>
function getParameterByName(name) {
name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
results = regex.exec(location.search);
return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
}
    const chat = document.getElementById('chat');
const uname = document.getElementById('uname');
    const messageInput = document.getElementById('message');
    const sendButton = document.getElementById('send');

    const socket = new WebSocket('ws://localhost:8080');//각자에 맞는 주소와 포트를 넣으면 된다.

function subscribe(channel) {
socket.send(JSON.stringify({command: "subscribe", channel: channel, uname:'<?php echo $uname;?>'}));
}

function sendMessage(uname,msg) {
socket.send(JSON.stringify({command: "message", uname:uname, message: msg}));
}

socket.onopen = function(e) {
  console.log("Connection established!");
  var channel = getParameterByName('channel');
  subscribe(channel);
};

    socket.onmessage = (event) => {//채팅내용을 받아서 뿌려줌
const myname = document.getElementById('uname').value
  var rmsg=JSON.parse(event.data);
  //var li = document.createElement('li');
  //li.innerHTML = rmsg.uname + ' : ' + rmsg.msg;
  const addhtml = document.createElement("div");
  if(myname!=rmsg.uname){
addhtml.innerHTML='<div class="chat-message-left pb-4"><div><img src="https://bootdey.com/img/Content/avatar/avatar3.png" class="rounded-circle mr-1" alt="'+rmsg.uname+'" width="40" height="40"><div class="text-muted small text-nowrap mt-2">'+rmsg.nowtime+'</div></div><div class="flex-shrink-1 bg-light rounded py-2 px-3 ml-3"><div class="font-weight-bold mb-1">'+rmsg.uname+'</div>'+rmsg.msg+'</div></div>';
  }else{
addhtml.innerHTML='<div class="chat-message-right pb-4"><div><img src="https://bootdey.com/img/Content/avatar/avatar1.png" class="rounded-circle mr-1" alt="'+rmsg.uname+'" width="40" height="40"><div class="text-muted small text-nowrap mt-2">'+rmsg.nowtime+'</div></div><div class="flex-shrink-1 bg-light rounded py-2 px-3 ml-3">'+rmsg.msg+'</div></div>';
  }
      chat.append(addhtml);
    };

    sendButton.addEventListener('click', () => {//채팅을 서버에 보냄
const uname = document.getElementById('uname').value?document.getElementById('uname').value:'익명';
const message = messageInput.value;
console.log({
                        uname : uname,
message : message
                    });
      sendMessage(uname,message);
      messageInput.value = '';
    });
  </script>

 

무료로 공개된 부트스트랩 템플릿을 가져왔다. 내가 올린 글은 오른쪽에 상대방이 보낸 메세지는 왼쪽에 표시되도록 했다.

 

http://localhost/chatroom.php?channel=test1&uname=손흥민

http://localhost/chatroom.php?channel=test1&uname=이강인

브라우저를 2개를 열어서 각각 위 주소를 하나씩 붙여 넣어 보자.

 

같은 채널에 이름만 다르게 접속했다. 대화를 해보면 이 글 맨위에 처럼 나타날 것이다. 아 물론 웹소켓 서버는 작동을 해놓고 해야한다. 웹소켓 서버 작동은 지난 시간 강좌를 확인해보자.

 

안되면 될때까지...

 

다음 시간엔 디비에 대화 내용을 저장하는 걸 해보자.

반응형

+ Recent posts