LabHub
Get started
배우기 러닝패스 코스

Building an EAI Middleware Layer

Convert Only at the Boundary, Reject What You Cannot Convert

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

한 줄 요약

변환 어댑터는 표준 전문(고정길이·EUC-KR·대외 코드)과 내부 JSON(UTF-8·내부 코드) 사이의 경계 한 곳에서만 형식을 바꾼다. 규칙은 레이아웃과 코드 매핑표라는 데이터로 두고, 모르는 코드·넘치는 길이·표현할 수 없는 글자는 고쳐서 넘기지 말고 거절한다.

왜 이게 필요했나

은행 안에는 두 세계가 함께 산다. 계정계와 대외 기관은 수십 년 된 고정길이 전문을 쓰고, 새 채널과 내부 API 는 JSON 을 쓴다. 두 세계를 직접 잇게 하면 새 채널을 만드는 팀마다 EUC-KR 과 바이트 패딩을 다시 배우고, 대외 코드와 내부 코드의 대응표를 각자 한 벌씩 들고 다니게 된다. 표가 여러 벌이면 언젠가 반드시 한 벌만 낡는다.

『Enterprise Integration Patterns』는 이 자리에 Message Translator 를 둔다. 서로 다른 데이터 형식을 쓰는 애플리케이션 사이에 형식을 바꿔 주는 필터를 끼우는 것으로, 객체지향의 Adapter 패턴을 메시징에 옮긴 것이라고 설명한다. 허브에 변환을 모으면 안쪽 시스템은 JSON 과 내부 코드만 알고, 바깥 표준이 바뀌어도 고칠 곳은 어댑터 하나다.

어떻게 동작하나

레이아웃은 데이터다. 필드마다 이름·길이·형식·JSON 키·매핑 도메인·소수 자리수(scale)를 한 줄씩 적은 표를 두고, 변환기는 그 표를 해석하기만 한다. 정의서가 바뀌면 표를 고친다. 거래마다 변환 코드를 따로 짜면 거래가 백 개일 때 패딩 규칙도 백 벌이 된다.

길이는 바이트다. 형식은 셋이다. N(숫자)은 오른쪽 정렬에 앞을 0 으로, AN(영숫자)과 H(한글 섞임)는 왼쪽 정렬에 뒤를 공백으로 채운다. H 필드의 길이는 EUC-KR 바이트라 한글 10글자가 20바이트 필드를 꽉 채운다. 글자 수로 세면 한글 11글자짜리 이름이 '20자 이하' 검사를 통과하고 22바이트가 되어, 뒤의 모든 필드가 2바이트씩 밀린다.

넘치면 자르지 않는다. 길이를 맞추려고 바이트로 자르면 한글 한 글자가 반으로 갈린다. 받는 쪽은 그 필드를 디코드하지 못하거나, 남은 첫 바이트를 다음 필드의 첫 바이트와 붙여 전혀 다른 글자로 읽는다. 글자 단위로 잘라도 문제는 남는다 — 수취인 이름이 잘린 이체는 업무 사고다. 변환기는 거절하고, 줄일지 말지는 채널과 업무가 정한다.

코드는 표로 바꾸고, 모르는 코드는 거절한다. 전문의 은행코드 201 은 내부에서 GRM 이다. 대응은 매핑표 한 곳에 두고 양방향으로 쓴다. 표에 없는 코드를 원문 그대로 흘려보내면 내부 시스템은 그 값을 자기 코드 체계로 해석한다 — 우연히 같은 값이 다른 뜻으로 있으면 엉뚱한 곳으로 돈이 간다. 합병으로 사용 중지된 코드도 표에서 빼서, 들어오는 순간 걸리게 한다.

왕복이 변환기의 명세다. 표준대로 만든 본문을 JSON 으로 바꿨다가 다시 본문으로 만들면 원래 바이트와 한 바이트도 달라서는 안 된다. 이 성질에서 규칙 몇 개가 저절로 나온다. AN·H 는 뒤 공백만 떼야 한다 — 앞 공백까지 떼면 되돌릴 때 사라진다. N 을 정수로 바꿔도 되는 것은 앞 0 이 채움일 뿐 값이 아니기 때문이다. 변환기를 고칠 때마다 표본 전체로 왕복을 돌리면, 무언가를 잃는 수정은 그 자리에서 드러난다.

