콘텐츠로 이동

사용자 사전 API

개념

바른은 사용자 사전 기능을 제공합니다. 형태소 분석 엔진이 기본적으로 다양한 조건의 문장들을 자체적으로 처리하지만, 특정한 단어의 경우에 처리하는 방향을 외부에서 정해줘야 하는 때도 있습니다. 예를 들어, 바른에서 "네네치킨을 먹습니다."를 분석하면, 나름대로 "네네/NNG", "치킨/NNG"로 적당히 잘 구분해 냅니다.

네네치킨을 만들거나 또는 유통하는 기업에서는 이를 한 단어로 보고 싶을 경우가 있습니다.

이때 "네네치킨"을 하나의 복합명사로 등록하는 방법이 필요합니다.

바른은 이를 사용자 사전이라고 정의하고 관련 API를 제공합니다.

다섯 가지 사전

바른에서 제공하는 사용자 사전은 크게 5가지로 구분됩니다.

고유명사 사전
고유명사 사전은 말 그대로 한 기존에 알려지지 않은 사람, 지역, 이벤트, 상품 등을 나타내는 명사를 말합니다.
복합명사 사전
복합명사의 경우, 바른에는 국립국어원 우리말샘에 등록된 약 100만 건 이상의 사전을 기본적으로 가지고 있습니다. 하지만, 여기에 나타나지 않는 새로운 복합명사가 나타날 수 있습니다. "압수수색"이 그런 경우일 것입니다. 또는 이를 줄여서 쓴 "압색"도 이런 경우라고 볼 수 있습니다. 복합명사는 결국 하나의 일반명사로 표현할 신조어들을 나타낼 수 있습니다.
복합명사 분리 사전
복합명사 분리 사전은 국립국어원의 규정상 하나의 단어처럼 보이지만 띄어쓰기를 해야 하는 단어들의 연속을 나타낼 때 쓰일 수 있습니다. 예를 들어 "형태소분석"이 붙어서 있더라도 한 단어로 굳어진 표현이라고 보기 힘듭니다. "형태소"와 "분석"으로 분리되어서 표현하는 게 더 좋다면, "형태소^분석"과 같이 표현하는 게 좋습니다. "데이터^산업"도 마찬가지일 것입니다.
동사 사전
동사 사전의 경우, 하나 이상의 단어들이 모여서 만들어진 동사이거나 신조어 동사입니다. 예를 들어서, 카톡하다와 같은 동사를 새롭게 등록할 필요가 있을 수도 있습니다. 주로 다른 곳에서 가져온 동사와 동사 파생 접미사가 결합하여 하나의 동사로 표현되는 경우입니다. 대부분 이 경우에는 가져온 2개의 단어가 원래의 뜻과 다르게 바뀐 경우가 많습니다. 흘낏하다의 경우, 흘낏이라는 부사에서 파생된 동사입니다. 흘낏거리다, 흘낏대다와 같이 모두 우리말샘에 하나의 동사로 등록되어 있습니다. 이런 동사들은 모두 바른에서 기본 동사 사전으로 가지고 있습니다. 형태소 분석 학습 데이터의 경우, 이런 경우 어떤 경우에는 "부사, 동사파생접미사"로 태깅한 경우가 꽤 있습니다. 바른에서는 제대로 태깅을 해주기 위해서 동사사전을 가지고 있습니다. 물론 사용자가 새로운 동사를 정의해서 등록할 수도 있습니다.
형용사 사전
형용사 사전도 동사 사전과 같은 맥락에서 사용합니다. 소녀스럽다같은 단어처럼 우리말샘에 등록된 단어는 기본적으로 형용사 사전에 등록되어 있습니다.

도메인

바른은 내부적으로 위에서 언급한 5가지 사용자 사전을 하나의 덩어리로 삼아서 여러 개의 사용자 서전 뭉치를 가질 수 있습니다. 예를 들어 금융업을 하는 회사의 경우, "은행 업무(수신, 여신)", "신용카드 업무", "투자 업무"는 성격이 많이 다릅니다. 이 회사가 바른을 사용할 경우, 사용자 사전을 만들었는데, 위 3개의 사전을 따로따로 등록해서 영구히 쓰고 싶다면, 새로 다른 3개의 제품 설정을 넣어야 할까요?

바른은 하나의 서버에서 사용자 사전을 제한없이 정의할 수 있습니다. 이때 각 사전을 구분하고 영역을 표시하기 위해서 도메인이라는 개념을 사용합니다.

정리하면, 바른은 도메인 별로 새로운 사전을 정의할 수 있습니다.

바른은 서버에 약 100여개의 사용자 사전이 정의되어 있다고 할 때, 아무런 도메인 설정을 주지 않고 호출을 하게 되면, 그냥 기본 결과물이 나옵니다. "은행"이라는 도메인을 지정해주고 호출을 하면, "영업점", "공인인증서"가 일반명사로 등록되어서 나타날 수 있습니다. "법률"이라는 도메인을 지정하게 되면, "민식이법"이 고유명사로 구분해낼 수도 있습니다.

도메인 이름의 제약

