본문 바로가기
Back-end

Node.js WebSocket 메시지 처리와 Buffer 객체 변환 이해하기

by suyeonnie 2025. 2. 6.

들어가며

 

Node.js와 WebSocket(ws) 라이브러리를 이용한 간단한 실시간 채팅 서버를 구현하는 실습을 하면서 데이터를 처리하는 방식이 다소 어렵게 느껴졌다. Buffer 객체라는 낯선 용어가 튀어 나온 탓일까...?

 

서버와 클라이언트에서 각각 어떻게 데이터를 처리하는 지 알아보고, 관련 개념을 정리해보자.

 

이번 실습의 주요 구현사항은 다음과 같다 :

  • 서버 : 클라이언트 연결 시마다 고유한 ID를 생성하여 관리
  • 클라이언트 : 채팅 메시지를 입력하면 서버로 전송되고, 서버는 이걸 모든 클라이언트에게 보냄

 

Node.js 버퍼(Buffer) 객체의 암시적 호출

 

좌) 클라이언트 우) 서버

 

웹 소켓이 성공적으로 열렸을 때, 클라이언트에서 socket.send를 통해 서버에게 보낸 메시지는 버퍼 객체 형태이다. `toString()` 함수로 문자열로 변환해도 되지만, 템플릿 리터럴 내부에서 버퍼를 사용하면 JavaScript의 암시적 형 변환에 의해 toString()이 자동으로 실행되어 우리가 원하는 문자열 '채팅방 입장~!'이 정상적으로 출력된다 😯

 

Buffer란?

사전적인(?) 의미 이진 데이터를 다룰 때 사용하는 메모리 공간이라고 한다.

 

Node.js에서 Buffer 객체는 웹 소켓에서 데이터를 받을 때 기본적으로 사용되는 객체이다. string, json등의 데이터 타입을 바이너리 형식으로 변환해서 저장할 수 있도록 도와주는 역할을 한다. 예를 들어 파일 읽기, 네트워크 요청 처리 등에서 바이트 단위 데이터 처리가 가능하도록 해준다. 그래서 바이트 단위 데이터 처리가 어디에 좋냐하면...데이터 처리가 빨라진다고 한다.

 

 

암시적 호출이란?

명시적 변환없이 자동으로 특정 동작이 수행되는 것을 말한다. 예를 들어, 객체가 문자열처럼 사용될 때 toString()이 자동 호출 되는 것이다.

const buffer = Buffer.from('Hello');
console.log(buffer.toString()); // Hello (명시적 호출)
console.log(buffer); // <Buffer 48 65 6c 6c 6f> (버퍼 출력)
console.log(`${buffer}`); // Hello (암시적 호출)

 

from 메서드는 buffer를 생성하는 가장 간단한 방법이다. 마지막 콘솔에서 toString()이 자동으로 호출되어 Hello가 출력된다.

 

[Object Object] 에러 - json.parse

원인

 

[server.js]

// 메시지 수신 이벤트 (클라이언트 -> 서버)
  socket.on('message', (message) => {
    // 메시지는 버퍼 객체 <Buffer ec b1 84 ed 8c 85 eb b0 a9 20 ec 9e 85 ec 9e a5 7e 21>

    // ✅ 연결된 모든 클라이언트에게 메시지 전송
    sockets.forEach((client) => {
    	// 👾 에러 발생한 이유: 문자열[object object]을 객체로 바꿔주라고 했기때문!!!
      client.send(`${client}`); 

    });
  });

 

인자로 전달된 client는 WebSocket 객체로 socket(요청하고 있는 클라이언트 1개)를 의미한다.

 

처음에 client.send(${client})라고 작성했을 때, 서버에서 템플릿 리터럴 ${client}을 사용하면 JavaScript에서 toString()이 암시적으로 호출된다.

 

그런데, WebSocket 객체의 기본 toString() 결과는 [object Object]이므로, 클라이언트에서 [object Object]라는 문자열을 받게 된다.

 

더보기

[object Object] 란?

 

객체를 문자열로 변환할 때 기본적으로 [object Object]로 출력되는데, 이건 toString() 메서드가 객체에서 자동 호출될 때 [object Object]가 반환되기 때문이다.

 

예 :

const obj = {name: "suyeon"}
console.log(`${obj}`); // [object Object]

const obj2 = {name: "suyeon"}
console.log(JSON.stringify(obj2)); // {"name":"suyeon"}

 

`send()` 함수는 문자열 또는 버퍼 객체를 전송할 수 있는데 그럼 왜 에러가 발생했나?

[object Object]라는 문자열은 JSON이 아니기 때문이다. 클라이언트에서 JSON.parse() 하려고 하면서 오류가 발생한다 :

 

 

[client.ejs]

//  [2] 서버로부터 메시지를 받았을 때
      socket.addEventListener('message', (e) => {
        console.log('서버로부터 받은 메세지', e.data); //// {'msg': 'ddd', 'name': 'ddd'}
        const data = JSON.parse(e.data); // json >> object
        console.log('객체로 변환!', data);

        // 채팅 내용 화면에 추가
        const li = document.createElement('li');
        li.textContent = `${data.name}: ${data.msg}`;
        ul.append(li);
      });

 

 

