3주차는 “벡터 검색을 얹으면 당연히 더 좋아지겠지”라는 기대로 시작했어요. 결론부터 말하면, 안 그랬어요. 이 글은 그 결과를 어떻게 받아들이고 기록했는지에 관한 이야기예요.
누구에게 도움이 될까요 — 새 기술(벡터·임베딩·RAG)을 붙이기 전에 “정말 필요한지” 재보고 싶은 분이요. 코딩을 몰라도 읽을 수 있게 썼어요.
Before — 왜 벡터 검색을 붙이려 했나
2주차까지 만든 ask.mjs는 키워드 부분일치로 검색해요. 세부계획에 처음부터 “3주차는 벡터를 얹어서 비교한다”고 적어뒀었고, “노트 500~1,000편 이하면 벡터가 필요 없다”는 원칙도 같이 적어뒀어요. 지금 논문이 21편이니, 이 원칙이 정말 맞는지 실측으로 확인해볼 참이었어요.
막힘 1 — 청킹 대상을 잘못 골랐어요
처음엔 논문 노트(SCHEMA 7칸 요약)를 청킹했어요. 만들고 나서 이렇게 물었더니
논문 원문을 청킹해서 벡터디비로 저장하는거지?
바로 걸렸어요. 노트는 이미 골라 뽑은 요약이라, 청킹해봤자 요약을 다시 요약하는 꼴이었어요. knowledge/raw/(논문 전문)를 논문 자체의 ## 절 제목 경계로 다시 청킹했어요 — 107개 청크에서 128개로 늘었어요.
막힘 2 — DuckDB가 “저장 완료”라고 거짓말했어요
임베딩까지 다 만들고 저장 스크립트를 돌렸더니:
청크 128개를 임베딩합니다...
DuckDB에 128개 청크 저장 완료
성공한 것처럼 보였어요. 그런데 실제로 질문을 던지니 “일치하는 청크를 못 찾았다”고 나왔어요. 직접 세어봤어요.
SELECT count(*) FROM chunks;
-- 결과: 0
0행이었어요. 원인을 찾아보니, DuckDB의 Node.js 드라이버가 ? 파라미터로 넘긴 JS 배열을 FLOAT[384](384차원 벡터) 타입으로 못 바꾸고 조용히 실패하는 버그였어요.
Conversion Error: Type VARCHAR with value '1,2,3' can't be cast to
the destination type FLOAT[3]...
에러 메시지조차 콜백을 안 넘기면 안 보이는 조용한 실패였어요. 벡터를 SQL 문자열 안에 직접 박아 넣는 방식([0.01, -0.02, ...]::FLOAT[384])으로 바꿔서 고쳤고, 그제야 진짜 128행이 들어갔어요.
진짜 결과 — 그리고 실망
파이프라인이 다 돌아간 뒤, 9단계에서 썼던 것과 똑같은 질문 10개로 다시 채점했어요.
| 키워드 검색(9단계) | 벡터 검색(11단계) | |
|---|---|---|
| 점수 | 21/40 (52.5%) | 9/40 (22.5%) |
기대와 반대였어요. 예를 들어 “SHINE 자기지도 학습 노이즈 제거 방식”을 물었더니, SHINE 논문은 top-3 안에도 안 들어오고 엉뚱하게 TEM 개념 페이지가 1위로 나왔어요. “CatBoost Amazon 데이터셋 성능 차이”를 물었을 때도, 전혀 관계없는 “키워드 검색 vs RAG” 논문이 1·2위를 가로챘어요.
이 결과를 보고 처음 든 느낌은 실망이었어요. 파이프라인 만드는 데 손이 많이 갔는데, 정작 점수는 더 나쁘게 나왔으니까요.
After — 그래도 그대로 기록했어요
원인을 파봤어요. 128개밖에 안 되는 작은 코퍼스에서는 1위와 3위의 유사도 차이가 0.020.05 수준일 때가 많았어요 — 이 정도는 신호라기보다 노이즈에 가까워요. 그리고 “오답”이라고 채점한 질문 중 절반은 정답 논문이 23위엔 있었어요.
| 벡터 검색 결과 | |
|---|---|
| 정답이 1위 | 3문항 |
| 정답이 2~3위(1위는 틀림) | 4문항 |
| 정답이 top-3 밖 | 3문항 |
결론: 지금 이 규모(논문 21편)에서는 벡터 검색을 쓸 이유가 없어요. 세부계획에 처음부터 적어뒀던 “500~1,000편 이하면 벡터 불필요”라는 원칙이 실측으로 확인된 거예요. ask.mjs(키워드)가 그대로 기본 검색으로 남고, 벡터 파이프라인은 나중에 논문이 훨씬 늘어나면 다시 켜서 재평가하기로 했어요.
배운 것
“성공했다”는 로그를 그대로 믿으면 안 돼요. DuckDB가 “128개 저장 완료”라고 찍었는데 실제론 0행이었어요. SELECT count(*)로 직접 세어보지 않았으면 몇 개월 동안 빈 데이터베이스로 검색하는 줄도 몰랐을 거예요.
결과가 기대와 다르게 나왔을 때, 포장하지 않고 그대로 적는 게 더 값져요. “벡터가 최신이니까 낫겠지”라는 성급한 가정을 실측으로 반증한 거예요. 실망스러운 결과지만, 이게 데이터 사이언티스트가 실제로 하는 일이에요 — 새 기법을 도입하기 전에 정말 필요한지 재보고, 필요 없으면 안 쓰는 것.
청킹 대상 하나가 전체 결과를 좌우해요. 노트(요약)를 청킹했다가 원문(raw)으로 바꾼 것처럼, 검색 품질은 “어떤 텍스트를 검색 대상으로 삼았는가”에서 이미 절반이 갈려요.
가져가서 쓰세요
”성공했다”는 로그를 의심하는 체크리스트
- 저장·삽입 스크립트가 “완료”라고 찍으면, 별도 명령으로 실제 행 수를 세어봤나요? (
SELECT count(*)같은) - 배치 삽입에 에러 콜백을 빠뜨리지 않았나요? (콜백 없으면 실패가 조용히 묻힙니다)
- 결과가 0건이거나 비어 있을 때, “데이터가 없어서”가 아니라 “저장이 실패해서”일 가능성을 먼저 의심했나요?
새 기법을 붙이기 전에 재보는 프롬프트
이 기법(예: 벡터 검색)을 붙이기 전에, 지금 있는 방식으로 먼저
기준선을 채점해줘. 같은 질문·같은 기준으로 새 기법도 채점해서
두 점수를 나란히 비교해줘. 새 기법이 이겨야 한다고 가정하지 말고.
다음 주
이미 세부계획에 정해져 있어요 — 새로 만들지 않고 회고·발표자료·전자책 정리로 마무리해요.