Projects

BizKit AI 서버: 모델 호출을 서비스 기능으로 만들기

긴 AI 작업을 비동기로 처리하고 모델의 역할을 제한한 BizKit AI 서버의 구현, 담당 범위와 설계 변경을 정리한 기록이다.

프로젝트와 문제

BizKit은 팀 CARO가 개발한 디지털 명함 서비스이다. 명함 생성과 OCR, 직무 기반 자기소개, GitHub 활동 분석, 자연어 검색을 개발했다. 나는 그중 AI 서버를 설계하고 구현했다. 팀 서비스는 최종 사용자에게 제공했지만, 개발한 모든 기능이 최종 제품에 포함된 것은 아니다. 리뷰 기능은 팀에서 제외했다.

AI 작업은 일반적인 데이터 조회보다 오래 걸렸다. 모델 추론 외에도 GitHub API와 검색 API를 호출해야 했고, 생성 결과를 그대로 서비스 응답으로 사용할 수도 없었다. 작업을 기다리는 방법과 결과를 검증하는 방법을 함께 설계해야 했다.

담당 범위

영역내가 맡은 일과 팀의 경계
AI 서버설계·구현, API 명세, 기능별 파이프라인 구성
모델 비교이미지 생성·LLM·임베딩 모델 비교. VLM OCR 모델 비교는 제외
배포GPU 모델 서버 배포를 담당. FastAPI 서버 배포는 클라우드 팀원이 담당
팀 서비스프론트엔드·Spring 백엔드·클라우드와 연동해 사용자에게 제공

AI 저장소에 구현을, AI 위키에 설계와 비교 기록을 남겼다. 이 글은 전체 흐름과 개인 기여를 소개하고, 세부 설계·측정·회고는 별도 글에서 다룬다.

구현한 작업 구조

Spring은 사용자와 명함 같은 도메인 데이터와 정책을 담당하고, FastAPI는 AI 처리 순서와 모델 호출·후처리를 담당하도록 나눴다. 비동기 경로에서는 요청을 접수한 뒤 Redis Streams에 작업을 넣고 Worker가 실행한다.

다음은 비동기 경로의 책임을 나타낸 개념도이다. 모든 기능의 네트워크 경유지를 동일하게 표현한 것은 아니다.

요청 접수와 모델 실행의 분리요청 접수와 모델 실행의 분리
요청 접수와 모델 실행의 분리좌우로 스크롤하여 전체 보기원본 크게 보기

클라이언트는 task_id로 상태와 결과를 조회한다. 작업 상태는 pending, running, completed, failed로 구분했다. 기존 동기 API도 있어 모든 요청이 이 경로를 거친 것은 아니다.

이 구조는 접수와 실행을 분리한다. 모델 계산 자체를 빠르게 만들거나, 재시도에 따른 중복 실행까지 자동으로 막아주는 구조는 아니다.

기능별로 제한한 모델의 역할

명함 생성: 배경과 텍스트 분리

명함의 전화번호와 이메일은 한 글자만 달라도 문제가 된다. 그래서 이미지 모델은 배경을 만들고 이름과 연락처는 별도 렌더러가 배치하도록 나눴다.

명함 배경 생성과 텍스트 렌더링의 분리명함 배경 생성과 텍스트 렌더링의 분리
명함 배경 생성과 텍스트 렌더링의 분리좌우로 스크롤하여 전체 보기원본 크게 보기

모델이 글자를 직접 그리지는 않지만, 입력 오타나 렌더러의 잘림·줄바꿈은 별도로 확인해야 한다.

명함 OCR: 추출과 도메인 처리를 분리

OCR은 업로드한 이미지에서 연락처 필드를 추출하고 정규화하는 작업이다. Spring 도메인 데이터 조회 없이 AI 서버에서 처리하는 경로를 설계했다.

인증·접근 통제는 여전히 필요하다. API 접근, 파일 업로드 제한, 추출한 개인정보의 보관 정책은 별도로 다뤄야 한다.

직무 분석: 생성 전후의 처리

회사·부서·직무·프로젝트 정보를 바탕으로 자기소개를 생성했다. 생성 전에는 임베딩 기반 직무 필터와 검색 컨텍스트를, 생성 후에는 JSON 검증과 캐시 저장을 두었다.

필터와 캐시는 불필요한 모델 호출을 줄이려고 넣었다. 정확도나 비용 절감 효과를 확인하려면 필터가 잘못 통과시킨 요청과 캐시가 잘못 재사용한 응답을 따로 평가해야 한다.

HEX의 방향 변경

HEX에서는 GitHub 활동을 수집하고 지표를 점수로 환산하는 부분을 구현했다. 처음에는 LLM의 점수 보정도 계획했다. 하지만 보정값이 타당하다고 설명할 정확한 근거를 제시하기 어려웠다.

그래서 점수는 정형화할 수 있는 지표로만 제공하고, LLM은 리뷰를 분석하는 방향으로 바꿨다. 이후 팀에서 리뷰 기능을 제외했다. LLM의 역할은 처음 계획부터 최종 제품까지 여러 번 달라졌다.

GitHub 활동 지표도 개발자의 협업 능력 전체를 객관적으로 측정한 값은 아니다. 관측한 활동을 정해진 규칙으로 환산한 값으로 범위를 제한했다. 방향 변경과 최종 기능 범위는 개발 회고에 자세히 정리했다.

모델 선택의 범위

이미지 생성은 SDXL Base, 임베딩은 BGE-m3-ko를 중심으로 구성했다. LLM 비교에서는 Qwen2.5 7B Instruct를 권장 모델로 정했다. 후속 README의 LLM 설정은 Qwen3-8B-AWQ이다.

Milvus 비교 실험에서는 IVF_FLAT을 선택했지만, 컬렉션 생성 코드의 기본 설정은 AUTOINDEX이다. 수치와 비교 조건은 모델 선택과 벤치마크에 정리했다.

결과와 한계

AI 서버를 설계·구현하고 GPU 모델 서버를 배포해, 팀 서비스를 사용자에게 제공하는 데 참여했다. 작업 접수와 실행을 분리했고, 이미지 모델이 만드는 배경과 렌더러가 배치하는 텍스트도 분리했다. HEX에서는 LLM 보정의 근거 부족을 인정하고 역할을 조정했다.

한편 서버리스 GPU 할당 문제와 두 계층의 재시도가 겹쳐 작업이 누적되고 충전 잔액을 모두 소모한 사고도 겪었다. 비동기 큐를 넣는 것만으로 실패와 비용 문제가 해결되지는 않았다. 이 사건 이후에는 재시도를 개별 함수의 옵션이 아니라 전체 요청 경로에서 검토해야 할 정책으로 보게 됐다.

모델 비교는 작은 평가셋에서 진행했다. 이 결과만으로 서비스 전체의 신뢰성이나 처리량, 운영 성공률과 비용 절감률을 판단하기는 어렵다.

더 자세히 보기