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

EAI 중간 계층 만들기

허브는 누구에게 줄지를 표로 정한다

LabHub 에서 이어서 보기

한 줄 요약

허브는 헤더의 거래코드와 수신기관만 보고 목적지를 정한다. 그 판단은 코드가 아니라 데이터(라우팅 표) 로 두고, 규칙이 겹칠 때 누가 이기는지를 문서로 못박고, 어디로도 보낼 수 없는 전문은 삼키지 않고 즉시 오류 응답으로 돌려준다.

왜 이게 필요했나

1모듈에서 본 것처럼 시스템끼리 직접 잇는 점대점 연결은 시스템 수가 늘면 선이 제곱으로 늘어난다. 허브-스포크 구조는 모든 선을 허브 하나로 모으고, 대신 허브에게 한 가지 일을 새로 맡긴다 — 이 전문을 누구에게 줄 것인가. Hohpe 와 Woolf 의 『Enterprise Integration Patterns』는 이 역할을 Content-Based Router 라고 부른다. 메시지 안의 데이터(필드가 있는지, 특정 필드의 값이 무엇인지)를 보고 보낼 채널을 고르는 구성 요소다. 우리 허브는 그중 헤더의 두 필드만 본다.

같은 글이 경고하는 것이 있다. 라우터는 목적지가 바뀔 때마다 고쳐야 하는 잦은 유지보수 지점이 되기 쉽고, 규칙이 복잡하면 설정 가능한 규칙 집합으로 목적지를 계산하는 형태를 택할 수 있다고 한다. 은행 허브에서는 이 경고가 그대로 현실이다. 거래코드는 매달 새로 생기고, 시스템이 이관되면 수십 개 코드의 목적지가 한꺼번에 바뀐다. 라우팅을 if tx_code == "BKTR0001" 같은 코드로 적어 두면 거래 하나를 추가할 때마다 허브를 다시 배포해야 하고, 허브 배포는 그 은행의 모든 거래를 잠깐씩 흔든다. 그래서 라우팅 규칙은 표(데이터)로 빼고, 허브 코드는 '표를 읽고 우선순위대로 고른다' 는 일만 한다. 표가 바뀌는 일은 잦고 규칙을 해석하는 방식이 바뀌는 일은 드물다 — 자주 바뀌는 것과 드물게 바뀌는 것을 떼어 두는 것이다.

어떻게 동작하나

원천은 현업의 인터페이스 목록이다. 어떤 거래가 어느 시스템으로 가는지는 개발자가 아니라 업무 담당이 엑셀로 관리하는 경우가 많다. 이 목록에는 폐기된 인터페이스, 개발 중인 인터페이스, 같은 거래의 옛 버전이 섞여 있다. 허브가 쓰는 라우팅 표는 이것을 정제한 결과물이다. 상태가 운영인 행만 싣고, 같은 거래코드가 여럿이면 가장 큰 버전 하나만 남긴다. 여기서 흔한 함정이 버전 비교다. 엑셀에서 내린 CSV 의 버전은 글자라서 문자열로 비교하면 "9""10" 보다 크다. 정수로 바꿔서 비교해야 한다.

표는 '어디로' 만이 아니라 '어떻게' 도 싣는다. 라우팅 표의 한 행에는 목적지와 함께 동기·비동기 구분과 타임아웃이 있다. 타임아웃이 거래마다 다른 데는 이유가 있다. 잔액 조회는 몇 초 안에 답해야 의미가 있지만, 보험 청구 서류 제출은 수십 초가 걸리는 것이 정상이다. 모든 거래를 한 값으로 묶으면, 짧게 잡으면 청구가 전부 실패로 보이고 길게 잡으면 멈춘 계정계를 기다리느라 허브의 연결이 쌓인다. 타임아웃은 거래의 성질이므로 거래코드와 같은 행에 둔다.

규칙은 네 가지이고 순위가 있다. 이 코스의 가상 기준(ROUTING.md)은 이렇게 정했다.

순위 규칙 조건 왜 이 자리인가
1 ORG 수신기관이 자행이 아니다 → FEP 다른 기관으로 나가는 전문은 대외 회선·기관별 형식이 모인 한 출구로만 나가야 한다
2 EXACT 거래코드 정확 일치 예외는 가장 구체적인 규칙으로 적는다
3 PREFIX 가장 긴 접두어 일치 CD* 로 업무군 전체를 묶되, CDLN* 같은 하위 묶음이 이긴다
4 NONE 아무것도 안 맞음 → E101 추측해서 보내지 않는다

