← 기술 블로그

걸러내야 할 것들 — skip_patterns 설계기

Phase: 2 — 파싱 시리즈: 공공데이터 레시피 대응 스토리: 운영 블로그 8화

이 글의 배경이 되는 이야기는 8화: “정제수, 설탕, 산도조절제(구연산)“에서 읽을 수 있습니다.

이 글을 읽으면 알 수 있는 것


문제: 쪼개면 나오는 것들

이전 글들에서 원재료 텍스트를 구분자로 쪼개고, 괄호를 분해하는 이야기를 했습니다. 이 과정을 거치면 텍스트 조각들이 나오는데, 이것들이 전부 원재료는 아닙니다.

밀가루(밀:미국산, 호주산), 코코아버터 5.2%, 산도조절제(구연산)

이걸 분해하면 이런 조각들이 나옵니다:

밀가루, 밀, 미국산, 호주산, 코코아버터, 5.2%, 산도조절제, 구연산

이 중에서 원재료인 것: 밀가루, 밀, 코코아버터, 구연산 원재료가 아닌 것: 미국산, 호주산, 5.2%, 산도조절제

후자를 걸러내는 것이 skip_patterns의 역할입니다.


걸러내야 할 것의 종류

98만 건의 데이터를 분석하면서 파악한, “원재료가 아닌 것”의 분류입니다.

1. 수치+단위 — 함량 정보

5.2%,  30mg,  0.5g,  100㎎,  2㎍

숫자 뒤에 단위가 붙은 패턴입니다. 원재료의 함량을 표시하는 것이지, 원재료 이름이 아닙니다. 숫자만 있는 경우(30, 100)도 마찬가지입니다.

2. 원산지 표현

국산, 국내산, 미국산, 호주산, 중국산, 캐나다산, 뉴질랜드산, 외국산, 수입산

“~산”으로 끝나는 원산지 정보입니다. 괄호 안에서 콜론 뒤에 나오는 경우가 많습니다. “밀:미국산”에서 “미국산” 부분.

3. 식품유형 분류명

과채가공품류, 식육가공품류, 수산가공품류, 유가공품류, 음료류, 면류, 빵류, 과자류…

이것들은 식품공전에 정의된 카테고리 이름입니다. 제조사가 원재료 앞뒤에 분류명을 적어놓는 경우가 있었습니다. “채소류”가 원재료로 잡히면 안 됩니다.

4. 첨가물 기능 분류명

합성보존료, 합성착색료, 산화방지제, 영양강화제, 유화제, 팽창제, 산도조절제…

“산도조절제(구연산)“에서 “구연산”은 원재료이지만, “산도조절제”는 그 기능을 설명하는 이름입니다. 실제 첨가물이 아니라 분류명인 거죠.

5. 수식어·상태 기술어

혼합형, 연한, 세균성, 비세균성, 심제외, 껍질제외, 함수

괄호 안에서 분해된 부산물입니다. “α-아밀라아제(세균성)“에서 “세균성”은 효소의 유래를 설명하는 것이지, 원재료가 아닙니다.

6. 기타 잔여물

첨307 (첨가물 코드), 6년근 (인삼 등급), 아연함유 (수식어)

분류하기 어려운 나머지들입니다. 첨가물 행정 코드, 원재료의 등급 표시, “~함유”로 끝나는 수식어 등.


갈림길: 허용 목록 vs 차단 목록

필터를 만드는 방식은 두 가지입니다.

방식동작장단점
허용 목록 (allowlist)사전에 있는 것만 통과정확하지만, 사전에 없는 새 원재료를 놓침
차단 목록 (blocklist)패턴에 걸리는 것만 제거, 나머지는 통과새 원재료도 통과하지만, 패턴이 계속 늘어남

차단 목록 방식을 선택했습니다.

이유는 표준 사전의 커버리지 때문입니다. 사전에 등록되지 않은 원재료가 실제로 존재합니다. 허용 목록 방식이면 이런 원재료가 전부 누락됩니다. 차단 목록은 “확실히 원재료가 아닌 것”만 제거하고 나머지는 일단 살려두는 방식이라, 커버리지에서 유리합니다.

대가는 있습니다. 새로운 유형의 비원재료가 발견될 때마다 패턴을 추가해야 합니다.


설계 원칙

패턴을 추가할 때 지킨 기준이 세 가지 있습니다.

첫째, 시작 부분만 검사합니다. 정규식의 ^(문자열 시작)를 사용합니다. “미국산”으로 시작하는 것은 원산지이지만, “미국산딸기”는 원재료일 수 있습니다. (실제로 그런 이름은 없었지만, 원칙은 보수적으로.)

둘째, 정확 일치를 선호합니다. 분류명(“과자류”, “음료류” 등)은 끝에 $를 붙여서 정확히 일치하는 경우만 걸러냅니다. “과자류”는 걸러내지만 “과자류가공품”은 통과시킵니다.

셋째, 대소문자를 구분하지 않습니다. 영문 패턴은 re.IGNORECASE 플래그를 사용합니다. “or”이 포함된 화학명 잔여물 등을 잡기 위해서입니다.


패턴이 늘어나는 과정

처음에는 원산지와 수치+단위, 두 가지 패턴으로 시작했습니다. 100건을 돌려보니, 분류명이 원재료로 잡히는 것이 보였습니다. 패턴을 추가했습니다.

1,000건을 돌려보니, “세균성”, “혼합형” 같은 괄호 분해 부산물이 나왔습니다. 또 추가.

10,000건을 돌려보니, “첨307” 같은 첨가물 코드가 나왔습니다. 또 추가.

이런 식으로 패턴이 점점 늘어났습니다. 현재 skip_patterns에는 7개의 주요 카테고리에 걸쳐 수십 개의 패턴이 들어 있습니다.

몰랐던 것이 있습니다. “이 정도면 충분하다”는 순간이 오지 않았다는 것. 규모를 키울 때마다 새 유형이 등장했습니다. 결국 “완벽한 필터”가 아니라 “충분히 좋은 필터”를 목표로 잡아야 했습니다.


결과

항목수치
필터 카테고리7종 (수치, 원산지, 식품유형, 첨가물기능, 수식어, 코드, 기타)
개별 패턴 수하나의 정규식 안에 약 40개
필터 방식차단 목록 (정규식 기반)
추가 필터1글자 제외, 50글자 초과 제외

한계

패턴은 한국어 기준입니다. 영문 성분표나 중문 성분표가 섞인 데이터에 대한 커버리지는 낮습니다.

“걸러야 할 것”과 “원재료”의 경계가 모호한 경우가 있습니다. “산도조절제”는 기능 분류명이라 걸러냈지만, 특정 맥락에서는 원재료명으로 쓰일 수 있습니다. 이런 경계 사례는 사전 매칭 단계에서 잡아야 합니다.

한 줄 교훈

필터는 “무엇을 남길 것인가”보다 “무엇을 확실히 제거할 수 있는가”로 시작해야 합니다. 확신 없는 것은 남겨두는 편이 낫습니다.


다음 글: HACCP 포장지 영양성분 파싱: ‘1g미만’과 ‘less than 1g’