← 기술 블로그

2-Pass Alias 정제: 데이터가 데이터를 하이재킹할 때

Phase: 3 — 매칭 시리즈: 공공데이터 레시피 대응 스토리: 운영 블로그 10화

이 글의 배경이 되는 이야기는 10화: 이 성분이 저 성분이랑 같은 건가요에서 읽을 수 있습니다.

이 글을 읽으면 알 수 있는 것


문제: 이명이 다른 원재료의 이름을 빼앗는다

식약처 I2520 API는 약 4만 3천 개의 원재료 표준 사전을 제공합니다. 각 항목에는 표준명(name_ko)과 이명(name_alias)이 있습니다. 이명은 “다른 이름”이라는 뜻입니다. 표준명이 “설탕”이면 이명에 “정백당, 백설탕”이 등록되어 있는 식입니다.

매칭 엔진은 이 이명을 활용합니다. 제조사가 “정백당”이라고 적어도 사전에서 “설탕”을 찾을 수 있으니까요.

그런데 이명 데이터를 자세히 살펴보니, 예상하지 못한 문제가 있었습니다.

어떤 원재료의 이명이, 다른 원재료의 표준명과 같은 경우가 있었습니다.

예를 들어 이런 상황입니다.

항목 A: 표준명 = "포도당"
항목 B: 표준명 = "물엿",  이명 = "포도당, 옥수수시럽"

“포도당”은 그 자체로 독립된 원재료입니다. 그런데 “물엿”의 이명 목록에도 “포도당”이 들어 있습니다.

이 상태에서 매칭 사전을 구축하면 어떻게 될까요?


하이재킹이 일어나는 과정

매칭 사전은 “이름 → 사전 항목”의 매핑입니다. 이 매핑을 구축하는 순서에 따라 결과가 달라집니다.

만약 표준명을 먼저 등록하고 이명을 나중에 등록하는데, 이명이 이미 등록된 표준명을 덮어쓴다면:

  1. “포도당” → 항목 A (표준명으로 등록)
  2. “물엿”의 이명 “포도당”이 등록되면서 → “포도당” → 항목 B로 덮어쓰기

제조사가 “포도당”이라고 적었을 때, 사전의 “포도당”이 아니라 “물엿”과 연결됩니다.

데이터가 데이터를 하이재킹한 것입니다.

이 문제가 까다로운 이유는, 눈에 잘 띄지 않기 때문입니다. 매칭은 성공합니다. 에러도 나지 않습니다. “포도당”이라는 이름이 사전의 어떤 항목과 연결되었으니까요. 단지 그 항목이 “포도당”이 아니라 “물엿”일 뿐입니다.


갈림길: 이명 충돌을 어떻게 처리할 것인가

방법동작리스크
등록 순서로 결정 (먼저 온 것이 승리)단순하지만 순서에 의존데이터 적재 순서가 바뀌면 결과가 달라짐
이명을 전부 등록하되 충돌 시 경고만유연하지만 위험하이재킹이 감지되지만 방지되지는 않음
충돌하는 이명을 사전에서 아예 제거강력하지만 이명 손실일부 유효한 이명이 삭제될 수 있음

세 번째를 선택했습니다.

이유는 명확했습니다. 표준명은 사전의 권위 있는 이름입니다. 어떤 이명도 표준명의 자리를 빼앗으면 안 됩니다. 충돌하는 이명을 제거하면 일부 유효한 연결이 사라질 수 있지만, 잘못된 연결이 생기는 것보다는 낫습니다.

이전 글에서 이야기한 원칙과 같습니다. 틀릴 바에야 모르겠다고 말하는 것이 맞다는 것.


해법: 2-Pass 구조

이명 정제는 두 단계로 이루어집니다.

Pass 1: 전체 표준명 집합을 구축합니다. 4만 3천 개의 원재료 표준명을 모두 모아서 집합(set)으로 만듭니다.

Pass 2: 이명이 있는 모든 항목을 순회하면서, 각 이명 토큰이 표준명 집합에 존재하는지 확인합니다. 존재하면 — 즉, 다른 원재료의 표준명과 같으면 — 해당 이명을 제거합니다.

Pass 1: 표준명 집합 = {"포도당", "물엿", "설탕", ...}

Pass 2: 물엿의 이명 "포도당, 옥수수시럽" 검사
  - "포도당" → 표준명 집합에 있음 → 제거
  - "옥수수시럽" → 표준명 집합에 없음 → 유지

결과: 물엿의 이명 = "옥수수시럽"

자기 자신의 표준명과 같은 이명도 제거합니다. 표준명이 “설탕”인데 이명에도 “설탕”이 있으면, 이 이명은 아무 역할도 하지 않으므로 삭제합니다.


왜 2-Pass인가

이 작업이 반드시 두 단계여야 하는 이유가 있습니다.

만약 데이터를 한 건씩 적재하면서 동시에 이명 정제를 하면, 아직 적재되지 않은 항목의 표준명과 충돌하는 이명을 놓칠 수 있습니다.

예를 들어, “물엿”을 먼저 적재할 때 “포도당”이라는 표준명이 아직 사전에 없을 수 있습니다. 그 시점에서는 “포도당”이 유효한 이명처럼 보입니다. “포도당” 항목이 나중에 적재되면 그때서야 충돌이 발생하지만, 이미 이명은 등록된 상태입니다.

2-Pass는 이 타이밍 문제를 해결합니다. 모든 데이터를 먼저 넣고(upsert), 그 다음에 전체를 한 번에 정제합니다. 적재 순서에 의존하지 않으므로, 데이터가 어떤 순서로 들어오든 결과가 동일합니다.


정제가 이루어지는 시점

2-Pass 정제는 표준 사전을 적재하는 과정의 후처리(post-import)로 실행됩니다.

  1. API에서 4만 3천 건의 원재료를 받아서 DB에 저장 (upsert)
  2. 모든 upsert가 완료된 후, 후처리가 자동 실행
  3. Pass 1: 전체 표준명 세트 구축
  4. Pass 2: 이명 순회 → 충돌 토큰 제거 → DB 반영

이 구조 덕분에 표준 사전이 업데이트될 때마다 이명 정제도 자동으로 이루어집니다. 새로운 원재료가 추가되어 새로운 충돌이 생기면, 다음 적재 시 자동으로 정제됩니다.


결과

항목수치
표준명 세트 크기약 4만 3천 개
정제 대상 항목 수이명이 있는 전체 항목
제거된 충돌 토큰 수적재 시마다 자동 집계

한계

유효한 이명이 삭제될 수 있습니다. 어떤 원재료의 이명이 다른 원재료의 표준명과 글자가 같지만, 실제로는 다른 맥락에서 사용되는 경우가 있을 수 있습니다. 이런 경우 이명이 불필요하게 삭제되어, 해당 이름으로 매칭이 안 될 수 있습니다.

하지만 이것은 의도된 트레이드오프입니다. 하이재킹으로 인한 잘못된 매칭보다, 매칭이 안 되는 것이 덜 위험합니다. 매칭이 안 된 것은 미매칭 목록에서 발견할 수 있지만, 잘못된 매칭은 발견하기가 훨씬 어렵습니다.

한 줄 교훈

매칭 엔진의 정확도는 매칭 로직만으로 결정되지 않습니다. 사전 데이터 자체가 오염되어 있으면, 아무리 좋은 로직이라도 틀린 결과를 냅니다.


다음 글: AI가 만든 매칭 엔진, 2,434건이 전부 틀렸다