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

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

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

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

[그림 8.05] 한글 라이브러리의 클래스 구성
[그림 8.05] 한글 라이브러리의 클래스 구성

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

public static class KoreanCharMaker
{

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

    const int BASE_CODE = 0xAC00;

    const int BASE_INIT = 0x1100;

    const int BASE_VOWEL = 0x1161;

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

   static string nonshiftkey_str = "ABCDFGHIJKLMNSUVXYZ";

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

    static string nonshiftkey_kstr = "ㅁㅠㅊㅇㄹㅎㅗㅑㅓㅏㅣㅡㅜㄴㅕㅍㅌㅛㅋ";

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

    static string chostring = "rRseEfaqQtTdwWczxvg";
    static string chostring_k = "ㄱㄲㄴㄷㄸㄹㅁㅂㅃㅅㅆㅇㅈㅉㅊㅋㅌㅍㅎ";

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

    static string[] jungstrs = null;
    static string[] jungstrs_k = null;
    static string[] jongstrs = null;
    static string[] jongstrs_k = null;
    static KoreanCharMaker()
    {
        jungstrs = new string[] { "k", "o", "i", "O", "j", "p", "u", "P", "h", "hk", "ho", "hl", "y", "n", "nj", "np", "nl", "b", "m", "ml", "l" };
        jungstrs_k = new string[]{"ㅏ","ㅐ","ㅑ","ㅒ","ㅓ","ㅔ","ㅕ","ㅖ","ㅗ","ㅘ","ㅙ","ㅚ","ㅛ","ㅜ","ㅝ","ㅞ","ㅟ","ㅠ","ㅡ","ㅢ","ㅣ"};
        jongstrs = new string[] { string.Empty, "r", "R", "rt", "s", "sw", "sg", "e", "f", "fr", "fa", "fq", "ft", "fx", "fv", "fg", "a", "q", "qt", "t", "T", "d", "w", "c", "z", "x", "v", "g" };
        jongstrs_k = new string[] { string.Empty, "ㄱ", "ㄲ", "ㄳ", "ㄴ", "ㄵ", "ㄶ", "ㄷ", "ㄹ", "ㄺ", "ㄻ", "ㄼ", "ㄽ", "ㄾ", "ㄿ", "ㅀ", "ㅁ", "ㅂ", "ㅄ", "ㅅ", "ㅆ", "ㅇ", "ㅈ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ" };
    }

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

    public static int GetInitSoundCode(char ch)
    {

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

       int index = chostring_k.IndexOf(ch);

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

        if (index != -1){    return index;    }

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

        return chostring.IndexOf(ch);
    }

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

    public static int GetVowelCode(string str)
    {

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

        int cnt = jungstrs.Length;
        for (int i = 0; i < cnt; i++)
        {

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

            if (jungstrs[i] == str){    return i;    }
        }

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

        for (int i = 0; i < cnt; i++)
        {
            if (jungstrs_k[i] == str)
            {
                return i;
            }
        }

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

       return -1;
    }

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

    public static int GetVowelCode(char ch)
    {
        return GetVowelCode(ch.ToString());
    }

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

    public static int GetFinalConsonantCode(string str)
    {
        int cnt = jongstrs.Length;
        for (int i = 0; i < cnt; i++)
        {
            if (jongstrs[i] == str){    return i;    }
        }
        for (int i = 0; i < cnt; i++)
        {
            if (jongstrs_k[i] == str){    return i;    }
        }
        return -1;
    }

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

    public static int GetFinalConsonantCode(char ch)
    {
        return GetFinalConsonantCode(ch.ToString());
    }

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

    public static char GetSingleJa(int value)
    {

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

        byte[] bytes = BitConverter.GetBytes((short)(BASE_INIT + value));
        return Char.Parse(Encoding.Unicode.GetString(bytes));
    }

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

    public static char GetSingleVowel(int value)
    {

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

        byte[] bytes = BitConverter.GetBytes((short)(BASE_VOWEL + value));
        return Char.Parse(Encoding.Unicode.GetString(bytes));
    }

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

    public static char GetCompleteChar(int init_sound, int vowel, int final)
    {

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

        int tempFinalConsonant = 0;
        if (final >= 0)
        {
            tempFinalConsonant = final;
        }

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

        int jungcnt = jungstrs.Length;
        int jongcnt = jongstrs.Length;

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

        int completeChar = init_sound * jungcnt * jongcnt +
                vowel * jongcnt + tempFinalConsonant + BASE_CODE;

        byte[] naeBytes = BitConverter.GetBytes((short)(completeChar));
        return Char.Parse(Encoding.Unicode.GetString(naeBytes));
    }

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

    public static char NonShiftKeyToLower(char ch)
    {

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

        if (nonshiftkey_str.Contains(ch.ToString()))
        {
           return Char.ToLower(ch);
        }
        int index =  nonshiftkey_kstr.IndexOf(ch);
        if (index !=-1)
        {

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

            return Char.ToLower(nonshiftkey_str[index]);
        }

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

        return ch;
    }

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

