본문 바로가기

마이그레이션

[마이그레이션] 유니코드 적용시 검토할 사항들

델파이 2009 이후 버전 부터는 유니코드 기반의 문자열을 사용합니다. 그래서 델파이 2007 이전버전에서 델파이 2009 이후 버전으로 마이그레이션 할 경우 소스코드에서 유니코드 사용에 대한 점검이 필요합니다.


일부 개발자들이 유니코드 대응을 마이그레이션의 큰 위험요소로 생각합니다. 하지만 알고 보면 대부분의 경우 기존 소스코드를 별도 추가작업 없이 유니코드 기반에서 그대로 사용할 수 있습니다. 단, 문자열을 다루는 코드에 대해서는 점검이 필요합니다.


유니코드와 델파이의 변경사항에 대해 살펴보고 기존코드를 유니코드 기반 델파이에서 동작하도록 점검하는 내용을 살펴보겠습니다.

목차

1. 유니코드와 델파이의 변경사항

  1.1. 유니코드란?

  1.2. 유니코드 지원에 의한 델파이 변경사항

2. 기존 코드를 유니코드 기반 델파이에서 동작하도록 수정하기

  2.1.. 유니코드 적용 시 검토 대상

  2.2. 유니코드 검토 대상 분석 도구 안내


1, 유니코드와 델파이의 변경사항

1.1. 유니코드란?

유니코드는 사실상 모든 문자들을 단일 캐릭터 셋으로 인코딩할 수 있는 문자 인코딩 방식입니다. 델파이는 2009 버전부터 IDE와 컴파일러, RTL, VCL 모두 완벽하게 유니코드를 지원합니다.

유니코드에 대한 자세한 내용은 닉하지스가 작성한 글을 박지훈님이 번역한 글과 엠바카데로 기술문서를 통해 더 자세히 확인할 수 있습니다.


1.2. 유니코드 지원에 의한 델파이 변경사항

델파이에서 유니코드 지원 시 변경된 사항은 다음과 같습니다.

  1. 문자열의 데이터 타입 선언의 변경

  2. 문자열 길이의 변경

  3. 문자 크기의 변경


문자열 데이터 타입 선언의 변경

 데이터 타입

 유니코드 지원하지 않는 버전

 (델파이 2007 이전)

 유니코드 지원하는 버전

 (델파이 2009 이후)

 string

 AnsiString

 UnicodeString

 Char

 AnsiChar

 WideChar

 PChar

 PAnsiChar

 PWideChar

 PAnsiChar

 PAnsiChar

 PAnsiChar

 PWideString

 PWideChar

 PWideChar

 AnsiString

 AnsiString

 AnsiString

 WideString

 WideString

 WideString


위 표와 같이 string은 유니코드 지원하지 않는 버전에서는 AnsiString으로, 유니코드를 지원하는 버전에서는 UnicodeString으로 선언됩니다. Char와 PChar도 선언된 데이터 타입에 차이가 있습니다.

반면, 명시적으로 Ansi, Wide가 포함된 데이터 타입은 유니코드 지원여부와 관계없이 동일한 데이터타입으로 사용됩니다.


문자열 길이의 변경

델파이는 유니코드 지원과 하위 호환성을 확보하기 위해 대부분의 문자열 함수가 UnicodeString과 AnsiString 두가지 데이터를 처리할 수 있도록 구현되었습니다.

즉, Length(문자열) 함수는 UnicodeString을 타입 문자열을 처리하는 Length함수와 AnsiString 타입 문자열을 처리하는 Length함수를 모두 제공합니다.

하지만, 같은 함수를 이용하더라도 캐릭터셋 특성 상 Length 함수는 UnicodeString과 AnsiString의 길이를 다르게 반환합니다.


 

 string(UnicodeString)

 AnsiString

 Length('한글1'); 

 3

 5

문자열 길이 변경에 따른 유의사항은 다음 장에서 다루도록 하겠습니다.


문자 크기의 변경

UnicodeString의 모든 문자의 크기는 2Bytes입니다. 이제 한글 한글자도 2Bytes, 영문 한자도 2Bytes, 숫자 한자도 2Bytes 모두 2Bytes입니다.

