7화에서 HACCP API 발견 이야기를 했습니다. C002의 한계를 보완할 포장지 원문 데이터를 찾았다고요.
이제 C002든 HACCP이든, 원재료 텍스트를 가지고 할 일은 같습니다. 긴 문자열을 개별 원재료로 쪼개는 것.
그런데 이 텍스트는 하나하나 분리된 목록이 아니었습니다. 한 덩어리의 긴 문자열이었어요.
이런 식입니다.
정제수, 설탕, 탈지분유(미국산), 코코아버터(가나산), 유크림, 전지분유, 카카오매스(가나산), 식물성유지(팜핵경화유), 유화제, 합성향료(바닐린)
이걸 사람이 읽으면, “아, 정제수가 들어가고, 설탕이 들어가고, 탈지분유가 들어가는구나” 라고 이해할 수 있습니다.
그런데 컴퓨터는 이걸 그냥 글자들의 나열로 봅니다. “정”, “제”, “수”, ”,”, ” ”, “설”, “탕”… 이런 식으로요.
이 긴 문자열을 의미 있는 단위로 쪼개는 작업이 필요했습니다. 이걸 ‘파싱’이라고 부릅니다.
파싱. 이 단어를 여러 번 들었는데, 처음에는 잘 와닿지 않았어요.
비유를 하나 들어볼게요.
누군가가 이렇게 말했다고 합시다. “나 오늘 사과랑 바나나랑 딸기 샀어.”
이 문장을 듣고 우리는 자연스럽게 이해합니다. 산 과일이 세 가지라는 것. 사과, 바나나, 딸기.
하지만 이 문장을 처리하는 프로그램을 만들려면, “랑”이 구분자라는 것을 알아야 하고, “샀어”는 과일 이름이 아니라 동사라는 것을 알아야 하고, “나”와 “오늘”은 과일 목록에 포함되지 않는다는 것을 알아야 합니다.
성분표 파싱도 비슷합니다. 사람에게는 당연한 것이, 컴퓨터에게는 전혀 당연하지 않은 거예요.
가장 단순한 경우를 먼저 생각해봤습니다.
정제수, 설탕, 정제소금
이건 쉽습니다. 쉼표(,)로 나누면 끝이에요. 정제수. 설탕. 정제소금.
그런데 실제 성분표는 이렇게 깔끔하지 않았습니다.
첫 번째 난관은 구분자였습니다.
성분표에서 원재료를 구분하는 기호가 하나가 아니었어요. 어떤 제품은 쉼표(,)로 구분하고, 어떤 제품은 가운뎃점(·)으로 구분하고, 어떤 제품은 슬래시(/)로 구분합니다. 심지어 줄바꿈으로 구분하는 경우도 있었어요.
같은 제품 안에서도 구분자가 섞여 있는 경우가 있었습니다.
그래서 “쉼표로 나누면 된다”는 단순한 접근은 바로 무너졌고, 여러 종류의 구분자를 동시에 인식할 수 있어야 했습니다.
두 번째 난관은 괄호였습니다.
이건 다음 화에서 더 자세히 다루겠지만, 간단히 말하면 이런 문제입니다.
밀가루(밀:미국산, 호주산)
여기서 쉼표로 나누면 어떻게 될까요?
“밀가루(밀:미국산”과 “호주산)“으로 쪼개집니다. 괄호가 잘려버리는 거예요.
괄호 안에 있는 쉼표는 구분자가 아닙니다. 밀가루의 원산지를 나열하는 쉼표예요. 그걸 구별해야 합니다.
그래서 파싱을 할 때, 지금 괄호 안에 있는지 밖에 있는지를 계속 추적해야 했습니다. 괄호가 열렸으면 “지금 안쪽이니까 쉼표가 나와도 자르지 마”, 괄호가 닫혔으면 “이제 바깥이니까 쉼표가 나오면 잘라도 돼”.
이 판단을 98만 건의 원재료 데이터 전체에 대해 해야 했습니다.
세 번째 난관은 걸러내야 할 것들이었습니다.
원재료 문자열에는 순수한 원재료 이름만 있는 게 아니었어요. 원산지 정보, 함량 정보, 설명 같은 것들이 섞여 있었습니다.
밀가루(밀:미국산, 호주산)
여기서 “미국산”, “호주산”은 원재료가 아니라 원산지 정보입니다. 이걸 원재료 목록에 넣으면 안 돼요.
L-글루탐산나트륨(향미증진제)
여기서 “향미증진제”는 원재료의 역할을 설명하는 말이지, 그 자체가 원재료는 아닙니다.
코코아버터 5.2%
여기서 “5.2%“는 함량 정보예요. 역시 원재료가 아닙니다.
이런 것들을 하나하나 걸러내는 규칙을 만들어야 했습니다. “미국산”, “호주산”, “국산” 같은 산지 표현을 모아서 걸러내고, “향미증진제”, “유화제”, “산도조절제” 같은 기능 설명을 걸러내고, 숫자와 단위가 결합된 패턴을 걸러내고.
처음에는 규칙이 몇 개면 될 줄 알았습니다. 그런데 만들다 보니 점점 늘어났어요. 예외가 끝없이 나타났거든요.
이렇게 해서 파싱 과정을 정리하면 이렇습니다.
1단계: 긴 문자열을 구분자로 쪼갠다. 단, 괄호 안의 구분자는 무시한다. 2단계: 쪼개진 각 조각에서 괄호 안의 부가 정보를 분리한다. 3단계: 원산지, 함량, 기능명 등 원재료가 아닌 것들을 걸러낸다. 4단계: 남은 것이 개별 원재료 이름이 된다.
글로 쓰면 네 줄이지만, 이 네 줄을 제대로 동작하게 만드는 데 꽤 많은 시행착오가 있었습니다.
특히 괄호 처리가 생각보다 복잡했어요. 괄호 안에 또 괄호가 있는 경우가 있었거든요.
그 이야기는 바로 다음 화에서 하겠습니다.