    public static bool EnableDoubleFinalConsonant(char ch)
    {

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

         int code = GetFinalConsonantCode(ch);
        return code == 1 || code == 4 || code == 8 || code == 17;
    }

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

    public static bool EnableDoubleVowel(char ch)
    {
        int code = GetVowelCode(ch);
        return EnableDoubleVowel(code);
    }

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

    internal static bool EnableDoubleVowel(int code)
    {
        return (code == 8) || (code == 13) || (code == 18);
    }

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

    internal static bool IsInitSound(char ch){    return GetInitSoundCode(ch) != -1;    }
    internal static bool IsVowel(string str){    return GetVowelCode(str) != -1;    }
    internal static bool IsCharKey(char ch){    return char.IsLetter(ch);    }
    internal static bool IsFinalConsonant(char ch){   return GetFinalConsonantCode(ch) != -1;   }
}
using System;
using System.Text;

namespace EHHangulLibrary
{
    /// <summary>
    /// 문자의 키 값을 완성형 한글로 변형해주는 정적 클래스/
    /// </summary>
    public static class KoreanCharMaker
    {
        const int BASE_CODE = 0xAC00;
        const int BASE_INIT = 0x1100;
        const int BASE_VOWEL = 0x1161;
        static string nonshiftkey_str = "ABCDFGHIJKLMNSUVXYZ";
                                               
        static string nonshiftkey_kstr = "ㅁㅠㅊㅇㄹㅎㅗㅑㅓㅏㅣㅡㅜㄴㅕㅍㅌㅛㅋ";
        static string chostring = "rRseEfaqQtTdwWczxvg";
        static string chostring_k = "ㄱㄲㄴㄷㄸㄹㅁㅂㅃㅅㅆㅇㅈㅉㅊㅋㅌㅍㅎ";
        static string[] jungstrs = null;
        static string[] jungstrs_k = null;
        static string[] jongstrs = null;
        static string[] jongstrs_k = null;
        
        static KoreanCharMaker()
        {
            jungstrs = new string[] { "k", "o", "i", "O", "j", "p", "u", "P", "h", "hk", "ho", "hl", "y", "n", "nj", "np", "nl", "b", "m", "ml", "l" };
            jungstrs_k = new string[]{"ㅏ","ㅐ","ㅑ","ㅒ","ㅓ","ㅔ","ㅕ","ㅖ","ㅗ","ㅘ","ㅙ","ㅚ","ㅛ", "ㅜ", "ㅝ", "ㅞ", "ㅟ", "ㅠ", "ㅡ","ㅢ","ㅣ"};
            jongstrs = new string[] { string.Empty, "r", "R", "rt", "s", "sw", "sg", "e", "f", "fr", "fa", "fq", "ft", "fx", "fv", "fg", "a", "q", "qt", "t", "T", "d", "w", "c", "z", "x", "v", "g" };
            jongstrs_k = new string[] { string.Empty, "ㄱ", "ㄲ", "ㄳ", "ㄴ", "ㄵ", "ㄶ", "ㄷ", "ㄹ", "ㄺ", "ㄻ", "ㄼ", "ㄽ", "ㄾ", "ㄿ", "ㅀ", "ㅁ", "ㅂ", "ㅄ", "ㅅ", "ㅆ", "ㅇ", "ㅈ", "ㅊ", "ㅋ", "ㅌ", "ㅍ", "ㅎ" };
        }



        /// <summary>
        /// 문자를 초성 코드 값으로 바꿔주는 메서드
        /// </summary>
        /// <param name="ch">문자</param>
        /// <returns>초성 코드 값</returns>
        public static int GetInitSoundCode(char ch)
        {
            int index = chostring_k.IndexOf(ch);
            if (index != -1)
            {
                return index;
            }
            return chostring.IndexOf(ch);
        }
        /// <summary>
        /// 문자열을 중성(모음) 코드 값으로 바꿔주는 메서드
        /// </summary>
        /// <param name="str">문자열</param>
        /// <returns>중성(모음) 코드 값</returns>
        public static int GetVowelCode(string str)
        {
            int cnt = jungstrs.Length;
            for (int i = 0; i < cnt; i++)
            {
                if (jungstrs[i] == str)
                {
                    return i;
                }
            }
            for (int i = 0; i < cnt; i++)
            {
                if (jungstrs_k[i] == str)
                {
                    return i;
                }
            }
            return -1;
        }
        /// <summary>
        /// 문자의 중성 코드 값 바꿔주는 메서드
        /// </summary>
        /// <param name="ch">문자</param>
        /// <returns>중성 코드 값</returns>
        public static int GetVowelCode(char ch)
        {
            return GetVowelCode(ch.ToString());
        }
        /// <summary>
        /// 문자열을 종성 코드 값으로 바꿔주는 메서드
        /// </summary>
        /// <param name="str">문자열</param>
        /// <returns>종성 코드 값</returns>
        public static int GetFinalConsonantCode(string str)
        {
            int cnt = jongstrs.Length;
            for (int i = 0; i < cnt; i++)
            {
                if (jongstrs[i] == str){    return i;    }
            }
            for (int i = 0; i < cnt; i++)
            {
                if (jongstrs_k[i] == str){     return i;    }
            }
            return -1;
        }
        /// <summary>
        /// 문자를 종성 코드 값으로 바꿔주는 메서드
        /// </summary>
        /// <param name="ch">문자</param>
        /// <returns>종성 코드 값</returns>
        public static int GetFinalConsonantCode(char ch)
        {
            return GetFinalConsonantCode(ch.ToString());
        }