.

2. 기존 코드를 유니코드 기반 델파이에서 동작하도록 수정하기

델파이에서 유니코드를 지원은 재검토 대상의 경우를 제외하고는 별도의 추가작업 없이 기존 코드를 그대로 사용할 수 있습니다.

2.1. 유니코드 적용 시 검토 대상

  • string 길이와 데이터 크기가 같다고 간주한 코드
  • char 데이터 크기를 1로 간주한 코드
  • string을 AnsiString으로 간주한 코드
  • 잠재적으로 수정될 필요가 있을 수 있는 코드

string 길이와 데이터 크기가 같다고 간주한 코드

UnicodeString 기반에서는 더이상 문자열(string)의 길이와 데이터 크기가 같지 않습니다.

UnicodeString의 Length 함수는 문자의 수를 반환하고, 문자의 크기는 2Bytes이기 때문에 문자열의 데이터 크기는 "문자열 길이 * SizeOf(Char)"로 계산하도록 코드를 전환해야 합니다.


다음 예제를 통해 확인합니다.

var
  Count: Integer;
  Buf1, Buf2: array[0..13] of Char;
begin
  Buf1 := '가나다라마바사아자차카타파하';
  // 잘못됨: AnsiString과 같이 길이로 데이터 복사
  Move(Buf1, Buf2, Length(Buf1)); // Buf2 = 가나다라마바사
  // 올바른: 문자열 길이와 문자 크기의 곱으로 데이터 복사
  Move(Buf1, Buf2, Length(Buf1) * SizeOf(Char)); // Buf2 =가나다라마바사아자차카타파하
end;

이제 기존과 같이 문자열을 길이로 복사하면 절반의 데이터만 복사된다는 점 유의하기바랍니다.


검토해야하는 대표적인 키워드(함수, 데이터타입)는 아래와 같습니다. 

 Length

 FillChar

 Read

 ReadBuffer

 Write

 WriteBuffer

 Copy

 Seek

 AllocMem

 GetMem

 StrAlloc

 AnsiStrAlloc

 Delete Insert Pos LeftStr RightStr MidStr
 Move     

기타, 메모리를 할당하고 복사하는 코드와 문자열 길이를 이용해 문자열을 조작하는 코드를 점검하시기 바랍니다.


Char 데이터 크기를 1로 간주한 코드

UnicodeString에서 Char의 데이터 크기는 2Bytes 입니다. 기존 코드에서 Char가 1Bytes로 가정하고 구현한 코드를 점검해야 합니다.


다음 예제는 문자열을 버퍼로 사용한 코드입니다. 문자열은 조작이 쉽기 때문에 예제와 같이 Char 배열 선언후 메모리에 복사하는 코드를 사용했다면, 데이터 크기가 기존에 비해 2배로 커질것 입니다. 

var Buf: array[0..3] of Char;
begin
  Buf1 := 'abcd';

  MemStream := TMemoryStream.Create;
  try
    MemStream.Write(Buf1, SizeOf(Buf1)); // SizeOf(Buf): Ansi=4, Wide=8
  finally
    MemStream.Free;
  end;
end;

이 경우, 문자를 1Byte로 사용해야 하는 경우 array of AnsiChar로 수정해야 하고, 데이터를 읽고, 쓰는 부분을 쌍으로 데이터가 일치하도록 점검하기 바랍니다.


그리고 문자열 Set 문법은 Char가 WideChar로 선언되어 있기 때문에 아래 예제와 같이 CharInSet 함수를 사용해야 합니다.

var C: Char;
begin
  C := Edit1.Text[1];

  // if C in ['a'..'z', 'A'..'Z'] then
  if CharInSet(C, ['a'..'z', 'A'..'Z']) then
  begin
    Label1.Caption := 'It is there';
end;


검토해야하는 대표적인 키워드(함수, 데이터타입)는 아래와 같습니다. 

 SizeOf

 Char

 of Char

 in

 

 



string을 AnsiString으로 간주한 코드

