티스토리 뷰

 

검색이든 질의처리든, 한국어를 다루는 일은 결국 "문장을 어떻게 쪼개느냐"에서 시작한다.

 

질의처리 글에서 검색어·필터·제거를 나눈다고 했고, BM25 글에서는 질의와 문서를 같은 단어로 맞춰야 점수가 붙는다고 했다. 두 글 모두 바닥에 같은 도구를 깔고 있었다.

 

형태소 분석기 Kiwi(github.com/bab2min/Kiwi, 파이썬 래퍼는 kiwipiepy)다. 이번 글은 그 바탕을 따로 짚는다.

1. 한국어는 띄어쓰기로 안 갈린다

영어는 띄어쓰기가 곧 단어 경계다. "marketing report"는 공백으로 자르면 끝이다. 그런데 한국어는 단어에 조사·어미가 찰싹 붙어 한 덩어리로 다닌다. "마케팅 보고서를 찾아줘"를 그냥 공백으로 자르면 이렇게 된다.

"마케팅 보고서를 찾아줘"
   띄어쓰기로만 자르면 →  [마케팅]  [보고서를]  [찾아줘]

문제는 "보고서를"이다. 이건 "보고서" + "를"인데 통으로 남았다.

 

그런데 문서 쪽은 보통 "보고서"로 색인돼 있다. 그러면 검색할 때 "보고서를" ≠ "보고서"라 매칭이 0이 된다.

 

"마케팅"도 질의에선 "마케팅을", 문서에선 "마케팅"으로 갈리면 같은 단어인데 서로 못 알아본다. 조사 하나 때문에 같은 단어가 다른 토큰으로 보이는 것, 이게 한국어 검색의 1번 함정이다.

 

그래서 한국어는 단어를 더 작은 단위로 쪼개서 "보고서"·"마케팅"이라는 같은 형태로 맞춰줘야 한다. 그 쪼개는 일이 형태소 분석이다.

2. 형태소 분석이 하는 일 — 그리고 왜 어려운가

형태소는 "의미를 가진 최소 단위"다. Kiwi에 문장을 넣으면 형태소로 쪼개고 각 조각에 품사를 붙여준다.

"김민수가 작성한 작년 마케팅 보고서 pdf"
   ↓ Kiwi 형태소 분석
김민수[고유명사]  가[조사]  작성[명사]  하[동사파생]  ㄴ[어미]
작년[명사]  마케팅[명사]  보고서[명사]  pdf[외국어]

핵심은 품사가 붙는다는 점이다. 어떤 조각이 명사이고 어떤 게 조사·어미인지 알면, "검색어로 쓸 것"과 "버릴 것"을 규칙으로 가를 수 있다. 모델의 직감이 아니라 품사라는 명확한 근거로.

 

그런데 이게 단순한 사전 찾기가 아니다. 한국어는 중의성이 심하다. 같은 글자가 문맥에 따라 전혀 다르게 쪼개진다.

"나는 학교에 간다"   →  나[대명사]  는[조사]
"하늘을 나는 새"      →  날[동사]    는[어미]

"나는"이라는 똑같은 글자가, 한쪽은 대명사+조사이고 다른 쪽은 동사다.

 

그래서 형태소 분석기는 앞뒤 문맥을 보고 품사를 정하는 일종의 시퀀스 라벨러에 가깝다. 단어만 보고 기계적으로 자르는 게 아니라, 문장 전체를 보고 가장 그럴듯한 분해를 고른다. Kiwi는 이걸 통계 모델과 사전으로 꽤 정확하게 풀어준다.

 

Kiwi가 붙이는 품사 태그는 세종 태그셋을 따른다. 종류가 많지만, 검색에서 자주 만나는 것만 추리면 이 정도다.

품사 태그   설명          예시
NNG        일반명사       보고서, 마케팅
NNP        고유명사       김민수, 서울
NP         대명사         나, 우리
NNB        의존명사       것, 수, 개
VV         동사           찾다, 만들다
VA         형용사         빠르다, 작다
MM         관형사         새, 모든
JKS·JKB    격 조사        이/가, 에서, 에게
JX         보조사         은/는, 도, 만
ETM·EF     어미           -ㄴ/-는, -다/-요
SL         외국어         pdf, AI
SN         숫자           5, 2025

