project/모임웹프로젝트

실시간 채팅 구현 스크립트 코드 구성

naspeciallist 2024. 11. 19. 14:25

웹소켓 라이브러리를 이용하여 실시간 채팅을 구현하는 코드를 구성을해 보았습니다.

 

먼저 웹소켓 라이브러리를 등록을 해 줍니다.

<!--    웹소켓 라이브러리-->
    <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/stompjs@2.3.3/lib/stomp.min.js"></script>

 

웹소켓 연결에 관한 스크립트 코드 입니다.

function connect() {
    if (stompClient !== null) {
        stompClient.disconnect(); // 기존 WebSocket 연결을 끊음
    }

    console.log("roomId로 WebSocket 연결 시도:", roomId);
    const socket = new SockJS('/ws');
    stompClient = Stomp.over(socket);

    stompClient.connect({}, function (frame) {
        console.log('WebSocket 서버에 연결되었습니다, frame:', frame);

        // 새로운 채팅방에 대한 구독 설정
        stompClient.subscribe(`/topic/chat/${roomId}`, function (messageOutput) {
            const message = JSON.parse(messageOutput.body);
            showMessage(message);
        });
    });
}

 

STOMP 프로토콜을 활용하여 WebSocket 서버와 통신하며, 특정 채팅방(roomId)에 대해 실시간 메시지를 구독하고 메시지를 화면에 표시하는 역할을 합니다.

 

  • stompClient.disconnect():
    • 새로운 WebSocket 연결을 만들기 전에 기존 연결이 존재하면 끊습니다. 이는 이전 연결이 유지되는 것을 방지하고, 새로운 채팅방에 대해 연결을 초기화하기 위함입니다.
  • SockJS('/ws'):
    • 클라이언트 측에서 서버와의 WebSocket 연결을 설정합니다.
    • SockJS는 브라우저 간 WebSocket 호환성을 높이기 위해 사용되는 라이브러리로, WebSocket을 지원하지 않는 환경에서도 fallback 메커니즘을 제공합니다.
  • Stomp.over(socket):
    • Stomp는 WebSocket 위에서 동작하는 메시징 프로토콜로, 메시지 브로커와의 통신을 단순화해줍니다.
    • 이 코드는 SockJS로 생성된 WebSocket 객체를 STOMP 클라이언트와 연결하는 역할을 합니다.

stompClient.connect():

  • WebSocket 서버와 연결을 시작합니다.
  • 첫 번째 매개변수 {}는 연결 시 전송할 헤더 정보를 포함하며, 현재는 비어 있습니다.
  • 두 번째 매개변수 function (frame)은 연결 성공 시 호출되는 콜백 함수입니다. frame은 서버로부터 전달된 연결 정보를 담고 있습니다.

 

  • stompClient.subscribe('/topic/chat/${roomId}', callback):
    • STOMP의 구독 기능으로, 지정된 채널(이 경우 /topic/chat/${roomId})의 메시지를 수신 대기합니다.
    • 특정 채팅방 ID(roomId)에 따라 해당 채팅방의 메시지만 수신하게 됩니다.
    • 서버가 이 경로(/topic/chat/${roomId})로 메시지를 발행하면, 클라이언트는 이를 받아 처리합니다.
  • 콜백 함수:
    • function (messageOutput)은 서버로부터 메시지가 전달되었을 때 실행됩니다.
    • 서버에서 전달된 메시지는 STOMP 메시지 포맷으로 전달되며, 이 중 body를 JSON으로 파싱하여 메시지 데이터를 추출합니다.
    • 추출된 메시지를 처리하기 위해 showMessage(message) 함수를 호출합니다.

그 다음에는 채팅창의 메세지를 보여주는 스크립트 코드 입니다.

function showMessage(message) {
    const chatMessages = document.getElementById('chatRoom');

    // 메시지 요소 생성
    const messageElement = document.createElement('div');
    messageElement.classList.add('message');

    if (message.sender === loggedInUserId) {
        messageElement.classList.add('user1');
    } else {
        messageElement.classList.add('user2');
    }

    const messageContent = document.createElement('p');
    messageContent.textContent = message.content;

    if (message.sender !== lastSenderId) {
        const senderName = document.createElement('span');
        senderName.textContent = message.senderName || "알 수 없는 사용자";
        messageElement.appendChild(senderName);
    }
    messageElement.appendChild(messageContent);

    // DOM에 바로 추가하지 않고, Fragment에 추가하여 성능 개선
    const fragment = document.createDocumentFragment();
    fragment.appendChild(messageElement);

    // 한 번에 DOM에 추가
    chatMessages.appendChild(fragment);

    // 스크롤 최신 메시지로 이동
    chatMessages.scrollTop = chatMessages.scrollHeight;

    lastSenderId = message.sender;
}

 

 

 

loggedInUserId을 이용하여 발신자를 구분하였고 같은 사용자가 연속으로 메시지를 보낼 경우 이름을 중복 표시하지 않아 UI를 깔끔하게 유지합니다.

message.sender, message.content, message.senderName 같은 데이터 필드를 사용해 사용자 맞춤형 UI를 동적으로 생성하였습니다.

 

DOM에 요소를 바로 추가하지 않고, 가상의 DOM 구조인 DocumentFragment에 먼저 추가한 뒤 한 번에 반영하여 성능을 개선하였습니다.

https://kimfield.tistory.com/entry/DOM-%EC%A1%B0%EC%9E%91%EC%9D%84-%EC%B5%9C%EC%86%8C%ED%99%94%ED%95%98%EC%97%AC-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94%ED%95%98%EA%B8%B0-DocumentFragment

 

DOM 조작을 최소화하여 성능 최적화하기 DocumentFragment

안녕하세요 초원입니다. 👩🏻‍💻최근 자바스크립트로 직접 DOM을 구현하는 과제 중,부모 요소에 appendChild를 호출하여 자식을 추가할 때마다브라우저가 매번 렌더링을 수행하는 문제를 겪었

kimfield.tistory.com

 

그 다음은 메세지 전송에 관한 스크립트 코드 입니다.

function sendMessage() {
    const messageInput = document.getElementById('messageInput');
    const messageContent = messageInput.value.trim();

    if (messageContent === '') {
        return; // 빈 메시지는 전송하지 않음
    }

    const message = {
        content: messageContent,
        roomId: roomId
    };

    // WebSocket을 통해 메시지를 전송
    if (stompClient && stompClient.connected) {
        stompClient.send(`/app/chat/${roomId}/sendMessage`, {}, JSON.stringify(message));
        messageInput.value = ''; // 메시지 입력창 초기화
    } else {
        console.error("WebSocket 연결이 되어 있지 않습니다.");
    }
}

빈 메시지를 전송하지 않도록 유효성 검증을 수행하였으며 메시지 생성 및 직렬화 처리를 했습니다.

사용자가 입력한 메시지를 JSON 형태로 포맷팅하여 서버와의 통신에 적합한 형태로 변환 후
WebSocket으로 전송하게 하였습니다.

STOMP 프로토콜을 통해 특정 채팅방 경로로 메시지를 서버에 전송할 수 있게 구성하였습니다.

메시지 전송 후 입력창을 초기화하여 사용자의 연속 입력을 할 수 있게 처리하였으며
연결 상태를 확인하고 오류를 처리하여 안정적인 동작을 할 수 있도록 하여 ux를 개선하였습니다.