2) 한글 라이브러리 만들기

이제 한글 문자 생성기에서 익힌 기술을 이용하여 한글 라이브러리를 만들어 봅시다.

 

한글 라이브러리에는 외부에서 접근해서 사용하는 Hangul 클래스와 실제 한글을 만드는 정적 클래스 KoreanCharMaker 클래스, 현재 입력하고 있는 상태(초성, 중성, 종성이 올 차례)를 나타내는 HangulState 클래스와 와 열거형 HGState로 구성합시다.

 

한글 클래스는 한글 키나 다른 키를 입력받는 메서드와 리셋하는 메서드를 제공합니다. 먼저 클래스 라이브러리 프로젝트 템플릿으로 EHHangulLibrary 프로젝트틀 생성하세요. 솔루션 이름은 한글 오토마타로 정할게요.

 

[그림 8.05] 한글 라이브러리의 클래스 구성
[그림 8.05] 한글 라이브러리의 클래스 구성
 먼저 앞에서 만들었던 한글 문자 생성기 부분을 이용하여 KoreanCharMaker 클래스를 정의합시다. KoreanCharMaker 클래스는 문자의 키 값을 완성형 한글로 변형해주는 정적 클래스입니다. 이번 솔루션에서는 이 클래스를 외부에서 사용하는 곳이 없지만 충분히 따로 사용할 가치가 있는 클래스이므로 클래스 접근 지정을 public으로 설정하세요.

한글의 BASE 코드와 초성, 종성의 시작 값을 상수로 정의하세요.

키보드 자판에는 쉬프트 키를 눌렀을 때와 누르지 않았을 때 차이가 있는 한글 자모도 있지만 차이가 없는 자모도 있습니다. 먼저 차이가 없는 자모를 멤버 필드로 선언 및 초기화하세요.

그리고 이 키에 해당하는 한글 자모도 선언 및 초기화합니다.

초성에 사용할 수 있는 자판과 대응하는 한글 자모를 선언 및 초기화하세요. 여기에 초기화하는 것은 실제 한글 초성 코드 값 순으로 문자열을 형성합시다.

중성과 종성은 이중 모음과 이중 받침이 있을 수 있어서 문자열을 원소로 하는 배열로 초기화해야 합니다. 초기화는 정적 생성자에서 수행하고 여기에서는 선언만 합시다.

문자를 초성 코드 값(인덱스)으로 바꿔주는 메서드를 제공합시다. 입력 인자로 문자를 받고 초성 코드 값을 반환하게 정의하세요.

먼저 초성에 사용할 수 있는 문자인지 확인합니다.

만약 초성 문자가 맞다면 코드 값을 반환하세요.

없다면 영문 알파벳으로 올 수도 있으니 영문 알파벳을 구성한 문자열에서 찾은 인덱스를 반환합니다. 물론 string 클래스는 문자열을 구성하지 않는 문자는 -1을 반환합니다.

문자열을 중성 코드 값으로 바꿔주는 메서드도 정의합시다. 중성은 이중 모음이 올 수 있어서 입력 인자로 중성 문자열을 받게 정의하세요.

먼저 전달받은 문자열이 중성에 해당하는지 확인합니다.

만약 존재하면 중성 코드 값(인덱스)를 반환하세요.

한글 중성을 설정한 문자열 배열에서도 확인하여 코드 값을 반환합니다.

없을 때는 -1을 반환합시다.

 

종성 문자를 코드 값으로 바꿔주는 메서드도 제공합시다.

문자열을 종성 코드 값으로 바꿔주는 메서드도 제공하세요. 원리는 중성 코드 값으로 바꿔주는 메서드와 같습니다.

 

문자를 종성 코드 값으로 바꿔주는 메서드도 제공하세요.

하나의 자음으로 구성하는 글자를 만드는 메서드도 필요합니다. 입력 인자로 초성 코드값을 받고 하나의 자음으로 구성한 한글을 반환할 것입니다.

한글의 BASE 값에 초성을 더한 값으로 byte 배열을 만듭니다. 그리고 이를 Encoding 클래스의 정적 멤버 Unicode의 GetString 메서드로 얻은 값을 char로 Parsing 하여 반환합니다.

하나의 모음으로 글자를 만드는 메서드도 필요합니다.

만드는 방법은 초성으로 구성한 한글을 만드는 것과 비슷합니다. 차이가 있는 부분은 모음의 시작 값에 입력 인자로 받은 초성 키 값으로 만든다는 것입니다.

 

초성, 중성, 종성으로 구성하는 한글을 만드는 메서드를 정의합시다.

먼저 종성이 -1이 아니면 임시로 설정합니다.

중성과 종성은 두 개의 문자로 구성할 수 있으므로 몇 자인지 구성합니다.

