AWS 기술 블로그

부트텐트의 생성형 AI 기반 교육과정 등록 자동화 시스템 구성하기

부트텐트는 부트캠프와 국비교육 등 SW∙AI 분야의 직무 교육 정보를 한곳에서 탐색할 수 있는 플랫폼입니다. 사용자는 현재 모집 중인 교육 과정을 확인하고, 분야·비용·일정 등 원하는 조건에 맞춰 교육을 비교할 수 있습니다.

부트텐트는 2022년부터 교육 정보를 빠르고 정확하게 제공하는 데 집중해왔으며, 그 과정에서 수강생과 교육기관을 연결하는 플랫폼으로서 영역을 확대해 왔습니다. 앞으로는 단순히 교육을 찾는 단계를 넘어, 개인의 생애주기와 목적에 맞는 교육을 추천하고 학습 여정을 함께하는 플랫폼이 되는 것을 목표로 하고 있습니다.

부트텐트는 Amazon BedrockAWS Step Functions 기반의 자동화 파이프라인을 구축하여, 교육과정 등록에 소요되는 시간을 69.0% 절감하고 필드 정확도 88.7%를 달성했습니다. Vision OCR 비용은 Claude 4.5 Haiku 대비 40% 절감했습니다.

교육기관 담당자가 모집 페이지 URL을 기반으로 모집 정보 자동 생성을 요청하면, 7단계 파이프라인이 페이지 검색 → 이미지 OCR → 55개 필드 구조화 추출 → 검증을 수행해 입력 초안을 생성하고, 담당자는 검수·보완 후 제출합니다. 핵심은 Amazon EC2에서 서빙하는 오픈소스 VLM(Qwen3-VL-8B)과 Amazon BedrockAnthropic Claude 모델을 결합한 하이브리드 전략으로, 한국어 OCR 품질과 비용을 동시에 확보한 것입니다.

이 글에서는 하이브리드 Vision OCR 설계, 구조화 추출 프롬프트 엔지니어링, Validation Agent를 통한 품질 보장 방법을 공유합니다.

Part 1. Why – 프로젝트 배경과 목표

프로젝트 배경(Pain-point)

부트텐트에 게시되는 교육 모집 정보는 대부분 교육기관 담당자 분들의 요청을 통해 등록됩니다. 관리자용 페이지에서 부트텐트의 내부 폼 양식에 맞춰 모집 정보를 입력하면, 등록 요청부트텐트 검수최종 게시 순서로 처리됩니다.

그런데 이 등록 요청 단계에서 담당자들이 상당한 입력 부담을 느끼고 있었습니다. 대부분의 교육기관은 이미 자체 홈페이지에 모집 정보를 게시하고 있지만, 이를 부트텐트 폼에 다시 입력하려면 다음과 같은 어려움이 있었습니다.

  • 입력 항목이 많아 동일 정보를 다시 옮겨 적는 데 시간이 많이 소요됩니다.
  • 교육기관의 표현 방식과 부트텐트의 분류 체계가 달라, 각 항목을 어떤 필드에 넣어야 할지 매번 판단해야 합니다.
  • 모집 공고가 포스터형 이미지 또는 스크롤형 이미지 형태로 제공되는 경우가 많아, 항목별 정보를 찾아 수기로 입력해야 합니다.

이로 인해 등록 요청이 지연되거나 입력 품질이 들쭉날쭉해지는 문제가 반복되었고, 정확한 교육 정보를 빠르게 제공한다는 부트텐트의 운영 목표에도 영향을 미치고 있었습니다.

프로젝트 목표(Needs)

이 문제를 해결하기 위해 부트텐트는 Amazon Bedrock 기반의 생성형 AI를 도입했습니다. 교육기관 담당자가 직접 입력(기존 방식) 대신 자동 생성(추출) 요청하면, 요청 시 제출한 모집 페이지 URL을 대상으로 AI가 페이지의 텍스트와 이미지를 분석해 모집 정보를 추출하고, 부트텐트 등록 폼 스키마에 맞춰 입력 초안을 자동 생성해 제안합니다.

즉, 기존의 입력등록 요청검수최종 등록 흐름에서 입력 단계를 최소화하여, 담당자가 모든 항목을 직접 작성하는 대신 자동 생성된 내용을 검토하고 보완하는 방식으로 전환하는 것을 목표로 했습니다. 이를 통해 등록 요청 지연을 줄이고, 입력 품질의 일관성을 확보하며, 궁극적으로 정확한 교육 정보를 빠르게 제공한다는 부트텐트의 운영 목표를 달성하고자 했습니다.

