open: keyword 단독 0건 트러블 + MeCab vs pg_trgm 근본해결 리서치

- 트러블: simple 토크나이저 한국어 구조적 한계 확정
- 리서치: MeCab-ko(근본 해결) vs pg_trgm(물리적 보완) 비교
- 근본 해결 1순위: MeCab-ko, 현실적 1단계: pg_trgm (보완 명시 필수)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
happybell80 2026-03-22 10:18:01 +09:00
parent 74503ab24b
commit 27ca166bcf
2 changed files with 148 additions and 0 deletions

View File

@ -0,0 +1,86 @@
---
type: research
tags: [research, rag, korean, mecab, pg_trgm, fts, postgresql]
status: open
research_target: 한국어 키워드 검색의 근본 해결책 비교 — MeCab-ko 형태소 분석 vs pg_trgm 물리적 보완
---
# 260323 한국어 키워드 검색 근본 해결 — MeCab vs pg_trgm 리서치
## 목적
- keyword 단독 검색 0건의 근본 원인(simple 토크나이저 한국어 한계)에 대한 해결책을 비교한다.
- "근본 해결"과 "물리적 보완"을 명확히 구분한다.
## 근본 원인 (확정)
PostgreSQL `simple` 토크나이저는 공백 기준 토큰화만 수행. 한국어 교착어 특성(조사 결합, 복합명사)으로 lexeme이 검색어와 구조적으로 불일치.
이것은 검색 로직 버그가 아니라 **분석기 자체의 언어적 한계**다.
## 해결책 비교
### MeCab-ko 형태소 분석기 (근본 해결)
**원리**: 한국어를 의미 단위로 해체. 조사/어미를 제거하고 어간만 lexeme으로 저장.
| 항목 | 내용 |
|------|------|
| 방식 | `textsearch_ko` 확장 + MeCab-ko 사전 |
| Recall | 100% (조사/복합어 완벽 분리) |
| Precision | 높음 (의미 단위 매칭) |
| 설치 비용 | 높음 (23서버 OS에 MeCab + 한국어 사전 설치, PG 확장 빌드) |
| 유지 비용 | 사전 업데이트 필요 (신조어, 고유명사) |
| PG14 호환 | 가능하나 EOL(2026-11) 고려 |
| 성능 | tsvector 기반이므로 GIN 인덱스 활용, FTS와 동일 속도 |
**적용 시 변경**:
- tsvector 트리거: `to_tsvector('simple', ...)``to_tsvector('korean', ...)`
- tsquery: `to_tsquery('simple', ...)``to_tsquery('korean', ...)`
- 기존 tsvector 전수 재생성 필요
### pg_trgm (물리적 보완)
**원리**: 텍스트를 3글자 단위로 기계적으로 쪼개 인덱싱. 언어 구조를 모름.
| 항목 | 내용 |
|------|------|
| 방식 | `CREATE EXTENSION pg_trgm` + `GIN(gin_trgm_ops)` |
| Recall | 높음 (글자 조각 매칭, 0건 방지) |
| Precision | 낮음 (짧은 검색어에서 노이즈. "차" 검색 시 "자동차", "차차차" 모두 히트) |
| 설치 비용 | 낮음 (`CREATE EXTENSION` 1줄) |
| 유지 비용 | 인덱스 크기 증가, INSERT 시 인덱스 업데이트 부하 |
| PG14 호환 | 기본 내장 |
| 성능 | similarity 계산 시 FTS보다 느림. ILIKE 인덱싱은 빠름 |
**적용 시 변경**:
- `chunk_text` 컬럼에 `GIN(gin_trgm_ops)` 인덱스 추가
- 검색 쿼리에 `similarity(chunk_text, :query)` 또는 `chunk_text ILIKE '%keyword%'` 추가
- 기존 tsvector 경로는 그대로 유지, 병렬 축으로 추가
## 판단
| 기준 | MeCab-ko | pg_trgm |
|------|----------|---------|
| 근본 해결 여부 | **근본 해결** | 물리적 보완 |
| 0건 방지 | ✅ | ✅ |
| 정확도 | 높음 | 낮음 |
| 설치 난이도 | 높음 | 낮음 |
| 장기 유지 | 사전 관리 필요 | 인덱스 크기 관리 |
**최종 목표**: MeCab-ko + pg_trgm 하이브리드
**현실적 1단계**: pg_trgm 먼저 적용 (0건 즉시 방지) → MeCab-ko 후속 도입
단, pg_trgm 적용 시 "이것은 근본 해결이 아니라 물리적 보완이다"를 문서와 코드에 명시한다.
## Unresolved
- MeCab-ko의 PG14 EOL(2026-11) 이후 PG16 마이그레이션과 동시 도입이 합리적인지
- pg_trgm 인덱스 크기가 3,474 청크 → 5만 청크 확장 시 어느 수준인지
- MeCab 사전에 Company X 고유명사(투자조합명, 기업명) 추가 가능 여부
## 관련 문서
- [260323 keyword 단독 검색 0건 트러블](../../troubleshooting/260323_companyx_rag_keyword_단독검색_전질의_0건.md)
- [260323 PostgreSQL simple FTS 한국어 한계 요약](./260323_PostgreSQL_simple_FTS_한국어_키워드검색_한계_및_대안_요약.md)
- [PostgreSQL pg_trgm 공식 문서](https://www.postgresql.org/docs/current/pgtrgm.html)

View File

@ -0,0 +1,62 @@
---
type: troubleshooting
tags: [companyx, rag, keyword, tsvector, simple, korean, skill-rag-file]
status: open
opened_date: 2026-03-23
severity: high
root_cause: PostgreSQL simple 토크나이저가 한국어 교착어를 처리 못 함. 공백 기준 토큰화로 조사 결합·복합명사가 하나의 lexeme이 되어 검색어와 불일치. prefix(:*)로 부분 완화했으나 전 질의 커버 불가.
---
# 260323 Company X RAG keyword 단독 검색 전 질의 0건
## 현상
- `search_mode: keyword` 단독 경로에서 대부분의 질문이 0건 반환
- hybrid 모드에서는 벡터 검색이 보완하여 17/17 통과하지만, keyword 축이 죽어 있음
- prefix(`:*`) + threshold 0.001 적용 후에도 특정 고유명사만 히트, 일반 질문은 0건
## 근본 원인
PostgreSQL `simple` 토크나이저는 공백 기준으로만 토큰을 분리한다. 한국어는 교착어로 어간에 조사/어미가 항상 붙기 때문에:
- `투자를` → lexeme `'투자를'` (검색어 `'투자'`와 불일치)
- `개인투자조합을` → lexeme `'개인투자조합을'` (검색어 `'개인투자조합'`와 불일치)
- `아크로셀의` → lexeme `'아크로셀의'` (검색어 `'아크로셀'`와 불일치)
prefix(`:*`)는 lexeme **접두**만 매칭하므로 `'투자를'`에서 `'투자:*'`는 히트하지만, `'개인투자조합을'`에서 `'투자:*'`는 히트하지 않는다 (투자가 중간에 있음).
이것은 검색 로직의 버그가 아니라 **토크나이저 자체의 한국어 구조적 한계**다.
## 근본 해결책
### 1순위: MeCab-ko 형태소 분석기 도입
- 한국어를 의미 단위로 해체하여 조사를 완벽히 제거
- `아크로셀의``[아크로셀]`, `투자를``[투자]`
- PostgreSQL용 `textsearch_ko` 확장 설치 후 tsvector 생성 시 `korean` 분석기 지정
- 23서버(PostgreSQL 14)에 확장 설치 필요. PG14 EOL(2026-11) 고려 사항 있음
- **정확도(Precision)와 재현율(Recall) 모두 근본 해결**
### 2순위: pg_trgm + GIN 인덱스
- 텍스트를 3글자 단위로 쪼개 인덱싱 (형태소 분석 없이 부분 문자열 매칭)
- `CREATE EXTENSION pg_trgm` + `GIN(gin_trgm_ops)` 인덱스
- 조사/복합어 무관하게 글자 조각으로 매칭 → 0건 방지
- Recall은 해결되지만 Precision이 하락할 수 있음 (짧은 검색어에서 노이즈)
- MeCab 대비 설치 비용 낮음
- **이것은 근본 해결이 아니라 물리적 보완이다**
### 하이브리드 조합 (최종 목표)
```
Total_Score = ts_rank(MeCab) × 0.8 + similarity(pg_trgm) × 0.2
```
- MeCab으로 정확한 의미 매칭 (고득점)
- pg_trgm으로 복합어/특수명사 보완 (저득점 보완)
- 현재 RRF 합산 구조에 자연스럽게 편입 가능
## 관련 문서
- [260321 하이브리드 검색 keyword recall=0 리서치 (닫힘)](../research/rag/260321_하이브리드검색_keyword_recall0_및_grounding_실패_원인확정_리서치.md)
- [260323 PostgreSQL simple FTS 한국어 한계 리서치](../research/rag/260323_PostgreSQL_simple_FTS_한국어_키워드검색_한계_및_대안_요약.md)