← 운영 블로그

12화: 새우깡 사건

회차: 12화

11화에서 매칭 엔진 이야기를 했습니다. 700만 건을 돌려서 99.5%의 매칭률을 달성했다고요. 꽤 만족스러웠다고요.

이번 화는 그 만족감이 무너진 이야기입니다.


어느 날, 제가 직접 서비스를 테스트하고 있었습니다. “새우깡”을 검색해봤어요.

제품 정보가 나왔고, 원재료 목록도 보였습니다. 그 아래에 영양성분 정보가 표시되어 있었어요. 열량, 탄수화물, 단백질, 지방, 나트륨.

그런데 뭔가 이상했습니다.

새우깡의 열량이… 맞지 않는 것 같았어요. 정확한 숫자는 기억나지 않지만, 제가 알고 있는 새우깡의 영양정보와 달랐습니다.

“에이, 내가 잘못 기억하고 있는 거겠지.”

처음에는 그렇게 넘겼습니다. 그런데 신경이 쓰여서 실제 제품 포장지를 찾아봤어요.

틀렸습니다. 화면에 표시된 영양정보가 실제 제품과 달랐어요.


등이 서늘해지는 순간이었습니다.

이 서비스의 존재 이유가 뭐였죠? 사용자에게 정확한 정보를 제공하는 것. 그런데 그 정보가 틀려 있었습니다.

바로 원인을 추적하기 시작했습니다.

새우깡의 영양정보가 어디서 왔는지를 거슬러 올라갔어요. 영양성분 데이터베이스에서 새우깡과 연결된 레코드를 찾았습니다.

그런데 그 레코드가 새우깡의 것이 아니었습니다. 전혀 다른 제품의 영양정보였어요.

어떻게 이런 일이?


원인은 영양성분 데이터를 제품과 연결하는 과정에 있었습니다.

5화에서 이야기한 것처럼, 영양성분 데이터와 제품 데이터는 서로 다른 API에서 옵니다. 이 둘을 연결하려면 공통된 식별자가 필요한데, 처음에 사용한 방법은 제품 이름과 제조사 이름을 비교하는 것이었습니다.

“제품 이름이 비슷하고, 제조사도 같으면 같은 제품이겠지.”

이 가정이 문제였습니다.


좀 더 자세히 설명하면 이렇습니다.

제품 데이터베이스에는 104만 건의 제품이 있습니다. 영양성분 데이터베이스에는 약 27만 건의 영양정보가 있고요.

이 두 데이터를 연결할 때, ‘품목보고번호’라는 고유한 번호로 연결하면 가장 정확합니다. 같은 번호를 가진 레코드끼리 짝을 맺으면 되니까요.

그런데 품목보고번호로 연결되지 않는 경우가 있었습니다. 영양성분 API에 번호가 누락되어 있거나, 번호 체계가 달라서 매칭이 안 되는 경우.

이런 경우에 대해, 이름과 제조사를 비교해서 연결하는 보조 방법을 사용했던 겁니다.

이 보조 방법으로 연결된 건이 2,434건이었습니다.


문제를 발견하고 나서, 이 2,434건을 전수 검증했습니다.

방법은 간단했어요. 이름으로 연결한 건에 대해, 다른 독립적인 식별자(품목보고번호)로 교차 확인하는 것.

“이름으로 연결한 A와 B가, 실제로 같은 품목보고번호를 가지고 있는가?”

결과를 보고 저는 말을 잃었습니다.

품목보고번호가 일치하는 건: 0건. 0건이요. 2,434건 중 단 한 건도 맞는 게 없었습니다.

더 자세히 살펴보니, 86.5%는 명백한 오매칭이었습니다. 전혀 다른 제품의 영양정보가 연결되어 있었어요.

이것은 1,045개의 제품에 영향을 미치고 있었습니다. 1,045개의 제품이 잘못된 영양정보를 표시하고 있었다는 뜻입니다.


이 사건이 저에게 남긴 것은 분명했습니다.

“비슷하니까 맞을 것이다”는 근거가 아니다.

이름이 비슷하다고 같은 제품이 아닙니다. 제조사가 같다고 같은 제품이 아닙니다. “아마 맞겠지”라는 추측으로 데이터를 연결하면, 1,045개의 제품에 잘못된 정보가 표시됩니다.

그리고 무엇보다 무서웠던 건, 제가 직접 새우깡을 검색해보지 않았더라면 이 문제를 발견하지 못했을 수도 있다는 것이었어요.

매칭률 99.5%라는 숫자에 안심하고 있었으니까요. 숫자는 높았지만, 그 안에 2,434건의 잘못된 연결이 숨어 있었습니다.


오매칭된 2,434건을 전부 제거했습니다.

그리고 나서 오래 생각했어요.

이런 일이 다시 일어나지 않으려면 어떻게 해야 할까. 매칭률이 높다는 것에 안심하지 않으려면. “비슷하니까 맞겠지”라는 판단을 다시는 하지 않으려면.

그 고민의 결과가 다음 화의 이야기입니다.