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 문법을 적용하고, BMS 맵의 경우에는 BMS 매크로 문법을 적용하여 분석이 이뤄져야 한다.

현재는 이 문제를 해결하기 위해 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 문자의 정렬 순서는 다음과 같다.

  • 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이 설정된다.

따라서, Mainframe의 업무 로직을 그대로 변환하려면 다음과 같이 소스를 수정해야 한다.

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

앞에서 언급된 유형의 업무 시스템 내부의 문자 정렬 처리 문제는 비록 간단하지는 않지만 사용자 프로그램을 수정함으로써 어느 정도 문제를 해결할 수는 있다. 하지만, 업무 시스템 내부의 문제가 아닌 일반 비전산 사용자에게도 영향을 줄 수 있는 유형이 존재하는데 이런 경우는 사용자에게 심각한 혼란을 일으킬 수 있다.

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

예를 들어 다음과 같은 업무 화면을 변환한다고 가정한다.

[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 값 중 가장 작은 값인 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

문자 정렬 문제 또한 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로 2Byte 문자를 변환한다. EUC-KR 문자 집합은 2Byte 문자 공백은 X’8140’, 1Byte 문자 공백은 X’20’이라는 값으로 매핑한다. 이는 EUC-KR 문자 집합에서는 'Single Byte Space + Single Byte Space = Double Byte Space’라는 공식이 성립되지 않는다는 것을 의미한다.

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

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

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