기술적 제약과 대응 전략

하지만 자동화 시스템을 구현하기 위해서는 몇 가지 기술적 제약을 먼저 해결해야 했습니다. 교육 모집 정보에는 일정, 지원자격, 비용 등 의사결정에 직접 영향을 주는 민감한 데이터가 많아, 작은 오류도 사용자 신뢰와 운영 리스크로 이어질 수 있기 때문입니다.

이러한 제약을 완화하기 위해 다음의 가드레일을 적용했습니다.

  • OCR 품질 비교 기준 수립: 대표 모집 페이지 유형별로 추출 결과를 비교하고, 오류 패턴(날짜, 금액, 자격요건 등)을 기준화했습니다.
  • 프롬프트/후처리 고도화: 부트텐트 폼 구조와 운영 원칙을 프롬프트에 반영해 필드 누락과 오매핑을 줄였습니다.
  • 휴먼루프(HITL): 최종 제출 전 담당자가 반드시 검수하도록 프로세스를 유지했습니다.
  • 점진적 도입: 충분한 내부 테스트 후 단계적으로 적용 범위를 확대하기로 했습니다.

Part 2. What – 아키텍처 설계

AS-IS

기존에는 교육기관 담당자가 AWS Amplify 배포된 교육기관용 관리자 페이지에 접속하여 모집 정보를 직접 입력하는 방식이었습니다. 입력된 데이터는 REST API → Amazon API Gateway → AWS Lambda를 거쳐 비즈니스 로직 및 유효성 검증 후 Amazon DynamoDB에 저장됩니다. 저장된 정보는 부트텐트 내부 운영진의 2 검수를 거친 뒤 최종 등록되는 구조입니다.

이처럼 외부 모집 페이지의 정보를 내부 시스템에 맞게 다시 정리하고 입력하는 전 과정이 사람의 판단과 수작업에 의존하고 있어, 데이터 입력에 많은 시간이 소요되고 운영 부담이 발생하는 문제가 있었습니다.

TO-BE : 7단계 파이프라인

새로운 시스템은 Amazon BedrockAWS Step FunctionsAmazon EC2(vLLM 서버)를 활용하여 다음 7단계 파이프라인으로 구성했습니다.

단계 역할 핵심 기술
1. Fetch URL 목록 조회 및 검증 외부 API, URL 유효성 검사
2. Retrieve 동적 페이지 렌더링 및 마크다운 변환 Playwright, Amazon S3
3. Vision 이미지 OCR (하이브리드) Qwen3-VL-8B(vLLM) + Claude 4.5 Haiku(Bedrock)
4. Extract 55개 필드 구조화 JSON 추출 + 검증 Claude 4.5 Sonnet(Bedrock), Validation Agent
5. Ingest 추출 데이터를 서비스 DB에 적재 Amazon DynamoDB
6. Update 등록 상태 관리 및 모드 분기 AWS Step Functions
7. Notify 처리 결과 알림 Slack Web API, AWS Lambda

이 글에서는 기술적 난이도가 높은 2~4단계(Retrieve·Vision·Extract)를 중심으로 설명하고, 1단계(Fetch)와 5~7단계(서비스 통합)는 개요 수준으로 다룹니다.

Part 3. How – 단계별 구현 상세

1단계 Fetch: URL 수집 및 검증

Fetch는 파이프라인의 진입점으로, 데이터베이스로 적재한 교육과정 URL 목록과 상태를 조회합니다. 이후 각 URL의 형식 검증(정규식 http/https, 경로 등)과 접근성 검증(HEAD 메소드, 타임아웃)과 중복 제거를 수행합니다. 유효하지 않은 URL은 이 단계에서 필터링되어 이후 단계의 불필요한 처리를 방지합니다.

2단계 Retrieve: 텍스트 및 이미지 탐색

Playwright 기반의 헤드리스 브라우저로 동적 페이지를 렌더링합니다. SPA로 구현된 교육기관 웹사이트도 JavaScript 실행 후 완전히 렌더링된 DOM을 확보할 수 있습니다. HTML을 마크다운으로 변환하고, iframe 내부 콘텐츠도 블랙리스트 기반으로 필터링하여 교육과정 정보를 탐색합니다. 결과는 Amazon Simple Storage Service(Amazon S3)에 저장됩니다.

3단계 Vision: 하이브리드 OCR 전략

왜 하이브리드인가 – 세 가지 제약

교육과정 웹페이지의 핵심 정보 상당 부분은 이미지 안에 존재합니다. 가격표, 커리큘럼 일정표, 강사 소개 카드 등이 디자인된 이미지로 제공되는 경우가 많기 때문입니다. API 기반 Vision 모델 하나만으로 이 모든 이미지를 처리하기에는 세 가지 문제가 있었습니다.

