LabHub
시작하기
배우기 러닝패스 코스

EAI 중간 계층 만들기

고정길이와 JSON 사이의 변환 어댑터

LabHub 에서 이어서 보기

목표

레이아웃 파일과 코드 매핑표를 읽어 고정길이 본문(EUC-KR)과 JSON 을 서로 바꾸는 변환기를 만들고, 바이트 길이·코드·인코딩·소수 규칙을 어기는 값을 거절하게 한다.

왜 중요한가

변환 계층의 실수는 조용하다. 글자 수로 센 길이는 뒤 필드를 모두 밀고, 모르는 코드를 흘려보내면 내부 시스템이 다른 뜻으로 읽고, float 을 거친 금리는 1 이 모자라고, 파이썬의 euc_kr 은 '똠' 을 8바이트로 바꿔 넘긴다. 그래서 규칙을 데이터(레이아웃·매핑표)로 두고, 고칠 수 없는 값은 고치지 않고 오류 코드로 거절한다.

단계

  1. 정의서 /opt/lab/fixtures/eaimw/transform/FXTR0001.md/root/eaimw/xform/FXTR0001.layout 으로 옮긴다. 머리글 name,length,type,key,map,scale, 정의서 순서대로 12행. name 은 영문명, key 는 JSON 키, map 은 코드 매핑 도메인(없으면 빈칸), scale 은 이 전문에서는 빈칸.
  2. /opt/lab/fixtures/eaimw/transform/CODES.md/root/eaimw/xform/codemap.csv 로 옮긴다. 머리글 domain,external,internal, 사용 중인 코드만, domain·external 순으로 정렬(LC_ALL=C sort).
  3. /root/eaimw/xform/f2j.py 를 만든다. python3 f2j.py <레이아웃> <매핑표> <본문파일> 이 JSON 한 줄을 출력하고 0 으로 끝난다. N 은 정수, AN·H 는 뒤 공백만 제거, 본문은 EUC-KR 로 엄격하게 읽고, map 이 있는 필드는 매핑표로 바꾼다. 거절할 때는 표준오류 첫 줄을 오류 코드로 시작하고 종료 코드 2: 매핑표에 없는 코드 UNMAPPED, 본문 길이 불일치·숫자 아님·EUC-KR 로 읽을 수 없음 BAD_VALUE.
  4. /root/eaimw/xform/j2f.py 를 만든다. python3 j2f.py <레이아웃> <매핑표> <JSON파일> 이 본문 바이트를 표준출력으로 낸다. N 은 오른쪽 정렬·앞 0, AN·H 는 왼쪽 정렬·뒤 공백, 길이는 EUC-KR 바이트. 거절(종료 코드 2): 길이·자릿수 초과 OVERFLOW(자르지 않는다), 매핑표에 없는 내부 코드 UNMAPPED, 키 없음·N 이 0 이상의 정수가 아님 BAD_VALUE.
  5. /root/eaimw/xform/roundtrip.py 를 만든다. python3 roundtrip.py <레이아웃> <매핑표> <본문파일>... 이 파일마다 <파일이름>,<결과> 한 줄을 출력한다 — f2j 가 거절하면 REJECT, f2j→j2f 결과가 원본 바이트와 같으면 SAME, 다르면 DIFF. 그리고 수신함의 FXTR0001-*.body 전부(이름순)에 대한 결과를 머리글 file,result 와 함께 /root/eaimw/xform/roundtrip.csv 로 저장한다.
  6. 정의서 /opt/lab/fixtures/eaimw/transform/DPRT0001.md/root/eaimw/xform/DPRT0001.layout 으로 옮기고(암묵 소수점 필드는 scale 4), f2j·j2f 가 scale 을 지원하게 고친다. f2j 는 "3.2500" 처럼 scale 자리까지 쓴 문자열, j2f 는 문자열만 받아 decimal 로 계산한다. 소수 자리가 scale 보다 많으면 OVERFLOW(반올림 금지), 문자열이 아니면(JSON 숫자 포함) BAD_VALUE.
  7. j2f 가 KS X 1001 완성형 밖의 글자(예: '똠', 이모지)를 NOT_KSX1001 로 거절하게 고친다(종료 코드 2). 한자·기호처럼 EUC-KR 2바이트인 글자는 그대로 받는다.
  8. 수신함 /opt/lab/fixtures/eaimw/transform/inbox/ 전체를 f2j 로 변환한다. 파일 이름 앞부분(FXTR0001·DPRT0001)이 레이아웃 이름이다. 성공하면 /root/eaimw/xform/out/<이름>.json(확장자 .body 를 뗀 이름), 거절되면 JSON 을 남기지 않는다. 결과를 머리글 file,result, 파일 이름순, 결과는 OK 또는 오류 코드로 /root/eaimw/xform/summary.csv 에 적는다.

참고

정의서를 레이아웃 파일로 옮긴다

