본문 바로가기
delphi

UTF8 문자열을 소켓을 통해 안전하게 전송하는 방법

by 천지조율 2024. 8. 7.

개요

Delphi2009 이후부터 기본 문자열이 유니코드를 지원하게 됨에 따라 인디 컴포넌트를 통해 전송되는 UTF8 문자열이 안전하게 전송되지 못하는 문제가 있다. UTF8 문자열을 소켓을 통해 안전하게 전송하는 방법을 찾아보자.

기존 처리 방식

전송부 코드

_Client := TIdTCPClient.Create(nil);
_Client.Host := Host;
_Client.Port := Port;
try
  _Client.Connect;
  _Client.IOHandler.WriteLn('Hello World!');
  _Client.IOHandler.WriteLn('안녕하세요!');
finally
  _Client.Free;
end;

수신부 코드 (TIdTCPServer)

procedure TfrmDebugMain.IdTCPServer1Execute(AContext: TIdContext);
var
  msg1, msg2 : string;
begin
  try
    msg1 := AContext.Connection.IOHandler.ReadLn;
    msg2 := AContext.Connection.IOHandler.ReadLn;

    Memo1.Lines.Add(msg1);
    Memo1.Lines.Add(msg2);
  finally
    //...
  end;
end;

위와 같은 기존 방식으로 처리했을 경우 영문은 정상적으로 나오나 한글 등 UTF8 문자열은 다음과 같이 나타난다.

Hello World!!

안전한 UTF8 문자열 전송 방법

전송 시 UTF8 문자열을 Base64로 인코딩 후 전송 및 수신 후 다시 디코딩하는 방식을 사용하는 것을 추천함.

Base64 Encode/Decode 함수

uses
  EncdDecd;

function Encode(const Input: string): AnsiString;
var
  utf8: UTF8String;
begin
  utf8 := UTF8String(Input);
  Result := EncdDecd.EncodeBase64(PAnsiChar(utf8), Length(utf8));
end;

function Decode(const Input: AnsiString): string;
var
  bytes: TBytes;
  utf8: UTF8String;
begin
  bytes := EncdDecd.DecodeBase64(Input);
  SetLength(utf8, Length(bytes));
  Move(Pointer(bytes)^, Pointer(utf8)^, Length(bytes));
  Result := string(utf8);
end;

전송부 코드 수정

uses
  EncdDecd;

function Encode(const Input: string): AnsiString;
var
  utf8: UTF8String;
begin
  utf8 := UTF8String(Input);
  Result := EncdDecd.EncodeBase64(PAnsiChar(utf8), Length(utf8));
end;

function Decode(const Input: AnsiString): string;
var
  bytes: TBytes;
  utf8: UTF8String;
begin
  bytes := EncdDecd.DecodeBase64(Input);
  SetLength(utf8, Length(bytes));
  Move(Pointer(bytes)^, Pointer(utf8)^, Length(bytes));
  Result := string(utf8);
end;

수신부 코드 수정

procedure TfrmDebugMain.IdTCPServer1Execute(AContext: TIdContext);
var
  msg1, msg2 : string;
begin
  try
    msg1 := Decode(AContext.Connection.IOHandler.ReadLn);
    msg2 := Decode(AContext.Connection.IOHandler.ReadLn);

    Memo1.Lines.Add(msg1);
    Memo1.Lines.Add(msg2);
  finally
    //...
  end;
end;

처리 결과

 
Hello World!
안녕하세요!

Base64 인코딩과 디코딩의 장점

데이터 무결성 유지

Base64 인코딩을 사용하면 데이터가 전송 중에 손상되지 않도록 보장할 수 있다. 이는 특히 네트워크 통신에서 중요한데, 인코딩된 데이터는 이진 데이터보다 전송 오류에 덜 민감하기 때문이다.

호환성 향상

Base64는 모든 텍스트 기반의 전송 프로토콜과 호환되므로, 텍스트 데이터를 안전하게 전송할 수 있다. 이는 특히 이메일, URL 인코딩, XML, JSON 등의 다양한 애플리케이션에서 유용하다.

Delphi에서 Base64 인코딩 사용 시 주의사항

인코딩 크기 증가

Base64 인코딩은 데이터 크기를 약 33% 증가시킨다. 따라서 대용량 데이터를 전송할 경우 네트워크 대역폭 사용량이 늘어날 수 있다. 이 점을 고려하여 데이터 전송 전략을 세우는 것이 중요하다.

인코딩 및 디코딩 성능

Base64 인코딩과 디코딩은 추가적인 연산을 필요로 하므로, 성능에 민감한 애플리케이션에서는 성능 테스트를 통해 시스템에 미치는 영향을 평가해야 한다.

실습 예제: 클라이언트-서버 애플리케이션 구현

클라이언트 코드 예제

procedure TForm1.SendData;
var
  Client: TIdTCPClient;
begin
  Client := TIdTCPClient.Create(nil);
  try
    Client.Host := '127.0.0.1';
    Client.Port := 12345;
    Client.Connect;
    Client.IOHandler.WriteLn(Encode('Delphi 클라이언트에서 보낸 데이터'));
    Client.IOHandler.WriteLn(Encode('안녕하세요!'));
    Client.Disconnect;
  finally
    Client.Free;
  end;
end;

서버 코드 예제

procedure TForm1.IdTCPServer1Execute(AContext: TIdContext);
var
  ReceivedStr1, ReceivedStr2: string;
begin
  try
    ReceivedStr1 := Decode(AContext.Connection.IOHandler.ReadLn);
    ReceivedStr2 := Decode(AContext.Connection.IOHandler.ReadLn);

    Memo1.Lines.Add('받은 데이터: ' + ReceivedStr1);
    Memo1.Lines.Add('받은 데이터: ' + ReceivedStr2);
  except
    on E: Exception do
      Memo1.Lines.Add('오류 발생: ' + E.Message);
  end;
end;

테스트 결과

클라이언트와 서버 간에 UTF8 문자열이 올바르게 전송되고 수신되는 것을 확인할 수 있다. 이 방식은 Delphi 환경에서 UTF8 문자열을 안전하게 전송하는 데 매우 유용하다.

결론

Delphi 애플리케이션에서 UTF8 문자열을 안전하게 전송하려면 Base64 인코딩과 디코딩을 사용하는 것이 효과적이다. 이를 통해 네트워크 통신에서 데이터 손상을 방지하고, 다양한 텍스트 기반 전송 프로토콜과의 호환성을 높일 수 있다. 본 문서에서 제시한 방법을 따라 구현하면, 안정적이고 호환성 높은 데이터 전송이 가능하다.