검색어로 쓰고 싶은 건 보통 명사 쪽(NNG·NNP)과 외국어(SL)다. 조사(J로 시작)·어미(E로 시작)는 버릴 것이고, 동사·형용사·관형사는 검색어로 두기 애매한 회색지대다.

3. 명사만 골라낸다 — 검색어 추출

검색어가 될 만한 건 대부분 명사다. 그래서 형태소 중에서 일반명사·고유명사·외국어만 남기고 나머지는 버린다. 코드로 옮기면 의외로 단순하다. 형태소로 쪼갠 뒤, 원하는 품사 태그만 거르면 된다.

from kiwipiepy import Kiwi
kiwi = Kiwi()

tokens = kiwi.tokenize("김민수가 작성한 작년 마케팅 보고서 pdf")
# 각 토큰은 .form(표면형)과 .tag(품사)를 가진다

WANT = {"NNG", "NNP", "SL"}          # 일반명사·고유명사·외국어
keywords = [t.form for t in tokens if t.tag in WANT]

이 한 줄짜리 필터가 형태소 분석으로 검색어를 뽑는 기본 골격이다. 조사·어미는 태그가 J·E로 시작하니 자연히 빠지고, 명사만 남는다.

 

다만 이대로면 빈틈이 있다. 예컨대 "작성한"의 "작성"도 일반명사(NNG)로 잡혀 딸려온다. 그래서 실제로는 보정을 몇 겹 더 얹었다. 동사로 파생되는 명사(작성→작성하다처럼 뒤에 동사화 접미사가 붙는 경우)는 빼고, 검색에 의미 없는 흔한 말(불용어)은 거르고, "5G"처럼 숫자와 단위가 붙은 표현은 쪼개지지 않게 묶는다.

 

파인튜닝 모델이 "5G"를 "G"로 흘리던 사고도, 여기서는 품사 조합 규칙으로 직접 다룰 수 있었다. "필요한 품사만 고르고, 예외를 규칙으로 메운다" — 이게 형태소 기반 추출의 전부다. 물론 트레이드오프는 있다. 명사만 남기면 "빠른", "최신" 같은 수식어나 동사가 담은 정보는 날아간다. "급하게 찾는 보고서"에서 "급하게"는 버려진다.

 

그래도 키워드 검색에서는 명사가 의미의 대부분을 지고 있어서, 잃는 것보다 또렷한 검색어를 얻는 쪽이 이득이었다. 무엇을 남기고 버릴지가 분명하니, 결과가 이상할 때 "왜 이 단어가 빠졌나"를 품사로 바로 설명할 수 있는 것도 컸다.

4. 조사를 떼어낸다

이름이나 지명에 붙은 조사도 형태소 분석으로 깔끔히 떼어낸다. "서울에서"는 "서울"로, "행정안전부가"는 "행정안전부"로 정리된다.

이게 중요한 이유는, 같은 지명·기관명이 조사 때문에 매번 다른 토큰으로 보이면 필터가 어긋나기 때문이다. "서울에서"·"서울의"·"서울"이 다 같은 "서울"로 모여야 지역 필터가 제대로 걸린다. 작성자도 마찬가지다. "김민수가"·"김민수는"·"김민수"가 따로 놀면 같은 사람을 못 묶는다.

 

여기서 한 가지 신경 쓴 건, 조사만 떼되 원본의 띄어쓰기는 건드리지 않는 것이었다. 무작정 형태소를 이어 붙이면 "미국 라스베이거스에"가 "미국라스베이거스"처럼 뭉쳐버린다. 그래서 조사로 판정된 조각만 들어내고 나머지 공백은 그대로 둬서, "미국 라스베이거스"로 자연스럽게 남게 했다. 사소해 보여도 이런 데서 어긋나면 지명·기관명이 통째로 깨진다.

5. 형태소 분석도 만능은 아니다

