From b26fdb438acdcc60a96437a526ad58c845495b01 Mon Sep 17 00:00:00 2001 From: ssi02014 Date: Sun, 12 Jan 2025 18:08:51 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20formatNumberWithUnits=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../formatNumberWithCurrency.utils.ts | 2 +- .../formatNumberWithCurrency/index.ts | 6 +- .../formatNumberWithUnits.types.ts | 27 +++++++ .../formatNumberWithUnits.utils.ts | 65 +++++++++++++++- .../formatter/formatNumberWithUnits/index.ts | 77 +++---------------- 5 files changed, 106 insertions(+), 71 deletions(-) create mode 100644 packages/utils/src/formatter/formatNumberWithUnits/formatNumberWithUnits.types.ts diff --git a/packages/utils/src/formatter/formatNumberWithCurrency/formatNumberWithCurrency.utils.ts b/packages/utils/src/formatter/formatNumberWithCurrency/formatNumberWithCurrency.utils.ts index 12b312b7..b6b94707 100644 --- a/packages/utils/src/formatter/formatNumberWithCurrency/formatNumberWithCurrency.utils.ts +++ b/packages/utils/src/formatter/formatNumberWithCurrency/formatNumberWithCurrency.utils.ts @@ -42,7 +42,7 @@ const getCurrencyOption = ( * @param {boolean} [currencyOption.space=false] - 숫자와 통화 기호 사이 공백 여부 * @returns {string} 통화 기호가 추가된 문자열 */ -export const addCurrency = ( +export const getFormattedNumberWithCurrency = ( value: number, options: FormatNumberCurrencyOptions & { isNegative: boolean } ): string => { diff --git a/packages/utils/src/formatter/formatNumberWithCurrency/index.ts b/packages/utils/src/formatter/formatNumberWithCurrency/index.ts index 2a02f4ea..7b7db37e 100644 --- a/packages/utils/src/formatter/formatNumberWithCurrency/index.ts +++ b/packages/utils/src/formatter/formatNumberWithCurrency/index.ts @@ -1,6 +1,6 @@ import { isNumber } from '../../validator/isNumber'; import { FormatNumberCurrencyOptions } from './formatNumberWithCurrency.types'; -import { addCurrency } from './formatNumberWithCurrency.utils'; +import { getFormattedNumberWithCurrency } from './formatNumberWithCurrency.utils'; /** * @description `숫자 혹은 숫자로 이뤄진 문자열`을 주어진 `통화 기호`를 추가하는 함수입니다. @@ -63,7 +63,7 @@ export function formatNumberWithCurrency( throw new Error('value는 숫자 혹은 숫자로 이뤄진 문자열이여야 합니다.'); } - return addCurrency(valueToUse, { + const formattedResult = getFormattedNumberWithCurrency(valueToUse, { symbol, position, space, @@ -71,4 +71,6 @@ export function formatNumberWithCurrency( commas, isNegative, }); + + return formattedResult; } diff --git a/packages/utils/src/formatter/formatNumberWithUnits/formatNumberWithUnits.types.ts b/packages/utils/src/formatter/formatNumberWithUnits/formatNumberWithUnits.types.ts new file mode 100644 index 00000000..17df2a98 --- /dev/null +++ b/packages/utils/src/formatter/formatNumberWithUnits/formatNumberWithUnits.types.ts @@ -0,0 +1,27 @@ +type FloorUnit = + | 1 + | 10 + | 100 + | 1_000 + | 10_000 + | 100_000 + | 1_000_000 + | 10_000_000 + | 100_000_000 + | 1_000_000_000 + | 10_000_000_000 + | 100_000_000_000 + | 1_000_000_000_000; + +export interface Unit { + unit: string; + value: number; +} + +export interface FormatNumberWithUnitsOptions { + units?: Unit[] | readonly Unit[]; + commas?: boolean; + floorUnit?: FloorUnit; + space?: boolean; + decimal?: number; +} diff --git a/packages/utils/src/formatter/formatNumberWithUnits/formatNumberWithUnits.utils.ts b/packages/utils/src/formatter/formatNumberWithUnits/formatNumberWithUnits.utils.ts index 27b8c94b..cdad8980 100644 --- a/packages/utils/src/formatter/formatNumberWithUnits/formatNumberWithUnits.utils.ts +++ b/packages/utils/src/formatter/formatNumberWithUnits/formatNumberWithUnits.utils.ts @@ -1,4 +1,8 @@ import { formatNumberWithCommas } from '../../formatter/formatNumberWithCommas'; +import { + FormatNumberWithUnitsOptions, + Unit, +} from './formatNumberWithUnits.types'; /** * @description 쉼표 사용 여부에 따라 숫자를 포맷팅하는 함수 @@ -7,9 +11,68 @@ import { formatNumberWithCommas } from '../../formatter/formatNumberWithCommas'; * @param {boolean} commas - 쉼표 사용 여부 * @returns {string} 포맷팅된 문자열 */ -export const getNumberWithConditionalCommas = ( +const getNumberWithConditionalCommas = ( value: number | string, commas: boolean ): string => { return commas ? formatNumberWithCommas(value) : String(value); }; + +/** + * @description 주어진 단위(units)에 따라 숫자를 포맷팅하는 함수 + * + * @param {number} value - 포맷팅할 숫자 값 + * @param {Unit[] | readonly Unit[]} units - 변환할 단위 배열 + * @param {Omit, 'units'>} options - 포맷팅 옵션 + * @param {boolean} options.commas - 천 단위 구분 쉼표 사용 여부입니다. + * @param {boolean} options.space - 단위 사이 공백 추가 여부입니다. + * @param {number} options.decimal - 소수점 자릿수입니다. + * @returns {string} 포맷팅된 문자열 + */ +export const getFormattedValueWithUnits = ( + value: number, + units: Unit[] | readonly Unit[], + options: Omit, 'units'> +): string => { + const { commas, space, decimal, floorUnit } = options; + + const absoluteValue = Math.abs(value); + const isNegative = value < 0; + + // value가 floorUnit(버림 단위)보다 작으면 '0'을 반환 + if (absoluteValue < floorUnit) { + return '0'; + } + + let formattedResult = ''; + let remainingValue = + floorUnit > 1 + ? Math.floor(absoluteValue / floorUnit) * floorUnit + : absoluteValue; + + // unit 별로 나누기 + for (let i = 0; i < units.length; i++) { + const { unit, value } = units[i]; + const quotient = Math.floor(remainingValue / value); + + if (quotient <= 0) continue; + + formattedResult += `${getNumberWithConditionalCommas( + quotient, + commas + )}${unit}${space ? ' ' : ''}`; + + remainingValue %= value; + } + + // 남은 remainingValue가 있으면 추가 + if (remainingValue > 0) { + formattedResult += `${getNumberWithConditionalCommas( + remainingValue.toFixed(decimal), + commas + )}`; + } + + // 음수일 경우 앞에 '-' 붙이며, 앞/뒤 공백 제거 + return (isNegative ? '-' : '') + formattedResult.trim(); +}; diff --git a/packages/utils/src/formatter/formatNumberWithUnits/index.ts b/packages/utils/src/formatter/formatNumberWithUnits/index.ts index 1a47c479..2c9b82f7 100644 --- a/packages/utils/src/formatter/formatNumberWithUnits/index.ts +++ b/packages/utils/src/formatter/formatNumberWithUnits/index.ts @@ -1,33 +1,6 @@ -import { getNumberWithConditionalCommas } from './formatNumberWithUnits.utils'; +import { getFormattedValueWithUnits } from './formatNumberWithUnits.utils'; import { isNumber } from '../../validator/isNumber'; - -interface Unit { - unit: string; - value: number; -} - -type FloorUnit = - | 1 - | 10 - | 100 - | 1_000 - | 10_000 - | 100_000 - | 1_000_000 - | 10_000_000 - | 100_000_000 - | 1_000_000_000 - | 10_000_000_000 - | 100_000_000_000 - | 1_000_000_000_000; - -interface FormatNumberWithUnitsOptions { - units?: Unit[] | readonly Unit[]; - commas?: boolean; - floorUnit?: FloorUnit; - space?: boolean; - decimal?: number; -} +import { FormatNumberWithUnitsOptions } from './formatNumberWithUnits.types'; /** * @description `숫자` 혹은 `숫자로 이루어진 문자열`을 주어진 `단위` 별로 포맷팅하는 함수입니다. @@ -95,9 +68,6 @@ export function formatNumberWithUnits( const sortedUnits = [...units].sort((a, b) => b.value - a.value); const valueToUse = isNumber(value) ? value : Number(value); - const isNegative = valueToUse < 0; - const absoluteValue = Math.abs(valueToUse); - // 에러 처리 if (isNaN(valueToUse)) { throw new Error('value는 숫자 혹은 숫자로 이뤄진 문자열이여야 합니다.'); @@ -111,44 +81,17 @@ export function formatNumberWithUnits( if (isValidFloorUnit) { throw new Error('floorUnit은 1을 포함한 10의 제곱수여야 합니다.'); } + if (!Number.isInteger(decimal) || decimal < 0) { throw new Error('decimal은 0 이상의 정수여야 합니다.'); } - // value가 floorUnit(버림 단위)보다 작으면 '0'을 반환 - if (absoluteValue < floorUnit) { - return '0'; - } - - let formattedResult = ''; - let remainingValue = - floorUnit > 1 - ? Math.floor(absoluteValue / floorUnit) * floorUnit - : absoluteValue; - - // unit 별로 나누기 - for (let i = 0; i < sortedUnits.length; i++) { - const { unit, value } = sortedUnits[i]; - const quotient = Math.floor(remainingValue / value); - - if (quotient <= 0) continue; - - formattedResult += `${getNumberWithConditionalCommas( - quotient, - commas - )}${unit}${space ? ' ' : ''}`; - - remainingValue %= value; - } - - // 남은 remainingValue가 있으면 추가 - if (remainingValue > 0) { - formattedResult += `${getNumberWithConditionalCommas( - remainingValue.toFixed(decimal), - commas - )}`; - } + const formattedResult = getFormattedValueWithUnits(valueToUse, sortedUnits, { + commas, + space, + decimal, + floorUnit, + }); - // 음수일 경우 앞에 '-' 붙이며, 앞/뒤 공백 제거 - return (isNegative ? '-' : '') + formattedResult.trim(); + return formattedResult; }