티스토리 뷰

우선 issue를 발행하고 todo에 있는 것들을 소 issue로 발행해 순차적으로 해결해야겠다.

 

1. 한글 자소 분리

[(초성) × 588 + (중성) × 28 + (종성)] + 44032(=0xAC00, "가"에 해당하는 코드 값)

한글에 대한 유니코드 공식은 얻었으나, 각 자리에 대한 값을 얻기위한 공식을 찾는 것이 조금 귀찮았다...^^ 우리에겐 구교수님이 계시니 바로 검색해봄. 

역시나 세상엔 천재들이 많다!! -> 공식 참고한 곳

 

이제 한글을 받아오면 자소를 분리하는 공식을 얻었으니 이를 적용해보자.

 

// 첫 문자인 "가"에 해당하는 값
const korGAUnicode = 0xAC00;
// 초성, 중성, 종성 시작 값
const [firstInitial, firstMedial, firstFinal] = [0x1100, 0x1161, 0x11A7];
// 초성, 중성, 종성 개수
const [initialCount, medialCount, finalCount] = [18, 21, 28];

우선 자소 분리 시 필요한 값을 선언해놓았다.

// 첫 문자인 "가"에 해당하는 값
const korGAUnicode = 0xAC00;
// 초성, 중성, 종성 시작 값
const [firstInitial, firstMedial, firstFinal] = [0x1100, 0x1161, 0x11A7];
// 초성, 중성, 종성 개수
const [initialCount, medialCount, finalCount] = [18, 21, 28];

/**
 * 글자에 해당하는 unicode 값을 구한다.
 *
 * @since 2023-01-29
 * @param {String} letter unicode로 변환할 글자
 * @returns {number} unicode
 */
const getUnicode = (letter) => {
  if (letter.length !== 1) {
    throw new Error("매개 변수는 한 글자여야 합니다.");
  }

  return letter.codePointAt(0);
}

/**
 * unicode가 가지고 있는 초성이 jomo block에서 몇 번째 글자인지 구한다.
 *
 * @since 2023-01-29
 * @param {Number} unicode 초성 순서 값을 구할 글자 unicode
 * @returns {number} 초성 순서(순서는 Hangul Jamo Unicode block 순서에 의거함)
 * @link https://en.wikipedia.org/wiki/Hangul_Jamo_(Unicode_block)
 */
const getInitialIndex = (unicode) => {
  if (typeof unicode !== "number") {
    throw new Error("unicode는 숫자여야합니다.");
  }

  return Math.floor((unicode - korGAUnicode) / (medialCount * finalCount));
}

/**
 * unicode가 가지고 있는 중성이 jomo block에서 몇 번째 글자인지 구한다.
 *
 * @since 2023-01-29
 * @param {Number} unicode 중성 순서 값을 구할 글자 unicode
 * @returns {number} 중성 순서(순서는 Hangul Jamo Unicode block 순서에 의거함)
 * @link https://en.wikipedia.org/wiki/Hangul_Jamo_(Unicode_block)
 */
const getMedialIndex = (unicode) => {
  if (typeof unicode !== "number") {
    throw new Error("unicode는 숫자여야합니다.");
  }

  return Math.floor((unicode - korGAUnicode) / finalCount % medialCount);
}

/**
 * unicode가 가지고 있는 종성이 jomo block에서 몇 번째 글자인지 구한다.
 *
 * @since 2023-01-29
 * @param {Number} unicode 종성 순서 값을 구할 글자 unicode
 * @returns {number} 종성 순서(순서는 Hangul Jamo Unicode block 순서에 의거함)
 * @link https://en.wikipedia.org/wiki/Hangul_Jamo_(Unicode_block)
 */
const getFinalIndex = (unicode) => {
  if (typeof unicode !== "number") {
    throw new Error("unicode는 숫자여야합니다.");
  }

  return Math.floor((unicode - korGAUnicode) % finalCount);
}

초성, 중성, 종성 값을 구하는 함수이다.

글자 별로 unicode 값을 구한 뒤, 그 값으로 자소 분리를 할 수 있게끔 하였다.

 