        /// <summary>
        /// 하나의 자음으로 글자를 만드는 메서드
        /// </summary>
        /// <param name="value">키 값</param>
        /// <returns>완성형 글자</returns>
        public static char GetSingleJa(int value)
        {
            byte[] bytes = BitConverter.GetBytes((short)(BASE_INIT + value));
            return Char.Parse(Encoding.Unicode.GetString(bytes));
        }
        /// <summary>
        /// 하나의 모음으로 글자를 만드는 메서드
        /// </summary>
        /// <param name="value">키 값</param>
        /// <returns>완성형 글자</returns>
        public static char GetSingleVowel(int value)
        {
            byte[] bytes = BitConverter.GetBytes((short)(BASE_VOWEL + value));
            return Char.Parse(Encoding.Unicode.GetString(bytes));
        }
        /// <summary>
        /// 초성, 중성, 종성 코드로 하나의 글자를 만드는 메서드
        /// </summary>
        /// <param name="init_sound">초성</param>
        /// <param name="vowel">중성</param>
        /// <param name="final">종성</param>
        /// <returns>완성형 글자</returns>
        public static char GetCompleteChar(int init_sound, int vowel, int final)
        {
            int tempFinalConsonant = 0;
            if (final >= 0){    tempFinalConsonant = final;    }
            int jungcnt = jungstrs.Length;
            int jongcnt = jongstrs.Length;
            int completeChar = init_sound * jungcnt * jongcnt +
                              vowel * jongcnt + tempFinalConsonant + BASE_CODE;
            byte[] naeBytes = BitConverter.GetBytes((short)(completeChar));
            return Char.Parse(Encoding.Unicode.GetString(naeBytes));
        }
        /// <summary>
        /// 쉬프트 키가 필요없는 문자를 소문자로 바꿔주는 메서드
        /// </summary>
        /// <param name="ch">문자</param>
        /// <returns>바뀐 문자</returns>
        public static char NonShiftKeyToLower(char ch)
        {
            if (nonshiftkey_str.Contains(ch.ToString()))               
            {
                return Char.ToLower(ch);
            }
            int index =  nonshiftkey_kstr.IndexOf(ch);
            if (index !=-1)
            {
                return Char.ToLower(nonshiftkey_str[index]);
            }
            return ch;
        }
        /// <summary>
        /// 이중 받침이 가능한 문자인지 확인하는 메서드
        /// </summary>
        /// <param name="ch">문자</param>
        /// <returns>이중 받침 가능 여부</returns>
        public static bool EnableDoubleFinalConsonant(char ch)
        {
            int code = GetFinalConsonantCode(ch);
            return code == 1 || code == 4 || code == 8 || code == 17;
        }
        /// <summary>
        /// 이중 모음이 가능한 문자인지 확인하는 메서드
        /// </summary>
        /// <param name="ch">문자</param>
        /// <returns>이중 모음 가능 여부</returns>
        public static bool EnableDoubleVowel(char ch)
        {
            int code = GetVowelCode(ch);
            return EnableDoubleVowel(code);
        }
        internal static bool IsInitSound(char ch)
        {
            return GetInitSoundCode(ch) != -1;
        }
        internal static bool IsVowel(string str)
        {
            return GetVowelCode(str) != -1;
        }
        internal static bool IsCharKey(char ch)
        {
            return char.IsLetter(ch);
        }
        internal static bool IsFinalConsonant(char ch)
        {
            return GetFinalConsonantCode(ch) != -1;
        }
        internal static bool EnableDoubleVowel(int code)
        {
            return (code == 8) || (code == 13) || (code == 18);
        }
    }
}

[소스 8.7] KoreanCharMaker.cs

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

enum HGState{
        TURN_INIT_SOUND, //초성을 기대
        TURN_VOWEL, //모음을 기대
        TURN_FINAL_CONSONANT //종성을 기대
}
class HangulState
{

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

    const char VOWEL_INIT_CHAR = char.MinValue;//초성 초기 문자

    const char FINAL_INIT_CHAR = char.MinValue; //종성 초기 문자

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