이제 초기값에 초성, 중성, 종성 값을 완성형 코드 값으로 계산합니다. 초성 값에 중성의 개수와 종성의 개수를 곱하고 중성에 종성의 개수를 곱합니다. 그리고 이들 값과 종성 값과 한글 초기 값을 더하면 완성형 코드 값을 계산할 수 있습니다.

 

쉬프트 키가 필요없는 문자를 소문자로 바꿔주는 메서드도 제공합시다.

먼저 쉬프트 키가 필요없는 문자인지 확인하여 소문자로 변환하여 반환하세요.

반환 값은 영문 문자열의 인덱스를 반환하세요.

그렇지 않을 때는 입력 인자로 받은 문자 그대로 전달합니다.

이중 받침이 가능한 문자인지 확인하는 메서드를 정의합시다.

종성 중에 이중 받침이 가능한 문자는 ㄱ,ㄴ,ㄹ,ㅂ입니다.

이중 모음이 가능한 문자인지 확인하는 메서드도 제공합니다.

이중 모음이 가능한 문자는 ㅗ, ㅜ, ㅡ 입니다.

char 형식 값이 문자인지 초성인지 중성인지 종성인지 확인하는 메서드도 각각 제공합시다.

 

[소스 8.7] KoreanCharMaker.cs

 

먼저 한글 상태를 정의하는 HGState 열거형과 HangulState 클래스를 정의합시다. 한글의 상태에는 초성이 올 것이라 기대하는 상태와 모음을 기대, 종성을 기대하는 상태를 열거합시다.

한글 상태는 하나의 한글이 형성되는 상태를 기억하는 클래스입니다. 먼저 const 멤버로 초성 초기 문자와 종성 초기 문자를 정의합시다. 여기에서는 char의 MinValue로 정의할게요.

초성과 중성, 종성을 멤버 필드로 기억해야 합니다. 그리고 이중 모음이 올 수도 있으므로 중성은 첫 모음과 이중 모임이 가능 여부를 기억하는 필드도 선언합시다. 종성도 이중 받침이 올 수 있으므로 첫 받침과 마지막 받침과 이중 받침이 가능 여부를 기억하는 멤버 필드도 선업합시다.

그리고 현재의 상태를 기억하는 멤버 필드도 추가하세요. 특히 현재의 상태는 Hangul 클래스에서 현재 어디까지 입력한 상태인지 알아야 하므로 가져오기 속성을 제공하세요.

 

생성자 메서드에서는 초기화 작업을 진행합니다. 미래를 위해 메서드로 정의합시다.

초기 상태는 초성이 오기를 기대하는 상태입니다.

현재 초성과 중성, 종성 및 멤버 필드들을 초기 값으로 설정하세요.

상태를 초성 상태로 전이하는 메서드를 제공합니다.

중성 상태로 전이하는 메서드도 제공합니다.

중성과 종성에 관한 나머지 멤버도 초기값으로 설정합니다.

 

종성 상태를 설정하는 메서드도 정의합시다.

종성에 관한 멤버 필드를 초기값으로 설정하세요.

현재 상태로 하나의 한글을 구하는 메서드를 제공합시다.

이중 모음인지 확인하는 메서드를 제공합시다.

 

종성을 설정하였는지 확인하는 메서드를 제공하세요.

종성이 존재하는지 확인하는 메서드를 제공하세요.

초성만 있는 것인지 확인하세요.

초성만 있을 때는 현재 상태는 모음을 기대하는 상태이면서 모음이 없을 때입니다.

단일 모음인지 확인하는 메서드도 정의합시다.

먼저 첫 모음이 vowel이 아니면 단일 모음이 아닙니다.

만약 모음이 존재하면서 모음을 기대하는 상태이거나 종성을 기대하는 상태인데 종성이 없는 상태이면 단일 모음입니다.

 

모음이 존재하는지 확인하는 메서드를 제공하세요.

모음이 초기 값이 아니면서 -1이 아니어야 합니다.

종성이 존재하는지 확인하는 메서드를 제공합시다.

종성이 초기 값이 아니면서 -1도 아니면 종성이 존재하는 것입니다.

종성이 하나인지 확인하는 메서드를 제공합시다.

현재 상태가 종성이 오기를 기대하면서 첫 종성이 존재하고 마지막 종성이 존재하지 않을 때입니다.

꽉 찼는지 확인하는 메서드도 제공하세요.

 

이중 종성이 가능한지 확인하는 메서드를 제공합시다.

이중 종성이 가능한 자음은 ㄱ, ㄴ, ㄹ, ㅂ 입니다.

모음을 설정하는 메서드를 제공합시다.

