본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 02. 11:13

정규표현식(Regex)이면 충분할 줄 알았다: 데이터 추출의 늪에 빠진 이야기

요약

정규표현식과 규칙 기반 파서의 한계를 극복하기 위해 LLM의 구조화된 출력(Structured Output) 기능을 활용하여 비정형 텍ext에서 데이터를 추출하는 방법을 다룹니다. JSON 스키마를 통해 다양한 형식의 송장 데이터를 안정적으로 파싱하는 실무적 경험을 공유합니다.

핵심 포인트

  • 정규표현식은 데이터 형식 변화에 매우 취약함
  • LLM의 함수 호출 기능을 통한 구조화된 데이터 추출 가능
  • JSON 스키마를 활용하면 예측 가능한 출력을 보장함
  • 문맥 이해를 통해 다양한 날짜 및 금액 형식을 처리 가능

몇 달 전, 저는 송장(invoice) 이메일을 자동으로 파싱하는 도구를 만들고 있었습니다. 다들 아시다시피 그런 식이죠: "ACME Corp의 송장 #12345 - $1,234.56, 납기일 2024-03-15"와 같은 제목 말입니다. 간단해 보였습니다. 저는 완벽한 정규표현식 (Regex) 패턴을 만드느라 하루를 보냈고, 처음 10개의 이메일에 성공적으로 작동했을 때 우쭐해했습니다.

그런 다음 11번째 이메일이 도착했습니다. 제목은 "ACME Corp에서 보낸 송장 (참조: INV-12345) – 2024-03-15까지 $1,234.56를 결제해 주세요"였습니다. 제 정규표현식이 깨졌습니다. 패턴을 수정했습니다. 하지만 12번째 이메일은 "송장: ACME Corp, 결제 금액: $1,234.56, 납기일: 2024-03-15"였습니다. 제 정규표현식은 선택적 그룹 (optional groups)과 전방 탐색 (lookaheads)이 뒤섞인 괴물로 변해갔습니다. 저는 제가 잘못된 길을 가고 있다는 것을 직감했습니다.

막다른 길들

더 많은 정규표현식 (Regex)

패턴 라이브러리를 구축하려고 시도했습니다. 약 60%의 케이스에는 작동했습니다. 하지만 새로운 업체가 등장할 때마다 새로운 형식이 도입되었습니다. 유지보수는 악몽 같았습니다. 기능을 만드는 시간보다 정규표현식을 디버깅하는 데 더 많은 시간을 썼습니다.

규칙 기반 파서 (Rule-Based Parsers)

Python의 dateutil과 간단한 문자열 매칭 방식으로 옮겨갔습니다. 여전히 취약했습니다. 날짜 형식이나 문구가 아주 조금만 달라져도 조용히 오류가 발생했습니다.

spaCy를 이용한 머신러닝 (ML)

"맞춤형 개체명 인식 (NER) 모델을 학습시키자!"라고 생각했습니다. 2주 동안 송장에 라벨링을 했습니다. 모델은 금액과 날짜를 찾는 법을 배웠지만, 어떤 날짜가 납기일이고 어떤 날짜가 송장 발행일인지와 같은 문맥 (context)을 이해하지 못했습니다. 게다가 새로운 필드를 위해 재학습을 하려면 더 많은 데이터와 라벨링이 필요했습니다.

결국 해결책이 된 것: LLM을 활용한 구조화된 출력 (Structured Output)

저는 모든 형식을 이해할 필요가 없다는 것을 깨달았습니다. 제가 필요로 하는 것은 영어(또는 다른 언어)를 읽고 구조화된 데이터를 안정적으로 추출할 수 있는 시스템이었습니다. 함수 호출 (function calling) (또는 구조화된 출력) 기능이 있는 대규모 언어 모델 (LLMs)이 정답이었습니다.

핵심 기술은 다음과 같습니다: 모델에게 자유 형식의 텍스트를 요청하는 대신, JSON 스키마 (JSON schema)를 제공하고 해당 스키마에 맞는 유효한 JSON을 출력하도록 지시하는 것입니다. 이 방식은 놀라울 정도로 잘 작동합니다.

코드 예시: 송장 데이터 추출하기

import json
from openai import OpenAI

