EBCDIC에서 ASCII 변환 이슈

Mainframe에서 운영 중인 업무 시스템을 개방형 환경으로 변환하는 데 있어 EBCDIC 데이터를 ASCII로 변환하는 과정은 모든 작업에서 필수적이다. 하지만, 이 과정에서는 다양한 문제점이 발생하므로, 이미 발견된 문제점에 대해 숙지하고 실제 작업에서 적용하는 작업이 매우 중요하다.

일반적으로 EBCDIC 데이터를 ASCII 데이터로 변환할 때의 문제점은 COBOL로 작성된 업무 프로그램의 원본 파일을 변환할 때 발생한다. 원본 파일을 변환할 때 발생하는 문제점 중 심각도가 높은 문제점은 다음과 같다.

  • HEX 값 처리

  • 문자 정렬(sort order) 처리

  • 2Byte 공백(double byte space) 처리

본 절에서는 문자 집합을 변환할 때 발생하는 문제점 중 심각도가 높은 위의 3가지 항목에 대해 예제를 통한 원인 파악 및 해결 과정을 제시한다.

1. HEX 값 처리

다음은 원본 파일에 HEX 값을 이용하여 작성된 로직이 포함된 COBOL 원본 프로그램을 dsmigin 툴을 사용하여 ASCII로 변환한 예이다.

01  WORK06-AREA.
           05  W06-KOUZA-NO.
               10  FILLER                  PIC X(01)  VALUE  X'F1'.
               10  W06-NO-1                PIC X(01).
               10  FILLER                  PIC X(01)  VALUE  X'F2'.
               10  W06-NO-2                PIC X(01).
               10  FILLER                  PIC X(01)  VALUE  X'F3'.
               10  W06-NO-3                PIC X(01).
               10  FILLER                  PIC X(01)  VALUE  X'F4'.
               10  W06-NO-4                PIC X(01).
               10  FILLER                  PIC X(01)  VALUE  X'F5'.
               10  W06-NO-5                PIC X(01).
               10  FILLER                  PIC X(01)  VALUE  X'F6'.
               10  W06-NO-6                PIC X(01).
               10  FILLER                  PIC X(01)  VALUE  X'F7'.
               10  W06-NO-7                PIC X(01).

위의 파일은 COBOL로 작성된 원본을 본 안내서에 기술된 일반적인 변환 절차를 통해 EBCDIC 문자를 ASCII 문자로 변환한 상태이다. 위의 파일은 성공적으로 변환이 이뤄져서 작업이 완료된 것처럼 보이지만, 사실은 다음과 같은 문제를 가지고 있을 수 있다.

다음은 위의 예제에서 한 부분을 발췌한 것이다.

10  FILLER                  PIC X(01)  VALUE  X'F1'.

위의 라인의 경우 EBCDIC 문자는 ASCII 문자로 완벽하게 변환되었지만, EBCDIC으로 작성되었던 고객의 COBOL 프로그램에 구현된 업무 로직은 정확하게 변환되지 않았을 가능성이 있다.

원본에서 X’F1’이 의미하는 내용이 F1이라는 문자가 아니고, X’F1’이 가리키는 EBCDIC 값 1인 경우를 가정해 본다. 일반적인 문자 집합 변환 절차로는 EBCDIC 값 1을 처리할 수 없으므로 이런 경우에는 수동으로 해당 값을 변경해 주어야 한다. 따라서, 원본 파일을 ASCII로 변환하고 다음과 같이 ASCII 값 1에 해당하는 값 31로 소스를 수정한다.

10  FILLER                  PIC X(01)  VALUE  X'31'.

다음은 위의 첫번째 예제 파일과 동일한 원본 파일을 dsmigin 툴을 사용하여 ASCII로 변환한 다음 해당 문자 표현을 문자가 가리키는 숫자 값으로 변경한 예이다.