    int init_sound;                 //초성
    int vowel;                       //중성(모음)
    char vowel_first;              //첫 모음
    bool vowelPossible;         //이중 모음이 가능 여부
    int final_conso;               //종성
    char final_consoFirst;       //종성의 첫 받침
    char final_consoLast;       //종성의 마지막 받침(이중 받침일 때 의미가 있음)
    bool final_consoPossible; //이중 받침 가능 여부

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

    HGState state;
    internal HGState State
    {
        get
        {
            return state;
        }
    }

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

    internal HangulState()
    {
        Init();
    }
    internal void Init()
    {

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

        state = HGState.TURN_INIT_SOUND;

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

        init_sound = -1;
        vowel = -1;
        final_conso = -1;
        vowelPossible = false;
        final_consoPossible = false;
        vowel_first = VOWEL_INIT_CHAR;
        final_consoFirst = FINAL_INIT_CHAR;
        final_consoLast = FINAL_INIT_CHAR;
    }

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

    internal void SetStateInitSound()
    {
        state = HGState.TURN_INIT_SOUND;
    }

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

    internal void SetStateVowel()
    {
        state = HGState.TURN_VOWEL;

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

        vowel = -1;
        final_conso = -1;
        vowelPossible = false;
        final_consoFirst = FINAL_INIT_CHAR;
        final_consoLast = FINAL_INIT_CHAR;
    }

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

    internal void SetStateFinalConsonant()
    {

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

        state = HGState.TURN_FINAL_CONSONANT;
        final_conso = -1;
        final_consoFirst = FINAL_INIT_CHAR;
        final_consoLast = FINAL_INIT_CHAR;
    }

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

    internal char GetCompleteChar()
    {
        return KoreanCharMaker.GetCompleteChar(init_sound, vowel, final_conso);
    }

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

    internal bool IsDoubleVowel()
    {
        bool check = KoreanCharMaker.EnableDoubleVowel(vowel);
        return vowelPossible && (check == false) && (ExistFristFinalConsonant() == false);
    }

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

    bool ExistFristFinalConsonant()
    {
        return final_consoFirst != FINAL_INIT_CHAR;
    }

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

    bool ExistLastFinalConsonant(){    return final_consoLast != FINAL_INIT_CHAR;    }

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

    internal bool IsInitSound()
    {

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

        return state == HGState.TURN_VOWEL && !ExistVowel();
    }

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

    internal bool IsSingleVowel()
    {

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

        if (vowel != KoreanCharMaker.GetVowelCode(vowel_first)){    return false;    }

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

        return (state == HGState.TURN_VOWEL && ExistVowel()) ||
               ((state == HGState.TURN_FINAL_CONSONANT) && (!ExistFinalConsonant()));
    }

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

    internal bool ExistVowel()
    {

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

        return (vowel != VOWEL_INIT_CHAR) && (vowel != -1);
    }

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

    internal bool ExistFinalConsonant()
    {

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

        return (final_conso != FINAL_INIT_CHAR) && (final_conso != -1);
    }

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

    internal bool IsSingleFinalConsonant()
    {

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

        return state == HGState.TURN_FINAL_CONSONANT && ExistFristFinalConsonant()
                       && (!ExistLastFinalConsonant());
    }

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

    internal bool IsFull(){    return ExistFristFinalConsonant() && ExistLastFinalConsonant();    }

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

    bool EnableDoubleFinalConsonant()
    {

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

        return final_conso == 1 || final_conso == 4 || final_conso == 8 || final_conso == 17;
    }

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

    void SetVowel(char ch)
    {

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

        if (KoreanCharMaker.EnableDoubleVowel(ch))
        {

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

            vowelPossible = true;
            state = HGState.TURN_VOWEL;
        }

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

        else
        {
            state = HGState.TURN_FINAL_CONSONANT;
        }

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

        vowel_first = ch;
    }

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

    internal bool InputAtInitSound(ref string source, char ch)
    {

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

        state = HGState.TURN_VOWEL;

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

        init_sound = KoreanCharMaker.GetInitSoundCode(ch);
        if (init_sound >= 0)
        {

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

            source += KoreanCharMaker.GetSingleJa(init_sound);
            return true;
        }

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

        return false;
    }

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

    internal bool InputFirstVowel(ref string source, char ch)
    {

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

        vowel = KoreanCharMaker.GetVowelCode(ch);

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

        if (vowel < 0){    state = HGState.TURN_INIT_SOUND;    return false;    }

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

        if (init_sound < 0)
        {
            source += KoreanCharMaker.GetSingleVowel(KoreanCharMaker.GetVowelCode(ch));
            Init();
        }

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

        else
        {
            SetVowel(ch);

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

            source = source.Substring(0, source.Length - 1);
            source += GetCompleteChar();
        }
        return true;
    }

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

    internal bool InputSecondVowel(ref string source, char ch)
    {

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

        string tempvowel = string.Empty;
        tempvowel += vowel_first;
        tempvowel += ch;

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

        int temp = KoreanCharMaker.GetVowelCode(tempvowel);
        if (temp >= 0)   //tempvowel이 모음
        {

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

            vowel = temp;

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

            source = source.Substring(0, source.Length - 1);
            source += GetCompleteChar();

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

            SetStateFinalConsonant();
            return true;
        }

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

        else
        {
            SetStateFinalConsonant();

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

            return false;
        }
    }

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

