뒤로
게
게으른 엔지니어 ·

안녕하세요,
제가 개발하면서 겪은 일들을 적는 블로그를 소개합니다.
혼자서 삽질하고 있는 거라, 훨씬 더 좋은 방법을 알고 계신 분들도 있을거지만, 이건 일종의 저의 삽질 기록지이면서 다시 똑같은 삽질을 하지 않기 위한 기록이라고 보시면 좋을 것 같습니다.

그 중에 하나의 시리즈를 소개하니 참고 하시고, 블로그 글 재미있게 읽어 주시면 감사하겠습니다.

제목: 고객사 뉴스 리포트 자동화, 5시간 걸리던 게 9분대로 줄기까지 — 아홉 번의 삽질 기록

고객사별로 관련 뉴스를 검색·크롤링·중복제거해서 LLM 요약까지 붙인 리포트를 자동으로 만드는 파이프라인을 혼자 만들었다. 처음엔 사람이 프롬프트 가이드를 보고 직접 검색·요약하던 걸 결정론적인 파이썬 파이프라인으로 옮기는 작업이었는데, 실제로 돌려보고 나서야 진짜 문제들이 하나씩 나오기 시작했다. 아홉 번의 삽질을 순서대로 정리하면 이렇다.

  1. 잘 되던 설정값을 그대로 옮겼다가 권한 오류
    원래 다른 에이전트가 잘 쓰던 저장 경로를 설정 파일에 그대로 옮겨왔는데, Permission denied가 떴다. 알고 보니 그 경로는 Docker 컨테이너 안에서만 존재하는 경로였다 — 컨테이너 밖에서 직접 도는 이 파이프라인에는 애초에 의미 없는 값이었다. 값만 복사하고, 그 값이 왜 유효했는지는 안 옮긴 게 문제였다.

  2. 종료 코드는 0인데 결과가 전부 실패
    전용 API 키를 새로 발급받아 붙였더니, 프로세스는 끝까지 잘 돌았는데 결과 파일 552개 행 전부가 "요약 실패"였다. 새 키가 특정 모델에 접근이 막혀 있었던 것 — 개별 실패를 조용히 넘기도록 만든 설계가, 이번엔 "전부 다" 실패해도 프로세스가 멀쩡히 끝나는 걸 가려버렸다.

  3. 회사명만 검색해도 관련 없는 기사가 계속 나왔다
    검색 결과에 그 회사와 상관없는 기사가 계속 섞여 나왔다. 원인은 여러 겹이었다 — 키워드가 회사명과 따로 놀았고, 검색 API 자체가 정확 일치가 아니라 유사도 기준으로 결과를 돌려주고 있었다. 후보를 늘려도 노이즈 비율은 그대로였고, 결국 회사명이 실제로 제목·요약에 있는지 직접 확인하는 필터를 추가해야 했다.

  4. 링크 하나 고쳤는데, 스물네 개가 더 있었다
    메일로 받은 리포트에서 링크 하나가 깨져 보였다. 원인은 "[단독]" 같은 한국 뉴스 제목 태그의 대괄호가 마크다운 링크 문법과 충돌한 것. 눈에 띈 두 줄만 고치고 끝난 줄 알았는데, 다시 제대로 스캔해보니 같은 패턴이 스물네 개 더 있었다.

  5. 규칙을 더 추가하는 대신, LLM한테 먼저 물어봤다
    이름이 흔한 단어와 겹치는 회사 하나가 계속 오탐을 냈다. 도메인 키워드를 붙여봐도 절반은 여전히 틀렸다. 규칙을 더 정교하게 짜는 대신 "이 기사가 정말 이 회사 얘기냐"를 LLM한테 먼저 판단시켜봤다 — 기존 방식이 통과시킨 13건 중 LLM은 6건만 관련 있다고 봤고, 직접 확인해보니 LLM 판단이 전부 맞았다. 전체 고객사로 넓혀 봐도 약 3분의 1이 같은 정도로 새고 있었다.

  6. 각주 버그를 고쳤는데, 알고 보니 한 번도 없던 버그였다
    리포트에 이상한 각주 번호가 보여서 프롬프트를 고쳤다. 그런데 고친 뒤에도 여전히 보였다. vi로 열면 없고 glow(마크다운 렌더러)로 열면 생기는 걸 확인하고 나서야, 이게 콘텐츠 문제가 아니라 렌더러가 테이블 안 링크를 각주로 바꿔버리는 동작이었다는 걸 알았다. 막아뒀던 방어 코드를 다 빼고 재현 실험까지 해보고서야, 애초에 LLM이 각주를 붙인 적이 한 번도 없었다는 게 확인됐다. 그 와중에 캐시 저장 함수 이름이 틀려서 예외가 조용히 삼켜지고 있던 진짜 버그를 하나 더 잡았다.

  7. CPU는 계속 도는데 다섯 시간째 결과가 안 나왔다
    1년치 리포트를 돌렸는데 다섯 시간 넘게 안 끝났다. CPU는 95% 넘게 돌고 있는데 출력 폴더는 텅 비어 있었다. 원인은 중복 제거 로직이 새 기사 하나를 지금까지 쌓인 전체(수만 건)와 전수 비교하는 O(n²) 구조였던 것. 같은 보도자료 재게재는 며칠 안에 몰려서 일어난다는 점을 활용해서, 날짜 버킷 기준 앞뒤 3일 범위로만 비교하도록 바꿨다.

  8. 매번 처음부터 다시 비교하는 게 아까웠다
    속도는 줄었지만, 리포트를 다시 돌릴 때마다 이미 판정 끝난 기사까지 처음부터 다시 비교하고 있다는 게 눈에 걸렸다. 판정 결과를 캐시에 저장해서 다음 실행부터는 건너뛰게 만들고, 크롤링 실패율(403, 타임아웃, SSL 실패 등)도 로그로 남겨서 하나씩 대응을 붙였다 — 그중 하나는 VPS IP가 차단당해서 집 PC를 거치는 프록시가 필요했다.

  9. 빨라진 진짜 이유는 다른 데 있었다
    캐싱을 붙이고 다시 돌려보니 LLM 요약 단계가 29분에서 2분 13초로, 열두 배 빨라졌다. 동시 처리 개수를 늘린 효과라고 생각하기 쉬웠는데, 계산을 다시 해보니 진짜 이유는 따로 있었다 — 직전 실행이 채워놓은 캐시를 그대로 물려받아서, 대부분이 새 API 호출이 아니라 캐시 히트였던 것. 그 과정에서 date와 datetime 문자열 포맷 불일치로 생긴 진짜 크래시도 하나 잡았다. 최종적으로 5시간 넘게 멈춰 있던 파이프라인이 9분 37초로 끝나는 데까지 왔다.

이 아홉 번의 삽질을 하나씩 더 자세히 기록한 시리즈가 블로그에 있습니다. → https://decipher99.com/series/customer-news-report/

게으른 엔지니어의 블로그

개발 과정에 대한 경험담에 대한 소개 블로그

0

댓글

로그인 후 댓글을 남길 수 있습니다.

아직 댓글이 없습니다.