01  WORK06-AREA.
           05  W06-KOUZA-NO.
               10  FILLER                  PIC X(01)  VALUE  X '31'.
               10  W06-NO-1                PIC X(01).
               10  FILLER                  PIC X(01)  VALUE  X '32'.
               10  W06-NO-2                PIC X(01).
               10  FILLER                  PIC X(01)  VALUE  X '33'.
               10  W06-NO-3                PIC X(01).
               10  FILLER                  PIC X(01)  VALUE  X '34'.
               10  W06-NO-4                PIC X(01).
               10  FILLER                  PIC X(01)  VALUE  X '35'.
               10  W06-NO-5                PIC X(01).
               10  FILLER                  PIC X(01)  VALUE  X '36'.
               10  W06-NO-6                PIC X(01).
               10  FILLER                  PIC X(01)  VALUE  X '37'.
               10  W06-NO-7                PIC X(01).

HEX 값 처리 문제는 프로그램 상에 직접 HEX 값이 기술되어 있어 문자 변환할 때 문제가 발생하는 것이 일반적이다. 하지만, 그 반대의 경우도 발생할 수 있다.

01  YEAR-TABLE.
           05  FILLER                  PIC  X(12)  VALUE '{{{{{{JJJJJJ'.
           05  FILLER                  PIC  X(12)  VALUE '{{{{{{{JJJJJ'.
           05  FILLER                  PIC  X(12)  VALUE '{{{{{{{{JJJJ'.
           05  FILLER                  PIC  X(12)  VALUE '{{{{{{{{{JJJ'.
           05  FILLER                  PIC  X(12)  VALUE '{{{{{{{{{{JJ'.
           05  FILLER                  PIC  X(12)  VALUE '{{{{{{{{{{{J'.
           05  FILLER                  PIC  X(12)  VALUE '{{{{{{{{{{{{'.
           05  FILLER                  PIC  X(12)  VALUE 'A{{{{{{{{{{{'.
           05  FILLER                  PIC  X(12)  VALUE 'AA{{{{{{{{{{'.
           05  FILLER                  PIC  X(12)  VALUE 'AAA{{{{{{{{{'.
           05  FILLER                  PIC  X(12)  VALUE 'AAAA{{{{{{{{'.
           05  FILLER                  PIC  X(12)  VALUE 'AAAAA{{{{{{{'.