KoreanMamber의 정적 메서드를 이용하여 이중 모음이 가능한지 확인합니다.

가능하면 vowelPossible을 true로 설정하고 현재 상태를 모음을 기대하는 상태로 설정합니다.

그렇지 않으면 종성을 기대하는 상태로 전이합니다.

그리고 초기 모음을 설정합니다.

초성을 문자열에 추가하는 메서드를 제공합시다.

먼저 다음 상태는 중성을 기대하는 상태로 전이합니다.

KoreanCharMaker 클래스의 정적 메서드 GetInitSoundCode 호출로 초성 코드 값을 구합니다.

초성 문자가 맞으면 초성 코드 값은 0보다 크거나 같습니다. 이 때 초성으로만 구성한 한글을 기존 문자열에 더합니다.

만약 초성 코드 값이 0보다 크거나 같지 않으면 실패를 반환합니다.

첫 모음을 추가하는 메서드도 제공합시다.

먼저 문자의 중성 코드 값을 구합니다.

만약 중성 코드가 아니면 상태를 초기 상태로 전이합니다.

초성이 없을 때는 모음 하나로 구성한 한글을 문자열에 추가하고 Init 메서드를 호출합니다.

초성이 있을 때는 중성을 설정합니다.

그리고 마지막 문자를 중성을 설정한 한글로 변경합니다.

두 번째 모음을 추가하는 메서드를 제공합시다.

현재 첫번째 모음 문자와 입력 인자로 받은 문자로 구성한 문자열을 만듭니다.

 

구성한 문자열로 종성 코드 값을 구합니다.

종성 코드 값이 0보다 크거나 같으면 유효한 모음입니다. 이중 모음을 구성한 문자열을 모음으로 설정합니다.

원본 문자열의 맨 마지막 문자를 새로 형성한 문자열로 변경합니다.

종성이 오기를 기대하는 상태로 전이합니다.

그렇지 않으면 첫 종성을 기대하는 상태로 전이합니다.

그리고 다시 입력할 수 있게 false를 반환합니다.

 

첫 종성을 추가하는 메서드를 구현합시다.

먼저 입력인자로 받은 문자를 종성 코드로 변환합니다.

종성 코드가 유효하면 원본 문자열의 마지막 문자를 종성을 추가한 완성형 문자로 변경합니다.

첫 종성 문자를 설정합니다.

입력 인자로 받은 문자가 모음일 때는 초기화를 하고 false를 반환하여 다시 입력 시도할 수 있게 합니다.

종성으로 쓸 수 없는 자음이 왔을 때도 초기화를 합니다.

첫 종성을 설정하는 메서드를 구현합시다.

입력 인자로 받은 문자를 첫 종성 문자로 설정합니다.

이중 받침이 가능한지 확인합니다.

두번째 종성을 추가하는 메서드를 제공합시다.

이중 받침이 가능하면 이중 모음을 설정하는 메서드를 호출합니다.

그렇지 않으면 입력 인자로 받은 문자가 초성인지 확인하여 초성이면 초기 상태로 전이합니다.

그렇지 않으면 마지막 문자의 종성을 새로운 초성으로 설정합니다.

 

두번째 받침을 설정하는 메서드를 구현합시다.

더 이상 종성은 받을 수 없습니다. final_consoPossible을 false로 지정합니다.

첫 받침과 입력 인자로 받은 문자로 임시 문자열을 구성합니다.

KoreanCharMaker의 정적 메서드 GetFinalConsonantCode로 종성 코드를 구합니다.

종성 코드가 0보다 크면 이중 받침입니다. 최종 종성을 입력 받은 문자로 설정하고 종성을 임시로 구성한 문자열로 설정합니다.

원본 문자열의 마지막 문자열 완성한 문자로 변경합니다.

마지막 종성을 초기 초성으로 변환하는 메서드를 구현합시다.

이중 받침으로 구성한 문자일 때는 마지막 문자를 마지막 받침만 없는 문자로 변경합니다.

그리고 마지막 받침으로 초성 코드를 구합니다.

그렇지 않다면 종성 문자를 얻어와서 원본 문자열의 마지막 문자에서 종성을 제거합니다.

그리고 종성으로 초성 코드를 구합니다.

받침으로 만든 초성 코드로 초성 글자를 만들어 원본 문자열에 추가하고 상태를 전이합니다.

종성을 지우는 메서드를 제공합시다.

상태를 종성을 기대하는 상태로 전이하고 모음을 설정합니다.

마지막 모음을 지우는 메서드를 구현합시다.

상태를 모음을 기대하는 상태로 전이합니다.

모음 코드를 구하여 설정합니다.

마지막 종성을 지우는 메서드를 구현합시다.

마지막 종성을 기대하는 상태로 전이한니다.