금액과 금리는 부동소수점을 거치지 않는다. 전문의 금리 0032500 은 암묵 소수점 넷째 자리라 3.2500 이다. 파이썬 decimal 문서 는 이진 부동소수점에서 1.1 + 2.23.3000000000000003 으로 보이고, 0.1 + 0.1 + 0.1 - 0.3 이 0 이 아니어서 등식 검사를 믿을 수 없다고 설명하며, 그래서 엄격한 등식이 필요한 회계에는 decimal 이 알맞다고 적는다. 같은 문서는 Decimal(0.1)Decimal('0.1') 이 다르다는 것도 보여 준다 — 한 번 float 이 된 값은 decimal 로 돌려도 이미 늦다. 그래서 JSON 에서도 숫자가 아니라 문자열 "3.2500" 으로 주고받는다. 뒤의 0 두 개는 자릿수를 알리는 정보이고, decimal 은 1.30 + 1.202.50 으로 두어 이 표기를 지킨다. 실제로 float("0.29") * 10028.999999999999996 이라, 정수로 바꾸면 1 이 모자란다.

EUC-KR 이라는 이름을 믿지 않는다. EUC-KR 이 담는 한글은 KS X 1001 완성형 2,350자다. 윈도에서 흔한 CP949(UHC)는 여기에 나머지 한글을 더해, 파이썬으로 세어 보면 유니코드 한글 음절 11,172자를 모두 2바이트로 담는다. 2,350자는 두 인코딩에서 바이트가 같아서 평소에는 아무 문제가 없다가 '똠' 같은 글자에서만 드러난다. 더 까다로운 것은 구현마다 다르다는 점이다. 파이썬 표준 인코딩 표euc_kr 코덱은 '똠' 을 오류로 거절하지 않는다. CPython 소스 의 주석대로 KS X 1001:1998 의 조합 시퀀스로 바꿔 8바이트를 낸다. 같은 글자를 glibc 의 iconv -t EUC-KR 은 거절한다. 2바이트 완성형만 아는 상대는 그 8바이트를 자모 네 글자로 읽는다. 그래서 변환기가 글자마다 "완성형 2바이트인가" 를 직접 판정하고, 아니면 거절한다.

현장에서 만나는 모습

반쪽 한글. 적요를 30바이트로 맞추려고 바이트로 자른 채널이 있으면, 대외 기관이 전문 전체를 형식 오류로 돌려보낸다. 오류는 대외에서 나지만 원인은 우리 채널의 자르기다. CP949 가 섞인 본문. 한 채널이 파일을 CP949 로 저장해 보내면 몇 달 동안 멀쩡하다가, 이름에 완성형 밖 글자가 있는 고객이 처음 오는 날 깨진다. 변환기가 EUC-KR 로 엄격하게 읽어 거절하면 그날 바로 원인이 보인다. 공백으로 채운 숫자. 금액 필드를 0 대신 공백으로 채워 보내는 시스템이 있다. 읽을 수는 있지만 왕복하면 바이트가 달라진다. 왕복 시험은 변환기의 버그뿐 아니라 이런 표준을 어긴 상대도 찾아낸다.

다음 실습에서 할 것

정의서를 레이아웃 파일로, 코드 정의서를 매핑표로 옮긴 뒤 변환기 두 개(f2j.py·j2f.py)를 직접 만든다. 표본 전부로 왕복 시험을 돌리고, 암묵 소수점을 decimal 로 다루고, 완성형 밖 글자를 거절하게 고친 다음 수신함 전체를 변환한다. 공용 라이브러리 lhconv.py 가 같은 일을 하지만 이 실습에서는 쓰지 않는다 — 4모듈부터 쓴다. 채점기는 매번 새 레이아웃과 새 값(한글 포함)으로 변환기를 돌린다.