본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 05. 17:28

텍스트 추출로 고생하고 계신가요? 지저분한 데이터를 마침내 정리한 방법

요약

비정형 텍스트 데이터에서 주요 정보를 추출하기 위해 정규 표현식 대신 LLM의 함수 호출(Function Calling) 기능을 활용한 사례를 소개합니다. 개념 증명부터 비용, 지연 시간, 환각 현상 등 프로덕션 환경에서의 실질적인 해결 방안을 다룹니다.

핵심 포인트

  • 정규 표현식의 한계를 LLM의 구조화된 데이터 추출로 극복
  • OpenAI Function Calling을 활용한 JSON 스키마 기반 추출
  • 비용 최적화를 위한 모델 배칭 및 저렴한 모델 활용
  • 지연 시간 해결을 위한 백그라운드 작업 큐 도입
  • 환각 방지를 위한 검증 규칙 및 재시도 로직 구축

몇 달 전, 저는 수신되는 이메일을 처리하는 레거시 시스템 (legacy system)을 물려받았습니다. 이메일에는 송장 (invoices), 구매 주문서 (purchase orders), 배송 확인서 (shipping confirmations)가 포함되어 있었는데, 모두 일관된 형식 없는 일반 텍스트 (plain text) 형태였습니다. 저의 업무는 송장 번호, 금액, 날짜, 업체명과 같은 주요 필드 (key fields)를 추출하여 데이터베이스 (database)로 전달하는 것이었습니다.

쉬워 보이죠? 저도 그렇게 생각했습니다. 하지만 실제로 데이터를 확인한 순간 상황이 달라졌습니다.

정규 표현식 (Regex)의 늪

저는 숙련된 개발자라면 누구나 그렇듯 정규 표현식 (regex)으로 시작했습니다. 알려진 각 형식에 맞춰 패턴을 작성했습니다. 업체가 대략 6개 정도였으니 얼마나 어렵겠나 싶었죠.

import re

