Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 숫자를 한글로 바꾸는 함수 추가 #254

Merged
merged 13 commits into from
Oct 12, 2024
38 changes: 38 additions & 0 deletions docs/src/pages/docs/api/numberToHangul.en.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
title: numberToHangul
---

import { Sandpack } from '@/components/Sandpack';

# numberToHangul

Converts the given number into a Korean numeral expression using Sino-Korean numbers.
It supports spacing for “man(萬)” units to accommodate various requirements.

```typescript
function numberToHangul(input: number, options?: { spacing?: boolean }): string;
```

## Examples

```tsx
numberToHangul(210_000); // '이십일만';
numberToHangul(12_345); // '일만이천삼백사십오';
numberToHangul(123_456_780); // '일억이천삼백사십오만육천칠백팔십';
numberToHangul(123_456_780, { spacing: true }); // '일억 이천삼백사십오만 육천칠백팔십';
```

## 사용해보기

<br />

<Sandpack>

```ts index.ts
import { numberToHangul } from 'es-hangul';

console.log(numberToHangul(123_456_780));
console.log(numberToHangul(123_456_780, { spacing: true }));
```

</Sandpack>
38 changes: 38 additions & 0 deletions docs/src/pages/docs/api/numberToHangul.ko.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
title: numberToHangul
---

import { Sandpack } from '@/components/Sandpack';

# numberToHangul

주어진 숫자를 한자어 수사로 바꿔줍니다.
다양한 요구사항에 대응하도록 '만(萬)' 단위로 띄어쓰기를 지원합니다.

```typescript
function numberToHangul(input: number, options?: { spacing?: boolean }): string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

input에 '133314' 처럼 string이 들어오지 않기로 하신 이유가 있나요? 저도 number만 받는게 좋을 것 같다고 생각해요!

  1. 내부에서 string value를 처리하는데 들어가는 복잡성
  2. 해당 함수는 숫자를 한글 단위로 변환 하는 것이 핵심이라서, 단일 책임만 가졌으면 하기 때문입니다

Copy link
Contributor Author

@BO-LIKE-CHICKEN BO-LIKE-CHICKEN Oct 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 저도 동일한 의견입니다.

es-hangul에서 제공하는 api들은 최소한의 기능과 책임을 가졌으면 좋겠어요.

numberToHangul의 경우에도 string이 들어가면서 복잡해지는 것보다는
numberToHangul을 사용하는 곳에서 형변환해서 넣어주는 것이 어렵지도 않고 함수가 가벼워질 것이라고 생각했습니다.

앞으로도 api를 제공할 때 사용하는 곳에서 유용하게 사용할 수 있는가? 도 함수의 책임과 함께 고민되면 좋을 것 같습니다!

```

## Examples

```tsx
numberToHangul(210_000); // '이십일만';
numberToHangul(12_345); // '일만이천삼백사십오';
numberToHangul(123_456_780); // '일억이천삼백사십오만육천칠백팔십';
numberToHangul(123_456_780, { spacing: true }); // '일억 이천삼백사십오만 육천칠백팔십';
```

## 사용해보기

<br />

<Sandpack>

```ts index.ts
import { numberToHangul } from 'es-hangul';

console.log(numberToHangul(123_456_780));
console.log(numberToHangul(123_456_780, { spacing: true }));
```

</Sandpack>
38 changes: 38 additions & 0 deletions docs/src/pages/docs/api/numberToHanuglMixed.en.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
title: numberToHangulMixed
---

import { Sandpack } from '@/components/Sandpack';

# numberToHangulMixed

Attaches the Korean number units that change every 4 digits to the given number.
It supports spacing for “man(萬)” units to accommodate various requirements.

```typescript
function numberToHangulMixed(input: number, options?: { spacing?: boolean }): string;
```

## Examples

