이 글의 배경이 되는 이야기는 12화: 새우깡 사건에서 읽을 수 있습니다.
이 글을 읽으면 알 수 있는 것
- AI가 설계한 이름 기반 매칭이 왜 전멸했는지
- “매칭률 99.5%“라는 숫자가 어떻게 거짓 안심을 만들었는지
- 이름 유사도 매칭을 폐기하고 품목보고번호 기반으로 전환한 과정
배경: 영양정보를 제품에 연결하는 문제
이 서비스에는 두 종류의 데이터가 있습니다.
하나는 제품 데이터. 104만 건의 식품 제품 정보입니다. 다른 하나는 영양성분 데이터. 약 27만 건의 영양정보(열량, 탄수화물, 나트륨 등)입니다.
이 둘은 서로 다른 API에서 옵니다. 사용자에게 “이 제품의 영양정보”를 보여주려면, 두 데이터를 연결해야 합니다.
가장 정확한 연결 방법은 품목보고번호라는 고유 식별자를 사용하는 것입니다. 같은 번호를 가진 제품 데이터와 영양정보를 짝지으면 됩니다.
품목보고번호로 대부분이 연결됐습니다. 하지만 일부는 안 됐습니다. 번호가 누락되었거나, 체계가 달라서 매칭이 안 되는 경우.
여기서 AI에게 보조 매칭 방법을 맡겼습니다.
AI가 만든 해법: 이름으로 연결하자
AI는 합리적으로 보이는 방법을 제안했습니다.
“품목보고번호로 연결이 안 되는 건에 대해, 제품 이름과 제조사 이름을 비교해서 연결하겠습니다.”
논리는 이랬습니다. 영양정보 API에 “새우깡”이라는 이름과 “농심”이라는 제조사가 있고, 제품 데이터에도 “새우깡”과 “농심”이 있으면 같은 제품일 것이다.
그럴듯했습니다.
AI는 이 로직을 구현했고, 실행했습니다. 2,434건이 이름 기반으로 연결됐습니다. 매칭 엔진 전체의 매칭률은 99.5%를 기록했습니다.
저는 이 숫자를 보고 안심했습니다.
새우깡을 검색한 날
어느 날 서비스를 직접 테스트하고 있었습니다. “새우깡”을 검색했습니다.
영양정보가 표시됐습니다. 그런데 뭔가 이상했습니다. 열량이 제가 알고 있는 새우깡의 수치와 달랐습니다.
처음에는 “내가 잘못 기억하고 있겠지” 하고 넘겼습니다. 하지만 신경이 쓰여서 실제 제품 포장지를 찾아봤습니다.
틀렸습니다.
화면에 표시된 영양정보는 새우깡의 것이 아니었습니다. 전혀 다른 제품의 영양정보가 새우깡에 연결되어 있었습니다.
전수 검증: 2,434건 중 맞는 것이 몇 건인가
등이 서늘해졌습니다. 이름 기반으로 연결한 2,434건 전체를 검증하기로 했습니다.
방법은 단순했습니다. 이름으로 연결한 건에 대해, 품목보고번호라는 독립적인 식별자로 교차 확인하는 것입니다. “이름으로 연결한 A와 B가, 실제로 같은 품목보고번호를 가지고 있는가?”
결과를 보고 말을 잃었습니다.
| 검증 항목 | 결과 |
|---|---|
| 이름 기반 매칭 건수 | 2,434건 |
| 품목보고번호 일치 건수 | 0건 |
| 명백한 오매칭 | 86.5% |
| 영향받은 제품 수 | 1,045개 |
0건. 2,434건 중 단 한 건도 품목보고번호가 일치하지 않았습니다.
86.5%인 2,106건은 명백한 오매칭이었습니다. 전혀 다른 제품의 영양정보가 연결되어 있었습니다. 나머지 13.5%도 확인할 수 없었을 뿐, 맞다는 증거는 없었습니다.
1,045개의 제품이 잘못된 영양정보를 표시하고 있었습니다. 그리고 제가 직접 새우깡을 검색해보기 전까지, 아무도 이 사실을 몰랐습니다.
왜 전부 틀렸는가
이름 기반 매칭이 전멸한 이유는 세 가지였습니다.
첫째, 같은 이름의 다른 제품이 너무 많습니다. “새우깡”은 농심의 제품이지만, 영양정보 API에는 시기별·용량별로 여러 “새우깡” 레코드가 있을 수 있습니다. 이름만으로는 어떤 것이 지금 판매 중인 제품인지 구분할 수 없습니다.
둘째, 이름 표기가 다릅니다. 같은 제품인데 한쪽은 “새우깡(90g)“이고 다른 쪽은 “새우깡 오리지널”일 수 있습니다. 이름 유사도가 높아도 같은 제품이라는 보장이 없고, 이름이 정확히 같아도 다른 제품일 수 있습니다.
셋째, 잘못 연결해도 에러가 나지 않습니다. 이것이 가장 위험한 점이었습니다. 프로그램은 정상적으로 실행되고, “2,434건 매칭 성공”이라고 보고합니다. 데이터가 틀렸다는 사실은 코드 수준에서 감지할 수 없습니다. 사람이 직접 확인하지 않으면 발견되지 않습니다.
폐기와 전환
이름 기반 매칭 2,434건을 전부 삭제했습니다.
그리고 원칙을 세웠습니다. 영양정보와 제품의 연결은 품목보고번호로만 합니다. 이름 유사도, 제조사 이름, 어떤 형태의 추측도 사용하지 않습니다.
품목보고번호로 연결이 안 되는 건은 연결하지 않습니다. 영양정보가 비어 있는 제품이 생기지만, 잘못된 영양정보가 표시되는 것보다는 훨씬 낫습니다.
이 사건 이후, 매칭률이라는 숫자를 다르게 보게 됐습니다.
99.5%라는 숫자에 안심했었습니다. 하지만 그 0.5%가 아니라, 99.5% 안에 숨어 있던 2,434건이 문제였습니다. 높은 매칭률은 정확성의 증거가 아닙니다. 검증 없는 매칭률은 아무 의미도 없습니다.
이 사건이 남긴 것
| 교훈 | 내용 |
|---|---|
| ”비슷하니까 맞겠지”는 근거가 아니다 | 이름이 같아도 다른 제품일 수 있다 |
| 매칭률 ≠ 정확도 | 99.5%라는 숫자가 거짓 안심을 만들었다 |
| 오매칭은 에러를 발생시키지 않는다 | 잘못 연결해도 프로그램은 “성공”으로 끝난다 |
| 검증 없는 매칭은 위험하다 | 2,434건을 넣기 전에 100건만 확인했어도 발견됐다 |
이 사건을 계기로 데이터 무결성 원칙을 수립했습니다. 교차검증 없이는 매칭을 적용하지 않는다는 원칙. 그 이야기는 다음 글에서 합니다.
한 줄 교훈
AI가 만든 로직이든 사람이 만든 로직이든, 검증 없이 데이터를 연결하면 틀립니다. 그리고 틀렸다는 사실을 알려주는 것은 에러 메시지가 아니라, 직접 확인한 사람입니다.