도메인 이름은 변수와 비슷한 규칙을 가지고 있습니다. 모두 알파벳(A-Z), 숫자, _, - 만 사용할 수 있습니다.

사전 아이템의 제약

  • 사전 단어에 아래 기호는 추가될 수 없습니다.
  • 사전 항목에는 공백은 포함될 수 없습니다.
  • 사전 항목에 빈 문자열도 추가될 수 없습니다.
  • 기타 기호 목록:
    '\"()[]`{}˝‘’“”′″〈〉《》「「」」『』【】〔〕<<>>≪≫!,.:;·˙‧∙,//:;․…‥!.?.?
    

사용해보기

Authorize를 눌러 ApiKey를 등록후 Try it out을 통해 이용해주세요.

5가지 API

사용자 사전 서비스는 별도의 서비스로 동작하지 않습니다. 바른에 내장되어 있습니다. 따라서 지정된 포트, 5656 포트로 접속하면 바로 연결됩니다. 이 사용자 사전 서비스에는 크게 5가지의 기능이 있습니다.

  • 기존의 모든 등록된 사전 목록 조회
  • 특정 도메인에 등록된 사전 데이터 가져오기
  • 특정 도메인의 사전 데이터 갱신 및 생성
  • 특정 도메인 및 전체 사용자 사전 삭제
  • 사전 충돌 검사

사용자 사전의 생성 및 수정

이 중에서 가장 많이 쓰일 3번째 UpdateCustomDictionary API를 중심으로 설명하겠습니다.

이 API는 UpdateCustomDictionaryRequest 메시지로 요청을 보내고 응답으로 UpdateCustomDictionaryResponse 메시지를 받습니다.

사용자 사전 생성 요청: UpdateCustomDictionaryRequest

이 메시지는 모든 사전 데이터를 가지고 있습니다. 기존에 이미 같은 도메인 이름을 가진 사전이 있다면, 바꿔치기합니다.

message UpdateCustomDictionaryRequest {
  string domain_name = 1;
  CustomDictionary dict = 2;
}

// 커스텀 사전의 데이터 전송 규격
message CustomDictionary {
  string domain_name = 1;
  // 고유명사용 사전
  DictSet np_set = 2;
  // 복합명사용 사전
  DictSet cp_set = 3;
  // 복합명사 분리용 사전
  DictSet cp_caret_set = 4;
  // 동사 사전
  DictSet vv_set = 5;
  // 형용사 사전
  DictSet va_set = 6;
}

message DictSet {
  map<string, int32> items = 1;
  DictType type = 2;
  string name = 10;
}


// 사전의 종류
enum DictType {
  // 기본 배포사전에 사용되는 포맷
  // 전송시에는 쓰이지 않는다.
  TOKEN_INDEX = 0;
  // 고유명사나 복합명사를 표기할 때 사용한다.
  WORD_LIST = 1;
  // # 전송시에만 사용하고 실제로는 쓰이지 추론시에는 쓰이지 않는다.
  WORD_LIST_COMPOUND = 2;
}

사전은 한꺼번에 고유명사, 복합명사, 복합명사 분리 사전, 동사 사전, 형용사 사전을 한꺼번에 업데이트합니다. 개별적으로 넣을 방법은 제공하지 않습니다. 개별 API를 따로 제공하면, 사용할 때 복잡도만 높일 따름입니다.

사전의 도메인 이름과 각 사전별 단어 목록만 넣어주면 됩니다.

DictSet을 만드는 게 제일 중요합니다. DictSet은 모두 5개 있습니다. 모두 위에서 알아본, 고유명사, 복합명사, 복합명사 분리 사전, 동사 사전, 형용사 사전에 해당합니다. 각각 메시지 내부에서는 np_set, cp_set, cp_caret_set, vv_set, va_set이라는 이름을 가지고 있습니다. DictSet은 일반적인 Set 데이터 구조입니다. 구글의 protobuf가 set을 지원하지 않아서 map 형태로 구현을 했습니다.

사전의 유형은 DictType으로 사용합니다. WORD_LIST를 사용하면 됩니다.

사용자 사전 생성 응답: UpdateCustomDictionaryResponse

응답 메시지는 매우 단순해서 성공, 실패 여부만 확인할 수 있습니다.

실패하는 경우는 흔하지 않지만, 대부분 시스템 오류가 나타난 경우일 것입니다. 참고로 grpc를 통한 호출에서 시스템이 부하를 견디지 못하고 에러를 내보낼 때는 적용 실패가 뜨지 않고, 예외로 전달되기 때문에 정상적인 실패 메시지를 받지 못하게 될 가능성이 더 높습니다.

API 사용 예

이들 데이터 구조를 이해한 상태라면, bareunpy 파이썬 클라이언트 라이브러리를 이용해서 바로 사용자 사전을 사용할 수 있습니다.

import json

from bareunpy import Tagger

# 설치한 자신의 호스트에 접속합니다.
# 아래에 "https://bareun.ai/"에서 이메일 인증 후 발급받은 API KEY("koba-..."")를 입력해주세요. "로그인-내정보 확인"
my_tagger = Tagger('YOUR-API-KEY', 'localhost', port=5656) # <- 본인의 API KEY로 교체 