초성,중성,종성 값을 분리하는 함수를 개별로 작성할 지, 함수를 하나로 두고 매개변수로 어떤 값을 구하는 것인지 판별할 지에 대해서 고민을 했다.

// 예시 코드
const getGraphemeIndex = (direction, unicode) => {
  if (typeof unicode !== "number") {
    throw new Error("unicode는 숫자여야합니다.");
  }

  switch (direction) {
    case "initial":
      return Math.floor((unicode - korGAUnicode) / (medialCount * finalCount));
    case "medial":
      return Math.floor((unicode - korGAUnicode) / finalCount % medialCount);
    case "final":
      return Math.floor((unicode - korGAUnicode) % finalCount);
    default:
      throw new Error("잘못된 요청입니다.");
  }
}

매개 변수로 판별 후 로직을 진행하는 함수의 예시이다.

 

direction 값에 대한 유효성 검사를 추가해야할뿐더러, 어떤 값을 넣어줘야하는지 한번에 알기 어렵다.

뭐 객체 freeze()를 이용해 Enum을 만들면 가능할지도 모르지만 개인적으로 이 함수는 그렇게까지 할 필요는 없을 것 같아서 함수를 각각 분리해줬다.

더보기

글을 적으면서 생각했는데, 두개의 방법을 합쳐도 될 것 같다.

getGraphemeIndex 함수를 객체로 선언 후 key 값으로 초성, 중성, 종성을 넣어주고, 그에 해당하는 함수를  value에 넣어주면 추후에 관리하기도 편할 듯?

리팩토링 해봐야겠다.

 

우선 결과부터 보자!!

  <script>
    window.addEventListener('DOMContentLoaded', function() {
      console.log(getGraphemeList("한글 자소 분리12ab"));
    });
  </script>

한글만 초,중,종성으로 분리되었다.

한글이 아니면 한글 여부를 false로 한 뒤 초성 값에 문자를 그대로 넣어주었다.

 

전체 코드

"use strict";

/* 구현 순서
  1. 단어를 한 글자씩 쪼갠다.
  2. 쪼갠 글자를 유니코드로 변환한다.
  3. 유니코드에서 초성~종성의 값을 구한다.
 */

// 첫 문자인 "가"에 해당하는 값
const korGAUnicode = 0xAC00;
// 초성, 중성, 종성 시작 값
const [firstInitial, firstMedial, firstFinal] = [0x1100, 0x1161, 0x11A7];
// 초성, 중성, 종성 개수
const [initialCount, medialCount, finalCount] = [18, 21, 28];

/**
 * 한국어 자소 분리를 진행한다.
 *
 * param을 한 글자씩 쪼개어 한국어일 경우에만 자소 분리를 수행한다.
 *
 * @since 2023-01-29
 * @param {String} word 자소 분리를 진행할 한국어
 * @returns {Object[]} {isKorean: Boolean, initial: String, medial: String||null, final: String||null}
 */
const getGraphemeList = (word) => {
  if (word.length < 1) {
    throw new Error("매개 변수는 한 글자이상이어야 합니다.");
  }

  return splitWord(word).reduce((arr, letter) => {
    if (letter.match(/[ㄱ-ㅎ|가-힣]/g)) { // 한글일 때만 자소 분리
      let {initial, medial, final} = splitGrapheme(getUnicode(letter));

      arr.push({
        isKorean: true,
        initial: getInitialLetter(initial),
        medial: getMedialLetter(medial),
        final: getFinalLetter(final)
      });
    } else {
      arr.push({
        isKorean: false,
        initial: letter
      });
    }

    return arr;
  }, []);
}

/**
 * unicode에 해당하는 한글을 반환한다.
 *
 * @since 2023-01-29
 * @param {Number} unicode 한글로 변환할 unicode
 * @returns {string} 한글
 */
const getLetterFromUnicode = (unicode) => {
  return String.fromCharCode(unicode);
}

