Narratix 프로젝트에는 유저가 업로드한 PDF를 분석하여 문항 유형, 제목, 내용 등을 자동으로 분류해주는 AI 라벨링 기능이 존재합니다.
AI 라벨링은 AI API을 활용해야 하므로 필연적으로 처리 시간이 길어질 수밖에 없습니다. 이는 유저가 결과를 확인하기까지 하염없이 기다려야 함을 의미하며, 사용자 경험을 저해하는 주된 요인이 되었습니다. 이를 해결하기 위해 비동기 이벤트 기반 아키텍처를 도입하여 시스템을 개선하기로 결정했습니다.
이 글에서는 Narratix의 비동기 아키텍처가 어떤 과정을 거쳐 현재의 모습으로 발전했는지 공유하고자 합니다.
AI 토큰 비용 효율화
PDF 원본을 바로 AI에게 전달할 경우, AI 토큰이 너무 많이 소모될 수 있다는 문제가 존재합니다. 이를 해결하기 위해 PDF 텍스트 추출 → 추출된 텍스트만 AI에게 전달하는 프로세스로 토큰 사용량을 최적화하고자 했습니다.
예외 처리와 사용자 피드백
긴 처리 과정 중에는 네트워크 이슈, AI 응답 포맷 오류, PDF 파싱 실패 등 다양한 예외가 발생할 수 있습니다. 비동기 처리 특성상 백그라운드에서 오류가 발생했을 때, 유저에게 성공/실패 여부를 명확하게 전달하는 것이 중요했습니다.
따라서 성공 또는 실패를 항상 유저에게 제공할 수 있는 비동기 이벤트 아키텍처를 고민하게 되었습니다.

첫 번째로 고려했었던 아키텍처입니다.
AWS의 관리형 서비스인 S3, SQS, Lambda를 적극 활용한 구조입니다. S3 업로드 이벤트를 트리거로 활용하여 안정적인 파이프라인을 구축하고자 했습니다. 저희 서비스는 최대 3개의 PDF를 업로드할 수 있으므로, 각 PDF가 독립적으로 라벨링을 수행하고 모두 완료되었을 때 유저에게 알림을 보내는 방식입니다.
하지만 이 아키텍처는 몇 가지 문제가 존재했습니다.
데이터 정합성 책임 전가: 1번 흐름을 보았을 때, 클라이언트가 S3 업로드에 실패할 경우, 다시 서버에 Job 삭제를 요청해야 합니다. 이는 트랜잭션 관리의 책임을 클라이언트에게 전가하는 구조가 되어 적절하지 않은 아키텍처라고 생각했습니다.
불필요한 리소스 낭비: S3 → SQS → Lambda 흐름에서 SQS는 불필요했습니다. S3 Event Notification을 통해 Lambda를 직접 트리거할 수 있음에도 큐를 두어 아키텍처 복잡도만 높였습니다. lambda가 아닌 실제 서버라면 S3에서 요청이 Push되는 구조를 SQS를 두어 pull 구조로 바꾸며 요청량을 지연시켜주는 역할을 할 수 있었지만, lambda는 AWS의 관리형 서비스이므로 이 과정이 불필요했습니다.
Lambda의 비용 효율성: Lambda는 메모리와 실행 시간에 비례해 과금됩니다. 따라서 CPU Bound 작업에는 적합할 수 있으나, AI 응답을 대기하는 긴 I/O Bound 작업에 Lambda를 사용하는 것은 비용적으로 비효율적이라고 판단했습니다.

따라서 두 번째로 고려하게된 아키텍처입니다
비용 절감과 유연성을 위해 별도의 Upload 서버와 Python Worker, 그리고 RabbitMQ를 도입했습니다.
Upload용 서버에 pdf 글자 추출을 하는 Python Worker와 RabbitMQ, AI API를 통해 AI 라벨링까지 진행할 수 있는 구조였습니다.
이를 통해 lambda 및 SQS의 비용을 줄이고, 멀티스레드 또는 비동기 IO를 이용해 AI Labeling을 처리할 수 있었습니다.
또한, RabbitMQ는 SQS와 다르게 필터링 기능이 존재하여 단일 브로커 내에서 단계별 처리를 수행할 수 있었습니다.
이 구조도 좋은 구조라고 생각했지만 이 구조도 몇 가지 걱정이 존재했습니다.
1. 구축 및 유지보수 비용: RabbitMQ 설정, Worker 서버 구축 및 관리 등 인프라 복잡도가 급격히 증가했습니다. 한정된 개발 기간과 인원을 고려했을 때 오버엔지니어링일 수 있다는 우려가 있었습니다.

따라서 인프라 관리 비용은 줄이면서, I/O 처리를 효율적으로 수행할 수 있는 절충안인 아키텍처입니다.
API서버가 Lambda를 호출하면, lambda가 S3에서 PDF를 가져와 글자를 추출하고 SQS에 전달하면 API 서버가 SQS에서 pull 방식으로 데이터를 가져와 라벨링을 진행합니다.