patterns = {
...

첫 번째 배치 (batch) 작업은 성공적이었습니다. 그러다 한 업체가 이메일 제목을 변경했습니다. 그다음 업체는 송장 번호를 본문 대신 PDF 첨부 파일에 포함하기 시작했습니다. 그러던 어느 날, 새로운 업체가 등장했고 저의 정규 표현식 (regex) 접근 방식은 완전히 무너졌습니다.

저는 패턴을 수정하는 데 2주를 보냈습니다. 하나의 수정 사항이 생기면 다른 세 가지 케이스가 망가졌습니다. 텍스트 형식이라는 두더지 잡기 게임을 하고 있는 기분이었습니다. 설상가상으로 비즈니스 팀에서는 세금 금액, 구매 주문 참조 번호와 같이 `

그 무렵, 저는 코드 생성 (code generation)을 위해 대규모 언어 모델 (LLMs)을 만져보기 시작했습니다. 그러다 문득 생각이 스쳤습니다. 왜 LLM을 사용하여 텍스트에서 구조화된 데이터 (structured data)를 추출하지 않을까? 이 아이디어 자체가 새로운 것은 아니지만, 도구들이 충분히 성숙해져서 오후 한나절 만에 개념 증명 (proof of concept)을 뚝딱 만들어낼 수 있었습니다.

접근 방식은 간단합니다:

  1. 추출하고자 하는 것에 대한 스키마 (schema)를 정의합니다.
  2. 원문 텍스트와 지침을 LLM에 보냅니다.
  3. 구조화된 응답 (보통 JSON)을 파싱 (parse)합니다.

다음은 OpenAI의 함수 호출 (function calling)을 사용한 최소한의 작동 예시입니다:

import json
from openai import OpenAI

...

단 한 번의 시도만에 성공했습니다. 패턴도 필요 없었고, 학습 (training)도 필요 없었습니다. 그저 스키마와 원문 텍스트만 있으면 되었습니다.

프로덕션 단계로 넘어가기

물론, 개념 증명 (proof of concept) 단계가 바로 프로덕션 (production)에 적용 가능한 것은 아닙니다. 저는 몇 가지 문제에 직면했습니다:

비용 (Cost): 추출 호출 한 번당 비용은 1센트의 아주 작은 일부이지만, 하루에 10,000통의 이메일을 처리한다면 그 금액은 상당해집니다. 저는 배칭 (batching)을 수행하고 단순한 사례에는 더 저렴한 모델을 사용하여 최적화해야 했습니다.

지연 시간 (Latency): LLM 호출에는 2~5초가 소요됩니다. 실시간 이메일 처리 측면에서 이는 용납할 수 없는 수준이었습니다. 그래서 추출 작업을 백그라운드 작업 큐 (background job queue)로 옮겼습니다.

환각 (Hallucinations): 때때로 모델이 송장 번호를 지어내기도 했습니다. 저는 검증 규칙(추출된 필드에 대한 정규 표현식 (regex) 체크)을 추가했고, 검증에 실패할 경우 더 엄격한 프롬프트 (prompt)로 재시도하도록 설정했습니다.

일관되지 않은 출력 (Inconsistent outputs): 함수 호출 (function calling)을 사용하더라도 모델이 때때로 필드를 누락하는 경우가 있었습니다. 저는 스키마를 필수 사항 (required)으로 만들고, 기본값으로 대체할 수 있는 폴백 (fallback)을 설정했습니다.

이러한 패턴을 서비스로 래핑 (wrap)한 도구들도 있습니다. 예를 들어, 저는 유사한 스키마 기반 설계를 갖춘 호스팅 추출 API를 제공하는 interwestinfo.com을 시도해 보았습니다. 이를 통해 API 키와 속도 제한 (rate limits)을 관리하는 번거로움을 줄일 수 있었지만, 저는 데이터 거주성 (data residency) 요구 사항이 매우 엄격했기 때문에 결국 자체 파이프라인 (pipeline)을 유지하기로 했습니다.

배운 점

  • 규칙 기반 추출 (Rule-based extraction)은 여전히 역할이 있습니다. 구조화가 잘 되어 있고 예측 가능한 데이터에는 유용합니다. 하지만 지저분한 실제 텍스트의 경우, LLM (Large Language Models)은 구원투수와 같습니다.
  • 스키마 설계 (Schema design)가 중요합니다. 스키마가 너무 모호하면 일관성 없는 결과를 얻게 됩니다. 형식과 필수 필드에 대해 명시적으로 정의하세요.
  • 출력을 맹목적으로 신뢰하지 마세요. 추출된 데이터는 항상 비즈니스 규칙에 따라 검증해야 합니다.
  • 작동하는 가장 작은 모델을 사용하세요. 비용이 저렴한 모델 (GPT-4o-mini 또는 로컬 LLM 등)로 시작하고, 정확도가 불충분할 경우에만 모델의 규모를 키우세요.

다음에 다시 한다면 다르게 할 점

처음부터 하이브리드 파이프라인 (hybrid pipeline)을 구축했을 것입니다. 표준적인 80%의 이메일에는 정규 표현식 (regex)을 사용하고, 까다로운 20%는 LLM으로 라우팅하는 방식입니다. 그랬다면 정규 표현식으로 인한 골칫거리와 API 비용을 모두 아낄 수 있었을 것입니다.

저는 여전히 이 시스템을 미세 조정하고 있습니다. 현재는 비용을 완전히 절감하기 위해 Llama 3와 같은 로컬 모델을 실험하고 있습니다. 하지만 "스키마를 정의하고, LLM이 이를 채우게 한다"는 핵심 접근 방식 덕분에 정신 건강을 지킬 수 있었습니다.

지저분한 텍스트에서 구조화된 데이터를 추출하는 여러분만의 방법은 무엇인가요? 이 작업을 위해 LLM을 사용해 보셨나요, 아니면 여전히 전통적인 정규 표현식 (regex)을 고수하시나요?

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0