채팅 서버 만들기 4

바그다드·2023년 4월 11일
0

채팅 서버 만들기

목록 보기
4/4
post-thumbnail
  • 일반적으로 채팅방은 다수의 사람이 있고, 앞에서 구현했던 보내는 사람과 받는 사람이 지정되어 있는 경우에 1:1채팅이나 귓속말을 할 때 사용되므로, 채팅방을 구현해보자

Chat클래스 수정

  • chat에 룸id라는 필드를 하나 추가하자
@Data
@Document(collection = "chat")
public class Chat {
    @Id
    private String id;
    private String msg;
    private String sender; // 보내는 사람
    private String receiver; // 받는 사람
    private LocalDateTime createdAt;
    private Integer roomNum; // 방 번호
}

ChatRepository 수정

    @Tailable
    @Query("{roomNum:?0}")
    Flux<Chat> mFindByRoomNum(Integer roomNum);

ChatController 수정

    @CrossOrigin
    @GetMapping(value = "/chat/roomNum/{roomNum}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<Chat> findByRoomNum(@PathVariable Integer roomNum) {
        return chatRepository.mFindByRoomNum(roomNum)
                .subscribeOn(Schedulers.boundedElastic());
    }
  • 이걸로 단체 채팅방을 위한 메서드를 추가하였다.

chat.js 수정

// 로그인 시스템 대신 임시 방편
let username = prompt("아이디를 입력하세요");
let roomNum = prompt("채팅방 번호를 입력하세요");

// SSE 연결하기
//EventSource url은 백틱으로 수정하고 ${roomNum}로 pathvariable을 넣어주자.
const eventSource = new EventSource(`http://localhost:8080/chat/roomNum/${roomNum}`);
eventSource.onmessage = (event) => {
    const data = JSON.parse(event.data);
    // 만약 로그인한 유저가 보낸 메세지라면
    if(data.sender === username){ 
        //파란 박스(오른쪽)를 생성하는 메서드로 연결해주고
        initMyMessage(data);
    }else{ // 로그인한 유저가 보낸 메세지가 아니라면
        //회색박스(왼쪽)를 생성하는 메서드로 연결해주자
        initYourMessage(data);
    }
}

// 파란박스 만들기
// 기존에 2개의 인자를 받는 대신에 data 객체 자체를 받아서 처리하자
function getSendMsgBox(data){
	// 날짜를 처리하기 위한 메서드
    let md = data.createdAt.substring(5,10);
    let tm = data.createdAt.substring(11,16);
    convertTime = tm + " | " + md;

    return `<div class="sent_msg">
    <p>${data.msg}</p>
    <span class="time_date"> ${convertTime} / ${data.sender} </span>
</div>`;
}


// 회색박스 만들기
function getReceiveMsgBox(data){
    let md = data.createdAt.substring(5,10);
    let tm = data.createdAt.substring(11,16);
    convertTime = tm + " | " + md;

    return `<div class="received_withd_msg">
    <p>${data.msg}</p>
    <span class="time_date"> ${convertTime} / ${data.sender} </span>
</div>`;
}

// 최초 초기화될 때 1번방에 3건이 있으면 3건을 다 가져옴
// addMessage()함수 호출시 db에 insert되고, 그 데이터가 자동으로 흘러들어온다(SSE)
// 파란박스 초기화하기
function initMyMessage(data){
    let chatBox = document.querySelector("#chat-box");

    let sendBox = document.createElement("div");
    sendBox.className="outgoing_msg";

    sendBox.innerHTML=getSendMsgBox(data);
    chatBox.append(sendBox);

    // 스크롤을 내려주는 이벤트
    document.documentElement.scrollTop = document.body.scrollHeight;
}

// 회색박스 초기화하기
function initYourMessage(data){
    let chatBox = document.querySelector("#chat-box");

    let receivedBox = document.createElement("div");
    receivedBox.className="received_msg";

    receivedBox.innerHTML=getReceiveMsgBox(data);
    chatBox.append(receivedBox);

    // 스크롤을 내려주는 이벤트
    document.documentElement.scrollTop = document.body.scrollHeight;
}

// ajax 채팅 메세지를 전송
// await로 이 메서드가 종료되지 않는다면 다른 메서드들도 완료가 되지 않기 떄문에 비동기 메서드로 바꿔줌
async function addMessage(){ 
    let msgInput = document.querySelector("#chat-outgoing-msg");

    let chat = {
        sender : username,
        roomNum : roomNum,
        msg: msgInput.value
    };

    fetch("http://localhost:8080/chat",{ // 통신에 시간이 걸리므로 await를 걸어 대기하게 해줌
        method:"post",
        body: JSON.stringify(chat), // js -> json으로 변환
        headers:{
            "Content-Type":"application/json;charset=utf-8"
        }
    });

    msgInput.value="";
}

// 버튼 클릭시 메시지 전송
document.querySelector("#chat-send").addEventListener("click", () => {
    addMessage();

})

// 엔터를 치면 메시지 전송
document.querySelector("#chat-outgoing-msg").addEventListener("keydown", (e) => {
    if(e.keyCode===13){
        addMessage();
    }
})
  • 이벤트가 발생하면 EventSource가 이를 캐치해 username에 따라 initMyMessage나 initYourMessage를 실행시켜 chatbox에 메세지 내용을 입력해주기 때문에 기존에 addMessage()에서 chatbox에 메세지 내용을 입력하는 부분은 삭제하였다.
  • 또 addMessage()에서 chatbox에 메세지 내용을 입력할 필요가 없기 때문에 fetch의 reponse 값을 따로 담아줄 필요가 없다.
  • 이제 브라우저 2개를 열어서 확인을 해보면 사용자는 자신이 작성한 메세지만 파란 박스로 뜨고 다른 사람이 쓴 메세지는 회색 박스로 뜨는 것을 확인할 수가 있다!

  • 그리고 위의 채팅방은 100번, 아래 채팅방은 101번인데 서로 채팅방에 영향을 미치지 않는다는 것도 확인하였다!

chat.html 수정

  • 이제 초기에 설정해뒀던 더미 데이터는 필요 없으므로 chat-box만 남겨두고 다 비워주자. 그리고 채팅 방을 확인해보면 프로필 부분에 더미 데이터가 뜨는것도 수정해주자
		  <div class="profile_name">
            &nbsp;&nbsp;&nbsp;&nbsp;<img src="./img/profile.png" class="mr-3 rounded-circle"> &nbsp;&nbsp;
            <span id="username"></span></div>

          <div class="container-fluid chat_section" id="chat-box">

          </div>
  • 프로필은 prompt로 정보를 받을 때 초기화할 것이다.

chat.js 수정

// 로그인 시스템 대신 임시 방편
let username = prompt("아이디를 입력하세요");
let roomNum = prompt("채팅방 번호를 입력하세요");

document.querySelector("#username").innerHTML=username;
  • 맨 위쪽 username과 roomnum을 받는 코드 바로 아래에 초기화 코드를 넣어주었다. 그러면 아래처럼 username이 뜨는 것을 확인할 수 있다.
  • 프로필이나 메세지가 정상적으로 뜨는 것을 확인할 수 있다!
  • 마지막으로 받은 메세지만 css를 조금 수정해주자
    mystyle.css에서 아래의 클래스를 수정해주자
.received_withd_msg { width: 57%; margin-bottom: 8px;}
.mesgs {
  float: left;
  padding: 30px 15px 0 25px;
  width: 60%;
}
  • 이걸로 채팅 서버를 만들어 보았다!!!
profile
꾸준히 하자!

0개의 댓글