← 운영 블로그

9화: 괄호 안의 괄호 안의 괄호

회차: 9화

8화에서 성분표 텍스트를 쪼개는 이야기를 했습니다. 구분자가 여러 종류이고, 괄호 안의 쉼표는 자르면 안 되고, 원산지나 함량 같은 부가 정보는 걸러내야 한다고요.

그중에서 가장 까다로웠던 것이 괄호였습니다.


1화에서 보여드린 예시를 다시 가져올게요.

쇠고기맛베이스시즈닝(정제소금, 쇠고기엑기스(쇠고기:호주산), 설탕, 마늘분, 양파분)

이 문장의 구조를 풀어보면 이렇습니다.

‘쇠고기맛베이스시즈닝’이라는 복합 원재료가 있고, 그 안에 정제소금, 쇠고기엑기스, 설탕, 마늘분, 양파분이 들어가 있습니다. 그런데 쇠고기엑기스 안에는 또 ‘쇠고기’가 들어가 있고, 그 쇠고기의 원산지는 호주산입니다.

괄호가 세 겹입니다.

사람이 읽으면 “아, 시즈닝 안에 엑기스가 있고, 엑기스 안에 쇠고기가 있구나” 하고 계층 구조를 이해할 수 있습니다.

컴퓨터는 이걸 어떻게 이해하죠?


핵심은 ‘깊이’를 세는 것이었습니다.

괄호가 열리면 깊이가 1 늘어나고, 괄호가 닫히면 깊이가 1 줄어듭니다.

쇠고기맛베이스시즈닝 ( 정제소금, 쇠고기엑기스 ( 쇠고기:호주산 ) , 설탕 )

처음에 깊이는 0입니다. 첫 번째 괄호가 열리면 깊이 1. 두 번째 괄호가 열리면 깊이 2. 두 번째 괄호가 닫히면 다시 깊이 1. 첫 번째 괄호가 닫히면 깊이 0.

깊이가 0일 때의 쉼표만 구분자로 인식하면 됩니다. 깊이가 1 이상이면 “지금 어떤 원재료의 내부 설명이니까 자르지 마”라는 뜻이에요.

원리 자체는 단순합니다. 그런데 실제 데이터에서는 예상 밖의 일들이 벌어졌어요.


어떤 제품은 괄호의 종류가 달랐습니다.

소괄호 (), 대괄호 [], 중괄호 {}, 심지어 【】 같은 것도 있었어요. 전부 같은 역할인데 모양이 다른 겁니다.

괄호가 열리기만 하고 닫히지 않는 경우도 있었습니다. 제조사가 성분표를 입력할 때 실수한 것으로 보이는데, 이런 데이터가 들어오면 깊이 계산이 틀어집니다. 열린 괄호가 닫히지 않으니, 그 뒤의 모든 쉼표를 “내부”로 인식해버려서 원재료가 하나도 분리되지 않는 거예요.

그래서 방어 로직을 넣어야 했습니다. “괄호가 열렸는데 한참을 가도 닫히지 않으면, 그냥 닫힌 것으로 간주하자.”


더 까다로운 경우도 있었습니다.

합성향료(2-Methyl-2-pentenoic acid)

이건 합성향료의 화학명이 괄호 안에 들어가 있는 경우입니다. 괄호 안의 내용이 원산지도 아니고, 하위 원재료도 아니에요. 화학 물질의 이름 자체에 괄호가 포함되어 있는 겁니다.

DL-메티오닌

이건 화학 명명법에서 쓰이는 접두어인데, 이런 이름에 괄호가 포함된 경우가 생각보다 많았습니다.

이런 것들을 일반적인 “괄호 안의 하위 원재료”와 구분해야 했어요.


결국 파싱 전략은 이렇게 됐습니다.

먼저, 원재료 표준 사전에 괄호가 포함된 이름이 있는지를 먼저 확인합니다. 예를 들어 사전에 “합성향료(바닐린)“이라는 이름이 통째로 등록되어 있다면, 그건 쪼개지 않고 하나의 원재료로 인식하는 거예요.

사전에 없는 경우에만 괄호를 열어서 안쪽을 분석합니다. 안쪽에 있는 것이 원산지인지, 하위 원재료인지, 함량 정보인지를 규칙에 따라 판단하고요.

이 ‘사전 먼저, 분해는 나중에’ 전략이 정해지기까지 여러 번의 시행착오가 있었습니다.

처음에는 무조건 괄호를 분해하는 방식으로 했다가, 사전에 있는 이름까지 쪼개버리는 문제가 생겼고, 그 다음에는 괄호를 아예 무시하는 방식으로 했다가, 하위 원재료를 놓치는 문제가 생겼거든요.


98만 건의 원재료 데이터를 전부 파싱하는 데에는 시간도 오래 걸렸지만, 그보다 더 오래 걸린 건 규칙을 다듬는 과정이었습니다.

100건을 돌려보면 새로운 예외가 나오고, 그걸 처리하면 또 다른 예외가 나오고. “이제 됐다” 싶으면 또 못 보던 패턴이 등장하고.

완벽한 파싱은 불가능했습니다. 제조사가 자유롭게 입력한 텍스트를 100% 정확하게 분석하는 것은, 아마 사람이 해도 쉽지 않을 거예요.

그래서 목표를 조금 현실적으로 잡았습니다. “대부분의 경우에 맞게 동작하고, 틀린 것은 나중에 잡아내자.”


파싱이 끝나면, 긴 문자열이 개별 원재료 이름들의 목록으로 바뀝니다.

정제수, 설탕, 탈지분유, 코코아버터, 유크림, 전지분유, 카카오매스, 식물성유지, 유화제, 합성향료

그런데 이것으로 끝이 아니에요.

이 이름들은 아직 ‘자유 텍스트’입니다. 제조사가 입력한 그대로의 이름이에요.

“설탕”이라고 적은 곳도 있고, “정백당”이라고 적은 곳도 있고, “백설탕”이라고 적은 곳도 있습니다. 전부 같은 것을 가리키는데 이름이 다릅니다.

이걸 하나의 표준 이름으로 연결하는 작업이 필요합니다. “설탕 = 정백당 = 백설탕 → 표준명: 설탕” 이렇게요.

다음 화에서 그 이야기를 하겠습니다.