/opt/lab/fixtures/eaimw/transform/FXTR0001.md 의 12개 필드를 /root/eaimw/xform/FXTR0001.layout(name,length,type,key,map,scale)으로 옮긴다.

정의서 1절의 표를 그대로 옮깁니다. name 은 영문명, key 는 JSON 키(대소문자 그대로), map 은 코드 매핑 열입니다. 길이를 다 더하면 정의서가 적은 업무부 합계와 같아야 합니다.

코드 정의서를 매핑표로 옮긴다

/opt/lab/fixtures/eaimw/transform/CODES.md 의 사용 중인 코드를 /root/eaimw/xform/codemap.csv(domain,external,internal)로 옮기고 domain·external 순으로 정렬한다.

도메인은 BANK·CHANNEL·KIND 셋입니다. 상태가 사용 중지인 행은 싣지 않습니다 — 그래야 그 코드가 든 전문이 변환에서 걸립니다. 대외 코드 01 의 앞 0 은 값의 일부입니다(엑셀로 열면 사라지는 그 0 입니다).

고정길이 → JSON

/root/eaimw/xform/f2j.py <레이아웃> <매핑표> <본문> 이 JSON 을 출력하고, 모르는 코드는 UNMAPPED, 형식·인코딩 오류는 BAD_VALUE 로 거절한다(종료 코드 2).

open(파일, 'rb') 로 읽어 레이아웃의 길이만큼 바이트로 자릅니다. 필드마다 decode('euc_kr') 을 하면 반쪽 한글과 CP949 전용 글자가 UnicodeDecodeError 로 드러납니다. N 은 strip 뒤 숫자인지 확인해 int 로, AN·H 는 rstrip(' ') 만 합니다. 매핑표에 없는 코드를 원문 그대로 넘기지 마세요.

JSON → 고정길이, 바이트로 센다

/root/eaimw/xform/j2f.py <레이아웃> <매핑표> 이 본문 바이트를 내고, 길이 초과는 OVERFLOW, 모르는 내부 코드는 UNMAPPED, 키 없음·정수 아님은 BAD_VALUE 로 거절한다.

H 필드의 길이는 len(값) 이 아니라 len(값.encode('euc_kr')) 입니다. 넘치면 잘라서 맞추지 말고 거절합니다 — 바이트로 자르면 한글이 반으로 갈립니다. 매핑은 매핑표를 뒤집은 사전(내부 → 대외)으로 찾습니다. N 은 rjust(길이, '0'), 나머지는 ljust(길이, b' ').

왕복 시험

/root/eaimw/xform/roundtrip.py 가 본문마다 SAME·DIFF·REJECT 를 가리고, 수신함 FXTR0001 표본 전부의 결과를 /root/eaimw/xform/roundtrip.csv 로 저장한다.

f2j 와 j2f 를 함수로 import 해서 씁니다. f2j 가 거절하면 REJECT, 되살린 바이트가 원본과 같으면 SAME 입니다. DIFF 가 나온 표본은 변환기 버그일 수도 있지만 상대가 표준을 어긴 것일 수도 있습니다 — 그 필드의 바이트를 od -c 로 보세요.

암묵 소수점은 decimal 로

/root/eaimw/xform/DPRT0001.layout 을 만들고(scale 4), f2j 는 "3.2500" 같은 문자열을, j2f 는 문자열 소수만 받아 decimal 로 계산하게 고친다.

0032500 을 scale 4 로 읽으면 Decimal(32500).scaleb(-4) = 3.2500 입니다. 되돌릴 때는 Decimal("3.25").scaleb(4) = 32500 입니다. float("0.29") * 100 은 28.999999999999996 이라 int 로 바꾸면 28 이 됩니다. 소수 자리가 scale 보다 많으면 반올림하지 말고 거절하세요.

완성형 밖의 글자를 거절한다

j2f 가 KS X 1001 완성형에 없는 글자('똠', 이모지)를 NOT_KSX1001 로 거절하고(종료 코드 2), 한자·기호 같은 2바이트 글자는 받게 고친다.

먼저 python3 -c "print('똠'.encode('euc_kr'))" 를 해 보세요 — 오류가 아니라 8바이트가 나옵니다. 글자마다 encode('euc_kr') 의 길이가 2 인지 확인하면 됩니다. 인코딩 자체가 실패하는 글자(이모지)도 같은 코드로 거절합니다.

수신함 전체를 변환한다

수신함의 모든 본문을 f2j 로 변환해 성공한 것은 /root/eaimw/xform/out/<이름>.json 으로, 결과 목록은 /root/eaimw/xform/summary.csv(file,result)로 남긴다.

파일 이름의 '-' 앞부분이 레이아웃 이름입니다. 종료 코드 2 면 표준오류 첫 줄의 첫 단어가 오류 코드입니다. 셸 리다이렉션은 실패해도 빈 파일을 만들어 두므로, 거절된 표본의 JSON 파일은 지우세요.