    internal bool InputFirstFinalConsonant(ref string source, char ch)
    {

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

        final_conso = KoreanCharMaker.GetFinalConsonantCode(ch);
        if (final_conso > 0)
        {

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

            source = source.Substring(0, source.Length - 1);
            source += GetCompleteChar();

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

            SetFirstFinalConsonant(ch);    return true;
        }

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

        if (KoreanCharMaker.GetVowelCode(ch) >= 0){    Init();    return false;    }

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

        if (KoreanCharMaker.GetInitSoundCode(ch) >= 0)
        {
            Init();
            final_consoPossible = false;    final_conso = 0;    return false;
        }
        return false;
    }

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

    void SetFirstFinalConsonant(char ch)
    {

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

        final_consoFirst = ch;

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

         final_consoPossible =EnableDoubleFinalConsonant());
    }

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

    internal bool InputSecondFinalConsonant(ref string source, char ch)
    {

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

        if (final_consoPossible){    return SetSecondFinalConsonant(ref source, ch);    }

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

        else
        {
            if (KoreanCharMaker.GetInitSoundCode(ch) >= 0){    Init();    }

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

            else{    LastFinalConsonantToInitSound(ref source);    }
        }
        return false;
    }

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

    private bool SetSecondFinalConsonant(ref string source, char ch)
    {

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

        final_consoPossible = false;

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

        string tempfinal_conso = string.Empty;
        tempfinal_conso += final_consoFirst;
        tempfinal_conso += ch;

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

        int temp = KoreanCharMaker.GetFinalConsonantCode(tempfinal_conso);

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

        if (temp > 0)
        {
            final_consoLast = ch;
            final_conso = temp;

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

            source = source.Substring(0, source.Length - 1);
            source += GetCompleteChar();
            return true;
        }
        return false;
    }

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

    private void LastFinalConsonantToInitSound(ref string source)
    {
        if (IsFull())
        {

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

            source = source.Substring(0, source.Length - 1);
            final_conso = KoreanCharMaker.GetFinalConsonantCode(final_consoFirst);
            source += GetCompleteChar();

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

            init_sound = KoreanCharMaker.GetInitSoundCode(final_consoLast);
        }

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

        else
        {
            char tempinit_sound = final_consoFirst;
            source = source.Substring(0, source.Length - 1);
            ClearFinalConsonant();
            source += GetCompleteChar();

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

            init_sound = KoreanCharMaker.GetInitSoundCode(tempinit_sound);
        }

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

        source += KoreanCharMaker.GetSingleJa(init_sound);
        SetStateVowel();
    }

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

    internal void ClearFinalConsonant()
    {

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

        SetStateFinalConsonant();
        SetVowel(vowel_first);
    }

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

    internal void ClearLastVowel()
    {

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

        state = HGState.TURN_VOWEL;

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

        vowel = KoreanCharMaker.GetVowelCode(vowel_first);
    }

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

    internal void ClearLastFinalConsonant()
    {

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

        state = HGState.TURN_FINAL_CONSONANT;
        final_consoLast = FINAL_INIT_CHAR;
        final_conso = KoreanCharMaker.GetFinalConsonantCode(final_consoFirst);
        final_consoPossible = true;
    }

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

    internal char GetJaFormInitSound()
    {

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

        return KoreanCharMaker.GetSingleJa(init_sound);
    }

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

    internal bool IsFirstFinalConsonant(){    return final_conso > 0;    }
}
using System;

