Skip to content
게으른 엔지니어의 기술 블로그
Go back

중복 클리핑 줄이려다가 2편 — 세 번 틀리고 나서야 숫자를 봤다

전 편에서 플러그인 코드 버그 두 개를 고쳤다면, 이번엔 진짜 처음 목표였던 쪽이다 — Obsidian Web Clipper로 같은 페이지를 두 번 클리핑하는 걸 어떻게 자동으로 찾아낼까.

1차: URL 정규화

Dataview로 source frontmatter의 URL이 같은 노트를 찾는 스크립트부터 짰다. 트래킹 파라미터(utm_source, fbclid 같은 것들)만 골라서 제거하고 나머지는 남기는 방식이었다.

const TRACKING_PARAMS = ["utm_source", "utm_medium", "fbclid", "gclid", ...];
u.searchParams.delete(k); // 목록에 있는 것만 제거

바로 구멍이 났다. 네이버 뉴스 기사를 PC랑 아이폰에서 각각 클리핑했더니 URL이 이렇게 달랐다.

PC:    ...028/0002814167?cds=news_media_pc&type=editn
iPhone: ...028/0002814167?cds=news_edit

cds, type은 목록에 없어서 안 걸러졌다. 목록에 추가해서 고쳤는데, 이 순간 근본적인 문제를 알아챘다 — 사이트마다 자기만의 트래킹 파라미터 이름을 쓴다. 이번엔 네이버였지만 다음엔 다른 사이트에서 또 새로운 이름이 나올 거고, 그때마다 하나씩 추가해야 한다. 두더지 잡기다.

2차: 쿼리스트링을 통째로 버려볼까

블록리스트 대신 아예 쿼리스트링 전체를 버리고 도메인+경로만 비교하는 쪽으로 바꿔봤다. 뉴스 기사는 보통 진짜 식별자가 경로에 있으니까.

바로 반례가 나왔다. travie.com/news/articleView.html?idxno=20040 — 이 사이트는 경로가 모든 기사에서 똑같고, 진짜 식별자가 쿼리스트링(idxno)에 있었다. 쿼리스트링을 버리는 순간, 완전히 다른 텍사스 여행 기사 8개가 전부 같은 걸로 뭉쳐버렸다.

정리하면 이렇다 — 어떤 사이트는 식별자가 경로에, 어떤 사이트는 쿼리스트링에 있다. 하나의 규칙으로 둘 다 맞출 방법이 없다. URL 구조로 접근하는 이상 계속 케이스 바이 케이스일 수밖에 없었다.

곁가지로 파본 것들

URL을 버리기 전에 다른 방향도 몇 개 확인했다.

셋 다 부분적으로만 쓸모 있고 전체 해법은 아니었다.

3차: 본문을 보기로 했다

URL 자체를 포기하고 본문 내용을 비교하는 쪽으로 방향을 완전히 틀었다. 같은 페이지를 재클리핑한 거면 URL이 뭐가 됐든 본문은 거의 똑같을 거라는 생각이었다. Dataview의 dv.io.load()로 노트 본문을 직접 읽어서, 앞부분을 문자 단위 bigram(두 글자씩 묶은 조각)으로 쪼갠 다음 두 노트 간 겹치는 비율(Jaccard 유사도)을 계산했다.

첫 실행 결과는 엉망이었다. 완전히 다른 책 요약 4개(“The Fearless Organization”, “Thanks for the Feedback”, “Nonviolent Communication”, “Crucial Conversations”)가 전부 중복으로 묶였다. 원인은 명확했다 — 전부 같은 사이트에서 클리핑한 거라, 앞부분에 있는 사이트 공통 템플릿(네비게이션, 가입 안내 같은 것)끼리 겹친 거였다. 진짜 내용이 아니라 껍데기가 비교된 셈이다.

제목 유사도를 같이 요구하는 조건을 추가해봤다. 이번엔 다른 종류의 오탐이 나왔다 — 같은 뉴스레터의 “Tech jobs market… part 3”와 “…job market… part 2”가 묶였다. 시리즈 파트라 제목도, 다루는 주제 어휘도 비슷했던 거다. 임계값을 올리고 비교 범위를 넓혀봤더니, 이번엔 반대로 오탐이 더 늘었다.

그만 찍고 숫자를 봤다

세 번 연속 감으로 값을 조정하다 틀렸다. 여기서 멈추고, 그룹으로 묶는 로직을 빼고 모든 쌍의 실제 유사도 점수를 그대로 뽑아서 순위표로 보여주는 진단용 스크립트로 바꿨다.

결과를 보니 경계선이 뚜렷했다.

유사도내용
1.000완전히 같은 페이지 재클리핑 (제목까지 동일)
0.893같은 페이지 재클리핑
0.727 이하같은 사이트의 책 요약끼리, 전부 다른 책

0.893과 0.727 사이에 확실한 절벽이 있었다. 짐작으로 여러 번 틀렸던 임계값을, 실제 데이터를 보고 나니 한 번에 잡을 수 있었다. 안전 마진을 두고 0.85로 정했다.

마지막으로 — 찾은 걸 바로 지울 수 있게

임계값이 맞았는지 확인하고 나서, 찾은 중복을 일일이 노트로 들어가서 하나씩 지우기엔 너무 게을렀다 — 어쨌든 게으른 엔지니어니까. 그래서 Dataview 결과 표에 체크박스와 “선택 항목 일괄 삭제” 버튼을 붙였다. 그룹마다 제일 오래된 노트는 기본적으로 “유지”로, 나머지는 “삭제 대상”으로 미리 체크해두고, 버튼을 누르면 완전 삭제가 아니라 휴지통으로 이동하게 했다 — 임계값이 또 어딘가에서 틀렸을 가능성을 대비한 안전장치다.

남는 생각

이번 삽질에서 제일 크게 배운 건 도구 얘기가 아니라 태도 얘기였다. 임계값을 세 번 연속 감으로 찍고 세 번 다 틀렸다. 그런데도 계속 “이번엔 다를 거야”라며 숫자만 바꿔가며 재시도했다. 실제로 문제가 풀린 건 그 패턴을 멈추고 “일단 있는 그대로의 숫자부터 보자”고 방향을 바꾼 순간이었다. 근거 없이 반복하는 시도보다, 잠깐 멈춰서 데이터를 직접 눈으로 확인하는 쪽이 결국 더 빨랐다.


Share this post:

Previous Part
중복 클리핑 줄이려다가 — 증상은 하나인데 원인은 둘이었다