초성으로 한 글자를 구하는 메서드를 제공합시다.

KoreanCharMaker 클래스의 정적 메서드 GetSingleja로 글자를 만들어 반환합니다.

첫 종성이 있는지 확인하는 메서드도 제공하세요.

 

[소스 8.8] HangulState.cs

 

이제 한글 오타마타에서 접근해서 사용할 클래스 Hangul을 만듭시다.

멤버 필드로 한글 입력 상태와 현재까지 입력한 내용을 기억할 멤버를 선언합니다.

현재까지의 입력한 문자열을 참조할 수 있는 속성을 제공합니다.

그리고 생성자에서는 새로운 한글 상태 개체를 생성하여 멤버 필드에 설정하고 현재까지의 문자열도 빈 문자열로 설정합니다.

초기 설정으로 전이하는 메서드를 제공합시다.

한글 상태 개체의 Init 메서드를 호출합니다.

 

새로운 문자를 추가하는 메서드를 제공합시다.

한글 자모가 아닌 키들에 대한 처리를 먼저 합시다. ‘\b’ 문자가 오면 문자를 되돌리는 작업을 진행합니다.

문자를 되돌리는 작업은 별도의 메서드로 만들기로 할게요.

만약 입력 인자로 받은 문자가 알파벳이 아니면 원본 문자열에 더합니다. 그리고 한글 상태 개체의 Init 메서드를 호출합니다.

먼저 쉬프트 키를 누르지 않을 때의 문자로 변환합시다.

그리고 현재 한글 상태 개체의 State 속성에 따라 초성을 기대하면 초성을 추가하는 메서드를 중성을 기대하면 중성을 추가하는 메서드를 종성을 기대하면 종성을 추가하는 메서드를 호출합니다.

한글이 아닌 문자를 소스에 추가하는 메서드를 구현합시다.

초기 상태로 만드는 Reset을 호출한 후에 현재까지의 문자열에 추가합니다.

초성을 추가하는 메서드를 구현합시다.

한글 상태 개체의 초성이 맞는지 확인하여 추가하는 메서드인 InputAtInitSound 메서드를 호출합니다. 만약 아니면 다시 Input 메서드를 호출합니다. InputAtInitSound 메서드에서는 초성을 기대할 때 초성이 아닌 문자를 추가하라고 전달하면 상태를 중성을 기대하는 상태로 전인한 후에 false를 반환하게 구현하였습니다.

중성을 추가하는 메서드도 구현합시다.

중성이 있는지 확인합니다.

없다면 첫번째 모음을 추가하는 메서드를 호출합니다. 만약 반환 값이 false 일 때는 다시 Input 메서드를 호출합니다.

만약 이미 모음이 있다면 두번째 모음을 추가하는 메서드를 호출합니다. 그리고 반환 값이 false일 때는 다시 Input 메서드를 호출합니다.

이제 종성을 추가하는 메서드를 구현합시다.

첫번째 종성이 있는지 확인합니다.

만약 없다면 첫번째 종성을 추가하는 메서드를 호출합니다. 반환 값이 false일 때는 Input을 호출합니다.

첫번째 종성이 있을 때는 두번째 종성을 추가하는 메서드를 호출합니다. 반환 값이 false일 때는 다시 Input 메서드를 호출합니다.

‘\b’ 키를 눌렀을 때 처리하는 BackKeyProc 메서드를 구현합시다.

만약 소스 길이가 0보다 작거나 같으면 바로 메서드를 끝냅니다.

만약 한글 상태가 초성을 기대하는 상태이고 마지막 입력한 것이 초성인지 확인합니다.

초성을 기다리는 상태로 설정합니다.

그리고 현재 문자열에서 마지막 문자를 제거한 후 메서드를 종료합니다.

 

마지막 입력이 중성인지 확인합니다.

중성을 기대하는 상태로 전이합니다.

먼저 마지막 문자를 지웁니다.

hs 개체의 초성 글자를 얻어와서 현재 문자열에 더한 후에 메서드를 끝냅니다.

이중 모음까지 들어온 상태인지 확인합니다.

마지막 문자를 지웁니다.

hs 개체의 마지막 모음을 지웁니다. 그리고 난 후에 source에 hs에서 문자를 형성하여 추가합니다.

종성의 받침이 하나 들어왔는지 확인합니다.

종성을 지웁니다.

현재 문자열의 마지막 문자를 지웁니다.

hs 개체에서 글자를 형성해서 문자열에 더한 후에 메서드를 끝냅니다.

꽉 찬 상태인지 확인합니다.

마지막 종성을 제거하고 현재 문자열의 마지막 문자를 hs 개체가 문자를 형성한 문자로 바꿉니다.