본문 바로가기
컴퓨터

[웹] DB 한글 초성 검색(sqlite3)

by skyjwoo 2023. 4. 21.
728x90
반응형

 

배경

 

지난 KBO 선수 퀴즈 사이트를 만들 때, 중요한 기능 중 하나가 선수 검색어 추천 기능을 구현하는 것이었다.

영어와 달리 한국어는 모아쓰기 때문에 '이정후'를 검색하면, 'ㅇㅣㅈㅓㅇㅎㅜ'의 자모를 키보드로 입력하게 된다. 

이때, 'ㅇㅣ'만 입력해도 '이'로 시작하는 이름들을 추천해주는 것이 목적이다.

 

즉, 목표는 다음과 같다.

 

DB로부터 입력된 한글 자모로 시작하는 입력값 추천받기

 

이 기능을 구현하기 위해 원래는 정규식을 사용하려 했으나, 현재 사용하고 있는 sqlite3에서는 지원하지 않기에 다른 방법을 고안했다. 

 

현재 DB가 새로운 데이터 추가가 이뤄지는 게 아니기에, 기존 데이터에 한글 자모 정보 열을 추가하기로 했다.

 

 

방법

 

즉, 기존 이름 열 정보를 참조하여 자모로 분리한 후, 자모열에 해당 정보를 기재한다. 그 결과는 다음 표와 같다.

 

이름 자모
이정후 ㅇㅣ_ㅈㅓㅇㅎㅜ_
강백호 ㄱㅏㅇㅂㅐㄱㅎㅗ_
박해민 ㅂㅏㄱㅎㅐ_ㅁㅣㄴ

 

한국어 음절 자모 분리는 검색하면 쉽게 찾아볼 수 있다. 

사용한 코드는 아래와 같다.

 

def split_syllable(syllable):
    #19개 초성
    onsets = ['ㄱ', 'ㄲ', 'ㄴ', 'ㄷ', 'ㄸ', 'ㄹ', 'ㅁ', 'ㅂ', 'ㅃ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅉ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ']

    #21개 중성
    nuclears = ['ㅏ', 'ㅐ', 'ㅑ', 'ㅒ', 'ㅓ', 'ㅔ', 'ㅕ', 'ㅖ', 'ㅗ', 'ㅘ', 'ㅙ', 'ㅚ', 'ㅛ', 'ㅜ', 'ㅝ', 'ㅞ', 'ㅟ', 'ㅠ', 'ㅡ', 'ㅢ', 'ㅣ']

    #28개 종성
    codas = ['_', 'ㄱ', 'ㄲ', 'ㄳ', 'ㄴ', 'ㄵ', 'ㄶ', 'ㄷ', 'ㄹ', 'ㄺ', 'ㄻ', 'ㄼ', 'ㄽ', 'ㄾ', 'ㄿ', 'ㅀ', 'ㅁ', 'ㅂ', 'ㅄ', 'ㅅ', 'ㅆ', 'ㅇ', 'ㅈ', 'ㅊ', 'ㅋ', 'ㅌ', 'ㅍ', 'ㅎ']
    jamos = []
    # 한글 자모인 경우
    if '가'<=syllable<='힣':
        syllable_number = ord(syllable) - ord('가') #해당 음절의 유니코드 번호
        # 588개 마다 초성이 바뀜. 중성(21개) * 종성(28개) = 588
        cho = (syllable_number)//588
        
        # 28개 마다 중성이 바뀐다. (종성의 수=28), 중성 x 종성의 콤비네이션만 얻기 위해 초성 값을 제외해준다. 
        jung = ((syllable_number) - (588*cho)) // 28 # syllable_number % (21*28) // 28, 21*28 = 588

        # 종성의 콤비네이션만 얻기 위해 초성, 중성 값을 제외해준다.
        jong = (syllable_number) - (588*cho) - 28*jung # syllable_number % 28
        jamos = ([onsets[cho], nuclears[jung], codas[jong]])
    else: #그 외 한글 자모, 알파벳 혹은 숫자 혹은 다른 글자인 경우
        jamos = ([syllable])
    return jamos

 

특징적인 점은 한국어 초/중/종성 정보가 모두 들어갈 수 있도록 종성이 없는 경우 '_' 표시를 넣었다.

이건 목적에 따라 다르긴 한데, 가령 '임창정, 이민정' 두 선수를 검색할 때, 모두 'ㅇㅣㅁ'  자모를 입력하게 된다. 

이때, '이'를 쓰고 'ㅁ'으로 넘어간 경우, '임창정'은 'ㅇㅣㅁ~' 이고, '이민정'은 'ㅇㅣ_ㅁ'인데, '이ㅁ'까지 입력했을 시 'ㅇㅣ_ㅁ'으로 변환되어 '이민정'만 검색된다. 

 

(물론 사람들이 검색할 때 오타가 있을 수 있어 최대한 많이 검색되게 하는 게 좋을 수도 있을 듯하다.. )

 

백엔드에서 구현한 추천 검색어 리스트 뽑는 API는 아래와 같다.

 

# 추천 검색어 리스트 얻기
@app.route('/search', methods = ['GET'])
def search():

    # 검색창 입력값 받기
    word = request.args.get('keyword')

    # 자모 분리하기
    split_word = name_split(word)

    if word != '':
        # DB 불러오기
        conn = sqlite3.connect('test_db.sqlite')
        df.to_sql('players', conn, if_exists='replace', index=False)

        # SQL 식 완성하기
        SQL = rf"""
        SELECT ID, 이름, 팀, 포지션 FROM players
        WHERE 이름분할 LIKE '{split_word}%'
        """

        # DB에 SQL 적용하기
        df_temp = pd.read_sql(SQL,conn)
        
        # 검색된 값 리스트로 가져오고 텍스트로 합하기
        temp_ls = list(zip(df_temp['이름'].to_list(), df_temp['팀'].to_list(), df_temp['포지션'].to_list()))
        temp_ls = list(map(lambda x: x[0] +' '+ x[1] +' '+ x[2], temp_ls))
        
    else:
        temp_ls = ['']
        
    return '\n'.join(temp_ls)

 

위에도 나와 있지만, SQL 코드는 아래처럼..

'ㅇㅣㅁ'을 다른 자모로 대체하면 된다. '이름분할'은 '자모로 분리된 열'을 말한다.

즉, 핵심은 WHERE ~ LIKE ~ 구문이다.

SELECT ID, 이름, 팀, 포지션 FROM players
WHERE 이름분할 LIKE 'ㅇㅣㅁ'

 

728x90
반응형

댓글