namespace EHHangulLibrary
{
    enum HGState
    {
        TURN_INIT_SOUND, //초성을 기대
        TURN_VOWEL, //모음을 기대
        TURN_FINAL_CONSONANT //종성을 기대
    }
    class HangulState
    {
        const char VOWEL_INIT_CHAR = char.MinValue;//초성 초기 문자
        const char FINAL_INIT_CHAR = char.MinValue; //종성 초기 문자
        int init_sound;                 //초성
        int vowel;                       //중성(모음)
        char vowel_first;              //첫 모음
        bool vowelPossible;         //이중 모음이 가능 여부
        int final_conso;               //종성
        char final_consoFirst;       //종성의 첫 받침
        char final_consoLast;       //종성의 마지막 받침(이중 받침일 때 의미가 있음)
        bool final_consoPossible; //이중 받침 가능 여부
        HGState state;
        internal HGState State
        {
            get{    return state;    }
        }
        internal HangulState(){    Init();    }
        internal void Init()
        {
            state = HGState.TURN_INIT_SOUND;
            init_sound = -1;
            vowel = -1;
            final_conso = -1;
            vowelPossible = false;
            final_consoPossible = false;
            vowel_first = VOWEL_INIT_CHAR;
            final_consoFirst = FINAL_INIT_CHAR;
            final_consoLast = FINAL_INIT_CHAR;
        }
        internal void SetStateInitSound()
        {
            state = HGState.TURN_INIT_SOUND;
        }
        internal void SetStateVowel()
        {
            state = HGState.TURN_VOWEL;
            vowel = -1;
            final_conso = -1;
            vowelPossible = false;
            final_consoFirst = FINAL_INIT_CHAR;
            final_consoLast = FINAL_INIT_CHAR;
        }
        internal void SetStateFinalConsonant()
        {
            state = HGState.TURN_FINAL_CONSONANT;
            final_conso = -1;
            final_consoFirst = FINAL_INIT_CHAR;
            final_consoLast = FINAL_INIT_CHAR;
        }
        internal char GetCompleteChar()
        {
            return KoreanCharMaker.GetCompleteChar(init_sound, vowel, final_conso);
        }
        internal bool IsDoubleVowel()
        {
            bool check = KoreanCharMaker.EnableDoubleVowel(vowel);
            return vowelPossible && (check == false) && (ExistFristFinalConsonant() == false);
        }
        bool ExistFristFinalConsonant()
        {
            return final_consoFirst != FINAL_INIT_CHAR;
        }
        bool ExistLastFinalConsonant()
        {
            return final_consoLast != FINAL_INIT_CHAR;
        }
        internal bool IsInitSound()
        {
            return state == HGState.TURN_VOWEL && !ExistVowel();
        }
        internal bool IsSingleVowel()
        {
            if (vowel != KoreanCharMaker.GetVowelCode(vowel_first))
            {
                return false;
            }
            return (state == HGState.TURN_VOWEL && ExistVowel()) 
               || ((state == HGState.TURN_FINAL_CONSONANT) && (!ExistFinalConsonant()));
        }
        internal bool ExistVowel()
        {
            return (vowel != VOWEL_INIT_CHAR) && (vowel != -1);
        }
        internal bool ExistFinalConsonant()
        {
            return (final_conso != FINAL_INIT_CHAR) && (final_conso != -1);
        }
        internal bool IsSingleFinalConsonant()
        {
            return state == HGState.TURN_FINAL_CONSONANT 
                         && ExistFristFinalConsonant() && (!ExistLastFinalConsonant());
        }
        internal bool IsFull()
        {
            return ExistFristFinalConsonant() && ExistLastFinalConsonant();            
        }
        bool EnableDoubleFinalConsonant()
        {
            return final_conso == 1 || final_conso == 4 || final_conso == 8 || final_conso == 17;
        }
        void SetVowel(char ch)
        {
            if (KoreanCharMaker.EnableDoubleVowel(ch))
            {
                vowelPossible = true;
                state = HGState.TURN_VOWEL;
            }
            else
            {
                state = HGState.TURN_FINAL_CONSONANT;
            }
            vowel_first = ch;
        }

        internal bool InputAtInitSound(ref string source, char ch)
        {
            state = HGState.TURN_VOWEL;
            init_sound = KoreanCharMaker.GetInitSoundCode(ch);
            if (init_sound >= 0)
            {
                source += KoreanCharMaker.GetSingleJa(init_sound);
                return true;
            }
            return false;
        }
        internal bool InputFirstVowel(ref string source, char ch)
        {
            vowel = KoreanCharMaker.GetVowelCode(ch);
            if (vowel < 0)//모음이 아닐 때
            {
                state = HGState.TURN_INIT_SOUND;
                return false;
            }
            if (init_sound < 0)//초성없이 모음을 먼저 입력
            {
                source += KoreanCharMaker.GetSingleVowel(
                                   KoreanCharMaker.GetVowelCode(ch));
                Init();
            }
            else
            {
                SetVowel(ch);
                source = source.Substring(0, source.Length - 1);
                source += GetCompleteChar();
            }
            return true;
        }
        internal bool InputSecondVowel(ref string source, char ch)
        {
            string tempvowel = string.Empty;
            tempvowel += vowel_first;
            tempvowel += ch;
            int temp = KoreanCharMaker.GetVowelCode(tempvowel);
            if (temp >= 0)   //tempvowel이 모음
            {
                vowel = temp;
                source = source.Substring(0, source.Length - 1);
                source += GetCompleteChar();
                SetStateFinalConsonant();
                return true;
            }
            else
            {
                SetStateFinalConsonant();
                return false;
            }
        }
        internal bool InputFirstFinalConsonant(ref string source, char ch)
        {
            final_conso = KoreanCharMaker.GetFinalConsonantCode(ch);
            if (final_conso > 0)
            {
                source = source.Substring(0, source.Length - 1);
                source += GetCompleteChar();
                SetFirstFinalConsonant(ch);
                return true;
            }
            if (KoreanCharMaker.GetVowelCode(ch) >= 0)//모음이 들어왔을 때
            {
                Init();
                return false;
            }
            if (KoreanCharMaker.GetInitSoundCode(ch) >= 0)//받침으로 쓸 수 없는 자음
            {
                Init();
                final_consoPossible = false;
                final_conso = 0;
                return false;
            }
            return false;
        }
        void SetFirstFinalConsonant(char ch)
        {
            final_consoFirst = ch;
            final_consoPossible = EnableDoubleFinalConsonant();
        }
        internal bool InputSecondFinalConsonant(ref string source, char ch)
        {
            if (final_consoPossible) //이중 받침이 가능할 때
            {
                return SetSecondFinalConsonant(ref source, ch);
            }
            else //받침이 완성이 되었을 경우
            {
                if (KoreanCharMaker.GetInitSoundCode(ch) >= 0){    Init();    }
                else //모음일 때
                {
                    LastFinalConsonantToInitSound(ref source);
                }
            }
            return false;
        }
        private bool SetSecondFinalConsonant(ref string source, char ch)
        {
            final_consoPossible = false;
            string tempfinal_conso = string.Empty;
            tempfinal_conso += final_consoFirst;
            tempfinal_conso += ch;
            int temp = KoreanCharMaker.GetFinalConsonantCode(tempfinal_conso);
            if (temp > 0)   //이중 받침
            {
                final_consoLast = ch;
                final_conso = temp;
                source = source.Substring(0, source.Length - 1);
                source += GetCompleteChar();
                return true;
            }
            return false; //그 외
        }

