글로벌 해운사 AI 업무자동화 PoC

글로벌 해운사 대상 4주 n8n 업무자동화 PoC에서 인프라·관측·성능 검증 트랙을 담당했습니다. n8n Community Edition을 Queue Mode로 구성한 5-VM greenfield 아키텍처를 부트스트랩하고, Grafana Alloy remote_write 기반 관측 스택을 구성했습니다. 물질안전보건자료(MSDS) 문서 검증 자동화의 하이브리드 OCR 프롬프트 PoC를 실행하고, k6 기반 성능·부하 테스트를 단독으로 수행했습니다. MS Teams AI 챗봇과 HITL 승인 게이트를 결선했습니다. 본 PoC 환경과 별개로, 팀이 폐쇄망 밖에서 워크플로를 개발·검증할 수 있도록 사내 AWS 계정에 같은 스택을 Terraform으로 재현했습니다.

Architecture

동기

글로벌 해운사 대상 4주 PoC 의 목표는 n8n 이 고객 환경에서 실제 업무자동화 플랫폼으로 동작하는지 검증하는 것이었습니다. 자동화 대상은 문서 기반 업무 두 축이었고, 그 위에 팀 협업 채널에서 실행하고 승인할 수 있는 사용자 흐름을 얹었습니다.

공개 카드에서는 고객명과 내부 시스템명을 쓰지 않습니다. 제가 직접 세우고 검증한 것만 남깁니다.

내가 담당한 트랙

인프라부터 관측, 성능 검증, 문서 처리 프롬프트, 협업 채널 연동, 그리고 팀이 쓸 별도 작업 환경까지 제가 했습니다. 항목별 내역은 오른쪽 「주요 업무」에 있습니다.

트랙경계
MSDS 처리 워크플로 자체팀원이 만들었습니다. 제 몫은 그 워크플로가 쓰는 추출 프롬프트와 그것을 평가하는 기구였습니다

Queue Mode 로 세운 실행 기반

단일 인스턴스 n8n 은 동시 실행이 늘면 실행 대기가 메인 프로세스를 막습니다. 그래서 처음부터 Queue Mode 로 설계해 실행을 worker 로 분리하고, Redis 를 큐 브로커로, PostgreSQL 을 상태 저장소로 두었습니다. 역할을 다섯 대의 VM 으로 나누고 이미지 버전을 고정해, 고객 인프라팀이 같은 구성을 다시 세울 수 있는 부트스트랩으로 만들었습니다.

관측은 같이 갔습니다. PoC 라도 “왜 느린가” 와 “어디서 막히는가” 에 데이터로 답할 수 있어야 하는데, 그 답이 없으면 뒤에 나올 성능 논의가 전부 인상 비교가 됩니다. Grafana Alloy 를 각 노드에서 중앙 스택으로 밀어 보내는 방식으로 두고, 워크플로 실행 시간과 큐 적체, 자원 사용량을 한 화면에 모았습니다. 부하를 걸 때 읽은 것이 이 화면입니다.

MSDS 에서 값과 좌표를 뽑기

물질안전보건자료는 표와 화학 정보가 섞인 문서라 단일 OCR 로는 오탈자와 표 붕괴가 생깁니다. 그래서 범용 OCR 로 1차 추출한 뒤 언어모델로 교차검증하는 하이브리드 프롬프트를 설계하고, 라이브 워크플로를 받아 고쳐 다시 올리는 루프로 반복 개선했습니다.

이 문서에서 뽑는 것은 값만이 아닙니다. 화면에 원문 위치를 표시하려면 값이 문서 어디에 있었는지도 같이 나와야 합니다. 값 정확도는 재는 기구가 있었는데 좌표는 없어서, 다른 모델이 더 나은 상자를 내는지 알 방법이 없었습니다.

그래서 재는 기구를 만들었습니다. 같은 프롬프트를 모델 넷에 동시에 흘려 좌표를 모으고, 사용자가 손으로 표시해 준 정답 상자와 겹치는 정도를 항목별로 점수 내 순위를 매겼습니다. 정답 상자를 언어모델에게 만들게 하려다 접었습니다 — 그러면 채점 기준을 채점 대상과 같은 종류의 물건으로 만드는 셈이라 점수가 무엇을 말하는지 알 수 없게 됩니다.

부하를 두 번 걸기

한 번으로 끝나지 않았습니다. 1차는 지금 설정에서 문제가 없는지 보는 것이었고, 2차는 천장이 어디인지 찾는 것이었습니다.

