데이터 프레이밍이란?
이 기술을 사용하는 이유는 TCP 통신의 메시지 경계 문제를 해결하기 위해서이다.
TCP는 데이터 스트림을 연속적으로 전송하므로, 하나의 큰 데이터 덩어리를 여러 번에 나눠 보내거나, 여러 개의 작은 메시지가 합쳐져서 한 번에 도착할 수 있다.
LengthDelmitedCodec과 같은 데이터 프레이밍 기술은 이러한 문제를 해결하고, 메시지 단위로 안정적인 통신을 가능하게 한다.
TCP는 바이트의 연속적인 흐름일 뿐이므로, 한 메시지가 어디서 시작하고 어디서 끝나는지 알 수 없다.
프레이밍 기술은 각 메시지에 명확한 시작과 끝을 부여하여, 수신자가 완전한 하나의 메시지를 정확히 파싱하도록 한다.
Framed와 같은 래퍼는 개발자가 복잡한 바이트 레벨의 파싱 로직을 직접 구현할 필요 없이, send()와 next()같은 간단한 메서드로 메시지를 주고 받을 수 있게 해준다.
LengthDelimitedCodec의 정확한 기능
LengthDelimitedCodec은 길이 접두사 프레이밍을 구현하는 코덱이다. 이 코덱은 데이터 스트림을 다룰 때, 두 가지 주요 기능을 수행한다.
// server
pub async fn run_server() {
let listener = TcpListener::bind("0.0.0.0:9999").await.unwrap();
let Ok((mut stream, addr)) = listener.accept().await else {
eprintln!("fail");
return;
};
// TcpStream을 LengthDelimitedCodec으로 래핑
let mut framed = Framed::new(stream, LengthDelimitedCodec::new());
while let Some(result) = framed.next().await {
match result {
Ok(bytes) => {
let message = String::from_utf8_lossy(&bytes);
println!("수신: {}", message);
}
Err(e) => {
eprintln!("통신 오류!: {}", e);
break;
}
}
}
}
// client
pub async fn run_client() {
let Ok(mut stream) = TcpStream::connect("127.0.0.1:9999").await else {
println!("fail to connect <127.0.0.1:9999>");
return;
};
// 보낼 데이터 설정
let send_data = vec!["안녕하세요", "만나서 반가워요", "잘 부탁드려요"];
let mut framed = Framed::new(stream, LengthDelimitedCodec::new());
for data in send_data {
if let Err(e) = framed.send(Bytes::from(data)).await {
eprintln!("메시지 전송 실패: {}", e);
}
}
}

TCP통신을 할 때, 보낼 데이터의 길이를 먼저 보낸 후 데이터를 보내는 방식으로 프로토콜을 커스텀 하는게 흔하다.
데이터 프레이밍 기술은 위 방식을 추상화하여 개발자가 편하게 사용할 수 있고 실수 없이 안정적으로 구현해놓은 기능인 것 같다.
모든 기술은 장점이 있다면 그에 따르는 트레이드오프가 있다.
데이터 프레이밍 기술의 주요 단점은 아래와 같다고 한다.
위와 같은 트레이드오프가 존재한다고는 하지만 압도적으로 편리한 사용성과 유연성을 고려했을 때, 앞으로 rust에서 TCP 통신을 할 때 적극적으로 사용할 예정이다.