```tsx
numberToHangulMixed(210_000); // '21만';
numberToHangulMixed(12_345); // '1만2,345';
numberToHangulMixed(123_456_780); // '1억2,345만6,780';
numberToHangulMixed(123_456_780, { spacing: true }); // '1억 2,345만 6,780';
```

## 사용해보기

<br />

<Sandpack>

```ts index.ts
import { numberToHangulMixed } from 'es-hangul';

console.log(numberToHangulMixed(1_2345_6780));
console.log(numberToHangulMixed(1_2345_6780, { spacing: true }));
```

</Sandpack>
38 changes: 38 additions & 0 deletions docs/src/pages/docs/api/numberToHanuglMixed.ko.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
title: numberToHangulMixed
---

import { Sandpack } from '@/components/Sandpack';

# numberToHangulMixed

주어진 숫자에 **4자리 마다 바뀌는 한글의 숫자 단위**를 붙여줍니다.
다양한 요구사항에 대응하도록 '만(萬)' 단위로 띄어쓰기를 지원합니다.

```typescript
function numberToHangulMixed(input: number, options?: { spacing?: boolean }): string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

함수명의 맥락을 고려할 때, numberToHangulMixed는 “숫자를 한글로 변환하고, 그 결과가 섞인 형태로 나타난다”는 순서와 흐름을 더 잘 설명하는 반면, numberToMixedHangul은 “숫자와 한글이 섞인 형태로 변환한다”는 점을 먼저 강조하고 �있는 것 같은데요.

고민을 해봤는데, 숫자를 한글로 변환하는게 메인 기능이므로 진행해주신 numberToHangulMixed 가 더 적절해보이네요 👍

Copy link
Contributor Author

@BO-LIKE-CHICKEN BO-LIKE-CHICKEN Oct 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 저도 함수명에 고민이 많았는데 공감대가 형성되어 기쁘네요!

이외의 명명 기준은 다음과 같습니다.

  1. numberToHangul이 기본값이 될 것이라고 생각했습니다.
  2. numberToHangul와 같은 인터페이스로 추가되는 함수들은 numberToHangul와 가장 닮은 이름을 가지기를 바랐어요.

```

## Examples

```tsx
numberToHangulMixed(210_000); // '21만';
numberToHangulMixed(12_345); // '1만2,345';
numberToHangulMixed(123_456_780); // '1억2,345만6,780';
numberToHangulMixed(123_456_780, { spacing: true }); // '1억 2,345만 6,780';
```

## 사용해보기

<br />

<Sandpack>

```ts index.ts
import { numberToHangulMixed } from 'es-hangul';

console.log(numberToHangulMixed(123_456_780));
console.log(numberToHangulMixed(123_456_780, { spacing: true }));
```

</Sandpack>
27 changes: 27 additions & 0 deletions src/_internal/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,30 @@ export const JONGSEONGS = (
'ㅎ',
] as const
).map(consonant => DISASSEMBLED_CONSONANTS_BY_CONSONANT[consonant]);

export const HANGUL_DIGITS = [
'',
'만',
'억',
'조',
'경',
'해',
'자',
'양',
'구',
'간',
'정',
'재',
'극',
'항하사',
'아승기',
'나유타',
'불가사의',
'무량대수',
'겁',
'업',
];
export const HANGUL_DIGITS_MAX = HANGUL_DIGITS.length * 4;
export const HANGUL_NUMBERS = ['', '일', '이', '삼', '사', '오', '육', '칠', '팔', '구'];
export const HANGUL_NUMBERS_FOR_DECIMAL = ['영', '일', '이', '삼', '사', '오', '육', '칠', '팔', '구'];
export const HANGUL_CARDINAL = ['', '십', '백', '천'];
38 changes: 11 additions & 27 deletions src/amountToHangul/amountToHangul.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,14 @@
export const HANGUL_DIGITS = [
'',
'만',
'억',
'조',
'경',
'해',
'자',
'양',
'구',
'간',
'정',
'재',
'극',
'항하사',
'아승기',
'나유타',
'불가사의',
'무량대수',
'겁',
'업',
];
export const HANGUL_DIGITS_MAX = HANGUL_DIGITS.length * 4;
export const HANGUL_NUMBERS = ['', '일', '이', '삼', '사', '오', '육', '칠', '팔', '구'];
export const HANGUL_NUMBERS_FOR_DECIMAL = ['영', '일', '이', '삼', '사', '오', '육', '칠', '팔', '구'];
export const HANGUL_CARDINAL = ['', '십', '백', '천'];