델파이 2007 이전 버전에서는 string이 곧 AnsiString, PChar가 PAnsiChar였기 때문에 별다른 생각없이 string과 PChar 데이터 타입을 사용한 코드가 있을 수 있습니다.


특히, DLL과 문자열 전달 시 문자열포인터로 PChar를 전달한 경우 아래와 같이 조치해야 합니다.

  • DLL을 직접 제작한 경우 유니코드 지원이 필요하다면 DLL과 DLL을 호출하는 실행파일 모두 Export 메소드의 문자열 포인터 데이터 타입을 PWideChar로 변경 후 다시 빌드해 사용해야 합니다.

  • DLL을 직접 제작하지 않은 경우 DLL 인터페이스 문서를 참고해 유니코드를 지원하는 메소드 여부 확인 후, PAnsiChar, PWideChar를 명시적으로 지정해야 합니다.

델파이에서도 내부적으로 윈도우 API를 호출합니다. 델파이도 위와 마찮가지로 AnsiString인 경우 PAnsiChar 메소드를 UnicodeString인 경우 PWideChar 메소드(보통 같은 메소드이름 마지막에 W가 붙은경우 WideChar 메소드입니다.)를 호출합니다.

만약, 윈도우 API를 직접 호출한 경우 아래와 같이 조치하기 바랍니다.

  • 유니코드를 지원하는 메소드(메소드명이 W로 끝나는 메소드)를 찾아 변경합니다.

  • 메소드가 유니코드를 지원하지 않는 경우 전달하는 문자열을 PAnsiChar로 형변환해 전달해야 합니다.

PAnsiChar로 형변환 시 데이터 손실이 발생할 수 있어 먼저 AnsiString으로 변환 후 PAnsiChar로 변환해야 합니다.(string을 바로 PAnsiChar로 변환 시 string에 영문이 있는 경우 무조건 그 다음 바이트에 0이 들어가 널바이트 문자열 처리하는 문자열 포인터에 일부 문자열만 전달됩니다.)
다음 예제와 같이 PAnsiChar로 형변환 후 데이터를 전달해야 합니다.
var path: string;
begin
  path := 'C:\Program Files\Internet Explorer\iexplore.exe';
  WinExec(PAnsiChar(AnsiString(path)), SW_SHOW);
end;

검토해야하는 대표적인 키워드(함수, 데이터타입)는 아래와 같습니다. 

 PChar

 AnsiChar

 PAnsiChar

 Pointer

 AppendStr

 GetProcAddress

 CreateProcess

 Chr

    



잠재적으로 수정될 필요가 있을 수 있는 코드

  • “of Char” 텍스트와 “of AnsiChar” 텍스트를 검색하여 버퍼가 유니코드에 맞게 사용되었는지 확인합니다. 

  • “string[” 텍스트를 검색하여 스트링 인덱스의 문자가 대입되는 변수가 Char(즉 WideChar)가 아닌 AnsiChar 타입 변수로 지정되도록 수정합니다. 

  • “AnsiString”, “AnsiChar”, “PAnsiChar”를 명시적으로 지정한 부분을 찾아 그럴 필요가 있고 제대로 되어 있는지 확인합니다. 

  • “ShortString”을 명시적으로 지정한 부분을 찾아 그럴 필요가 있고 제대로 되어 있는지 확인합니다. 

  • “Length(” 텍스트를 검색하여 Length가 SizeOf와 동일한 의미로 사용되지는 않았는지 확인합니다. 

  • “Copy(” , “Seek(” , “Pointer(” , “AllocMem(”, “GetMem(” 텍스트를 검색하여 스트링 혹은 문자 배열에 대해 제대로 동작하는지 확인합니다.


다음 내용을 통해 유니코드 적용에 대한 내용을 더 살펴볼 수 있습니다.

2.3. 유니코드 검토 대상 분석 도구 안내

위에서 소개한 유니코드 적용 시 검토 대상을 분석하고, 안내해 주는 도구입니다. 아래 링크에서 다운로드 할 수 있습니다.


마이그레이션 참고 리소스