위의 예제에서 “{“라는 문자는 프로그램 로직에서 문자로 사용된 것이 아니라 ZD(Zone Decimal) 타입으로 “{“라는 문자 자체의 HEX 값에 해당하는 X’C0’ 문자가 가리키는 ZD 값을 의미한다.

이 경우는 Mainframe ZD X’C0’에 해당하는 ASCII의 ZD 값이 X’30’이므로, X’30’에 해당하는 ASCII 문자 0으로 변경해야 사용자 업무를 그대로 변환할 수 있다. 단, EBCDIC 원본 데이터를 ASCII 데이터로 변환한 후에 소스를 수정할 것을 권장한다.

01  YEAR-TABLE.
           05  FILLER                  PIC  X(12)  VALUE '000000qqqqqq'.
           05  FILLER                  PIC  X(12)  VALUE '0000000qqqqq'.
           05  FILLER                  PIC  X(12)  VALUE '00000000qqqq'.
           05  FILLER                  PIC  X(12)  VALUE '000000000qqq'.
           05  FILLER                  PIC  X(12)  VALUE '0000000000qq'.
           05  FILLER                  PIC  X(12)  VALUE '00000000000q'.
           05  FILLER                  PIC  X(12)  VALUE '000000000000'.
           05  FILLER                  PIC  X(12)  VALUE '100000000000'.
           05  FILLER                  PIC  X(12)  VALUE '110000000000'.
           05  FILLER                  PIC  X(12)  VALUE '111000000000'.
           05  FILLER                  PIC  X(12)  VALUE '111100000000'.
           05  FILLER                  PIC  X(12)  VALUE '111110000000'.

앞서 언급한 HEX 값 문제의 경우 사용자가 작성한 프로그램 소스 상에 기술된 HEX가 값을 의미하는 것인지 아니면 해당 값이 가리키는 EBCDIC 문자(또는 반대의 경우에도 해당)를 의미하는 것인지가 문자 집합 변환 과정에서는 자동으로 구분되지 않는다.

원본 파일의 HEX 값이 어떤 의미인지 파악하려면 프로그램 소스가 COBOL로 작성된 경우에는 COBOL 문법을 적용하고, PSAM 포맷 정의인 경우에는 PSAM 포맷 정의 문법을 적용하여 분석이 이뤄져야 한다.

현재는 이 문제를 해결하기 위해 TmaxSoft의 숙련된 컨설턴트가 마이그레이션 단계에 직접 참여하여 원본 파일 분석 후 작업을 진행하도록 하고 있다. 향후에는 단계적으로 적용 범위를 넓혀가면서 프로그램 단위로 자동 분석이 가능하도록 툴을 개발할 예정이다.

2. 문자 정렬 처리

EBCDIC 문자를 ASCII로 변환했을 때 눈으로 인식되는 문자 자체에는 문제가 없는 것처럼 보일 수도 있다. 하지만 두 문자 체계가 지닌 고유한 특성으로 인해 실제 사용자 프로그램을 변환하여 사용할 때는 문제가 나타날 수 있다.

본 절에서는 문자 체계의 고유 특성으로 인해 발생할 수 있는 대표적인 문제 중 하나인 정렬 문제에 대해 기술하고 그 해결 방법을 제시한다.

문자 정렬 순서를 이용하는 프로그램

다음은 Mainframe에서 사용 중인 COBOL 소스를 ASCII 문자로 변환한 소스의 일부분이다.

IF      W01-XX     <=       '99'     THEN
        MOVE    'Y'    TO      W01-CC
ELSE
        MOVE    'N'    TO      W01-CC
END-IF.

소스 상에서 EBCDIC 문자를 ASCII 문자로 변환한 것 자체는 성공적으로 진행되었다. 하지만, 사용자 프로그램 측면에서 업무 로직에 오류가 발생할 가능성이 존재한다.

EBCDIC 문자와 ASCII 문자의 "정렬 순서"(collating sequence)는 다음과 같이 서로 다르다.

  • EBCDIC

    a < z < A < Z < 0 < 9
  • ASCII

     0 < 9 < A < Z < a < z

이 때문에 앞의 예제에서 W01-XX 값이 'AA’라고 가정했을 때, 위의 프로그램이 Mainframe 상에서 작동한다면 W01-CC에 'Y’가 설정되고, UNIX 상에서 작동한다면 W01-CC에 'N’이 설정된다. 즉, EBCDIC 문자 'AA’를 ASCII 문자 'AA’로 EBCDIC 문자 '99’를 ASCII 문자 '99’로 정상적으로 변환을 하더라도 기존의 Mainframe에서 'AA' < '99’인 특성이 OpenFrame에서는 '99' < 'AA’로 바뀌게 되고 이러한 특성을 이용하여 작성된 응용 프로그램은 마이그레이션 후에 기존과 다른 실행 결과를 보이게 된다.

일반적으로 프로그램 언어 또는 소트 프로그램은 문자의 정렬 순서(collating sequence)를 지정할 수 있다. 이를 활용하면 ASCII 문자 체계에서 EBCDIC collating sequnce를 이용할 수 있다. 예를 들어 UNIX용 Fujitsu Net COBOL의 경우 옵션을 지정하여 EBCDIC collating sequence를 사용할 수 있다.

또는 문자 정렬 순서를 이용하여 작성된 응용 프로그램 로직이 극히 일부인 경우라면 다음과 같이 응용 프로그램의 로직을 변경하는 것도 하나의 방법이다.

IF      W01-XX     <       'zz'       THEN
        MOVE    'Y'    TO      W01-CC
ELSE
        MOVE    'N'    TO      W01-CC
END-IF.
색인화된 정보(KSDS, ISAM, Index Database)의 레코드 순서 차이

EBCDIC과 ASCII 문자 체계의 문자 정렬 순서상의 차이는 위에서 설명한 업무 프로그램 코드에 존재하는 문제 뿐만 아니라 해당 프로그램이 사용하는 데이터셋이나 데이터베이스의 색인에 사용되는 키 필드의 순서 비교에도 영향을 주기 때문에 마이그레이션 전과 후의 시스템이 다르게 동작하게 되는 원인의 하나가 된다.

실제 업무 시스템을 개발하거나 운영하는 전산 전문가들은 EBCDIC에서는 ZZ < 99이지만, ASCII에서는 99 < ZZ이므로 문자 집합을 변환할 때 정렬 순서가 변경되어야 한다는 사실을 교육을 통해 충분히 인지시킬 수 있다. 하지만, 실제로 업무시스템을 이용하는 일반 비전산 사용자들에게 이러한 환경적 변화를 숙지시키기 어려울 수 있다. 따라서, 이 문제는 마이그레이션 과정에서는 간과되기 쉽지만 해당 시스템을 사용하는 최종 일반 사용자에게는 사용상의 혼란을 일으킬 수 있다.

예를 들어 KSDS이나 ISAM와 같은 색인형 데이터셋이나 또는 OpenFrame NDB의 색인 데이터베이스(Index Database)에서 특정 검색 키값을 입력 받아서 그 이후의 레코드를 한 페이지씩 출력하는 검색 업무 화면을 변환한다고 가정한다.

Mainframe에서는 다음과 같은 화면이 출력될 것이다.

[User Address List]
--------------------------------------------------------------------------

    ID : AAAAAAAA


  ID              NAME                 ADDRESS
 -------------------------------------------------------------------------
  AAAAAAAA        KIM                  SEOUL
  BBBBBBBB        LEE                  PUSAN
  CCCCCCCC        PARK                 SEOUL
  HHHHHHHH        AHN                  DAEGU
  LLLLLLLL        CHO                  GWANGJU
  MMMMMMMM        CHOI                 INCHEON
  NNNNNNNN        KWAK                 BUPYOUNG
  XXXXXXXX        IM                   SUNGNAM
  ZZZZZZZZ        SEO                  GURI
--------------------------------------------------------------------------
 <F1> Menu    <F2> Prev     <F3> Next                       <Enter> Search

위의 업무는 Mainframe 환경이라면 ID 값(Alpha-numeric 필드임을 가정한다.) 중 가장 작은 값인 AAAAAAAA를 이용하여 ID 목록 전체를 조회할 수 있을 것이다. 하지만 개방형 환경이라면 AAAAAAAA를 입력해도 ID 목록 전체를 조회할 수 없다.

또한 '11111111’이라는 ID가 존재한다고 가정했을 때, Mainframe 환경에서는 <F3> 키를 눌러 다음 화면을 조회하면 '11111111' 결과를 조회할 수 있지만, 개방형 환경에서는 'ZZZZZZZZ' 이후의 ID 목록은 조회되지 않는다. 결과적으로 개방형 환경에 대한 지식이 없고, 기존 Mainframe에 익숙한 사용자는 '11111111’이라는 ID 정보가 실제로 프로그램에 존재한다 해도 그 사실을 인지하기 어렵다.

다음은 개방형 환경에서 ID 목록 전체를 조회(AAAAAAAA 대신 00000000 사용)하는 예제 화면이다.

[User Address List]
--------------------------------------------------------------------------

    ID : 00000000


  ID              NAME                 ADDRESS
 -------------------------------------------------------------------------
  11111111        NOH                  SEOUL
  88888888        KANG                 DAEJEON
  AAAAAAAA        KIM                  SEOUL
  BBBBBBBB        LEE                  PUSAN
  CCCCCCCC        PARK                 SEOUL
  HHHHHHHH        AHN                  DAEGU
  LLLLLLLL        CHO                  GWANGJU
  MMMMMMMM        CHOI                 INCHEON
  NNNNNNNN        KWAK                 BUPYOUNG
--------------------------------------------------------------------------
 <F1> Menu    <F2> Prev     <F3> Next                       <Enter> Search

위와 같은 차이는 응용 프로그램에 기인하는 것이 아니라 응용 프로그램이 사용하는 색인 데이터셋의 키 필드의 정렬 순서에 있어서 Mainframe EBCDIC과 개방형 환경의 ASCII의 문자 정렬 순서가 다르기 때문에 발생한다. 색인 데이터셋이나 색인화된 데이터베이스를 관리하는 제품들 역시 별도의 문자 정렬 순서를 지정할 수 있는 방안이 존재하므로 이를 활용하여 문제를 해결할 수 있다.

OpenFrame TSAM의 KSDS나 이를 이용하여 구현된 OpenFrame NDB 인덱스 데이터베이스의 경우 copybook에 EBC 타입을 정의하여 EBCDIC 문자 정렬 순서를 이용할 수 있다. EBC 타입 설정에 대한 자세한 설명은 "OpenFrame 데이터셋 안내서"의 "Appendix E. OpenFrame VSAM 성능 최적화"를 참고한다.

문자 정렬 순서의 차이에 의한 문제 역시 HEX 값 처리와 마찬가지로 프로그램의 동작을 직접 확인한 후에 해결해야 하는 문제이기 때문에 숙련된 컨설턴트를 통해 문제를 해결하는 방법을 취하고 있다. 따라서, 마이그레이션 작업자는 이 문제에 대해서 숙지할 필요가 있다.

3. 2Byte 공백 처리

Mainframe 환경에서 2Byte 공백은 X’4040’으로 1Byte 공백인 X’40’ 두 개를 합친 것과 동일한 값으로 인식된다.

               10  W-K-00.
                   20  W-K-00-1                    PIC  X(01).
                   20  W-K-00-2                    PIC  X(01).
               10  W-K-01        REDEFINES       W-K-00.
                   20  W-K-01-1                    PIC  G(01).

     * . . .

         MOVE SPACE TO W-K-01-1.

         IF W-K-00-1 = SPACE THEN
              DISPLAY 'DOUBLE BYTE SPACE = SINGLE BYTE SPACE * 2'
         END-IF.

위의 COBOL 프로그램이 Mainframe 환경에서 구동된다면 'DOUBLE BYTE SPACE = SINGLE BYTE SPACE * 2’라는 메시지가 출력되겠지만, 개방형 환경에서 구동된다면 해당 메시지가 출력되지 않는다.

결국 Mainframe에서 2Byte 공백 처리하는 방식은 개방형 환경으로 변환됐을 때 사용자 프로그램의 로직이 변경되는 결과를 가져온다.

이런 문제가 발생하는 원인은 다음과 같다. EUC-KR을 사용하는 개방형 환경으로 데이터를 변환하는 경우를 가정할 때 EUC-KR 문자 집합에서 2Byte 문자 공백은 X’8140’이고 1Byte 문자 공백은 X’20’이라는 값이기 때문에, Mainframe 환경에서와는 달리 EUC-KR 문자 집합에서는 'Single Byte Space + Single Byte Space = Double Byte Space’라는 특성이 변환 후에는 유지되지 않기 때문이다.

이 문제를 해결하는 방법은 여러 가지가 있으나 대부분의 문제는 컴파일러에서 지원되는 기능에 따라 쉽게 해결할 수도 있고, 해결이 불가능할 수도 있다.

OpenFrame에서는 일반적으로 개방형 환경에서 2Byte 공백을 사용하지 않는 방향으로 해결책을 제시하고 있다. 가능하다면 2Byte 공백을 사용하지 않고, 꼭 필요한 경우에는 1Byte 공백 두개로 2Byte 공백 하나를 대체하는 방법을 사용하고 있다. 이 방법은 COBOL과 같은 사용 컴파일러의 경우에는 대부분 2Byte 공백을 무시하도록 처리하는 옵션을 제공하므로 간단하게 문제를 해결할 수 있다.

OpenFrame 내의 데이터를 Mainframe 환경으로 전달해야 하는 상황에서는 2Byte 공백이 필요한 경우이므로 1Byte 공백 두개로 대체하는 방법을 사용할 수 없다. 그러나 이 경우에는 OpenFrame 내의 CPM 상에서 처리가 가능하도록 지원하고 있다.