import {
HANGUL_DIGITS_MAX,
HANGUL_NUMBERS_FOR_DECIMAL,
HANGUL_NUMBERS,
HANGUL_DIGITS,
HANGUL_CARDINAL,
} from '@/_internal/constants';

/**
* @deprecated 더 유연하게 사용 가능한 `numberToHangul`을 이용해 주세요
*/
export function amountToHangul(amount: string | number) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

amountToHangul은 다음 메이저 버전 업데이트 때 제거 될 것 같은데,
주석으로 deprecated를 명시해두는것은 어떨까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 저도 이 부분 함께 논의해보고 싶었습니다.

docs: deprecated amountToHangul에 반영했어요!

const [rawIntegerPart, rawDecimalPart] = String(amount)
.replace(/[^\d.]+/g, '')
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ export { hasBatchim } from './hasBatchim';
export { canBeChoseong, canBeJongseong, canBeJungseong } from './canBe';
export { getChoseong } from './getChoseong';
export { amountToHangul } from './amountToHangul';
export { numberToHangul } from './numberToHangul';
export { numberToHangulMixed } from './numberToHangulMixed';
1 change: 1 addition & 0 deletions src/numberToHangul/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './numberToHangul';
43 changes: 43 additions & 0 deletions src/numberToHangul/numberToHangul.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { numberToHangul } from './numberToHangul';

describe('numberToHangul', () => {
test('기본 변환', () => {
expect(numberToHangul(210_000)).toBe('이십일만');
expect(numberToHangul(12_345)).toBe('일만이천삼백사십오');
expect(numberToHangul(123_456_780)).toBe('일억이천삼백사십오만육천칠백팔십');
});

test('공백 포함 변환', () => {
expect(numberToHangul(210_000, { spacing: true })).toBe('이십일만');
expect(numberToHangul(12_345, { spacing: true })).toBe('일만 이천삼백사십오');
expect(numberToHangul(123_456_780, { spacing: true })).toBe('일억 이천삼백사십오만 육천칠백팔십');
});

test('0과 10,000보다 작은 경우', () => {
expect(numberToHangul(0)).toBe('영');
expect(numberToHangul(1)).toBe('일');
expect(numberToHangul(2)).toBe('이');
expect(numberToHangul(3)).toBe('삼');
expect(numberToHangul(4)).toBe('사');
expect(numberToHangul(5)).toBe('오');
expect(numberToHangul(6)).toBe('육');
expect(numberToHangul(7)).toBe('칠');
expect(numberToHangul(8)).toBe('팔');
expect(numberToHangul(9)).toBe('구');
expect(numberToHangul(10)).toBe('십');
expect(numberToHangul(11)).toBe('십일');
expect(numberToHangul(20)).toBe('이십');
expect(numberToHangul(30)).toBe('삼십');
expect(numberToHangul(100)).toBe('백');
expect(numberToHangul(101)).toBe('백일');
expect(numberToHangul(110)).toBe('백십');
expect(numberToHangul(200)).toBe('이백');
expect(numberToHangul(300)).toBe('삼백');
expect(numberToHangul(1_000)).toBe('천');
expect(numberToHangul(1_001)).toBe('천일');
expect(numberToHangul(1_100)).toBe('천백');
expect(numberToHangul(1_200)).toBe('천이백');
expect(numberToHangul(1_234)).toBe('천이백삼십사');
expect(numberToHangul(9_999)).toBe('구천구백구십구');
});
});
46 changes: 46 additions & 0 deletions src/numberToHangul/numberToHangul.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { HANGUL_CARDINAL, HANGUL_DIGITS, HANGUL_NUMBERS } from '@/_internal/constants';

