Post

Elastic Search 에서 한글 초성 검색 지원: 초성, 중성, 종성 분리로 검색 개선하기

나의 욕심으로 한글 초성 검색 기능을 넣었다.


사실 노래방처럼 빠르게 검색해야하는 상황이 아니라면 초성으로 검색하는 일은 별로 없겠지만! 오로지 나의 욕심으로 한글 초성 검색 기능을 넣어 보았다. 이 기회로 어떻게 한글이 인코딩 되어있는지도 공부 할 겸 말이다!

초성 중성 종성으로 이루어진 한글

한글은 초성 중성 종성으로 이루어져있다. 각각 1개씩이 모여서 총 3개가 합쳐지만 한글자가 만들어진다. 초성은 19개, 중성은 21개, 종성은 27개 (없는경우까지) 로 구성이 되어있고 한글글자는 Unicode에서 AC00 부터 시작한다. (다른 나라 언어도 있으니까!)

그리고 초성, 중성, 종성을 3차원 큐브 형태로 글자를 만든다. 그냥 글자를 막 넣은것이 아니었다!!!! 그림이 없으니까 설명이 어려운데 그러니까

1
2
3
4
5
6
7
8
9
10
11
12
char[] cho = {
	'ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ',
	'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'
};
char[] jung = {
		'ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ', 'ㅗ', 'ㅘ',
		'ㅙ', 'ㅚ', 'ㅛ', 'ㅜ', 'ㅝ', 'ㅞ', 'ㅟ', 'ㅠ', 'ㅡ', 'ㅢ', 'ㅣ'
};
	
char[] jong = {
	'', 'ㄱ', 'ㄲ', 'ㄳ', 'ㄴ', 'ㄵ', 'ㄶ', 'ㄷ', 'ㄹ', 'ㄺ', 'ㄻ', 'ㄼ', 'ㄽ', 'ㄾ', 'ㄿ', 'ㅀ', 'ㅁ', 'ㅂ', 'ㅄ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'
};

이렇게 배열이 있으면 한글 글자는 AC00 + 원하는 초성 ind * 21 *28 + 원하는 중성 ind * 28 + 원하는 종성 이렇게 완성이 된다는 것이다! 너무.. 신기했다!! 따라서 초성 중성 종성을 이렇게 나눌수 있게 된다

초성 중성 종성 분리하기

그럼 이제 글자가 들어왔을때 그것을 초성, 중성, 종성으로 분리를 하면 된다. 만드는 식은 위에 있으니 그것을 기반으로 쌱쌱 나누고 나머지를 가져와준다.

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
31
32
33
34
35
36
37
public void extractInitialConsonants(String input) {
	StringBuilder resultCho = new StringBuilder();
	StringBuilder resultJung = new StringBuilder(); 
	StringBuidler resultJong = new StringBuilder();
	
	// 한글 유니코드 범위: 가 (U+AC00) ~ 힣 (U+D7A3)
	// 초성: 19개 (ㄱ ~ ㅎ)
	char[] cho = {
		'ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ',
		'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'
	};
	char[] jung = {
            'ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ', 'ㅗ', 'ㅘ',
            'ㅙ', 'ㅚ', 'ㅛ', 'ㅜ', 'ㅝ', 'ㅞ', 'ㅟ', 'ㅠ', 'ㅡ', 'ㅢ', 'ㅣ'
    };
        
    char[] jong = {
        '', 'ㄱ', 'ㄲ', 'ㄳ', 'ㄴ', 'ㄵ', 'ㄶ', 'ㄷ', 'ㄹ', 'ㄺ', 'ㄻ', 'ㄼ', 'ㄽ', 'ㄾ', 'ㄿ', 'ㅀ', 'ㅁ', 'ㅂ', 'ㅄ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ'
    };
	
	for (char ch : input.toCharArray()) {
		if (ch >= 0xAC00 && ch <= 0xD7A3) {
			int unicode = ch - 0xAC00; // 한글의시작지점 뺀다
			int choIndex = unicode / (21 * 28); // 중성, 종성 개수 만큼 나누다
			int jungIndex = (unicode % (21 * 28)) / 28;
			int jongIndex = unicode % 28;
			resultCho.append(cho[choIndex]);
			resultJung.append(jung[jungIndex]);
			resultJong.append(jong[jongIndex]);
		} else {
			// Not Korean  
		}
	}
	System.out.println(choIndex.toString);
	System.out.println(jungIndex.toString);
	System.out.println(jongIndex.toString);
}

이렇게 하면 초성, 중성 종성을 분리할수 있게 된다! 따라서 초성 검색을 위해서 위의 코드에서 초성 부분만 가져와서 ElasticSearch 에 indexing 했더니 아주 멋지게 초성검색이 되었다!

This post is licensed under CC BY 4.0 by the author.

© 병욱. Some rights reserved.