Skip to content

Commit

Permalink
feat: 한글 자모음을 인자로 입력받아 한글 문자로 합성하는 함수 추가 (#53)
Browse files Browse the repository at this point in the history
  • Loading branch information
evan-moon committed Apr 17, 2024
1 parent 52b9d55 commit 314f0a9
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/sweet-weeks-tie.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"es-hangul": minor
---

feat: 한글 자모음을 인자로 입력받아 한글 문자로 합성하는 함수 추가
48 changes: 48 additions & 0 deletions src/combineHangulCharacter.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { describe, it, expect, assert } from 'vitest';
import { combineHangulCharacter, combineVowels } from './combineHangulCharacter';

describe('combineHangulCharacter', () => {
it('종성으로 겹받침으로 구성될 수 있는 문자 두 개를 받으면 겹받침을 생성한다. (ㄱ, ㅏ, ㅂㅅ)', () => {
expect(combineHangulCharacter('ㄱ', 'ㅏ', 'ㅂㅅ')).toEqual('값');
});

it('종성이 입력되지 않았다면 받침이 없는 문자로 합성한다. (ㅌ, ㅗ)', () => {
expect(combineHangulCharacter('ㅌ', 'ㅗ')).toEqual('토');
});

it('종성이 입력되었다면 받침을 추가한다. (ㅌ, ㅗ, ㅅ)', () => {
expect(combineHangulCharacter('ㅌ', 'ㅗ', 'ㅅ')).toEqual('톳');
});

it('초성이 될 수 없는 문자가 초성으로 입력되면 에러를 반환한다. (ㅏ, ㅏ, ㄱ)', () => {
assert.throws(() => combineHangulCharacter('ㅏ', 'ㅏ', 'ㄱ'), Error, 'Invalid hangul Characters: ㅏ, ㅏ, ㄱ');
});

it('중성이 될 수 없는 문자가 중성으로 입력되면 에러를 반환한다. (ㄱ, ㄴ, ㅃ)', () => {
assert.throws(() => combineHangulCharacter('ㄱ', 'ㄴ', 'ㅃ'), Error, 'Invalid hangul Characters: ㄱ, ㄴ, ㅃ');
});

it('종성이 될 수 없는 문자가 종성으로 입력되면 에러를 반환한다. (ㄱ, ㅏ, ㅃ)', () => {
assert.throws(() => combineHangulCharacter('ㄱ', 'ㅏ', 'ㅃ'), Error, 'Invalid hangul Characters: ㄱ, ㅏ, ㅃ');
});

it('온전한 한글 문자가 하나라도 입력되면 에러를 반환한다. (가, ㅏ, ㄱ)', () => {
assert.throws(() => combineHangulCharacter('가', 'ㅏ', 'ㄱ'), Error, 'Invalid hangul Characters: 가, ㅏ, ㄱ');
});
});

describe('combineVowels', () => {
it('겹모음이 될 수 있는 모음이 순서대로 입력되면 겹모음으로 합성한다.', () => {
expect(combineVowels('ㅗ', 'ㅏ')).toBe('ㅘ');
expect(combineVowels('ㅜ', 'ㅔ')).toBe('ㅞ');
expect(combineVowels('ㅡ', 'ㅣ')).toBe('ㅢ');
});
it('겹모음이 될 수 있는 모음이라고 해도 틀린 순서로 입력되면 Join한다.', () => {
expect(combineVowels('ㅏ', 'ㅗ')).toBe('ㅏㅗ');
expect(combineVowels('ㅣ', 'ㅡ')).toBe('ㅣㅡ');
});
it('이미 겹모음인 문자와 모음을 합성하려고 시도하면 Join한다.', () => {
expect(combineVowels('ㅘ', 'ㅏ')).toBe('ㅘㅏ');
expect(combineVowels('ㅝ', 'ㅣ')).toBe('ㅝㅣ');
});
});
87 changes: 87 additions & 0 deletions src/combineHangulCharacter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import {
COMPLETE_HANGUL_START_CHARCODE,
DISASSEMBLED_VOWELS_BY_VOWEL,
HANGUL_CHARACTERS_BY_FIRST_INDEX,
HANGUL_CHARACTERS_BY_LAST_INDEX,
HANGUL_CHARACTERS_BY_MIDDLE_INDEX,
} from './constants';
import { canBeChosung, canBeJongsung, canBeJungsung } from './utils';

/**
* @name combineHangulCharacter
* @description
* 인자로 초성, 중성, 종성을 받아 하나의 한글 문자를 반환합니다.
* ```typescript
* combineHangulCharacter(
* // 초성
* firstCharacter: string
* // 중성
* middleCharacter: string
* // 종성
* lastCharacter: string
* ): string
* ```
* @example
* combineHangulCharacter('ㄱ', 'ㅏ', 'ㅂㅅ') // '값'
* combineHangulCharacter('ㅌ', 'ㅗ') // '토'
*/
export function combineHangulCharacter(firstCharacter: string, middleCharacter: string, lastCharacter = '') {
if (
canBeChosung(firstCharacter) === false ||
canBeJungsung(middleCharacter) === false ||
canBeJongsung(lastCharacter) === false
) {
throw new Error(`Invalid hangul Characters: ${firstCharacter}, ${middleCharacter}, ${lastCharacter}`);
}

const numOfMiddleCharacters = HANGUL_CHARACTERS_BY_MIDDLE_INDEX.length;
const numOfLastCharacters = HANGUL_CHARACTERS_BY_LAST_INDEX.length;

const firstCharacterIndex = HANGUL_CHARACTERS_BY_FIRST_INDEX.indexOf(firstCharacter);
const middleCharacterIndex = HANGUL_CHARACTERS_BY_MIDDLE_INDEX.indexOf(middleCharacter);
const lastCharacterIndex = HANGUL_CHARACTERS_BY_LAST_INDEX.indexOf(lastCharacter);

const firstIndexOfTargetConsonant = firstCharacterIndex * numOfMiddleCharacters * numOfLastCharacters;
const firstIndexOfTargetVowel = middleCharacterIndex * numOfLastCharacters;

const unicode =
COMPLETE_HANGUL_START_CHARCODE + firstIndexOfTargetConsonant + firstIndexOfTargetVowel + lastCharacterIndex;

return String.fromCharCode(unicode);
}

/**
* @name curriedCombineHangulCharacter
* @description
* 인자로 초성, 중성, 종성을 받아 하나의 한글 문자를 반환하는 `combineHangulCharacter` 함수의 커링된 버전입니다.
* @example
* const combineMiddleHangulCharacter = curriedCombineHangulCharacter('ㄱ')
* const combineLastHangulCharacter = combineMiddleHangulCharacter('ㅏ')
* combineLastHangulCharacter('ㄱ') // '각'
*/
export const curriedCombineHangulCharacter =
(firstCharacter: string) =>
(middleCharacter: string) =>
(lastCharacter = '') =>
combineHangulCharacter(firstCharacter, middleCharacter, lastCharacter);

/**
* @name combineVowels
* @description
* 인자로 두 개의 모음을 받아 합성하여 겹모음을 생성합니다. 만약 올바른 한글 규칙으로 합성할 수 없는 모음들이라면 단순 Join합니다.
* ```typescript
* combineVowels(
* // 첫 번째 모음
* vowel1: string
* // 두 번째 모음
* vowel2: string
* ): string
* ```
* @example
* combineVowels('ㅗ', 'ㅏ') // 'ㅘ'
* combineVowels('ㅗ', 'ㅐ') // 'ㅙ'
* combineVowels('ㅗ', 'ㅛ') // 'ㅗㅛ'
*/
export const combineVowels = (vowel1: string, vowel2: string) =>
Object.entries(DISASSEMBLED_VOWELS_BY_VOWEL).find(([, value]) => value === `${vowel1}${vowel2}`)?.[0] ??
`${vowel1}${vowel2}`;
8 changes: 4 additions & 4 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,8 @@ export function getFirstConsonants(word: string) {
* canBeChosung('ㅏ') // false
* canBeChosung('가') // false
*/
export function canBeChosung(character: string) {
return hasValueInReadOnlyStringList(HANGUL_CHARACTERS_BY_FIRST_INDEX,character);
export function canBeChosung(character: string): character is (typeof HANGUL_CHARACTERS_BY_FIRST_INDEX)[number] {
return hasValueInReadOnlyStringList(HANGUL_CHARACTERS_BY_FIRST_INDEX, character);
}

/**
Expand All @@ -86,7 +86,7 @@ export function canBeChosung(character: string) {
* canBeChosung('ㄱㅅ') // false
* canBeChosung('가') // false
*/
export function canBeJungsung(character: string) {
export function canBeJungsung(character: string): character is (typeof HANGUL_CHARACTERS_BY_MIDDLE_INDEX)[number] {
return hasValueInReadOnlyStringList(HANGUL_CHARACTERS_BY_MIDDLE_INDEX, character);
}

Expand All @@ -108,7 +108,7 @@ export function canBeJungsung(character: string) {
* canBeChosung('ㅏ') // false
* canBeChosung('ㅗㅏ') // false
*/
export function canBeJongsung(character: string) {
export function canBeJongsung(character: string): character is (typeof HANGUL_CHARACTERS_BY_LAST_INDEX)[number] {
return hasValueInReadOnlyStringList(HANGUL_CHARACTERS_BY_LAST_INDEX, character);
}

Expand Down

0 comments on commit 314f0a9

Please sign in to comment.