첫째, 이미지 해상도 제한입니다. Amazon Bedrock의 Anthropic Claude Messages API는 이미지 크기를 최대 8,000×8,000px로 제한합니다. 교육기관의 전체 일정표나 세부 안내 이미지는 이 제한을 초과하는 경우가 있어 타일링(분할) 처리가 필요한데, 타일링은 API 호출 횟수와 비용, 할당량(throttling) 리스크를 동시에 증가시킵니다. 반면 GPU 인스턴스에서 자체 호스팅하는 모델은 이러한 제한이 상대적으로 여유롭습니다.

둘째, 비용 구조의 차이입니다. API 기반 Vision 모델은 이미지 크기에 비례하여 토큰이 소비되는 변동비 구조인 반면, GPU 인스턴스는 시간당 고정 비용만 발생합니다. 이미지 처리량이 많아질수록 고정비 구조가 유리해집니다.

셋째, OCR 정확도가 중요합니다. Vision 모델이 이미지 속 텍스트를 정확하게 읽어내지 못하면, 이후 Extract 단계에서 아무리 뛰어난 LLM을 사용하더라도 정확한 결과를 기대할 수 없습니다. Vision 모델의 선택이 전체 파이프라인의 품질을 좌우하는 셈입니다.

이러한 이유로, Amazon EC2 GPU 인스턴스에서 오픈소스 VLM(Qwen3-VL-8B)을 Primary로 서빙하고, Amazon Bedrock의 Claude 4.5 Haiku를 Fallback으로 활용하는 하이브리드 전략을 채택했습니다.

Qwen3-VL-8B vs Claude 4.5 Haiku 비교

Vision 단계의 목적은 범용 추론보다 OCR 특화 성능에 가깝습니다. 경량 모델로도 충분한 품질을 기대할 수 있어, Claude 4.5 Haiku 모델과 Qwen3-VL-8B 모델을 비교 대상으로 선정했습니다.

항목 Qwen3-VL-8B Claude 4.5 Haiku
유형 오픈소스 VLM 상용 VLM (Bedrock)
라이선스 Apache 2.0 종량제 API
한국어 OCR 지원 (32개 언어) 지원
OCRBench 896/1000 미공개
비용 구조 무료 (GPU 인프라 필요)
g6e.xlarge($2.288)
종량제 ($1/MTok in, $5/MTok out)
주요 장점 OCR 특화 성능, 해상도 제한 없음 범용 멀티모달 추론, 관리형 서비스
주요 한계 GPU 인프라 필요(2025.12 기준 Bedrock VL 모델 지원) 이미지당 토큰 비용 발생, 8,000px 해상도 제한

성능 검증

벤치마크 수치가 실제 한국어 교육과정 페이지에서도 유효한지 확인하기 위해, 동일한 이미지를 두 모델로 OCR 처리한 결과를 비교했습니다.

원문 4.5 Haiku 결과 Qwen3-VL 결과
세종사이버대학교 렌세종사이버대학교 세종사이버대학교
숙명여자대학교 숭실여자대학교 숙명여자대학교
두산에너빌리티 우선에너지 두산에너빌리티
겸임교수 김영교수 겸임교수
AWS 공인 강사 AWS 클인 강사 AWS 공인 강사
양자컴퓨팅 왕자형컴퓨팅 양자컴퓨팅
에이전틱 AI 에지적 AI 에이전틱 AI
바이브코딩 바이크그림들 바이브코딩
캡스톤 프로젝트 랙스톤 프로젝트 캡스톤 프로젝트
시각화(Matplotlib) 사각형(Matplotlib) 시각화(Matplotlib)

검증한 12개 핵심 용어 중 Qwen3-VL-8B는 전부 정확히 인식한 반면, Claude 4.5 Haiku는 전부 오인식했습니다. Claude 4.5 Haiku는 한글 자소 수준의 체계적 혼동(ㅎ→ㅇ, ㅋ→ㅌ, ㅊ→ㅅ 등)과 함께 브랜드명·기관명을 완전히 다른 단어로 치환하는 오류가 빈번했습니다. OCR 정확도가 최종 추출 품질을 직접 결정하므로, 이 결과는 Qwen3-VL-8B를 Primary 모델로 선택한 근거가 되었습니다.