/**
 * unicode에 해당하는 초성 값을 반환한다.
 *
 * @since 2023-01-29
 * @param {Number} unicode 한글로 변환할 초성 unicode
 * @returns {string} 한글(초성)
 */
const getInitialLetter = (unicode) => {
  return getLetterFromUnicode(unicode + firstInitial);
}

/**
 * unicode에 해당하는 중성 값을 반환한다.
 *
 * @since 2023-01-29
 * @param {Number} unicode 한글로 변환할 중성 unicode
 * @returns {string} 한글(중성)
 */
const getMedialLetter = (unicode) => {
  return getLetterFromUnicode(unicode + firstMedial);
}

/**
 * unicode에 해당하는 종성 값을 반환한다.
 *
 * @since 2023-01-29
 * @param {Number} unicode 한글로 변환할 종성 unicode
 * @returns {string} 한글(종성)
 */

const getFinalLetter = (unicode) => {
  return getLetterFromUnicode(unicode + firstFinal);
}

/**
 * 단어를 한 글자씩 쪼개어 배열로 반환한다.
 *
 * @since 2023-01-29
 * @param {String} word 쪼갤 단어
 * @returns {String[]} 한 글자씩 쪼개진 결과
 */
const splitWord = (word) => {
  return [...word];
}

/**
 * unicode에 해당하는 글자를 자소 분리한다.
 *
 * @since 2023-01-29
 * @param {Number} unicode 자소 분리할 글자
 * @returns {Object} {initial, medial, final} 자소 분리 결과
 */
const splitGrapheme = (unicode) => {
  let initial = getInitialIndex(unicode);
  let medial = getMedialIndex(unicode);
  let final = getFinalIndex(unicode);

  return {initial, medial, final};
}

/**
 * 글자에 해당하는 unicode 값을 구한다.
 *
 * @since 2023-01-29
 * @param {String} letter unicode로 변환할 글자
 * @returns {number} unicode
 */
const getUnicode = (letter) => {
  if (letter.length !== 1) {
    throw new Error("매개 변수는 한 글자여야 합니다.");
  }

  return letter.codePointAt(0);
}

/**
 * unicode가 가지고 있는 초성이 jomo block에서 몇 번째 글자인지 구한다.
 *
 * @since 2023-01-29
 * @param {Number} unicode 초성 순서 값을 구할 글자 unicode
 * @returns {number} 초성 순서(순서는 Hangul Jamo Unicode block 순서에 의거함)
 * @link https://en.wikipedia.org/wiki/Hangul_Jamo_(Unicode_block)
 */
const getInitialIndex = (unicode) => {
  if (typeof unicode !== "number") {
    throw new Error("unicode는 숫자여야합니다.");
  }

  return Math.floor((unicode - korGAUnicode) / (medialCount * finalCount));
}

/**
 * unicode가 가지고 있는 중성이 jomo block에서 몇 번째 글자인지 구한다.
 *
 * @since 2023-01-29
 * @param {Number} unicode 중성 순서 값을 구할 글자 unicode
 * @returns {number} 중성 순서(순서는 Hangul Jamo Unicode block 순서에 의거함)
 * @link https://en.wikipedia.org/wiki/Hangul_Jamo_(Unicode_block)
 */
const getMedialIndex = (unicode) => {
  if (typeof unicode !== "number") {
    throw new Error("unicode는 숫자여야합니다.");
  }

  return Math.floor((unicode - korGAUnicode) / finalCount % medialCount);
}

/**
 * unicode가 가지고 있는 종성이 jomo block에서 몇 번째 글자인지 구한다.
 *
 * @since 2023-01-29
 * @param {Number} unicode 종성 순서 값을 구할 글자 unicode
 * @returns {number} 종성 순서(순서는 Hangul Jamo Unicode block 순서에 의거함)
 * @link https://en.wikipedia.org/wiki/Hangul_Jamo_(Unicode_block)
 */
const getFinalIndex = (unicode) => {
  if (typeof unicode !== "number") {
    throw new Error("unicode는 숫자여야합니다.");
  }

  return Math.floor((unicode - korGAUnicode) % finalCount);
}
반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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
글 보관함