규칙 기반의 바탕이라고 해서 완벽하진 않았다. 형태소 분석에도 분명한 한계가 있었다.

 

첫째는 신조어·고유어다. 사전에 없는 새 단어나 도메인 용어는 엉뚱하게 쪼개진다. 앞서 "5G"가 깨지던 것처럼, 분석기가 모르는 말은 익숙한 조각으로 잘못 분해해버린다. 이건 결국 사용자사전으로 보강해줘야 했다.

 

둘째는 중의성 오분석이다. 문맥으로 품사를 고르다 보니 가끔 틀리게 잡는다. 위의 "나는" 같은 경우가 짧은 질의에서 종종 헷갈렸다.

 

정리하면, 형태소 분석은 모델처럼 "알아서 일반화"해주지는 않는다. 모르는 말이 나오면 사람이 사전을 채워줘야 한다. 대신 동작이 투명하고, 틀렸을 때 어디서 틀렸는지 바로 짚어 고칠 수 있다. 검색 본경로처럼 빠르고 예측 가능해야 하는 자리에선 이 통제 가능함이 일반화 능력보다 더 값졌다.

6. 왜 Kiwi였나

한국어 형태소 분석기는 여럿 있다. KoNLPy로 묶이는 Mecab·Okt 같은 것들도 많이 쓰인다.

 

그중 Kiwi를 고른 이유는 몇 가지였다. 먼저 설치가 깔끔하다. Mecab은 성능은 좋지만 사전 설치가 환경마다 골치 아픈 걸로 악명 높은데(특히 윈도우 ㅠㅠ), Kiwi는 그런 설치 지옥이 없었다. 그리고 빠르다.

 

C++로 구현돼 있어 가볍고, 온디바이스처럼 자원이 빠듯한 환경에서도 부담이 적었다. 사용자사전으로 도메인 복합명사를 추가하기 쉬운 것도, 신조어 한계를 메우는 데 결정적이었다. 무엇보다 LLM 호출 없이 매 질의를 즉석에서 분석할 수 있어서, "빠르고 예측 가능해야 하는" 검색 본경로에 두기 알맞았다.

7. Kiwi로 더 할 수 있는 것

이번엔 형태소 분석과 명사 추출에만 썼지만, Kiwi의 쓰임은 거기서 끝이 아니다. 한국어 전처리에 두루 쓸 만한 기능이 더 있다.

문장 분리         긴 글을 문장 단위로 자른다 — 색인 청크 나눌 때 유용
사용자 사전       도메인 복합명사·신조어 등록 → 오분리 방지 (앞서 5G·복합명사 보정이 이것)
띄어쓰기 교정     띄어쓰기가 엉망인 입력도 정리해준다
오타에 강한 분석   약간의 오타가 섞여도 형태소를 복원하는 모드가 있다
원형 복원         "만든"의 원형 "만들다"처럼 활용형을 기본형으로 되돌린다

그래서 Kiwi는 질의 분석뿐 아니라 색인 단계의 토큰화, 문서 텍스트 정제, 문장·청크 분할까지 한국어 파이프라인 곳곳에 한 번 깔아두면 두루 재활용된다. 검색 한 군데 쓰자고 들였는데, 결과적으로 전처리 전반의 공용 도구가 됐다.

마치며

형태소 분석은 화려한 기술이 아니다. 논문에 나올 최신 기법도 아니고, 데모에서 박수받을 만한 것도 아니다.

 

그런데 한국어 검색에서는 이게 바닥이다. 검색어 추출도, 작성자 인식도, 슬롯 분해도 결국 "문장을 형태소로 제대로 쪼갰는가" 위에 선다. 바탕이 흔들리면 그 위에 아무리 좋은 검색기·리랭커를 얹어도 흔들린다.

 

새 모델을 얹기 전에 이 바탕부터 단단히 다지는 게, 돌아보면 가장 효율 좋은 투자였다. 이 위에서 질의를 슬롯과 키워드로 나눈 이야기는 [규칙 기반 질의처리기 글]에서 이어진다.

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2026/06   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함