이 결과는 단순히 OCR 모델 선택의 문제가 아니라, 전체 파이프라인 품질의 상한선을 결정합니다. Vision 단계에서 “자부담금 30만원”을 정확히 읽어야, Extract 단계에서 `costOption: “paid”`로 올바르게 매핑할 수 있습니다. 실제로 골든 데이터셋 평가에서 확인된 mismatch 오류 6건 중 상당수는 OCR 정확도가 아닌 매핑 규칙 문제였으며, 이는 OCR 품질이 확보된 덕분에 오류 원인을 프롬프트 개선으로 좁힐 수 있었음을 의미합니다.

Fallback 구조 구현

앞서 설명한 하이브리드 전략의 핵심은, vLLM 호출을 먼저 시도하고 실패 시 Amazon Bedrock으로 자동 전환하는 Fallback 구조입니다.

def analyze_tile_with_fallback(tile_bytes, model_id, vllm_url):
    """vLLM을 먼저 시도하고, 실패하면 Bedrock으로 전환합니다."""
    try:
        text, inp, out = analyze_with_vllm(tile_bytes, model_id, vllm_url)
        return text, inp, out, "vllm"
    except Exception:
        text, inp, out = analyze_with_bedrock(tile_bytes, FALLBACK_MODEL)
        return text, inp, out, "bedrock_fallback"

def analyze_image(image_bytes, vllm_url=None):
    """핵심만 남긴 Fallback 구조 예시."""
    # ... 이미지 리사이즈 및 이미지 분할

    for tile in tiles:
        text, inp, out, provider = analyze_tile_with_fallback(tile, model_id, effective_vllm_url)

`used_provider`는 각 이미지의 실제 호출 경로를 추적하는 변수입니다. 배치 메타데이터에 `fallback` 횟수와 `provider`, `fallback_model`을 기록하여 Fallback 발생 빈도와 vLLM 안정성을 모니터링합니다.

vLLM은 OpenAI 호환 API를 제공하므로 `client = OpenAI(base_url=vllm_url)`로 호출하고, Fallback 시에는 동일한 OCR 프롬프트를 Amazon Bedrock의 Anthropic Messages API 형식으로 전달합니다. 두 Provider의 호출 방식은 다음과 같습니다.

# vLLM을 사용하여 OCR 처리
def analyze_with_vllm(tile_bytes, model_id, base_url):
    """vLLM은 OpenAI 호환 API로 호출."""
    client = OpenAI(base_url=base_url, api_key=VISION_API_KEY)

    # OpenAI 호환 API로 요청을 만들어줍니다.
    response = client.chat.completions.create(
        model=model_id,
        messages=[
            {
                "role": "user",
                "content": [
                    {
                        "type": "image_url",
                        "image_url": {"url": image_url},
                    },
                    {"type": "text", "text": OCR_PROMPT},
                ],
            }
        ],
        max_tokens=4000,
        temperature=0.7,
        top_p=0.8,
        presence_penalty=1.5,
        extra_body={"top_k": 20},
    )

# Bedrock을 사용하여 OCR 처리
def analyze_with_bedrock(tile_bytes, model_id):
    """Fallback 시에는 같은 OCR 프롬프트를 Bedrock Messages 형식으로 전달합니다."""
    client = boto3.client("bedrock-runtime", region_name=BEDROCK_REGION)

    # Bedrock Messages 형식으로 요청을 만들어줍니다.
    request_body = {
        "anthropic_version": "bedrock-2023-05-31",
        "max_tokens": 4000,
        "messages": [
            {
                "role": "user",
                "content": [
                    {
                        "type": "image",
                        "source": image_source,
                    },
                    {"type": "text", "text": OCR_PROMPT},
                ],
            }
        ],
    }

vLLM 호출 시 `presence_penalty=1.5`와 `top_k=20`은 OCR 결과에서 텍스트 반복·중복을 방지하기 위한 Qwen 모델의 핵심 파라미터입니다. Amazon Bedrock 호출은 `invoke_model()` 메서드에 Anthropic Messages API 형식의 요청 본문을 전달하며, 이미지는 base64로 인코딩합니다.

Vision 프롬프트 개선

실제 운영 과정에서 OCR 결과를 분석한 결과, 다음과 같은 문제들이 확인되었습니다.

  • 작은 글씨 정보 누락
  • 숫자/날짜 오추출
  • 복잡한 레이아웃에서 읽기 순서 혼란
  • 타일 OCR 처리 과정에서 중복 또는 누락 발생

부트캠프 페이지 특성상 날짜, 금액, 수강 인원 등 중요한 정보가 작은 글씨로 표시되는 경우가 많습니다. 이를 해결하기 위해 핵심 원칙에 “작은 글씨, 캡션, 주석도 빠짐없이 추출” 규칙을 추가하고, “날짜, 숫자, 금액은 특히 정확하게 추출” 규칙과 함께 모델이 스스로 검증하도록 정확성 체크리스트(Accuracy Checklist) 섹션을 신설했습니다.