...
{
  "vendor_name": "Widgets Inc.",
  "invoice_number": "INV-7890",
...

작동 원리 (Why This Works)

  • 모델이 언어 이해 (Language understanding) 능력을 사용하여 문맥으로부터 필드를 추론합니다.
  • 사용자가 스키마 (Schema)를 제어하므로 출력이 예측 가능합니다.
  • 최소한의 프롬프트 엔지니어링 (Prompt engineering)만으로 작동합니다. 원하는 내용을 설명하기만 하면 됩니다.
  • 다양한 변형을 처리합니다: "due by April 30, 2024", "due date: 2024-04-30", "payment deadline: 30/04/2024" 모두 동일한 ISO 날짜 형식을 생성합니다.

뼈아픈 교훈 (The Hard Lessons)

LLM (Large Language Models)은 마법이 아닙니다. 제가 배운 점은 다음과 같습니다:

  1. 비용 (Cost) – 추출할 때마다 소액의 비용이 발생합니다. 적은 양(하루 수백 건)이라면 괜찮습니다. 하지만 수백만 건이라면 더 저렴한 대안이 필요합니다.
  2. 지연 시간 (Latency) – OpenAI의 응답 시간은 보통 1~3초입니다. 실시간 애플리케이션의 경우 너무 느릴 수 있습니다.
  3. 환각 (Hallucinations) – 이메일에 필수 필드가 포함되어 있지 않으면 모델이 임의로 만들어낼 수 있습니다. 출력을 검증하고 required 필드를 현명하게 설정해야 합니다.
  4. 컨텍스트 길이 (Context length) – 긴 이메일은 잘릴 수 있습니다. 청킹 (Chunking)과 2단계 파이프라인 (분류 + 추출) 방식이 도움이 됩니다.
  5. 모델 선택 (Model choice) – GPT-4가 가장 좋지만, GPT-3.5-turbo는 복잡한 스키마에서 가끔 실패합니다. 프로덕션 환경을 위해, 저는 내부적으로 재시도(Retry)와 검증을 처리하는 전용 API로 전환했습니다. Interwest Info의 AI API와 같은 여러 서비스가 있습니다 (저는 OpenAI의 속도 제한(Rate limits)에 걸린 후 이를 사용했습니다). 하지만 기술적 접근 방식은 동일합니다.

이 방식을 사용하지 말아야 할 때 (When NOT to Use This Approach)

  • 데이터가 매우 구조화되어 있고 고정되어 있는 경우 (예: CSV 컬럼), 정규표현식 (Regex)이나 파서 (Parser)가 더 빠르고 저렴합니다.
  • 실시간 추출 (밀리초 단위)이 필요한 경우, LLM은 너무 느립니다.
  • 보장된 정확성이 필요한 경우 (예: 의료 데이터), LLM은 이를 제공할 수 없습니다.

다음에 다시 한다면 다르게 할 점 (What I'd Do Differently Next Time)

처음부터 LLM 방식으로 시작하겠지만, 다음과 같은 폴백 체인 (Fallback chain)도 구축할 것입니다:

  1. 알려진 패턴에 대해서는 정규표현식 (Regex)을 먼저 시도합니다.
  2. 실패할 경우, LLM을 호출합니다.
  3. 시간이 지남에 따라 정규표현식 라이브러리를 개선할 수 있도록 모든 과정을 로그로 남깁니다.

또한, 생성을 더욱 강력하게 제약하기 위해 jsonformeroutlines와 같은 구조화된 출력 (structured output) 라이브러리를 사용할 것입니다.

핵심 요약 (The Takeaway)

정규표현식 (Regex)은 잘 정의된 문제에 매우 훌륭합니다. 하지만 현실 세계의 텍스트는 무질서합니다. LLM은 수백만 개의 규칙을 만들지 않고도 그 무질서를 처리할 수 있는 방법을 제공합니다. 핵심은 LLM을 만능 해결사 (silver bullet)가 아니라, 여러분의 파싱 도구 상자 (parsing toolbox)에 있는 하나의 도구로 취급하는 것입니다.

이제 궁금합니다: 무질서한 문서에서 데이터를 추출할 때 여러분이 선호하는 방식은 무엇인가요? 여전히 정규표현식 (regex)과 씨름하고 계신가요, 아니면 LLM 진영에 합류하셨나요? 댓글로 알려주세요.

AI 자동 생성 콘텐츠

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

원문 바로가기
0

댓글

0