export function numberToHangul(input: number, options?: { spacing?: boolean }): string {
if (input === 0) {
return '영';
}

const koreanParts: string[] = [];
let remainingDigits = input.toString();
let placeIndex = 0;

while (remainingDigits.length > 0) {
const currentPart = remainingDigits.slice(-4);

koreanParts.unshift(`${numberToKoreanUpToThousand(Number(currentPart))}${HANGUL_DIGITS[placeIndex]}`);

remainingDigits = remainingDigits.slice(0, -4);
placeIndex++;
}

if (options?.spacing) {
return koreanParts
.filter(part => part !== '')
.join(' ')
.trim();
}

return koreanParts.join('');
}

function numberToKoreanUpToThousand(num: number): string {
if (num < 0 || num > 9999) {
throw new Error('0 이상 9999 이하의 숫자만 입력 가능합니다.');
}

const koreanDigits = num
.toString()
.split('')
.reverse()
.map((digit, index) => (digit === '0' ? '' : HANGUL_NUMBERS[Number(digit)] + HANGUL_CARDINAL[index]))
.reverse()
.join('');

return koreanDigits.replace(/일천/, '천').replace(/일백/, '백').replace(/일십/, '십') || '';

}
1 change: 1 addition & 0 deletions src/numberToHangulMixed/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './numberToHangulMixed';
43 changes: 43 additions & 0 deletions src/numberToHangulMixed/numberToHangulMixed.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { numberToHangulMixed } from './numberToHangulMixed';

describe('numberToHangulMixed', () => {
test('기본 변환', () => {
expect(numberToHangulMixed(210_000)).toBe('21만');
expect(numberToHangulMixed(12_345)).toBe('1만2,345');
expect(numberToHangulMixed(123_456_780)).toBe('1억2,345만6,780');
});

test('공백 포함 변환', () => {
expect(numberToHangulMixed(210_000, { spacing: true })).toBe('21만');
expect(numberToHangulMixed(12_345, { spacing: true })).toBe('1만 2,345');
expect(numberToHangulMixed(123_456_780, { spacing: true })).toBe('1억 2,345만 6,780');
});

test('0과 10,000보다 작은 경우', () => {
expect(numberToHangulMixed(0)).toBe('0');
expect(numberToHangulMixed(1)).toBe('1');
expect(numberToHangulMixed(2)).toBe('2');
expect(numberToHangulMixed(3)).toBe('3');
expect(numberToHangulMixed(4)).toBe('4');
expect(numberToHangulMixed(5)).toBe('5');
expect(numberToHangulMixed(6)).toBe('6');
expect(numberToHangulMixed(7)).toBe('7');
expect(numberToHangulMixed(8)).toBe('8');
expect(numberToHangulMixed(9)).toBe('9');
expect(numberToHangulMixed(10)).toBe('10');
expect(numberToHangulMixed(11)).toBe('11');
expect(numberToHangulMixed(20)).toBe('20');
expect(numberToHangulMixed(30)).toBe('30');
expect(numberToHangulMixed(100)).toBe('100');
expect(numberToHangulMixed(101)).toBe('101');
expect(numberToHangulMixed(110)).toBe('110');
expect(numberToHangulMixed(200)).toBe('200');
expect(numberToHangulMixed(300)).toBe('300');
expect(numberToHangulMixed(1_000)).toBe('1,000');
expect(numberToHangulMixed(1_001)).toBe('1,001');
expect(numberToHangulMixed(1_100)).toBe('1,100');
expect(numberToHangulMixed(1_200)).toBe('1,200');
expect(numberToHangulMixed(1_234)).toBe('1,234');
expect(numberToHangulMixed(9_999)).toBe('9,999');
});
});
Loading