        private void LastFinalConsonantToInitSound(ref string source)
        {
            if (IsFull())
            {
                source = source.Substring(0, source.Length - 1);
                final_conso = KoreanCharMaker.GetFinalConsonantCode(final_consoFirst); 
                source += GetCompleteChar();
                init_sound = KoreanCharMaker.GetInitSoundCode(final_consoLast); 
            }
            else
            {
                char tempinit_sound = final_consoFirst;
                source = source.Substring(0, source.Length - 1);
                ClearFinalConsonant();
                source += GetCompleteChar();
                init_sound = KoreanCharMaker.GetInitSoundCode(tempinit_sound);
            }
            source += KoreanCharMaker.GetSingleJa(init_sound);
            SetStateVowel(); //상태를 중성 입력 대기로 전이
        }
        internal void ClearFinalConsonant()
        {
            SetStateFinalConsonant();
            SetVowel(vowel_first);
        }
        internal void ClearLastVowel()
        {
            state = HGState.TURN_VOWEL;
            vowel = KoreanCharMaker.GetVowelCode(vowel_first);
        }
        internal void ClearLastFinalConsonant()
        {
            state = HGState.TURN_FINAL_CONSONANT;
            final_consoLast = FINAL_INIT_CHAR;
            final_conso = KoreanCharMaker.GetFinalConsonantCode(final_consoFirst);
            final_consoPossible = true;
        }
        internal char GetJaFormInitSound(){  return KoreanCharMaker.GetSingleJa(init_sound);  }
        internal bool IsFirstFinalConsonant(){    return final_conso > 0;    }
    }
}

[소스 8.8] HangulState.cs

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

public class Hangul
{

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

    HangulState hs;
    string source;

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

    public string Text
    {
        get{    return source;    }
        set{    source = value;    }
    }

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

    public Hangul()
    {
        hs = new HangulState();
        Text = string.Empty;
    }

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

    public void Reset()
    {

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

        hs.Init();
    }

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

    public void Input(char ch)
    {

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

        if (ch == '\b')
        {

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

            BackKeyProc();
            return;
        }

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

        if (!Char.IsLetter(ch))
        {
            source += ch;
            hs.Init();
            return;
        }

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

        ch = KoreanCharMaker.NonShiftKeyToLower(ch);

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

        switch (hs.State)
        {
            case HGState.TURN_INIT_SOUND: InputInitSoundProc(ch); break;
            case HGState.TURN_VOWEL: InputVowelProc(ch); break;
            case HGState.TURN_FINAL_CONSONANT: InputFinalConsonantProc(ch); break;
        }
    }

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

    public void InputNoKorea(char ch)
    {

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

        Reset();
        source += ch.ToString();
    }

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

    private void InputInitSoundProc(char ch)
    {

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

        if (!hs.InputAtInitSound(ref source, ch))
        {
            Input(ch);
        }
    }

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

    private void InputVowelProc(char ch)
    {

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

        if (hs.ExistVowel() == false)
        {

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

            if (hs.InputFirstVowel(ref source, ch) == false)
            {
                Input(ch);
            }
        }

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

        else
        {
            if (hs.InputSecondVowel(ref source, ch) == false){    Input(ch);    }
        }
    }

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

    private void InputFinalConsonantProc(char ch)
    {

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

        if (hs.IsFirstFinalConsonant() == false)
        {

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

            if (hs.InputFirstFinalConsonant(ref source, ch) == false){    Input(ch);    }
        }

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

        else
        {
            if (hs.InputSecondFinalConsonant(ref source, ch) == false)
            {
                Input(ch);
            }
        }
    }

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

