사용자 정의 표시를 위한 형식화된 숫자의 개별 부분을 가져오는 방법
형식화된 숫자를 구성 요소로 분리하여 사용자 정의 스타일을 적용하고 복잡한 인터페이스 구축
소개
'format()' 메서드는 "$1,234.56" 또는 "1.5M"과 같은 완전히 형식화된 문자열을 반환합니다. 이는 단순 표시에는 잘 작동하지만, 개별 부분을 다르게 스타일링할 수 없습니다. 통화 기호를 굵게 만들거나, 소수 부분을 다른 색상으로 지정하거나, 특정 구성 요소에 사용자 정의 마크업을 적용할 수 없습니다.
자바스크립트는 이 문제를 해결하기 위해 'formatToParts()' 메서드를 제공합니다. 단일 문자열을 반환하는 대신, 형식화된 숫자의 각 부분을 나타내는 객체 배열을 반환합니다. 각 부분은 'currency', 'integer', 또는 'decimal'과 같은 유형과 '$', '1234', 또는 '.'과 같은 값을 가집니다. 이러한 부분을 처리하여 사용자 정의 스타일을 적용하고, 복잡한 레이아웃을 구축하거나, 형식화된 숫자를 풍부한 사용자 인터페이스에 통합할 수 있습니다.
형식화된 문자열을 사용자 정의하기 어려운 이유
"$1,234.56"과 같은 형식화된 문자열을 받으면, 통화 기호가 어디서 끝나고 숫자가 어디서 시작되는지 쉽게 식별할 수 없습니다. 다양한 로케일은 기호를 다른 위치에 배치합니다. 일부 로케일은 다른 구분자를 사용합니다. 이러한 문자열을 안정적으로 파싱하려면 Intl API에 이미 구현된 형식 지정 규칙을 복제하는 복잡한 로직이 필요합니다.
통화 기호를 다른 색상으로 표시하는 대시보드를 고려해보세요. 'format()'을 사용하면 다음과 같은 작업이 필요합니다:
- 어떤 문자가 통화 기호인지 감지
- 기호와 숫자 사이의 공백 고려
- 로케일 간 다른 기호 위치 처리
- 숫자가 손상되지 않도록 문자열을 신중하게 파싱
이 접근 방식은 취약하고 오류가 발생하기 쉽습니다. 로케일 형식 지정 규칙이 변경되면 파싱 로직이 손상됩니다.
'formatToParts()' 메서드는 구성 요소를 별도로 제공하여 이 문제를 해결합니다. 로케일에 관계없이 어떤 부분이 무엇인지 정확히 알려주는 구조화된 데이터를 받습니다.
formatToParts를 사용하여 숫자 구성요소 가져오기
formatToParts() 메서드는 반환 값을 제외하고 format()과 동일하게 작동합니다. 동일한 옵션으로 포맷터를 생성한 다음 format() 대신 formatToParts()를 호출합니다.
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
});
const parts = formatter.formatToParts(1234.56);
console.log(parts);
이는 다음과 같은 객체 배열을 출력합니다:
[
{ type: "currency", value: "$" },
{ type: "integer", value: "1" },
{ type: "group", value: "," },
{ type: "integer", value: "234" },
{ type: "decimal", value: "." },
{ type: "fraction", value: "56" }
]
각 객체는 해당 부분이 나타내는 것을 식별하는 type 속성과 실제 문자열을 포함하는 value 속성을 포함합니다. 각 부분은 포맷된 출력에서와 동일한 순서로 나타납니다.
모든 값을 함께 연결하여 이를 확인할 수 있습니다:
const formatted = parts.map(part => part.value).join("");
console.log(formatted);
// 출력: "$1,234.56"
연결된 부분들은 format()을 호출했을 때와 정확히 동일한 출력을 생성합니다.
부분 유형 이해하기
type 속성은 각 구성요소를 식별합니다. 다양한 포맷팅 옵션은 서로 다른 부분 유형을 생성합니다.
기본 숫자 포맷팅의 경우:
const formatter = new Intl.NumberFormat("en-US");
const parts = formatter.formatToParts(1234.56);
console.log(parts);
// [
// { type: "integer", value: "1" },
// { type: "group", value: "," },
// { type: "integer", value: "234" },
// { type: "decimal", value: "." },
// { type: "fraction", value: "56" }
// ]
integer 유형은 정수 부분을 나타냅니다. 그룹 구분자가 숫자를 분할할 때 여러 integer 부분이 나타납니다. group 유형은 천 단위 구분자를 나타냅니다. decimal 유형은 소수점을 나타냅니다. fraction 유형은 소수점 이후의 숫자를 나타냅니다.
통화 포맷팅의 경우:
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "EUR"
});
const parts = formatter.formatToParts(1234.56);
console.log(parts);
// [
// { type: "currency", value: "€" },
// { type: "integer", value: "1" },
// { type: "group", value: "," },
// { type: "integer", value: "234" },
// { type: "decimal", value: "." },
// { type: "fraction", value: "56" }
// ]
currency 유형은 로케일 규칙에 따라 숫자 앞이나 뒤에 나타납니다.
백분율의 경우:
const formatter = new Intl.NumberFormat("en-US", {
style: "percent"
});
const parts = formatter.formatToParts(0.1234);
console.log(parts);
// [
// { type: "integer", value: "12" },
// { type: "percentSign", value: "%" }
// ]
percentSign 유형은 퍼센트 기호를 나타냅니다.
간결 표기법의 경우:
const formatter = new Intl.NumberFormat("en-US", {
notation: "compact"
});
const parts = formatter.formatToParts(1500000);
console.log(parts);
// [
// { type: "integer", value: "1" },
// { type: "decimal", value: "." },
// { type: "fraction", value: "5" },
// { type: "compact", value: "M" }
// ]
compact 유형은 K, M 또는 B와 같은 크기 표시자를 나타냅니다.
숫자 부분에 사용자 정의 스타일 적용하기
'formatToParts()'의 주요 사용 사례는 다양한 구성 요소에 서로 다른 스타일을 적용하는 것입니다. 부분 배열을 처리하여 특정 유형을 HTML 요소로 감쌀 수 있습니다.
통화 기호를 굵게 표시하기:
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
});
const parts = formatter.formatToParts(1234.56);
const html = parts
.map(part => {
if (part.type === "currency") {
return `<strong>${part.value}</strong>`;
}
return part.value;
})
.join("");
console.log(html);
// 출력: "<strong>$</strong>1,234.56"
이 접근 방식은 모든 마크업 언어에서 작동합니다. 부분 배열을 처리하여 HTML, JSX 또는 다른 형식을 생성할 수 있습니다.
소수 부분을 다르게 스타일링하기:
const formatter = new Intl.NumberFormat("en-US", {
minimumFractionDigits: 2
});
const parts = formatter.formatToParts(1234.5);
const html = parts
.map(part => {
if (part.type === "decimal" || part.type === "fraction") {
return `<span class="text-gray-500">${part.value}</span>`;
}
return part.value;
})
.join("");
console.log(html);
// 출력: "1,234<span class="text-gray-500">.50</span>"
이 패턴은 소수 부분이 더 작거나 밝게 표시되는 가격 표시에서 일반적으로 사용됩니다.
음수 색상 코딩하기
금융 애플리케이션에서는 종종 음수를 빨간색으로 표시합니다. 'formatToParts()'를 사용하면 마이너스 기호를 감지하고 그에 따라 스타일을 적용할 수 있습니다.
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
});
function formatWithColor(number) {
const parts = formatter.formatToParts(number);
const hasMinusSign = parts.some(part => part.type === "minusSign");
const html = parts
.map(part => part.value)
.join("");
if (hasMinusSign) {
return `<span class="text-red-600">${html}</span>`;
}
return html;
}
console.log(formatWithColor(-1234.56));
// 출력: "<span class="text-red-600">-$1,234.56</span>"
console.log(formatWithColor(1234.56));
// 출력: "$1,234.56"
이 접근 방식은 음수 표시에 다른 기호나 위치를 사용하는 로케일을 포함하여 모든 로케일에서 음수를 안정적으로 감지합니다.
다양한 스타일로 커스텀 숫자 표시 구축하기
복잡한 인터페이스는 종종 여러 스타일링 규칙을 결합합니다. 서로 다른 클래스나 요소를 여러 부분 유형에 동시에 적용할 수 있습니다.
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
});
function formatCurrency(number) {
const parts = formatter.formatToParts(number);
return parts
.map(part => {
switch (part.type) {
case "currency":
return `<span class="currency-symbol">${part.value}</span>`;
case "integer":
return `<span class="integer">${part.value}</span>`;
case "group":
return `<span class="group">${part.value}</span>`;
case "decimal":
return `<span class="decimal">${part.value}</span>`;
case "fraction":
return `<span class="fraction">${part.value}</span>`;
case "minusSign":
return `<span class="minus">${part.value}</span>`;
default:
return part.value;
}
})
.join("");
}
console.log(formatCurrency(1234.56));
// 출력: "<span class="currency-symbol">$</span><span class="integer">1</span><span class="group">,</span><span class="integer">234</span><span class="decimal">.</span><span class="fraction">56</span>"
이러한 세분화된 제어를 통해 각 구성 요소에 대한 정밀한 스타일링이 가능합니다. 그런 다음 CSS를 사용하여 각 클래스를 다르게 스타일링할 수 있습니다.
사용 가능한 모든 부분 유형
사용된 포맷팅 옵션에 따라 type 속성은 다음과 같은 값을 가질 수 있습니다:
integer: 정수 부분 숫자fraction: 소수 부분 숫자decimal: 소수점 구분자group: 천 단위 구분자currency: 통화 기호literal: 포맷팅에 의해 추가된 공백이나 기타 리터럴 텍스트percentSign: 퍼센트 기호minusSign: 음수 표시자plusSign: 양수 표시자(signDisplay가 설정된 경우)unit: 단위 포맷팅을 위한 단위 문자열compact: 간결 표기법의 크기 표시자(K, M, B)exponentInteger: 과학적 표기법의 지수 값exponentMinusSign: 지수의 음수 부호exponentSeparator: 가수와 지수를 구분하는 기호infinity: 무한대 표현nan: 숫자가 아님 표현unknown: 인식되지 않은 토큰
모든 포맷팅 옵션이 모든 부분 유형을 생성하지는 않습니다. 받는 부분은 숫자 값과 포맷터 구성에 따라 달라집니다.
과학적 표기법은 지수 관련 부분을 생성합니다:
const formatter = new Intl.NumberFormat("en-US", {
notation: "scientific"
});
const parts = formatter.formatToParts(1234);
console.log(parts);
// [
// { type: "integer", value: "1" },
// { type: "decimal", value: "." },
// { type: "fraction", value: "234" },
// { type: "exponentSeparator", value: "E" },
// { type: "exponentInteger", value: "3" }
// ]
특수 값은 특정 부분 유형을 생성합니다:
const formatter = new Intl.NumberFormat("en-US");
console.log(formatter.formatToParts(Infinity));
// [{ type: "infinity", value: "∞" }]
console.log(formatter.formatToParts(NaN));
// [{ type: "nan", value: "NaN" }]
접근성 있는 숫자 표시 만들기
formatToParts()를 사용하여 형식이 지정된 숫자에 접근성 속성을 추가할 수 있습니다. 이는 스크린 리더가 값을 올바르게 읽을 수 있도록 도와줍니다.
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
});
function formatAccessibleCurrency(number) {
const parts = formatter.formatToParts(number);
const formatted = parts.map(part => part.value).join("");
return `<span aria-label="${number} US dollars">${formatted}</span>`;
}
console.log(formatAccessibleCurrency(1234.56));
// 출력: "<span aria-label="1234.56 US dollars">$1,234.56</span>"
이렇게 하면 스크린 리더가 형식이 지정된 표시 값과 적절한 컨텍스트가 있는 기본 숫자 값을 모두 읽을 수 있습니다.
특정 숫자 범위 강조 표시
일부 애플리케이션은 특정 범위 내에 있는 숫자를 강조 표시합니다. formatToParts()를 사용하면 적절한 형식을 유지하면서 값에 따라 스타일을 적용할 수 있습니다.
const formatter = new Intl.NumberFormat("en-US");
function formatWithThreshold(number, threshold) {
const parts = formatter.formatToParts(number);
const formatted = parts.map(part => part.value).join("");
if (number >= threshold) {
return `<span class="text-green-600 font-bold">${formatted}</span>`;
}
return formatted;
}
console.log(formatWithThreshold(1500, 1000));
// 출력: "<span class="text-green-600 font-bold">1,500</span>"
console.log(formatWithThreshold(500, 1000));
// 출력: "500"
숫자는 로케일에 맞게 적절한 형식을 받으면서 비즈니스 로직에 따라 조건부 스타일이 적용됩니다.
formatToParts와 format을 사용해야 하는 경우
사용자 지정 없이 단순히 형식이 지정된 문자열이 필요한 경우 format()을 사용하세요. 이는 대부분의 숫자 표시에서 일반적인 경우입니다.
다음과 같은 경우에는 formatToParts()를 사용하세요:
- 숫자의 다른 부분에 다른 스타일을 적용해야 할 때
- 형식이 지정된 숫자로 HTML 또는 JSX를 구성할 때
- 특정 구성 요소에 속성이나 메타데이터를 추가할 때
- 형식이 지정된 숫자를 복잡한 레이아웃에 통합할 때
- 형식이 지정된 출력을 프로그래밍 방식으로 처리할 때
formatToParts() 메서드는 단일 문자열 대신 객체 배열을 생성하기 때문에 format()보다 약간 더 많은 오버헤드가 있습니다. 이 차이는 일반적인 애플리케이션에서는 무시할 수 있지만, 초당 수천 개의 숫자 형식을 지정하는 경우 format()이 더 좋은 성능을 보입니다.
대부분의 애플리케이션에서는 성능보다는 스타일링 요구 사항에 따라 선택하세요. 출력을 사용자 지정할 필요가 없다면 format()을 사용하세요. 사용자 지정 스타일이나 마크업이 필요하다면 formatToParts()를 사용하세요.
파트가 로케일별 형식을 유지하는 방법
파트 배열은 로케일별 형식 규칙을 자동으로 유지합니다. 다양한 로케일은 기호를 다른 위치에 배치하고 다른 구분자를 사용하지만, formatToParts()는 이러한 차이를 처리합니다.
const usdFormatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
});
console.log(usdFormatter.formatToParts(1234.56));
// [
// { type: "currency", value: "$" },
// { type: "integer", value: "1" },
// { type: "group", value: "," },
// { type: "integer", value: "234" },
// { type: "decimal", value: "." },
// { type: "fraction", value: "56" }
// ]
const eurFormatter = new Intl.NumberFormat("de-DE", {
style: "currency",
currency: "EUR"
});
console.log(eurFormatter.formatToParts(1234.56));
// [
// { type: "integer", value: "1" },
// { type: "group", value: "." },
// { type: "integer", value: "234" },
// { type: "decimal", value: "," },
// { type: "fraction", value: "56" },
// { type: "literal", value: " " },
// { type: "currency", value: "€" }
// ]
독일어 형식은 통화 기호를 숫자 뒤에 공백과 함께 배치합니다. 천 단위 구분자는 마침표이고, 소수점 구분자는 쉼표입니다. 스타일링 코드는 로케일에 관계없이 파트 배열을 동일한 방식으로 처리하며, 형식은 자동으로 조정됩니다.
literal 타입은 포맷터에 의해 삽입된 공백이나 다른 카테고리에 맞지 않는 텍스트를 나타냅니다. 독일어 통화 형식에서는 숫자와 통화 기호 사이의 공백을 나타냅니다.
formatToParts와 프레임워크 컴포넌트 결합하기
React와 같은 현대적인 프레임워크는 formatToParts()를 사용하여 효율적으로 컴포넌트를 구축할 수 있습니다.
function CurrencyDisplay({ value, locale, currency }) {
const formatter = new Intl.NumberFormat(locale, {
style: "currency",
currency: currency
});
const parts = formatter.formatToParts(value);
return (
<span className="currency-display">
{parts.map((part, index) => {
if (part.type === "currency") {
return <strong key={index}>{part.value}</strong>;
}
if (part.type === "fraction" || part.type === "decimal") {
return <span key={index} className="text-sm text-gray-500">{part.value}</span>;
}
return <span key={index}>{part.value}</span>;
})}
</span>
);
}
이 컴포넌트는 모든 로케일과 통화에 대해 적절한 형식을 유지하면서 다양한 파트에 다른 스타일을 적용합니다.