1차에서는 동시 작업을 1에서 60까지 일곱 단계로 올렸고 전 구간 실패가 없었습니다. 처리량은 동시 10건에서 천장을 친 뒤 20, 50, 60 으로 올려도 오르지 않았는데, 그 지점의 worker 는 처리 지연 11밀리초에 메모리 10퍼센트로 놀고 있었습니다. 천장은 자원이 아니라 보수적으로 잡아 둔 동시 처리 설정값이었습니다.

2차는 그 조치를 적용하고 다시 쟀습니다. 부하도 바꿨습니다 — 1차는 순수 대기만 흉내 냈는데, 실제 작업은 외부 응답을 기다리는 24초 뒤에 문서를 파싱하는 1.5초가 붙습니다. 그 파싱을 부하에 넣자 1차가 못 본 것이 나왔습니다. 메모리를 쓰는 주체가 worker 가 아니었습니다. worker 자체는 450MB 로 가볍고, 문서와 버퍼는 전부 워크플로 안의 코드를 실제로 돌리는 별도 실행기에 쌓이고 있었습니다. 1차에서 “worker 에 제한을 걸자” 고 적었던 것을 그 실행기 쪽으로 옮겼습니다.

가드레일은 일부러 넘겨서 확인했습니다. 제한을 초과하도록 부하를 인가하니 그 실행기만 중단됐다가 다시 뜨고 호스트와 worker 는 살아 있었습니다. 동시 처리는 15에서 120까지 올려 봤는데 worker 는 CPU 5.6퍼센트에 지연 13밀리초로 여전히 여유가 있었고, 실질 상한은 파싱을 처리하는 쪽의 동시 한도가 정하고 있었습니다. 성능이 꺾이는 지점을 보고 그 3분의 2 수준을 권고값으로 잡아 단계적으로 올리도록 전달했습니다.

AWS 샌드박스 — 팀이 밖에서 쓸 환경

본 PoC 환경은 폐쇄망 안이라 승격 절차와 방화벽, 분리된 콘솔을 매번 거쳐야 했습니다. 워크플로를 만들고 고치는 일이 하루에도 여러 번인데 그 왕복이 붙으니 팀의 반복 속도가 먼저 떨어졌습니다. 그래서 같은 스택을 사내 AWS 계정에 축소 구성으로 다시 세워 팀이 언제든 붙어 쓸 환경을 따로 만들었습니다. 고객사 AWS 를 운영한 것이 아니라 고객 환경을 밖에서 흉내 낸 것입니다.

전부 Terraform 으로 선언하고 부팅 스크립트가 컨테이너와 리버스 프록시를 한 번에 띄우도록 해서, 환경을 다시 세울 때 사람이 손으로 할 일이 남지 않게 했습니다. 기동한 뒤에는 헬스체크와 인증서, 그리고 워커가 준비 상태를 찍는 것까지 확인해 큐 배선을 봤습니다.

시크릿 배치가 이 구성에서 가장 신경 쓴 부분입니다. 값을 저장소에 두지 않고 파라미터 저장소의 암호화 항목으로 넣은 뒤, 인스턴스 역할이 전용 접두 아래만 읽게 하고 복호화도 그 저장소를 거친 요청일 때만 허용했습니다. 값이 Terraform 상태 파일에도 저장소에도 메신저에도 남지 않는 것이 목적이었습니다. 셸 접근도 세션 관리자로 열어 키를 나눠 줄 일을 없앴습니다.

배운 점

  • 부하를 걸어 본 천장이 시스템의 한계인 줄 알았는데 제가 적어 둔 설정값이었고, 그 설정을 올려 다시 재니 이번에는 병목이 예상한 프로세스에 없었습니다. 두 번 다 지금 보고 있는 수치가 무엇의 한계인지를 잘못 잡고 있었습니다.
  • 재는 기구가 없으면 비교가 안 됩니다. 값 정확도는 측정할 수 있어서 모델을 바꿔 볼 수 있었는데, 좌표는 잴 방법이 없어서 지금 쓰는 모델이 나은지조차 몰랐습니다. 채점표를 먼저 만들고 나서야 그 질문에 답할 수 있었습니다.
  • 폐쇄망에서 먼저 느려진 쪽은 팀이었습니다. 워크플로 한 줄을 고치는 데 승격과 방화벽과 콘솔 왕복이 붙으니 하루에 시도할 수 있는 횟수가 줄었고, 밖에 같은 스택을 하나 더 세우는 비용이 그 왕복보다 쌌습니다.