멀티 컬럼 레이아웃에서는 읽기 순서가 뒤섞이거나 일부 섹션이 누락되는 문제가 있어, 복잡한 레이아웃 처리 규칙(위→아래, 왼→오른 순서, 배경색/카드로 구분된 섹션 포함)을 추가했습니다.

세로로 긴 이미지를 타일 단위로 잘라 OCR하는 과정에서 타일 경계의 텍스트 중복/누락 문제가 발생하여, 타일 이미지 처리 섹션에 중복 제거 및 잘린 텍스트 연결 규칙을 정의했습니다.

4단계 Extract: Claude Sonnet 4.5 구조화 추출

파이프라인의 마지막 분석 단계인 Extract는 Vision 단계에서 생성된 통합 마크다운을 입력으로 받아 10개 카테고리, 55개 필드의 구조화된 JSON을 추출합니다. 이미지 속 텍스트가 `<OCR>` 태그로 원본 마크다운에 이미 삽입되어 있으므로, 추출 모델은 텍스트와 이미지 정보를 하나의 연속된 문서로 인식할 수 있습니다.

프롬프트 설계 5대 기법

추출 프롬프트는 Anthropic의 Claude 프롬프팅 모범 사례를 참고하여 설계했습니다. 적용한 주요 기법은 다음과 같습니다.

1) XML 태그 기반 프롬프트 구조화

추출 프롬프트 전체를 `<system>`, `<task>`, `<instructions>`, `<schema>`, `<code_reference>`, `<normalization>`, `<output_format>`, `<validation>` 등 13종의 XML 태그로 섹션을 분리했습니다. Claude의 “XML 형식 표시자 사용” 모범 사례에 따른 것으로, 모델이 역할 정의·추출 규칙·스키마·검증 체크리스트를 혼동 없이 참조할 수 있습니다.

<system>데이터 추출 전문가 역할 정의</system>
<task>추출 목표 명시</task>
<instructions>
  <rule name="출력_근거_제한">...</rule>
  <rule name="값_처리">...</rule>
</instructions>
<schema>10개 카테고리, 55개 필드 정의</schema>
<code_reference>15종 코드 참조 테이블</code_reference>
<normalization>날짜·금액·시간 변환 규칙</normalization>
<output_format>JSON 템플릿</output_format>
<validation>8개 항목 검증 체크리스트</validation>

2) 완전한 스키마 정의와 코드 참조 테이블

55개 필드의 JSON 템플릿을 `<output_format>`에 전체 포함하고, `<code_reference>`에 15종의 코드 참조 테이블을 정의했습니다. “구조화된 출력”과 “지시사항을 명확하게 작성” 모범 사례에 해당하며, categories, tags, city, classTypeOption 등 열거형 필드의 유효 값을 코드표로 제한하여 모델이 정의되지 않은 값을 생성하는 것을 최대한 차단합니다.

<codes name="categories">
web: 웹개발
mobile: 모바일
data: 데이터/AI
infra: 클라우드·보안
...
</codes>

3) Hallucination 방지 규칙

“환각 최소화” 모범 사례에 따라, 출력 근거 제한과 값 처리 규칙을 이중으로 적용했습니다. 모델이 제공된 텍스트에 존재하지 않는 정보를 생성하거나, 근거 없이 값을 추측하는 것을 명시적 규칙으로 차단합니다.

<rule name="출력_근거_제한">
- 모든 필드 값은 제공된 OCR/HTML에 실제로 등장한 문자열을 그대로 사용
- 날짜/숫자 포맷만 정규화. 요약/의역/추가 설명 금지
- 제공된 텍스트에 근거가 없으면 null 또는 빈 배열
</rule>

<rule name="값_처리">
- 명시되지 않은 정보는 반드시 null
- 빈 문자열 "" 대신 null
- 추측 금지
</rule>

4) 정규화 예시와 검증 체크리스트

“예시와 세부사항에 주의” 모범 사례에 따라, 날짜·금액·시간의 변환 규칙을 구체적인 변환 예시와 함께 프롬프트에 포함했습니다. 모델이 올바른 형식을 추론이 아닌 참조를 통해 적용하도록 하며, 출력 전 8개 항목의 자기 검증 체크리스트로 형식 일관성을 보장합니다.

<rule name="날짜">
"2025년 11월 24일" -> "2025-11-24"
"2025.11.24" -> "2025-11-24"
"11/24" -> "2025-11-24" (올해 기준)
</rule>

