Skip to content

Commit

Permalink
feat: 소수점 처리 추가
Browse files Browse the repository at this point in the history
  • Loading branch information
ssi02014 committed Jan 9, 2025
1 parent 421b279 commit 1e14a87
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ describe('formatNumberWithUnits', () => {
const value = 12345;

expect(formatNumberWithUnits(value, { floorUnit: 10000 })).toBe('1만');

// value보다 floorUnit이 크면 '0'을 반환
expect(formatNumberWithUnits(value, { floorUnit: 100000 })).toBe('0');
});

it('commas가 true라면 ","를 추가하며, false라면 제외해야 합니다.', () => {
Expand All @@ -61,6 +64,17 @@ describe('formatNumberWithUnits', () => {
);
});

it('decimal가 주어진 값에 따라 소수점 자리수를 적용해야 합니다.', () => {
expect(formatNumberWithUnits(1234567.123, { decimal: 2 })).toBe(
'123만 4,567.12'
);

// 만약, floorUnit가 1보다 크면 소수점 자리수를 적용하지 않습니다.
expect(
formatNumberWithUnits(1234567.123, { decimal: 2, floorUnit: 1000 })
).toBe('123만 4,000');
});

it('사용자 정의 단위를 적용할 수 있습니다.', () => {
const customUnits = [
{ unit: 'K', value: 1000 },
Expand All @@ -83,4 +97,24 @@ describe('formatNumberWithUnits', () => {
).toBe('1,234,000');
});
});

describe('예외 케이스', () => {
it('value가 숫자 혹은 숫자로 이뤄진 문자열이 아니면 예외를 발생시킵니다.', () => {
expect(() => formatNumberWithUnits('d123ㅇ4567')).toThrow(
'value는 숫자 혹은 숫자로 이뤄진 문자열이여야 합니다.'
);
});

it('floorUnit가 1 이상의 정수가 아니면 예외를 발생시킵니다.', () => {
expect(() =>
formatNumberWithUnits(1234567, { floorUnit: -1 as unknown as 10 })
).toThrow('floorUnit은 1 이상의 정수여야 합니다.');
});

it('decimal이 0보다 작으면 예외를 발생시킵니다.', () => {
expect(() =>
formatNumberWithUnits(1234567, { decimal: -1 as unknown as number })
).toThrow('decimal은 0 이상의 정수여야 합니다.');
});
});
});
38 changes: 25 additions & 13 deletions packages/utils/src/formatter/formatNumberWithUnits/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface FormatNumberWithUnitsOptions {
commas?: boolean;
floorUnit?: FloorUnit;
space?: boolean;
decimal?: number;
}

const ONE_TRILLION = 1_000_000_000_000;
Expand All @@ -46,7 +47,7 @@ export const DEFAULT_UNITS: Unit[] = [
* @returns {string} 포맷팅된 문자열
*/
export const getNumberWithConditionalCommas = (
value: number,
value: number | string,
commas: boolean
): string => {
return commas ? formatNumberWithCommas(value) : String(value);
Expand All @@ -61,6 +62,7 @@ export const getNumberWithConditionalCommas = (
* @param {boolean} [options.commas=true] - 천 단위 구분 쉼표 사용 여부
* @param {number} [options.floorUnit=1] - 버림 단위(기본값: 1). 버림 단위보다 작은 숫자는 '0'으로 반환합니다.
* @param {boolean} [options.space=true] - 단위 사이 공백 추가 여부
* @param {number} [options.decimal=0] - 소수점 자리수
* @returns {string} 포맷팅된 문자열
* @throws 입력값이 숫자로 변환할 수 없는 경우 에러 발생
*
Expand All @@ -83,22 +85,20 @@ export const getNumberWithConditionalCommas = (
* formatNumberWithUnits(1234567, { floorUnit: 10000 }) // "123만"
*
* @example
* // 소수점 자리수
* formatNumberWithUnits(1234567.123, { decimal: 2 }) // "123만 4,567.12"
*
* // floorUnit이 1보다 크면 소수점 자리수를 적용하지 않습니다.
* formatNumberWithUnits(1234567.123, { decimal: 3, floorUnit: 1000 }) // "123만 4,000"
*
* @example
* // 사용자 정의 단위
* const customUnits = [
* { unit: 'K', value: 1000 },
* { unit: 'M', value: 1000000 },
* ];
*
* formatNumberWithUnits(1234567, {
* units: customUnits,
* floorUnit: 1000,
* }) // "1M 234K"
*
* // 단위 적용 X
* formatNumberWithUnits(1234567, {
* units: [],
* floorUnit: 1000,
* }); // "1,234,000"
* formatNumberWithUnits(1234567, { units: customUnits, floorUnit: 1000 }) // "1M 234K"
*/
export function formatNumberWithUnits(
value: number | string,
Expand All @@ -109,6 +109,7 @@ export function formatNumberWithUnits(
space = true,
commas = true,
floorUnit = 1,
decimal = 0,
} = options;

const valueToUse = isNumber(value) ? value : Number(value);
Expand All @@ -117,6 +118,14 @@ export function formatNumberWithUnits(
throw new Error('value는 숫자 혹은 숫자로 이뤄진 문자열이여야 합니다.');
}

if (!Number.isInteger(floorUnit) || floorUnit < 1) {
throw new Error('floorUnit은 1 이상의 정수여야 합니다.');
}

if (!Number.isInteger(decimal) || decimal < 0) {
throw new Error('decimal은 0 이상의 정수여야 합니다.');
}

// value가 floorUnit(버림 단위)보다 작으면 '0'을 반환
if (Math.abs(valueToUse) < floorUnit) {
return '0';
Expand All @@ -127,7 +136,10 @@ export function formatNumberWithUnits(
const sortedUnits = [...units].sort((a, b) => b.value - a.value);

let formattedResult = '';
let remainingValue = Math.floor(absoluteValue / floorUnit) * floorUnit;
let remainingValue =
floorUnit > 1
? Math.floor(absoluteValue / floorUnit) * floorUnit
: absoluteValue;

// unit 별로 나누기
for (let i = 0; i < sortedUnits.length; i++) {
Expand All @@ -147,7 +159,7 @@ export function formatNumberWithUnits(
// 남은 remainingValue가 있으면 추가
if (remainingValue > 0) {
formattedResult += `${getNumberWithConditionalCommas(
remainingValue,
remainingValue.toFixed(decimal),
commas
)}`;
}
Expand Down

0 comments on commit 1e14a87

Please sign in to comment.