개발자공부일기
oneof 본문
oneof는 하나의 필드만 활성화되도록 하는 기능으로, 통신 패킷에서 여러 선택 가능한 데이터 중 하나만 보낼 때 사용됩니다. 예를 들어, 클라이언트와 서버가 서로 다른 메시지 타입(텍스트, 이미지, 상태 변경 등)을 주고받을 때 유용합니다.
oneof 필드는 oneof 공유 메모리의 모든 필드를 제외하고 일반 필드와 같으며 최대 한 필드를 동시에 설정할 수 있습니다.
oneof의 member를 설정하면 다른 모든 member는 자동으로 지워집니다.
선택한 언어에 따라 특별한 case() 또는 WhatOneof() 메서드를 사용하여 oneof에 어떤 값이 세팅되는지 확인할 수 있습니다.
Node.js 환경에서 TCP 통신을 할 때 Protocol Buffers의 oneof를 사용하는 예제
1. Protobuf 파일 정의
다음은 oneof를 사용하는 예제 Protobuf 정의 파일입니다 (message.proto).
syntax = "proto3";
message Packet {
oneof payload {
string text_message = 1;
bytes image_data = 2;
int32 status_code = 3;
}
}
설명:
- Packet은 TCP 통신에 사용될 메시지입니다.
- oneof payload는 한 번에 text_message, image_data, 또는 status_code 중 하나만 설정 가능합니다.
2. Protobuf 컴파일
protoc를 사용해 .proto 파일을 JavaScript로 컴파일합니다.
protoc --js_out=import_style=commonjs,binary:. message.proto
이 명령으로 message_pb.js 파일이 생성됩니다.
3. Node.js에서 TCP 통신 구현
서버 코드 (server.js):
const net = require('net'); // TCP 통신을 위한 기본 모듈
const { Packet } = require('./message_pb'); // Protobuf 파일에서 생성된 메시지 클래스 로드
// TCP 서버 생성
const server = net.createServer((socket) => {
console.log('Client connected.');
// 클라이언트로부터 데이터 수신
socket.on('data', (data) => {
try {
// 수신한 바이너리 데이터를 Protobuf 메시지로 디코딩
const receivedPacket = Packet.deserializeBinary(data);
// 활성화된 oneof 필드를 확인
const field = receivedPacket.getPayloadCase();
// 활성화된 필드에 따라 처리
switch (field) {
case Packet.PayloadCase.TEXT_MESSAGE:
// text_message 필드가 활성화된 경우
console.log('Received text message:', receivedPacket.getTextMessage());
break;
case Packet.PayloadCase.IMAGE_DATA:
// image_data 필드가 활성화된 경우
console.log('Received image data (length):', receivedPacket.getImageData().length);
break;
case Packet.PayloadCase.STATUS_CODE:
// status_code 필드가 활성화된 경우
console.log('Received status code:', receivedPacket.getStatusCode());
break;
default:
// 어떤 필드도 활성화되지 않은 경우
console.log('Unknown payload type.');
}
} catch (error) {
console.error('Error processing data:', error.message);
}
});
// 클라이언트 연결 종료 시 호출
socket.on('end', () => {
console.log('Client disconnected.');
});
});
// 서버 시작 및 포트 4000에서 대기
server.listen(4000, () => {
console.log('Server listening on port 4000');
});
클라이언트 코드 (client.js):
const net = require('net'); // TCP 클라이언트 생성용 기본 모듈
const { Packet } = require('./message_pb'); // Protobuf 파일에서 생성된 메시지 클래스 로드
// 서버에 연결
const client = net.connect(4000, 'localhost', () => {
console.log('Connected to server.');
// 1. 텍스트 메시지 전송
const packet = new Packet(); // Packet 메시지 생성
packet.setTextMessage('Hello, Server!'); // text_message 필드 설정
// Protobuf 메시지를 직렬화(바이너리 변환)하여 서버로 전송
client.write(packet.serializeBinary());
});
client.on('data', (data) => {
// 서버로부터 받은 데이터를 처리 (현재 예제에서는 생략 가능)
console.log('Received from server:', data.toString());
});
client.on('end', () => {
// 서버 연결 종료 시 호출
console.log('Disconnected from server.');
});
4. 주요 동작 설명
- oneof 필드 활성화:
클라이언트는 Packet 메시지에 text_message, image_data, 또는 status_code 중 하나만 설정합니다. - 데이터 직렬화 및 전송:
packet.serializeBinary()로 데이터를 바이너리 형식으로 직렬화한 뒤 서버로 전송합니다. - 서버에서 필드 확인:
서버는 Packet.deserializeBinary(data)로 바이너리 데이터를 디코딩하고, getPayloadCase()를 호출해 활성화된 필드를 확인합니다. - 유연한 메시지 처리:
oneof을 사용하면 다양한 데이터 타입을 처리하는 코드가 간결하고 명확해집니다.
5. 실행 예제
- 클라이언트가 텍스트 메시지를 전송:
- Received text message: Hello, Server!
- 클라이언트가 이미지 데이터를 전송 (예: Buffer로 설정):
- Received image data (length): 1024
- 클라이언트가 상태 코드를 전송:
- Received status code: 200
'TIL(Today I Learned)' 카테고리의 다른 글
패킷에 헤더 붙여서 전송하기 (0) | 2025.01.24 |
---|---|
프로그래머스 문제:타켓 넘버 (0) | 2025.01.23 |
게임 서버 아키텍처 (0) | 2025.01.20 |
운영체제 (0) | 2025.01.15 |
버퍼 객체 (0) | 2025.01.14 |