<validation>
출력 전 확인:
1. 모든 필드가 포함되어 있는가?
2. 날짜가 YYYY-MM-DD 형식인가?
3. 코드값 필드가 지정된 코드표 내 값인가?
4. JSON 형식이 유효한가?
...
</validation>

5) 결정적 출력과 날짜 컨텍스트 주입

temperature를 0.1로 설정하여 모델의 출력 변동성을 최소화합니다. 데이터 파이프라인에서는 동일한 입력에 대해 일관된 추출 결과를 보장하는 것이 창의적인 응답보다 중요합니다. 또한 교육기관은 동일한 페이지에서 여러 기수의 일정을 안내하는 경우가 많아, `<today_date>` 태그로 오늘 날짜를 동적 주입하여 현재 또는 가장 가까운 미래 기수의 정보만 추출하도록 유도합니다.

Extract 프롬프트 개선

실제 운영 과정에서 Extract 단계에 다음과 같은 추가 개선을 적용했습니다.

초기에는 빈 값이 `null`로 생성되어 DB에서 “값이 없는 경우”와 “아직 채워지지 않은 값”을 구분하기 어려웠습니다. 이를 해결하기 위해 타입별 기본값 규칙을 적용했습니다.

타입 기본값
string ""
number 0
boolean false
list []
map {}

또한 기존 스키마에서 실제 필요한 정보를 충분히 담지 못한 항목들을 보완하고, 엣지 케이스 대응을 위해 구체적인 예시 추가, 값 생성 제한 조건 명시, 필드 생성 순서 명확화를 진행했습니다.

마지막으로 Validation 체크리스트를 확장하여 null 값 생성 여부, 리스트 필드의 데이터 개수 제한, 프론트단 필수 필드 존재 여부를 검증하도록 했습니다.

추출 결과 검증을 위한 Validator Agent 도입

Extract 단계에서 생성된 구조화 데이터에 대해 정확성을 확인하려면 OCR 원문이나 모집 페이지를 다시 확인해야 했습니다. 이를 보완하기 위해 별도의 Validator Agent를 추가했습니다. Validator Agent는 OCR 결과와 Extract 단계에서 생성한 JSON을 비교하여, 추출 결과가 원문 및 내부 운영 규칙에 맞게 생성되었는지 검증합니다.

Validator Agent는 다음 다섯 가지 역할을 수행합니다.

1) 스키마 검증 필수 필드 존재 여부, 맵·리스트 구조 누락, 타입 일관성 등을 검사합니다.

"timeSpecified": {
  "status":"WARN",
  "issue_type":"missing",
  "value":null,
  "issue": "timeSpecified 맵 누락"
}

2) OCR 근거 기반 사실 검증 — 시작일, 장소, 정원, 비용 등 명시적 필드를 OCR 원문과 JSON 값으로 직접 대조합니다.

"startDate": {
  "status":"PASS",
  "json_value":"2026-04-06",
  "ocr_evidence":"2026.4.6(월) ~ 2026.7.1(수)",
  "issue":null
}

3) 누락 필드 탐지 — OCR 원문에는 존재하지만 JSON에 반영되지 않은 항목을 찾아냅니다.

"projects": {
  "status":"WARN",
  "json_value":null,
  "ocr_evidence":"기업 연계 마케팅 실무 / 팀 프로젝트 발표",
  "issue":"OCR에 기업연계 실무 프로젝트, 팀 프로젝트 발표 등 프로젝트 관련 내용이 있으나 누락됨",
  "issue_type":"missing"
}

4) 규칙 위반 오매핑 탐지 부트텐트 내부 규칙에 맞지 않는 값이나 잘못된 매핑을 검출합니다.

"costOption": {
  "status":"FAIL",
  "json_value":"free",
  "ocr_evidence":"자부담금 30만원",
  "issue": "자비부담금이 있으므로 costOption은 'paid'여야 함",
  "issue_type":"mismatch"
}

5) Hallucination 가능성 탐지 — OCR 원문에 직접 근거가 없는데 문맥상 그럴듯하게 생성된 값을 식별합니다.

"targets": {
  "status":"WARN",
  "json_value": [
"마케팅 직무 취업 준비생",
"K뷰티 산업에 관심 있는 분"
  ],
  "ocr_evidence":"K뷰티 마케팅 전문 강의",
  "issue":"일부 항목은 OCR 원문에 직접 명시되지 않아 hallucination 가능성 있음",
  "issue_type":"hallucination"
}