    private void BackKeyProc()
    {

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

        if (source.Length <= 0){    return;    }

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

        if ((hs.State == HGState.TURN_INIT_SOUND) || hs.IsInitSound())
        {

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

            hs.SetStateInitSound();

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

            source = source.Substring(0, source.Length - 1);
            return;
        }

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

        if (hs.IsSingleVowel())
        {

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

            hs.SetStateVowel();

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

            source = source.Substring(0, source.Length - 1);

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

            source += hs.GetJaFormInitSound();
            return;
        }

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

        if (hs.IsDoubleVowel())
        {

마지막 문자를 지웁니다.

            source = source.Substring(0, source.Length - 1);

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

            hs.ClearLastVowel();
            source += hs.GetCompleteChar();
            return;
        }

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

        if (hs.IsSingleFinalConsonant())
        {

종성을 지웁니다.

            hs.ClearFinalConsonant();

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

            source = source.Substring(0, source.Length - 1);

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

            source += hs.GetCompleteChar();
            return;
        }

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

        else if (hs.IsFull())
        {

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

            hs.ClearLastFinalConsonant();
            source = source.Substring(0, source.Length - 1);
            source += hs.GetCompleteChar();
        }
    }
}
using System;
namespace EHHangulLibrary
{
    /// <summary>
    /// 한글 오토마타
    /// </summary>
    public class Hangul
    {
        HangulState hs;
        string source;
        /// <summary>
        /// 한글 오토마타로 만들어진 문자열
        /// </summary>
        public string Text
        {
            get{    return source;    }
            set{    source = value;    }
        }
        /// <summary>
        /// 한글 오토마타 생성기
        /// </summary>
        public Hangul()
        {
            hs = new HangulState();
            Text = string.Empty;
        }
        /// <summary>
        /// 한글 오토마타 초기화
        /// </summary>
        public void Reset(){    hs.Init();    }
        /// <summary>
        /// 새로운 문자를 추가하는 메서드
        /// </summary>        
        /// <param name="ch">추가할 문자</param>
        public void Input(char ch)
        {
            if (ch == '\b')
            {
                BackKeyProc();
                return;
            }
            if (!Char.IsLetter(ch))
            {
                source += ch;
                hs.Init();
                return;
            }
            ch = KoreanCharMaker.NonShiftKeyToLower(ch);
            switch (hs.State)
            {
                case HGState.TURN_INIT_SOUND: InputInitSoundProc(ch); break;
                case HGState.TURN_VOWEL: InputVowelProc(ch); break;
                case HGState.TURN_FINAL_CONSONANT: InputFinalConsonantProc(ch); break;
            }
        }
        /// <summary>
        /// 한글이 아닌 문자를 소스에 추가
        /// </summary>
        /// <param name="ch">추가할 문자</param>
        public void InputNoKorea(char ch)
        {
            Reset();
            source += ch.ToString();
        }
        private void InputInitSoundProc(char ch)
        {
            if (!hs.InputAtInitSound(ref source, ch)){    Input(ch);    }
        }
        private void InputVowelProc(char ch)
        {
            if (hs.ExistVowel() == false)
            {
                if (hs.InputFirstVowel(ref source, ch) == false){    Input(ch);    }
            }
            else
            {
                if (hs.InputSecondVowel(ref source, ch) == false)
                {
                    Input(ch);
                }
            }
        }
        private void InputFinalConsonantProc(char ch)
        {
            if (hs.IsFirstFinalConsonant() == false)
            {
                if (hs.InputFirstFinalConsonant(ref source, ch) == false)
                {
                    Input(ch);
                }
            }
            else
            {
                if (hs.InputSecondFinalConsonant(ref source, ch) == false)
                {
                    Input(ch);
                }
            }
        }
        private void BackKeyProc()
        {
            if (source.Length <= 0){    return;    }
            if ((hs.State == HGState.TURN_INIT_SOUND) || hs.IsInitSound())
            {
                hs.SetStateInitSound();
                source = source.Substring(0, source.Length - 1);
                return;
            }
            if (hs.IsSingleVowel())
            {
                hs.SetStateVowel();
                source = source.Substring(0, source.Length - 1);
                source += hs.GetJaFormInitSound();
                return;
            }
            if (hs.IsDoubleVowel())
            {
                source = source.Substring(0, source.Length - 1);
                hs.ClearLastVowel();
                source += hs.GetCompleteChar();
                return;
            }
            if (hs.IsSingleFinalConsonant())
            {
                hs.ClearFinalConsonant();
                source = source.Substring(0, source.Length - 1);
                source += hs.GetCompleteChar();
                return;
            }
            else if (hs.IsFull())
            {
                hs.ClearLastFinalConsonant();
                source = source.Substring(0, source.Length - 1);
                source += hs.GetCompleteChar();
            }
        }
    }
}

[소스 8.9] Hangul.cs