'구체적인 것이 이긴다' 는 발상은 IP 라우팅과 같다. RFC 1812 는 라우터가 경로를 고를 때 특정 호스트 경로를 먼저, 그다음 네트워크 접두어를 접두어가 긴 순서로 고른다고 적는다. CD*CDLN* 이 둘 다 맞으면 더 긴 쪽이 더 많은 것을 알고 있는 규칙이다.

우선순위는 표의 행 순서가 아니어야 한다. '위에서부터 처음 맞는 행' 으로 구현하면 행 순서가 숨은 규칙이 된다. 누군가 엑셀을 거래코드순으로 정렬해 다시 내리기만 해도 CD*CDLN* 보다 위로 올라가고, 카드론 전문이 조용히 카드계로 간다. 아무 오류도 나지 않는다. 그래서 우선순위는 코드가 명시적으로 계산한다 — 정확 일치를 먼저 찾고, 접두어는 길이로 정렬한다.

라우팅 불가는 응답으로 돌려준다. 표에 없는 거래코드를 받은 허브가 로그만 남기고 전문을 버리면, 보낸 채널은 응답을 기다리다 타임아웃이 나서야 실패를 안다. 그 사이 고객 화면은 돌고, 채널이 재전송까지 하면 같은 헛걸음이 두 번 난다. 허브는 받은 요청을 뒤집어 응답 코드 E101 을 실은 응답 전문을 즉시 만든다(1모듈의 응답 만들기 그대로 — 거래코드·GUID 유지, 기관 맞바꿈). 반면 형식이 틀린 전문(E102)에는 이 응답을 만들 수 없다. 헤더 자체를 믿을 수 없으니 GUID 도 믿을 수 없다.

결정마다 감사 로그를 남긴다. "이 거래가 왜 정보계로 갔나" 라는 질문에 답하려면 목적지만이 아니라 어느 규칙이 맞았는지(EXACT·PREFIX·ORG·NONE)와 GUID 가 있어야 한다. 로그는 덧붙이기만 한다. 덮어쓰는 감사 로그는 감사 로그가 아니다.

현장에서 만나는 모습

가장 흔한 사고는 넓은 접두어가 새 거래를 삼키는 것이다. 카드계로 가는 CD* 규칙이 있는 상태에서 계정계로 가야 할 새 거래 CDLN0010 이 생겼는데 표에 정확 일치 행을 넣는 것을 잊으면, 그 거래는 오류 없이 카드계로 간다. 카드계는 모르는 거래라 거절하고, 장애 회의는 카드계를 먼저 의심한다. 감사 로그에 PREFIX 가 찍혀 있으면 원인이 한 줄로 보인다. 정확 일치를 기대했던 거래가 접두어로 나갔다는 것 자체가 신호다.

두 번째는 폐기 인터페이스가 표에 남는 것이다. 시스템을 이관하고 옛 행을 지우지 않으면, 한동안은 아무도 그 코드로 보내지 않으니 조용하다. 어느 날 누군가 그 코드를 재사용하면 폐기된 시스템으로 트래픽이 간다. 상태 열을 두고 '운영만 싣는다' 를 기계적으로 지키는 이유다. 세 번째는 이 모듈의 버전 비교 함정처럼 목록을 정제하는 스크립트의 사소한 실수다. 라우팅 표를 만든 뒤에는 원래 목록과 대조하는 시험을 한 번 돌려야 한다.

다음 실습에서 할 것

현업의 지저분한 인터페이스 목록을 정제해 라우팅 표 routes.csv 를 만들고, 라우터 router.py 를 한 단계씩 키운다 — 정확 일치, 접두어(긴 것이 이김), 기관 기반 규칙, 전문 헤더로 라우팅, E101 응답, 감사 로그. 채점기는 매번 새 거래코드와 목적지 이름으로 무작위 표를 만들어 라우터를 돌리므로, 규칙을 코드에 박아 넣으면 통과할 수 없다. 마지막에 수신함 30건을 배분한다.