Validator Agent 도입 이후, 사람이 전체 OCR 원문을 다시 읽는 대신 문제가 탐지된 필드 중심으로 검수 범위를 좁힐 수 있게 되었습니다.

5~7단계: 서비스 통합 (Ingest · Update · Notify)

앞서 소개한 1단계(Fetch) → 2단계(Retrieve) → 3단계(Vision) → 4단계(Extract) 파이프라인에 이어, Extract 단계에서 생성된 구조화 JSON을 실제 서비스 시스템과 연결하는 5단계(Ingest) → 6단계(Update) → 7단계(Notify) 를 구현했습니다.

Fetch → Retrieve → Vision → Extract → Ingest → Update → Notify

앞선 단계가 웹 페이지의 데이터를 수집하고 분석하는 역할이라면, 이후 단계는 해당 데이터를 실제 서비스 시스템과 연결하여 등록 및 상태 관리, 알림까지 처리합니다.

단계 설명
5단계: Ingest Extract 단계에서 생성된 구조화 JSON을 전달받아 내부 부트텐트 폼 형식에 맞는 등록 요청 데이터를 생성합니다.
6단계: Update 타겟이 되는 URL 리스트에서 처리 상태 정보를 업데이트하여 처리 완료 상태로 변경합니다.
7단계: Notify 작업 완료 후 Slack 채널로 메시지 알림을 전송합니다. 이 단계는 파이프라인의 시작과도 연결되어 작업 시작, 완료, 실패 시점에도 알림이 발생하도록 구성하였습니다.

또한 초기에는 URL 기반으로만 워크플로가 시작되었지만, 담당자가 일부 항목을 이미 입력한 경우에도 데이터를 보완할 수 있도록 모드 분기 처리를 추가했습니다.

모드 설명
Create 모드 담당자가 입력한 URL을 기준으로 새로운 데이터를 생성하는 워크플로우
Update 모드 기존 데이터의 일부 항목을 보완하기 위한 데이터 업데이트 워크플로우

Part 4. Results – 결과 및 성과

품질 평가: 골든 데이터셋 기준

추출 자동화의 품질을 정량적으로 확인하기 위해, 사람이 직접 모집 페이지 원문을 기준으로 교육 8건에 대한 골든 데이터셋을 구축했습니다. 각 교육마다 54개 필드 전체를 직접 검토하여 정답값을 정의했고, 이를 기준으로 Extractor와 Validator의 결과를 비교했습니다. 총 평가 대상은 432개 필드(54개 × 8건) 였습니다.

평가 시 오류는 다음 세 가지로 구분했습니다.

  • missing: 원문에 근거가 있어 채워져야 하지만 비어 있는 경우
  • mismatch: 값은 생성되었지만 원문 또는 내부 규칙과 다르게 잘못 매핑된 경우
  • hallucination: 원문에 직접 근거가 없는데 생성된 경우

골든 데이터셋 기준으로 Extractor 결과를 비교한 결과, 총 432개 필드 중 49개 필드에서 실제 오류가 발생했습니다. 이를 기준으로 계산한필드 정확도는 88.7%(383/432), 오류율은 11.3%(49/432)이었습니다.

오류 유형별로 보면 다음과 같았습니다.

  • missing 18건
  • mismatch 25건
  • hallucination 6건

즉, 누락된 값을 채우는 경우보다 잘못 매핑되거나 과도하게 생성된 오입력 계열의 오류가 더 많이 발생했습니다.

동일한 골든 데이터셋을 기준으로 Validator의 결과도 함께 점검했습니다. Validator는 49건의 실제 오류 중 48건을 검출했으며, 1건은 검출하지 못했습니다. 이를 기준으로 계산한 오류 검출률은 98.0%(48/49) 였습니다.

시간 절감 효과

프로젝트 성과를 평가하기 위해, 사람이 모든 항목을 직접 입력하는 기존 방식과 AI가 생성·검수한 결과를 사람이 확인·보완하여 제출하는 방식의 소요 시간을 비교했습니다.

교육과정명 기존 방식 AI 생성·검수 방식 절감률
교육과정 A 11분 30초 3분 35초 68.8%
교육과정 B 13분 22초 3분 55초 70.7%
교육과정 C 15분 44초 4분 30초 71.4%
교육과정 D 17분 6분 43초 60.5%
교육과정 E 20분 36초 6분 70.9%
교육과정 F 13분 4분 5초 68.6%
교육과정 G 9분 40초 2분 28초 74.5%
교육과정 H 10분 36초 5분 47초 45.4%
교육과정 I 12분 3분 8초 73.9%
교육과정 J 15분 50초 3분 30초 77.9%
평균 약 13분 56초 약 4분 19초 약 69.0%