# 사용자 사전 저장
# 사전 이름으로 영문, 숫자, 기호('-', '_')만 사용 가능
# 사전 단어에 기호는 추가될 수 없습니다(복합명사 분리사전의 ^ 기호 제외).
cust_dic = my_tagger.custom_dict("my_dict_01") 
cust_dic.copy_np_set({'내고유명사', '걸어서세계속으로', '크리스토퍼놀란'})
cust_dic.copy_cp_set({'코로나19', '바나나우유'})
cust_dic.copy_cp_caret_set({'코로나^백신', '디지털^인문학'})
cust_dic.copy_vv_set({'카톡하다', '맑내하다'})
cust_dic.copy_va_set({'로맨틱하다', '신박하다', '판타스틱하다'})
cust_dic.update()

# 이전 사용자 사전 불러오기
cust_dict2 = my_tagger.custom_dict("my_dict_01")
cust_dict2.load()

text = '내고유명사로 존재하는 "크리스토퍼놀란"은 코로나19 상황 속에서도 걸어서세계속으로라는\
        TV 프로그램을 통해 바나나우유를 마시며 카톡하다가 \
        떠오른 신박한 아이디어로 판타스틱한 로맨틱 영화를 만들 계획이며, \
        그 과정에서 디지털인문학의 도움을 받아 코로나백신에 대한 사회적 이슈도 함께 다루려 한다.'

# pos 정보만 출력
my_tagger.set_domain('my_dict_01')
response = my_tagger.pos(text) 
print(f'형태소 분석 결과: {response}')

# 형태소 분석 결과를 딕셔너리 형태로 저장
response2 = my_tagger.tags([text]) 
res_dict = json.loads(response2.as_json_str())
idx = 1
for token in res_dict['sentences'][0]['tokens']:
    custom_token = [f"{t['text']['content']}/{t['tag']}, {t['outOfVocab']}" \
                    for t in token['morphemes'] if t['outOfVocab'] == 'IN_CUSTOM_DICT']
    if len(custom_token) != 0:
        print(f"{idx}: {custom_token}")
        idx += 1
형태소 분석 결과: [('내고유명사', 'NNP'), ('로', 'JKB'), ('존재하', 'VV'), ('는', 'ETM'), ('"', 'SS'), ('크리스토퍼놀란', 'NNP'), ('"', 'SS'), ('은', 'JX'), ('코로나19', 'NNG'), ('상황', 'NNG'), ('속', 'NNG'), ('에서', 'JKB'), ('도', 'JX'), ('걸어서세계속으로', 'NNP'), ('라는', 'ETM'), ('TV', 'SL'), ('프로그램', 'NNG'), ('을', 'JKO'), ('통하', 'VV'), ('아', 'EC'), ('바나나우유', 'NNG'), ('를', 'JKO'), ('마시', 'VV'), ('며', 'EC'), ('카톡하', 'VV'), ('다가', 'EC'), ('떠오르', 'VV'), ('ㄴ', 'ETM'), ('신박하', 'VA'), ('ㄴ', 'ETM'), ('아이디어', 'NNG'), ('로', 'JKB'), ('판타스틱하', 'VA'), ('ㄴ', 'ETM'), ('로맨틱', 'NNG'), ('영화', 'NNG'), ('를', 'JKO'), ('만들', 'VV'), ('ㄹ', 'ETM'), ('계획', 'NNG'), ('이', 'VCP'), ('며', 'EC'), (',', 'SP'), ('그', 'MMD'), ('과정', 'NNG'), ('에서', 'JKB'), ('디지털', 'NNG'), ('인문학', 'NNG'), ('의', 'JKG'), ('도움', 'NNG'), ('을', 'JKO'), ('받', 'VV'), ('아', 'EC'), ('코로나', 'NNP'), ('백신', 'NNG'), ('에', 'JKB'), ('대하', 'VV'), ('ㄴ', 'ETM'), ('사회', 'NNG'), ('적', 'XSN'), ('이슈', 'NNG'), ('도', 'JX'), ('함께', 'MAG'), ('다루', 'VV'), ('려', 'EC'), ('하', 'VX'), ('ㄴ다', 'EF'), ('.', 'SF')]

1: ['내고유명사/NNP, IN_CUSTOM_DICT']
2: ['크리스토퍼놀란/NNP, IN_CUSTOM_DICT']
3: ['코로나19/NNG, IN_CUSTOM_DICT']
4: ['걸어서세계속으로/NNP, IN_CUSTOM_DICT']
5: ['바나나우유/NNG, IN_CUSTOM_DICT']
6: ['카톡하/VV, IN_CUSTOM_DICT']
7: ['신박하/VA, IN_CUSTOM_DICT']
8: ['판타스틱하/VA, IN_CUSTOM_DICT']
9: ['디지털/NNG, IN_CUSTOM_DICT', '인문학/NNG, IN_CUSTOM_DICT']

도움이 되었나요?