...그래서 유효하지 않은 JSON이라고 에러가 뜬 것이다

 

 

해결

[server.js]

// 메시지 수신 이벤트 (클라이언트 -> 서버)
  socket.on('message', (message) => {
    // 메시지는 버퍼 객체 <Buffer ec b1 84 ed 8c 85 eb b0 a9 20 ec 9e 85 ec 9e a5 7e 21>

    sockets.forEach((client) => {
      client.send(`${message}`); //✅
    });
  });

 

암시적 형 변환에 의해 객체에서 문자열로 변환된 메시지를 모든 클라이언트(소켓)에 보내주면 된다.

 

`JSON.stringify()`와 `JSON.parse()`

 

함수

  • JSON.stringify() : 객체 -> JSON 문자열 변환
  • JSON.parse() : JSON 문자열 -> 객체 변환

 

예:

[client.ejs]

//  [2] 서버로부터 메시지를 받았을 때
      socket.addEventListener('message', (e) => {
        console.log('서버로부터 받은 메세지', e.data); //// {'msg': 'ddd', 'name': 'ddd'}
        const data = JSON.parse(e.data); // json >> object
        console.log('객체로 변환!', data);

        // 채팅 내용 화면에 추가
        const li = document.createElement('li');
        li.textContent = `${data.name}: ${data.msg}`;
        ul.append(li);
      });

 

서버로부터 받은 메시지 JSON 문자열 형태 데이터를 객체로 변환하기 위해 JSON.parse 함수를 사용했다.

 

예:

[client.ejs]

 // [3] 사용자가 메시지 입력 후 전송 버튼 눌렀을 때
      chatForm.addEventListener('submit', (e) => {
        e.preventDefault();

        // 사용자가 입력한 데이터 객체 생성
        const chatData = { msg: msg.value, name: name.value };
        console.log('chatData', chatData); // {msg: 'ddd', name: 'ddd'}

        // ❓ chatData를 JSON으로 바꿔서 보내는 이유
        // socket.send(chatData); // '클라이언트: 객체 -> 서버: 객체' 로 전달됨

        // 💡서버에 데이터 전송 (string으로 변경해서 보내야함)
        // 왜냐면 우리가 원하는 형식으로 보내기 위해서! (string -> string)
        socket.send(JSON.stringify(chatData));

        msg.value = '';
        name.value = '';
      });

 

chatData를 그대로 보내면 클라이언트에서 서버로 데이터 전송 시 객체 -> 객체 형태로 전달이 된다.

 

[server.js]

 // 메시지 수신 이벤트 (클라이언트 -> 서버)
  socket.on('message', (message) => {
    // 메시지는 버퍼 객체 <Buffer ec b1 84 ed 8c 85 eb b0 a9 20 ec 9e 85 ec 9e a5 7e 21>

    // console.log('=------------');
    // console.log(message); // 버퍼객체
    // console.log(message.toString()); // 문자열 '채팅방 입장~!'
    console.log(`${clientId} 에게 받은 메시지 : ${message}`); 
    // [object object] -> 클라이언트 JSON처리 후 {"msg" : "ddd", "name":"ddd"}
    // console.log('=------------');

    // ✅ 현재 연결된 소켓에게만 메시지 전송
    // - socket: 요청하고 있는 클라이언트 1개
    // socket.send('안녕하세요?');

    // ✅ 연결된 모든 클라이언트에게 메시지 전송
    sockets.forEach((client) => {
      // client.send(`${client}`); // 👾 에러 발생한 이유: 문자열[object object]을 객체로 바꿔주라고 했기때문!!!
      client.send(`${message}`); //버퍼 객체 암시적 호출
    });
  });

 

그래서 서버에서 전달 받은 message는 버퍼 객체 형태이다. 위에서 언급한 것처럼 템플릿 리터럴 내에서 객체는 문자열로 바뀌므로 [object Object]라고 나오게된다. 따라서 클라이언트에서 서버로 데이터 전달 시 우리가 원하는 형태(문자열)로 전달하기 위해서는 객체를 문자열로 바꾸는 작업이 필요한 것이다!

 

 

마치며

수박 겉핥기 하듯이 Node.js 버퍼 객체의 개념에 대해서 훑어보았다. 그래도 코드를 이해하는 데 도움이 많이 됐고, 웹 소켓에서 JSON 데이터를 주고 받는 방식에 대해 이해할 수 있었다.

 

개인적으로는 JavaScript 기초가 왜 튼튼해야하는 지 다시 한 번 절감했달까 😨

 


🔖  관련 글

-서울시 청년취업사관학교 웹 개발자 풀스택 과정

 

Node.js Web Socket 💬 | 15주차(1)

📂 오늘 배운 내용Node.js와 WebSocket(ws) 라이브러리를 이용한 간단한 실시간 채팅 서버서버 : 클라이언트 연결 시마다 고유한 ID를 생성하여 관리클라이언트 : 채팅 메시지를 입력하면 서버로 전송

suyeon-dev.tistory.com

 

🌐  참고

- Node.js 버퍼 클래스

- Node.js의 버퍼 이해하기