다만 내부 테스트에는 부트텐트 팀원들이 Validator 오류로 검출한 항목의 사실 여부를 확인하고, 올바른 값으로 수정하기 위해 모집 페이지를 다시 확인하는 시간이 포함되었습니다. 실제 사용 환경에서는 교육기관 담당자가 이미 모집 내용을 알고 있는 경우가 많아, 검수에 드는 시간은 이보다 더 줄어들 가능성이 있습니다.

비용 및 운영 안정성

  • 한국어 OCR 정확도 확보: 12개 핵심 용어 검증에서 Qwen3-VL-8B는 전부 정확히 인식한 반면, Haiku는 전부 오인식했습니다. 오픈소스 VLM의 OCR 특화 성능이 최종 추출 품질을 결정하는 핵심 요소임을 확인했습니다.
  • 비용 절감: AWS Step Functions와 서버리스 아키텍처를 결합하여 필요 시에만 Amazon EC2 GPU 인스턴스를 동적으로 할당함으로써, 대용량 처리 시 Haiku 대비 약 40%의 비용을 절감했습니다.
  • 운영 안정성: vLLM을 Primary로, Amazon Bedrock을 Fallback으로 구성한 하이브리드 구조 덕분에 GPU 인스턴스 장애 시에도 파이프라인이 중단 없이 동작합니다.

마무리

이 글에서는 부트텐트의 교육과정 등록 자동화 시스템을 구성하면서, 오픈소스 VLM과 Amazon Bedrock을 결합한 하이브리드 Vision OCR 전략과 프롬프트 엔지니어링 기법을 소개했습니다. 한국어 이미지 OCR이 필요한 데이터 파이프라인을 구축할 때, GPU 인스턴스에서의 오픈소스 모델 서빙과 관리형 API 서비스를 상황에 맞게 조합하는 접근 방식이 품질과 비용 모두에서 효과적일 수 있음을 확인했습니다. 비정형 웹 데이터를 구조화하거나, 이미지 속 한국어 텍스트 추출에 비슷한 고민을 갖고 계신 분들께 이번 사례가 도움이 되었으면 합니다.

JungHong Park

스마일샤크 박준홍 AM (Account Manager) / JungHong Park

스마일샤크의 Account Manager(AM)는 AWS 파트너 전문가로서, AI 기술을 통한 비즈니스 경쟁력 강화를 목표로 고객의 니즈를 파악하고, 스마일샤크의 AI 전문성과 AWS 서비스를 결합하여 고객 성장을 이끄는 역할을 맡고 있습니다

Byeongju Choi

스마일샤크 최병주 SA (Solutions Architect) / Byeongju Choi

스마일샤크의 Solutions Architect(SA)는 AWS 기술 전문가로서, AWS Bedrock 기반의 생성형 AI 솔루션을 포함하여 고객의 비즈니스 요구사항을

Sayun Jung

내로우게이트 정사윤 (대표) / Sayun Jung

누구나 자신에게 맞는 교육을 쉽게 찾고, 자신만의 길을 만들어갈 수 있도록 돕기 위해 부트텐트를 운영하고 있습니다. 빠르게 변화하는 교육 트렌드와 채용 시장 환경에 맞춰, 교육 정보를 더 빠르고 정확하게 제공할 수 있도록 AX를 적용하고 있습니다.

Yujin Gwon

내로우게이트 권유진 (백엔드 개발자) / Yujin Gwon

부트텐트의 백엔드 개발자로서, AWS 기반 클라우드 환경에서 서비스의 안정적인 운영과 확장성을 확보하고, 데이터 구조와 흐름을 설계하여 비즈니스 로직을 구현합니다. 또한 생성형 AI 도입을 통해 데이터 처리 자동화와 운영 효율을 높이며, 서비스의 데이터 품질과 사용자 경험을 지속적으로 고도화하고 있습니다.

Shinchul Bang

Shinchul Bang

방신철 솔루션즈 아키텍트는 AWS를 처음 사용하시는 다양한 산업의 고객들이 성공적으로 클라우드 서비스를 도입하시는 것을 돕고 있습니다. 고객이 클라우드를 통해 비즈니스를 더욱 가속화 하고 확장할 수 있도록 안정적이고 효율적인 아키텍처를 제안드리고 있습니다.

Dahee Choi

Dahee Choi

기업이 클라우드 활용해 제품/서비스 혁신에 집중할 수 있도록 최적의 AWS 서비스와 아키텍처를 제안하고 고객의 디지털 트랜스포메이션 여정을 함께합니다.