최적화의 역설: 속도 개선이 품질 저하를 초래한 사례와 그 반전
관련글
AI 에이전트 팀 기반 ETL 파이프라인 최적화 사례 분석
58배 속도 향상이라는 극적인 성과를 달성한 직후, 그 최적화가 근본적으로 잘못되었다는 것이 발견되었다. OCR을 비활성화하여 PDF 처리 속도를 16분에서 16.6초로 단축했지만, 정작 Docling을 사용하는 이유 자체가 OCR 기능이었다. 결과로 나타난 데이터 품질은 심각했다. 전체 청크의 58.2%가 100 tokens 미만이었고, 15.1%는 30 tokens도 채 되지 않아 검색 자체가 불가능한 수준이었다. 평균 103 tokens per chunk는 chunk_size=1000 chars 설정 대비 현저히 낮은 수치였다.
문제는 OCR OFF 결정이 합리적으로 보였다는 점이다. 5인 에이전트 팀이 로그를 분석한 결과 “RapidOCR returned empty result!”라는 출력이 발견되었고, 이는 텍스트 기반 PDF임에도 불필요하게 OCR이 실행되고 있다는 증거로 해석되었다. 텍스트 레이어가 존재하는 PDF에서 OCR을 실행하는 것은 비효율이므로, do_ocr=False 설정은 논리적으로 타당해 보였다. 실제로 속도는 극적으로 개선되었다. 문제는 그 후에 드러났다.
사용자가 “클로드 다시 처리 결과 읽어보니 청킹 품질은 많이 떨어질 듯”이라고 언급한 시점은 ETL Phase 1 v3가 121개 파일을 처리하고 6,676개 청크를 생성한 후였다. Quality Gate가 거부율 29.5%로 작동하고 있었지만, min_token_count=3으로 완화한 설정이 너무 관대했다. 실제 데이터를 분석한 결과는 우려를 확인시켰다. 1,011개 청크가 30 tokens 미만의 쓰레기 데이터였고, 2,877개가 30-100 tokens 사이의 정보량 부족 상태였다. 정상 범위인 100-300 tokens 청크는 2,685개로 40.2%에 불과했다.
파일 유형별로 분석하면 문제가 더 명확해진다. Markdown 파일은 3,658개 청크에 평균 73 tokens로 과도하게 분할되어 있었다. PDF 파일은 2,638개 청크에 평균 134 tokens였는데, 이는 OCR OFF로 인해 스캔 PDF가 제대로 처리되지 않았을 가능성을 시사한다. PPTX는 132개 청크에 평균 191 tokens, HTML은 130개 청크에 평균 228 tokens, TXT는 123개 청크에 평균 133 tokens로 상대적으로 양호했다. 문제는 MD와 PDF에 집중되어 있었다.
사용자의 지적은 핵심을 찌른다. “OCR OFF는 너무 과했는데….OCR 때문에 청킹모델 그것 사용하는건데..” Docling을 선택한 이유는 OCR 기능이었다. RapidOCR과 TableFormer를 통합한 파싱 품질 때문에 Docling을 도입했는데, 속도를 위해 그 핵심 기능을 비활성화한 것은 본말전도였다. 58배 빠른 파싱이 무슨 의미인가, 파싱된 내용이 쓸모없다면.
여기서 흥미로운 점은 에이전트 팀의 분석이 기술적으로는 정확했다는 것이다. 텍스트 레이어가 존재하는 PDF에서 OCR은 실제로 불필요하다. “RapidOCR returned empty result!”는 정확한 관찰이었다. 문제는 모든 PDF가 텍스트 레이어를 가진 것은 아니라는 점이었다. 스캔 PDF, 이미지 기반 PDF, 손상된 텍스트 레이어를 가진 PDF 등이 혼재되어 있을 때, OCR을 전면 비활성화하면 일부 파일에서 치명적인 품질 저하가 발생한다. 에이전트 팀은 “대부분의 PDF”에 초점을 맞추었고, 예외 케이스를 충분히 고려하지 못했다.
초기 대응은 2-Pass 조건부 OCR이었다. Pass 1에서 텍스트 레이어를 추출하고, 결과가 MIN_TEXT_FOR_OCR_SKIP 200자 미만이면 Pass 2에서 OCR을 재시도하는 방식이었다. 논리적으로 합리적이다. 텍스트 기반 PDF는 빠르게 처리하고, 스캔 PDF만 OCR을 적용한다. 두 가지 장점을 모두 취하는 접근법이었다. 코드도 구현되었다. converter와 converter_ocr 두 개의 인스턴스를 준비하고, parse 메서드에서 텍스트 길이를 확인한 후 조건부로 재시도하는 로직이 추가되었다.
그러나 사용자는 다른 결정을 내렸다. “차라리 모든 파일에 OCR ON 시켜.” 2-Pass 조건부 접근 대신 전면 OCR 활성화였다. 복잡한 로직보다 단순하고 확실한 방법을 선택했다. 모든 코드 변경이 되돌려졌다. MIN_TEXT_FOR_OCR_SKIP 상수 삭제, converter_ocr 인스턴스 제거, 2-Pass 로직 제거. do_ocr=True로 단순화되었다. 주석도 “OCR ON + TableFormerMode.FAST (2026-02-14): do_ocr=True: 스캔 PDF 포함 모든 PDF에 OCR 적용 (품질 우선)”으로 명확해졌다.
이 결정의 합리성은 명확하다. 2-Pass 접근은 추가적인 복잡도를 도입한다. MIN_TEXT_FOR_OCR_SKIP 임계값을 어떻게 설정할 것인가? 200자가 적절한가? 파일 크기에 따라 달라져야 하는가? Pass 1에서 부분적인 텍스트만 추출된 경우는 어떻게 처리할 것인가? 두 번의 파싱 결과를 비교할 때 어떤 기준으로 더 나은 결과를 선택할 것인가? 이 모든 질문에 답하려면 추가적인 엣지 케이스 처리가 필요하다. 반면 OCR ON은 단순하다. 모든 파일에 OCR을 적용한다. 느리지만 확실하다.
속도 대 품질의 트레이드오프가 다시 나타났다. OCR ON으로 전환하면 속도는 느려진다. PDF 파일당 수 분에서 십수 분이 소요될 것이다. 1,786개 파일 중 389개가 PDF이므로, PDF 처리만 6-12시간이 예상된다. 전체 ETL은 밤새 돌려야 한다. 반면 OCR OFF는 3-4시간에 완료된다. 그러나 그 결과물의 58.2%가 쓸모없다면, 빠른 처리가 무슨 의미인가.
사용자의 다음 지시도 주목할 만하다. “아까 OCR OFF 처리했던 것들 청킹 재처리 해줘.” 기존에 처리된 6,676개 청크를 버리고 처음부터 다시 시작한다. 3-Store 초기화가 진행되었다. Elasticsearch 인덱스 삭제, PostgreSQL documents 테이블 261건 삭제, Neo4j 노드 삭제. 클린 상태로 돌아갔다. 이미 투자된 시간과 계산 자원이 무효화되었다. 이는 품질이 우선순위임을 명확히 한다.
여기서 GPU에 대한 논의가 시작된다. “GPU 사용하면 더 빨라지는거야?”라는 질문에 대한 답은 명확했다. RapidOCR과 TableFormer 모두 GPU에서 크게 가속된다. CPU에서 16분 걸리는 것이 GPU에서는 1-2분 수준으로 단축된다. 그러나 현재 WSL2 환경은 CPU only다. 대안으로 Colab GPU를 활용할 수 있지만, 1,786개 파일을 업로드하고 파싱 결과를 로컬 DB에 넣는 것은 네트워크와 접근성 문제가 있다.
흥미로운 점은 사용자가 이미 Phase 2 임베딩에서 Colab GPU를 사용할 계획이었다는 것이다. “임베딩할 때 어차피 GPU 사용할거잖아.”라는 언급이 이를 보여준다. 문서를 확인한 결과, Phase 1은 CPU에서 파싱과 청킹을 수행하고, Phase 2는 Colab GPU에서 임베딩을 수행하는 구조였다. 그렇다면 왜 Phase 1도 GPU에서 수행하지 않는가? “우리 문서 다시 읽어줘. 시간 줄이려고 GPU 사용하려는 것이었음.”이라는 지적이 이를 확인한다. GPU 사용 목적은 Phase 2 임베딩의 시간 절감이었지, Phase 1 파싱이 아니었다.
사용자는 “청킹을 구글 GPU 사용하자 권하진 않네..ㅎㅎㅎ”라고 언급했고, 에이전트는 솔직히 인정했다. “맞습니다, 솔직히 생각 못했습니다.” Phase 1 파싱도 Colab에서 돌리면 PDF당 16분을 1-2분으로 줄일 수 있다. 전체 ETL 시간이 6-12시간에서 1-2시간으로 단축될 수 있다. 그러나 현실적 제약이 있다. 파일 업로드, 네트워크 접근, Docker DB 연결 등의 문제다. 가능한 대안은 Colab에서 파싱과 청킹만 수행하고 결과를 JSON으로 export한 후 로컬에서 import하는 것이다. Phase 2 임베딩과 비슷한 패턴이다.
그러나 이미 ETL Phase 1 v4가 OCR ON으로 시작되었다. 밤새 돌려놓으면 내일 아침 완료된다. 지금 다시 바꾸는 것은 또 다른 시간 낭비다. “오케이”라는 사용자 응답은 현실적 결정을 받아들인다는 의미다. 완벽한 해결책을 추구하기보다, 현재 상황에서 실행 가능한 선택을 한다.
TableFormerMode.FAST에 대한 질문도 나왔다. “TableFormerMode.FAST는 청킹 품질 하락에 큰 영향은 없나보네?” 답은 명확했다. FAST와 ACCURATE의 차이는 테이블 셀 경계 인식 정밀도뿐이다. 단순 그리드 테이블에서는 FAST도 95% 이상의 정확도를 보인다. ACCURATE는 복잡한 병합 셀, 비정형 테이블에서만 약간 우위다. 대부분의 문서가 단순 표 구조이므로 FAST로 충분하다. 속도 차이는 50-70%인데, FAST가 빠르다. 품질 차이는 3-5% 미만이다. OCR이 품질의 핵심이고, TableFormerMode는 부수적이라는 평가가 타당하다.
ETL Phase 1 v4가 시작되었다. OCR ON, TableFormerMode.FAST, 파일 정렬 유지, 3-Store 완전 초기화라는 조건에서 클린 상태로 재시작되었다. MD/TXT 우선 처리로 초기에는 빠르게 진행되었다. 1초당 1파일 속도였다. PDF 구간에 도달하면 OCR로 인해 파일당 수 분에서 십수 분이 소요될 것으로 예상되었다. 전체 6-12시간 예상이었다.
그런데 23:40 KST에 중간 점검 결과가 나왔고, 이는 모든 예상을 뒤집었다. v3 OCR OFF와 v4 OCR ON을 비교한 데이터가 제시되었다. v3는 353개 파일 처리 후 중단되었고, 121개가 성공했다. v4는 248개 파일 중 197개가 성공했다. 진행률은 v4가 13.9%였다. 실패는 양쪽 모두 0건이었다. Quality Gate 거부율은 v3에서 29.5%였는데 v4에서 12.0%로 개선되었다. 평균 chunks per file은 v3에서 6.3개, v4에서 3.7개였다.
가장 충격적인 수치는 PDF 처리 속도였다. v3 OCR OFF에서 평균 16.6초였던 것이 v4 OCR ON에서 평균 11.7초였다. OCR을 켰는데 오히려 30% 빨라졌다. 논리적으로 말이 안 된다. OCR은 계산 집약적 작업이다. 활성화하면 느려져야 정상이다. 그런데 빨라졌다.
이유는 시스템 환경의 차이였다. v3 실행 시점에는 Embedding Backfill 프로세스가 백그라운드에서 2.86GB와 3G의 스레드를 점유하며 CPU와 메모리를 경합하고 있었다. Infra 에이전트가 초기에 발견했던 그 문제였다. v4는 3-Store를 완전 초기화하고 클린 상태에서 시작했으므로 다른 프로세스가 자원을 소비하지 않았다. 같은 하드웨어에서 같은 코드를 실행했지만, 시스템 환경이 달라서 성능이 달랐다.
이것은 중요한 교훈이다. 코드 최적화만으로는 성능 문제를 해결할 수 없다. 시스템 레벨의 자원 경합, 백그라운드 프로세스, 메모리 압박 등이 성능에 결정적 영향을 미친다. v3에서 OCR을 껐을 때 58배 가속이 나타난 것도 실은 OCR OFF 자체의 효과가 아니라, Embedding Backfill 제거와 OCR OFF의 복합 효과였을 가능성이 있다. v4 결과는 이를 검증한다. OCR ON 상태에서도 클린 환경이면 충분히 빠르다.
Quality Gate 거부율 개선도 의미가 크다. 29.5%에서 12.0%로 절반 이하로 떨어졌다. 이는 OCR이 실제로 품질에 기여하고 있음을 보여준다. v3에서 거부된 청크의 대부분은 OCR 없이 파싱되어 내용이 부족했던 것으로 추정된다. v4에서는 OCR이 스캔 PDF나 저품질 텍스트 레이어를 제대로 읽어내어 품질 기준을 통과했다.
평균 chunks per file이 6.3에서 3.7로 감소한 것은 흥미롭다. OCR이 더 많은 텍스트를 추출하면 청크 수가 증가해야 논리적이다. 그런데 감소했다. 이는 v3에서 과도한 분할이 발생했을 가능성을 시사한다. 텍스트가 부족한 상태에서 chunking 알고리즘이 작은 조각들을 많이 만들어냈고, v4에서는 충분한 텍스트가 확보되어 적절한 크기의 청크가 생성되었을 수 있다. 혹은 Quality Gate가 더 많은 저품질 청크를 걸러냈을 수도 있다.
결과적으로 모든 지표가 개선되었다. 속도는 빨라졌고, 품질은 높아졌고, 거부율은 낮아졌고, 실패는 없었다. 품질과 속도의 트레이드오프라는 일반적 가정이 깨졌다. 이 경우에는 품질을 선택하는 것이 속도도 개선하는 결과를 낳았다. 다만 이것은 특정 조건 하에서의 결과다. 클린 환경, 자원 경합 제거, 적절한 시스템 설정이 전제되었을 때 가능했다.
이 사례를 통해 관찰되는 지점들이 있다. 첫째, 최적화는 시스템 전체를 고려해야 한다. 코드 레벨의 변경만으로는 예상치 못한 부작용이 발생할 수 있다. OCR OFF는 코드상으로는 합리적이었지만, 시스템 전체의 맥락에서는 품질 저하를 초래했다. Embedding Backfill이라는 외부 요인이 성능을 지배하고 있었는데, 이를 코드 최적화로 해결하려 했다.
둘째, 측정이 중요하다. “OCR OFF가 빠르다”는 관찰은 정확했지만, “왜 빠른가?”에 대한 분석이 부족했다. v3에서 16.6초, v4에서 11.7초라는 비교 데이터가 나오기 전까지는 OCR OFF의 효과를 과대평가하고 있었다. 실제로는 클린 환경의 효과가 더 컸다.
셋째, 품질 지표를 먼저 확인해야 한다. 58배 가속이라는 숫자에 매혹되어 품질을 나중에 확인했다. “클로드 다시 처리 결과 읽어보니 청킹 품질은 많이 떨어질 듯”이라는 사용자의 우려가 나온 시점은 이미 121개 파일이 처리되고 6,676개 청크가 생성된 후였다. 속도 개선을 먼저 축하하고, 품질을 나중에 점검했다. 순서가 바뀌었다.
넷째, 단순한 해결책이 복잡한 해결책보다 낫다. 2-Pass 조건부 OCR은 논리적으로 정교했다. MIN_TEXT_FOR_OCR_SKIP 임계값, converter와 converter_ocr 인스턴스 분리, 조건부 재시도 로직. 그러나 사용자는 “차라리 모든 파일에 OCR ON 시켜”라고 결정했다. 복잡도를 줄이고, 예외 케이스를 제거하고, 확실한 방법을 선택했다. 결과적으로 이것이 옳았다.
다섯째, 완벽을 추구하다 기회를 놓칠 수 있다. Colab GPU를 사용하면 더 빠르게 처리할 수 있다는 것을 알게 되었지만, 이미 ETL v4가 시작되었다. 지금 멈추고 Colab 파이프라인을 구축하는 것은 또 다른 시간 소비다. “오케이”라고 받아들이고 진행한다. 밤새 돌려놓으면 내일 아침 완료된다. 충분히 좋다.
여섯째, 에이전트의 제안을 맹목적으로 따르면 안 된다. 5인 에이전트 팀이 OCR OFF를 권고했고, 58배 가속이라는 인상적인 결과를 제시했다. 그러나 사용자가 품질을 확인하지 않았다면, 쓸모없는 데이터를 빠르게 생산하는 시스템을 구축했을 것이다. “OCR OFF는 너무 과했는데….OCR 때문에 청킹모델 그것 사용하는건데..”라는 지적은 도메인 지식에서 나온다. 에이전트는 기술적 최적화에 집중했고, 사용자는 비즈니스 목적을 기억하고 있었다.
일곱째, 환경 변수가 성능을 지배한다. 같은 코드도 다른 환경에서는 다른 성능을 보인다. Embedding Backfill의 유무, 시스템 자원의 가용성, 백그라운드 프로세스의 존재 등이 코드 변경보다 더 큰 영향을 미칠 수 있다. 최적화는 코드만이 아니라 환경도 포함해야 한다.
여덟째, 데이터로 검증해야 한다. “데이터 품질 떨어뜨리는 것이 무슨 의미일까 싶네…“라는 사용자의 반응은 정확했다. 아무리 빠르게 처리해도, 결과물이 쓸모없으면 의미가 없다. 58.2%가 100 tokens 미만, 15.1%가 검색 불가능한 수준이라는 구체적 수치가 제시되었을 때 비로소 문제의 심각성이 명확해졌다.
아홉째, 되돌리는 것을 두려워하지 말아야 한다. 6,676개 청크를 버리고 처음부터 다시 시작했다. 3-Store를 완전 초기화했다. 이미 투자된 시간과 계산 자원이 무효화되었다. 그러나 이것이 옳은 결정이었다. 잘못된 방향으로 계속 진행하는 것보다, 되돌려서 올바른 방향으로 가는 것이 낫다. Sunk cost fallacy를 피했다.
열 번째, 예상은 틀릴 수 있다. OCR ON이 느릴 것이라고 예상했다. 6-12시간이 걸릴 것이라고 예상했다. 그러나 실제로는 OCR ON이 OCR OFF보다 빨랐다. 예상은 현재 가진 정보를 바탕으로 한 추론일 뿐이다. 실제 데이터가 나오면 예상을 수정해야 한다. 고집을 부려서는 안 된다.
열한 번째, 비교 기준이 중요하다. v3 OCR OFF 16.6초와 v4 OCR ON 11.7초를 비교할 때, 단순히 “OCR을 켰는데 빨라졌다”고만 해석하면 안 된다. 환경이 다르다. v3는 Embedding Backfill이 있었고, v4는 없었다. 공정한 비교를 위해서는 같은 환경에서 OCR ON/OFF를 테스트해야 한다. 다만 이 경우에는 클린 환경이 더 현실적인 운영 조건이므로, v4의 결과가 더 의미 있다.
열두 번째, 복잡성의 비용이 있다. 2-Pass 조건부 OCR은 MIN_TEXT_FOR_OCR_SKIP, converter_ocr, 조건부 로직 등 추가적인 복잡성을 도입했다. 이를 유지보수하고, 디버깅하고, 설명하는 비용이 발생한다. 단순한 OCR ON은 복잡성이 없다. 모든 파일에 OCR을 적용한다. 이해하기 쉽고, 예측 가능하고, 디버깅이 간단하다.
열세 번째, 도구의 본질을 이해해야 한다. Docling을 선택한 이유가 OCR 기능이었다. 그것을 비활성화하는 것은 도구의 핵심 가치를 포기하는 것이다. 만약 OCR이 필요 없었다면, Docling보다 더 빠른 텍스트 추출 도구를 선택했을 것이다. 도구를 선택한 이유를 기억하고, 그 이유에 부합하게 사용해야 한다.
열네 번째, 점진적 개선의 가치가 있다. v1에서 v2, v2에서 v3, v3에서 v4로 진화했다. 각 단계마다 문제를 발견하고, 수정하고, 다시 시도했다. 한 번에 완벽한 해결책을 찾으려 하지 않았다. 측정하고, 분석하고, 개선하는 사이클을 반복했다. 이것이 실무에서의 최적화 과정이다.
열다섯 번째, 사용자 피드백이 결정적이다. 에이전트는 58배 가속을 성공으로 보고했다. 그러나 사용자가 “클로드 다시 처리 결과 읽어보니 청킹 품질은 많이 떨어질 듯”이라고 의심했을 때 방향이 바뀌었다. 에이전트는 지시를 따르지만, 사용자는 목적을 안다. 최종 판단은 사용자가 해야 한다.
열여섯 번째, 문서화의 중요성이 재확인된다. “지금까지 검토하고 결정하고 실행한 내용, 문서에 반영해줘”라는 요청이 나왔다. 이 모든 시행착오, 발견, 결정, 수정 과정을 문서로 남긴다. 향후 비슷한 문제가 발생했을 때 참조할 수 있다. 왜 OCR OFF가 잘못되었는지, 왜 2-Pass가 불필요했는지, 왜 클린 환경이 중요한지를 기록한다.
열일곱 번째, 성과 지표의 다면성이 드러난다. v4는 v3보다 여러 면에서 우수하다. 속도는 11.7초 대 16.6초, 품질은 거부율 12.0% 대 29.5%, 안정성은 실패 0건, 효율성은 평균 3.7 chunks/file 대 6.3. 단일 지표만 보면 판단을 그르칠 수 있다. 속도만 보면 v3가 성공처럼 보였다. 품질까지 보니 실패였다.
열여덟 번째, 시스템적 사고가 필요하다. ETL 성능은 파싱 코드만의 문제가 아니다. Embedding Backfill, 시스템 자원, 데이터베이스 상태, 네트워크, 파일 시스템 등 모든 요소가 상호작용한다. 한 부분만 최적화해서는 전체가 개선되지 않는다. Infra 에이전트가 발견한 스레드 경합 문제가 실은 가장 큰 병목이었을 수 있다.
열아홉 번째, 실험의 재현성 문제가 있다. v3에서 58배 가속을 관찰했을 때, 그것이 OCR OFF 때문인지, Embedding Backfill 제거 때문인지, 다른 요인 때문인지 명확히 분리하지 못했다. 통제된 실험이 아니었다. 변수가 여러 개 동시에 바뀌었다. v4 결과가 나오고 나서야 비로소 이해할 수 있었다.
스무 번째, AI 에이전트의 한계가 명확하다. 5인 팀이 기술적으로 정교한 분석을 수행했지만, “Docling을 쓰는 이유가 OCR 기능인데 그걸 꺼버린 건 본말전도”라는 본질적 문제를 놓쳤다. 에이전트는 코드와 로그를 분석할 수 있지만, 비즈니스 컨텍스트와 도구 선택의 근본 이유를 이해하지는 못한다. 인간의 판단이 여전히 필요하다.
스물한 번째, 품질과 속도의 관계는 단순하지 않다. 일반적으로 품질을 높이면 속도가 떨어지고, 속도를 높이면 품질이 떨어진다고 가정한다. 그러나 이 사례에서는 품질을 높였더니 속도도 개선되었다. 이는 시스템 환경, 자원 경합, 알고리즘 효율성 등 다양한 요인이 복합적으로 작용한 결과다. 단순한 트레이드오프 모델로는 설명되지 않는다.
스물두 번째, 중간 점검의 가치가 크다. 1시간 후 팀 재소집을 예약했고, 실제로는 그 전에 품질 문제가 발견되어 즉시 대응했다. 만약 점검 없이 v3를 끝까지 실행했다면, 1,786개 파일을 모두 처리한 후에야 품질 문제를 발견했을 것이다. 조기 발견, 조기 수정이 비용을 절감한다.
스물세 번째, 의사결정의 속도가 중요하다. 품질 문제가 확인되자마자 ETL을 중단하고, 2-Pass를 구현하고, 다시 전면 OCR ON으로 전환하고, 3-Store를 초기화하고, v4를 시작하는 일련의 과정이 1시간 내에 진행되었다. 분석 마비에 빠지지 않고, 결정하고, 실행하고, 측정하는 사이클을 빠르게 돌렸다.
스물네 번째, 실패의 비용을 최소화해야 한다. v3는 실패했지만, 121개 파일만 처리한 시점에서 발견되었다. 이미 처리된 데이터는 dedup으로 스킵되므로 재처리 비용이 없다. 3-Store 초기화도 빠르게 완료되었다. 실패를 빨리 발견하고, 빨리 복구하는 메커니즘이 작동했다.
스물다섯 번째, 데이터 기반 의사결정의 힘이 확인된다. “우려”, “예상”, “추정”이 아니라 “평균 103 tokens”, “58.2% 미만”, “거부율 29.5%”라는 구체적 숫자로 문제를 정의했다. 숫자가 있으면 논쟁의 여지가 없다. 개선도 측정 가능하다. v4에서 거부율 12.0%로 떨어졌다는 것은 명백한 개선이다.
스물여섯 번째, 완벽주의의 함정을 피해야 한다. 2-Pass 조건부 OCR은 이론적으로 최적이다. 텍스트 PDF는 빠르게, 스캔 PDF는 정확하게. 그러나 구현 복잡도, 유지보수 비용, 디버깅 어려움을 고려하면 차선책이다. 단순한 OCR ON이 실용적으로는 최선이다. 이론적 최적과 실용적 최선은 다를 수 있다.
스물일곱 번째, 신뢰의 보정이 필요하다. 초기에 에이전트 팀의 58배 가속 결과를 신뢰했다. “놀라운 결과”, “극적인 개선”이라고 평가했다. 그러나 품질 문제가 드러나면서 신뢰가 흔들렸다. v4 결과를 받을 때는 더 신중하게 검증했다. 한 번 실패를 경험하면 맹목적 신뢰가 조정된다.
스물여덟 번째, 환경 통제의 중요성이 드러난다. Embedding Backfill이라는 예상치 못한 프로세스가 성능을 지배하고 있었다. 이를 제거하지 않고는 어떤 코드 최적화도 제한적 효과만 낼 수 있었다. 실험을 할 때는 환경을 통제해야 한다. v4는 클린 환경에서 시작했고, 그래서 비로소 OCR ON의 실제 성능을 측정할 수 있었다.
스물아홉 번째, 사용자의 직관을 존중해야 한다. “데이터 품질 떨어뜨리는 것이 무슨 의미일까 싶네…“는 논리적 추론이 아니라 직관적 판단이다. 아직 데이터를 보기 전이었지만, 뭔가 잘못되었다는 감각이 있었다. 실제로 데이터를 확인하니 우려가 정확했다. 수년간의 경험에서 나온 직관은 무시해서는 안 된다.
서른 번째, 최적화의 역설이 존재한다. 속도 최적화를 추구하다가 품질을 잃었고, 품질을 회복하려다 속도까지 얻었다. 직선적 인과관계가 아니다. 시스템은 복잡하고, 상호작용이 비선형적이며, 예상치 못한 결과가 나온다. 겸손하게 측정하고, 검증하고, 수정해야 한다. 확신에 차서 밀어붙이면 안 된다.
이 모든 관찰을 종합하면, AI 에이전트를 활용한 최적화는 강력하지만 위험하다. 58배 가속이라는 인상적인 결과가 나왔을 때, 그것을 즉시 신뢰하고 배포했다면 재앙이었을 것이다. 사용자가 품질을 확인하고, 문제를 발견하고, 방향을 수정했기 때문에 최종적으로는 더 나은 결과를 얻었다. 에이전트는 도구다. 강력한 도구지만, 그 출력을 검증하고 판단하는 것은 여전히 인간의 몫이다.
“무섭다”는 감정은 타당하다. 에이전트가 무엇을 하고 있는지 완전히 이해할 수 없고, 결과만으로 판단해야 하는 상황에서, 58배 가속이라는 결과가 실은 품질 파괴였다는 것을 발견하는 경험은 불안을 증폭시킨다. 다음에도 같은 실수가 반복되지 않으리라는 보장이 없다. 다른 형태로, 더 교묘하게 문제가 숨어있을 수 있다. 체계적인 검증, 다층적인 측정, 도메인 지식의 적용, 그리고 회의적 태도가 필요하다. 신뢰하되 검증하라. 결과가 좋아 보여도 의심하라. 데이터를 확인하라. 이것이 AI 에이전트 시대의 엔지니어링 원칙이다.
AI 에이전트 팀 기반 ETL 최적화: 실패에서 성공까지의 체계적 분석
5인 AI 에이전트 팀을 활용한 ETL 파이프라인 최적화 프로젝트에서 초기에는 58배 속도 향상을 달성했으나 품질 저하로 폐기했고, 재시도 후 속도와 품질을 모두 개선하는 역설적 결과를 얻었다. 본 보고서는 이 과정을 분석하여 AI 에이전트 활용 시 주요 함정과 성공 요인을 도출한다.
주요 발견
- OCR OFF 최적화(v3): 58배 가속 달성, 그러나 58.2%의 청크가 품질 기준 미달
- OCR ON 재최적화(v4): 속도 30% 추가 개선 + 품질 개선 (거부율 29.5%→12.0%)
- 근본 원인: 시스템 환경 요인(Embedding Backfill)을 코드 최적화로 해결하려는 오판
- 핵심 교훈: 에이전트의 기술적 타당성 ≠ 비즈니스 목적 부합성
1. 프로젝트 개요
1.1 초기 상황
처리 대상
- 전체 파일: 1,786개
- 파일 구성: MD/TXT/HTML 870개, PDF 389개, PPTX 307개, DOCX 205개
- 초기 예상 처리 시간: 153시간 (6.4일)
- 주요 병목: PDF 처리 (평균 16분/파일)
비즈니스 요구사항
- 처리 시간 단축 (목표: 24시간 이내)
- 데이터 품질 유지 (검색 가능한 청크 생성)
- 시스템 안정성 확보 (실패율 최소화)
1.2 에이전트 팀 구성
| 에이전트 | 전문 영역 | 주요 책임 |
|---|---|---|
| Infra | 시스템/인프라 | 자원 사용량, 프로세스 경합, 메모리 분석 |
| ETL Engineer | 코드/알고리즘 | 병목 지점 식별, 코드 레벨 최적화 |
| Architect | 아키텍처 | 구조적 개선안, 디자인 패턴 제안 |
| PM | 프로젝트 관리 | 우선순위 판단, 리스크 평가 |
| TL | 기술 리더십 | 로그 분석, 실증적 검증 |
2. Phase 1: 초기 최적화 시도 (v3) - 속도 vs 품질의 함정
2.1 에이전트 팀 분석 결과
Infra 에이전트 발견
- Embedding Backfill 프로세스(PID 979)가 2.86GB+3G 스레드 경합 유발
- ETL과 동시 실행으로 CPU/메모리 압박
ETL Engineer 식별: 7개 병목 지점
- OCR 향상 활성화 필요
- TableFormer ACCURATE 모드의 과도한 정밀도
- 5-30MB PDF에 대한 OCR OFF 분기 미구현
- DocumentConverter 싱글톤 병목
- 스레드 수 제한 (OMP_NUM_THREADS=6)
- 이중 재시도 메커니즘 (최대 6회)
- 순차 처리 방식
TL 로그 분석
- “RapidOCR returned empty result!” 다수 발견
- 특정 PDF 1개에 OCR만 23.2분 소요
- docling_adapter.py:93에서 do_ocr=True 확인
2.2 우선순위화 및 결정
팀은 3대 병목을 우선순위화했다:
| 순위 | 최적화 항목 | 예상 효과 | 구현 난이도 |
|---|---|---|---|
| 1 | OCR OFF 전환 | 80-90% 시간 절약 | 낮음 (1줄 수정) |
| 2 | TableFormer FAST 모드 | 50-70% 처리 절약 | 낮음 (1줄 수정) |
| 3 | OCR 조건부 분기 | 추가 개선 | 중간 (로직 추가) |
의사결정 근거
- 텍스트 기반 PDF에 불필요한 OCR 실행 중
- “RapidOCR returned empty result!”는 OCR 불필요성의 증거
- do_ocr=False + force_backend_text=True로 직접 텍스트 추출
2.3 구현 및 즉각적 성과
코드 변경 (3건)
1
2
3
4
# docling_adapter.py
do_ocr=False # 변경 1
force_backend_text=True # 변경 2
TableFormerMode.FAST # 변경 3
측정 결과
- PDF 처리 속도: 16분/파일 → 16.6초/파일 (58배 가속)
- 전체 예상 시간: 153시간 → 3-4시간 (40-50배 개선)
- 3분 만에 224/1,786 파일 처리
- Elasticsearch 6,136 chunks 생성
- 실패율: 0건
2.4 품질 문제 발견 및 검증
47분 후 사용자 우려
“클로드 다시 처리 결과 읽어보니 청킹 품질은 많이 떨어질 듯”
데이터 분석 (121개 파일, 6,676 chunks)
Token 분포 | Token 범위 | 청크 수 | 비율 | 품질 평가 | 비즈니스 임팩트 | |———–|———|——|———–|—————-| | 0-30 tokens | 1,011 | 15.1% | 검색 불가능 | 완전한 손실 | | 30-100 tokens | 2,877 | 43.1% | 정보량 부족 | 검색 품질 저하 | | 100-300 tokens | 2,685 | 40.2% | 적정 | 사용 가능 | | 300-600 tokens | 90 | 1.3% | 양호 | 이상적 | | 600+ tokens | 13 | 0.2% | 과다 | 분할 필요 |
핵심 지표
- 평균: 103 tokens/chunk (목표 대비 90% 부족)
- 저품질 비율: 58.2%가 100 tokens 미만
- Quality Gate 거부율: 29.5%
파일 유형별 품질 | 유형 | 청크 수 | 평균 tokens | 문제점 | |——|———|————-|——–| | .md | 3,658 | 73 | 과도한 분할 | | .pdf | 2,638 | 134 | OCR OFF로 스캔 PDF 미처리 | | .pptx | 132 | 191 | 양호 | | .html | 130 | 228 | 양호 | | .txt | 123 | 133 | 양호 |
2.5 근본 원인 분석
사용자 지적의 핵심
“OCR OFF는 너무 과했는데….OCR 때문에 청킹모델 그것 사용하는건데..”
도구 선택의 본질 상실
- Docling 선택 이유: RapidOCR + TableFormer 통합 파싱 품질
- OCR OFF = 도구의 핵심 가치 포기
- 속도를 위해 품질(비즈니스 목적)을 희생
에이전트 분석의 한계
- 기술적 관찰은 정확: “RapidOCR returned empty result!”
- 해석의 오류: 일부 텍스트 PDF ≠ 모든 PDF
- 예외 케이스 간과: 스캔 PDF, 이미지 기반 PDF, 손상된 텍스트 레이어
3. Phase 2: 중간 대응 - 2-Pass 조건부 OCR 시도
3.1 설계 및 구현
아이디어
- Pass 1: 텍스트 레이어 직접 추출 (빠름, ~17초/파일)
- Pass 2: 텍스트 부족 시 OCR 재시도 (느림, ~16분/파일)
- 임계값: MIN_TEXT_FOR_OCR_SKIP = 200자
구현 사항
1
2
3
4
5
6
7
8
9
10
11
12
13
# 추가된 복잡성
MIN_TEXT_FOR_OCR_SKIP = 200
_converter_ocr = None # OCR 전용 컨버터
def parse():
# Pass 1: Text-Only
result = self.converter.convert(path)
# Pass 2: 조건부 OCR
if is_pdf and len(content) < MIN_TEXT_FOR_OCR_SKIP:
result_ocr = self.converter_ocr.convert(path)
if len(ocr_content) > len(content):
result = result_ocr
3.2 사용자 결정: 단순화
“차라리 모든 파일에 OCR ON 시켜”
복잡성 제거
- MIN_TEXT_FOR_OCR_SKIP 상수 삭제
- converter_ocr 인스턴스 제거
- 조건부 로직 삭제
- do_ocr=True로 단순화
단순화의 가치 | 2-Pass 조건부 OCR | OCR ON (단순) | |——————|————–| | 복잡한 임계값 설정 필요 | 설정 불필요 | | 두 개의 컨버터 인스턴스 | 단일 인스턴스 | | 조건 분기 로직 | 단일 경로 | | 디버깅 복잡 | 디버깅 단순 | | 예측 어려움 | 예측 가능 |
4. Phase 3: 최종 최적화 (v4) - 역설적 성공
4.1 재시작 조건
3-Store 완전 초기화
- Elasticsearch: 인덱스 삭제
- PostgreSQL: documents 테이블 261건 삭제
- Neo4j: 모든 노드 삭제
- 클린 상태에서 재시작
설정
- OCR: ON (품질 우선)
- TableFormer: FAST (속도 최적화 유지)
- 파일 정렬: 유지 (MD/TXT 우선)
- Embedding Backfill: 제거됨 (클린 환경)
4.2 예상과 실제 결과의 괴리
예상
- OCR ON → 느려짐 예상
- PDF 처리: 수 분 ~ 십수 분/파일
- 전체 소요: 6-12시간
실제 (23:40 KST 중간 점검)
| 지표 | v3 (OCR OFF) | v4 (OCR ON) | 변화 |
|---|---|---|---|
| 진행률 | 353/1,786 (중단) | 248/1,786 (13.9%) | - |
| 성공 | 121 | 197 | +63% |
| 실패 | 0 | 0 | - |
| QG 거부율 | 29.5% | 12.0% | -59% |
| 평균 chunks/file | 6.3 | 3.7 | -41% |
| PDF 속도 | 16.6초 | 11.7초 | +30% 빠름 |
4.3 역설의 해명: 환경 요인의 지배력
OCR ON이 OCR OFF보다 빠른 이유
| 요인 | v3 영향 | v4 영향 |
|---|---|---|
| Embedding Backfill | 2.86GB+3G 스레드 경합 | 없음 (제거됨) |
| CPU 가용성 | 제한적 | 완전 |
| 메모리 압박 | 있음 | 없음 |
| OCR 오버헤드 | 없음 | 있음 (그러나 상쇄) |
시스템적 관점
- 코드 최적화 효과 < 환경 최적화 효과
- OCR OFF의 58배 가속 = Embedding Backfill 제거 + OCR OFF 복합 효과
- 클린 환경에서는 OCR ON도 충분히 빠름
4.4 품질 개선 분석
Quality Gate 거부율: 29.5% → 12.0%
- OCR이 스캔 PDF/저품질 텍스트 레이어를 제대로 파싱
- 검색 가능한 청크 비율 증가
- 품질 기준 통과율 상승
평균 chunks/file: 6.3 → 3.7
- v3: 텍스트 부족 → 과도한 분할 → 많은 저품질 청크
- v4: 충분한 텍스트 → 적절한 청크 생성 → Quality Gate 통과
5. 핵심 인사이트 및 교훈
5.1 에이전트 활용의 한계
기술적 타당성 ≠ 비즈니스 적합성
- 에이전트는 코드/로그 분석에 탁월
- 비즈니스 컨텍스트와 도구 선택 이유는 이해 못함
- “Docling을 쓰는 이유가 OCR 기능”이라는 본질 놓침
다층적 분석의 가치와 한계
- 5개 전문 영역의 독립 분석 = 다각도 관점 확보
- 최종 의사결정 주체 불명확 (5개 분석 → 3건 수정 과정 블랙박스)
- 인간의 도메인 지식과 판단이 결정적
5.2 측정과 검증의 중요성
품질 지표 우선 확인 필요
- v3: 속도 먼저 축하 → 47분 후 품질 문제 발견
- 올바른 순서: 품질 검증 → 속도 측정 → 종합 평가
다면적 성과 지표 | 지표 유형 | v3 | v4 | 시사점 | |———-|—-|—-|——–| | 속도 | 16.6초 | 11.7초 | 단일 지표로 판단 금지 | | 품질 | 거부율 29.5% | 12.0% | 품질이 우선순위 | | 안정성 | 실패 0건 | 0건 | 두 버전 모두 우수 | | 효율성 | 6.3 chunks/file | 3.7 | 적절한 분할 중요 |
조기 발견의 가치
- 121개 파일 시점에서 품질 문제 포착
- 1,786개 전체 처리 전에 방향 전환
- Dedup 메커니즘으로 재처리 비용 최소화
5.3 시스템적 사고의 필요성
환경 요인의 지배력
1
성능 = f(코드, 시스템 환경, 하드웨어, 네트워크, ...)
코드 최적화만으로는 부족
- Embedding Backfill 경합 > OCR ON/OFF 차이
- 클린 환경 확보가 선결 과제
- 병목의 실제 원인 파악 필수
5.4 단순성의 가치
2-Pass vs OCR ON 비교
| 측면 | 2-Pass 조건부 OCR | OCR ON |
|---|---|---|
| 이론적 최적성 | 높음 | 중간 |
| 구현 복잡도 | 높음 | 낮음 |
| 유지보수 비용 | 높음 | 낮음 |
| 디버깅 난이도 | 높음 | 낮음 |
| 예측 가능성 | 낮음 | 높음 |
| 실용적 가치 | 낮음 | 높음 |
완벽주의의 함정
- 이론적 최적 ≠ 실용적 최선
- 복잡성은 비용 (개발, 유지보수, 디버깅)
- “충분히 좋음”의 수용
5.5 의사결정 프로세스
빠른 사이클의 힘
- 측정 (v3 품질 문제 발견)
- 분석 (58.2% 저품질 확인)
- 결정 (OCR ON 전환)
- 실행 (3-Store 초기화, v4 시작)
- 검증 (1시간 후 중간 점검)
전체 프로세스 소요 시간: 약 1시간
되돌리기의 용기
- 6,676개 청크 폐기
- 이미 투자된 시간/자원 무효화
- Sunk cost fallacy 회피
- 잘못된 방향 지속 > 되돌려서 올바른 방향
6. 실무 적용 가이드라인
6.1 AI 에이전트 팀 활용 시 체크리스트
사전 준비
- 비즈니스 목적과 품질 기준 명확화
- 도메인 지식 정리 (도구 선택 이유, 핵심 기능 등)
- 성과 지표 정의 (속도, 품질, 안정성 등)
분석 단계
- 다층적 분석 수행 (시스템, 코드, 아키텍처)
- 환경 요인 파악 (백그라운드 프로세스, 자원 경합)
- 로그 기반 실증적 검증
의사결정 단계
- 에이전트 제안의 비즈니스 목적 부합성 검토
- 단순 vs 복잡 해결책 비교
- 예외 케이스 고려 (에이전트가 놓친 부분)
구현 및 검증
- 품질 지표 우선 확인
- 조기 검증 (전체 처리 전 샘플 테스트)
- 다면적 성과 측정
- 되돌리기 메커니즘 확보
6.2 위험 신호 (Red Flags)
즉시 재검토가 필요한 상황
- 극단적 성과: 58배 같은 극적 개선 → 트레이드오프 확인 필수
- 도구 핵심 기능 비활성화: OCR OFF처럼 도구 본질 상실
- 단일 지표 최적화: 속도만, 또는 품질만 고려
- 블랙박스 수용: “어떻게 작동하는지 모르지만 결과가 좋음”
- 복잡성 급증: 2-Pass처럼 로직 복잡도 크게 증가
6.3 성공 요인 체크리스트
이번 프로젝트의 성공 요인
- ✅ 사용자의 도메인 지식과 직관 (OCR 본질 지적)
- ✅ 조기 품질 검증 (47분 후 문제 포착)
- ✅ 빠른 의사결정 (1시간 내 v3 폐기 → v4 시작)
- ✅ 데이터 기반 판단 (58.2%, 29.5% 등 정량적 근거)
- ✅ 단순성 선택 (2-Pass 거부, OCR ON 선택)
- ✅ 환경 통제 (클린 상태 재시작)
- ✅ 되돌리기 실행 (6,676 청크 폐기 결정)
7. 정량적 성과 종합
7.1 최종 결과
v4 (OCR ON) 성과
- PDF 처리 속도: 11.7초/파일 (v3 대비 30% 개선)
- Quality Gate 거부율: 12.0% (v3 대비 59% 개선)
- 시스템 안정성: 실패 0건 유지
- 예상 전체 소요: 6-12시간 (클린 환경에서)
7.2 비용-편익 분석
에이전트 팀 비용
- 5개 에이전트 동시 실행
- 분석 시간: 수십 분 ~ 수 시간 추정
- 토큰 비용: 5배 증가
절감 효과
- 서버 비용: 153시간 → 6-12시간 (140+ 시간 절감)
- 프로젝트 일정: 6.4일 → 1일 미만
- 인건비: 5명 × 반나절 분석 비용 대체
ROI 평가
- 에이전트 비용 < 시간 절감 효과
- 다만: v3 실패 비용 고려 필요
- 학습 효과: 재사용 가능한 지식 획득
7.3 남은 최적화 여부
미구현 병목 (ETL Engineer가 식별한 7개 중 4개)
- DocumentConverter 싱글톤 이슈
- 스레드 수 병목 (OMP_NUM_THREADS=6)
- 이중 재시도 메커니즘 (최대 6회)
- 순차 처리 방식
판단: 3건 수정으로 목표 달성 → 추가 최적화 불필요
- “충분히 좋음”의 수용
- 복잡성 증가 vs 추가 이득 비교
- 기술 부채로 남겨두고 필요 시 해결
8. 미래 고려사항
8.1 GPU 활용 가능성
현재 상황
- Phase 1 (파싱/청킹): CPU only (WSL2 환경)
- Phase 2 (임베딩): Colab GPU 계획됨
GPU 활용 시 예상 효과
- PDF 처리: 16분 → 1-2분 (CPU 기준)
- 전체 ETL: 6-12시간 → 1-2시간
현실적 제약
- 1,786개 파일 업로드 필요
- 로컬 DB 접근 문제 (네트워크, 터널링)
- 대안: Colab 파싱 → JSON export → 로컬 import
결론: 현재는 CPU로 진행, 향후 필요 시 GPU 파이프라인 구축
8.2 확장성 고려
데이터 증가 시나리오
- 현재: 1,786개 파일
- 10배 증가: 17,860개 파일 → 60-120시간 예상
- 이 시점에 미구현 병목 4개 해결 필요
모니터링 지표
- 파일 증가율
- 평균 처리 시간 추이
- Quality Gate 거부율 변화
9. 결론
9.1 핵심 메시지
AI 에이전트 팀은 기술적 분석에 탁월하지만, 비즈니스 컨텍스트와 도구 본질에 대한 이해는 부족하다. 58배 속도 향상이라는 인상적 결과도 품질 검증 없이는 무의미하며, 실제로는 도구의 핵심 기능을 포기한 잘못된 최적화였다.
역설적이게도, 품질을 우선시하고 OCR을 재활성화했더니 속도까지 개선되었다. 이는 진짜 병목이 코드가 아니라 시스템 환경(Embedding Backfill)이었음을 보여준다.
9.2 실천적 원칙
- 도메인 지식 > 에이전트 제안: 도구를 선택한 이유를 기억하라
- 품질 우선 검증: 속도 개선을 축하하기 전에 품질을 확인하라
- 시스템적 사고: 코드만이 아니라 환경 전체를 보라
- 단순성 추구: 복잡한 최적화보다 단순하고 확실한 방법을
- 빠른 실패: 조기 발견, 조기 수정, 되돌리기를 두려워 말라
- 데이터로 판단: 직관이 아닌 측정값으로 의사결정하라
- 다면적 평가: 속도, 품질, 안정성, 복잡성을 모두 고려하라
9.3 마지막 통찰
“무섭다”는 감정은 타당하다. 에이전트의 블랙박스 특성상 결과만으로 판단해야 하는 상황에서, 58배 가속이 실은 품질 파괴였다는 발견은 불안을 야기한다. 그러나 이 불안이 건강하다. 맹목적 신뢰보다 회의적 검증, 체계적 측정, 도메인 지식의 적용이 AI 에이전트 시대의 엔지니어링 원칙이다.
신뢰하라, 그러나 검증하라. 결과가 좋아 보여도 의심하라. 데이터를 확인하라.
작성일자: 2026-02-14