스타일링을 위해 포맷된 출력을 조각으로 분할하는 방법
formatToParts()를 사용하여 커스텀 스타일링을 위한 포맷된 출력의 개별 구성 요소에 액세스하기
소개
JavaScript 포매터의 format() 메서드는 "$1,234.56" 또는 "January 15, 2025"와 같은 완전한 문자열을 반환합니다. 이는 단순한 표시에는 적합하지만 개별 부분을 다르게 스타일링할 수 없습니다. 통화 기호를 굵게 만들거나, 월 이름에 다른 색상을 적용하거나, 특정 구성 요소에 커스텀 마크업을 적용할 수 없습니다.
JavaScript는 이 문제를 해결하기 위해 formatToParts() 메서드를 제공합니다. 단일 문자열을 반환하는 대신, 포맷된 출력의 각 부분을 나타내는 객체 배열을 반환합니다. 각 부분은 currency, month 또는 element와 같은 타입과 실제 문자열을 포함하는 값을 가집니다. 그런 다음 이러한 부분을 처리하여 커스텀 스타일링을 적용하거나, 복잡한 레이아웃을 구축하거나, 포맷된 콘텐츠를 풍부한 사용자 인터페이스에 통합할 수 있습니다.
formatToParts() 메서드는 NumberFormat, DateTimeFormat, ListFormat, RelativeTimeFormat, DurationFormat를 포함한 여러 Intl 포매터에서 사용할 수 있습니다. 이는 JavaScript의 모든 국제화 포맷팅에서 일관된 패턴을 제공합니다.
포맷된 문자열을 쉽게 스타일링할 수 없는 이유
"$1,234.56"과 같은 포맷된 문자열을 받으면 통화 기호가 끝나고 숫자가 시작되는 위치를 쉽게 식별할 수 없습니다. 로케일마다 기호를 다른 위치에 배치합니다. 일부 로케일은 다른 구분 기호를 사용합니다. 이러한 문자열을 안정적으로 파싱하려면 Intl API에 이미 구현된 포맷팅 규칙을 복제하는 복잡한 로직이 필요합니다.
통화 기호를 다른 색상으로 표시하는 금액을 보여주는 대시보드를 생각해 보세요. format()를 사용하면 다음을 수행해야 합니다:
- 어떤 문자가 통화 기호인지 감지
- 기호와 숫자 사이의 공백 처리
- 로케일별로 다른 기호 위치 처리
- 숫자를 손상시키지 않도록 문자열을 신중하게 파싱
이 접근 방식은 취약하고 오류가 발생하기 쉽습니다. 로케일 형식 규칙이 변경되면 파싱 로직이 손상됩니다.
날짜, 목록 및 기타 형식화된 출력에도 동일한 문제가 존재합니다. 로케일별 형식 규칙을 다시 구현하지 않고는 형식화된 문자열을 파싱하여 구성 요소를 식별할 수 없습니다.
formatToParts() 메서드는 구성 요소를 별도로 제공하여 이 문제를 해결합니다. 로케일에 관계없이 어떤 부분이 무엇인지 정확히 알려주는 구조화된 데이터를 받습니다.
formatToParts 작동 방식
formatToParts() 메서드는 반환 값을 제외하고 format()와 동일하게 작동합니다. 동일한 옵션으로 포매터를 생성한 다음 format() 대신 formatToParts()를 호출합니다.
이 메서드는 객체 배열을 반환합니다. 각 객체에는 두 가지 속성이 포함됩니다:
type:currency,month또는literal와 같이 해당 부분이 나타내는 것을 식별합니다value: 해당 부분의 실제 문자열을 포함합니다
부분은 형식화된 출력에 나타나는 순서와 동일하게 표시됩니다. 모든 값을 결합하면 format()를 호출한 것과 정확히 동일한 출력이 생성되는지 확인할 수 있습니다.
이 패턴은 formatToParts()를 지원하는 모든 포매터에서 일관됩니다. 특정 부분 유형은 포매터에 따라 다르지만 구조는 항상 동일합니다.
형식화된 숫자를 부분으로 분할
NumberFormat 포매터는 형식화된 숫자를 분해하기 위한 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" }
]
각 객체는 해당 부분이 무엇을 나타내는지 식별하고 그 값을 제공합니다. currency 유형은 통화 기호를 나타냅니다. integer 유형은 정수 자릿수를 나타냅니다. group 유형은 천 단위 구분 기호를 나타냅니다. decimal 유형은 소수점을 나타냅니다. fraction 유형은 소수점 이하 자릿수를 나타냅니다.
부분들이 형식화된 출력과 일치하는지 확인할 수 있습니다.
const formatted = parts.map(part => part.value).join("");
console.log(formatted);
// Output: "$1,234.56"
연결된 부분들은 format()를 호출한 것과 정확히 동일한 출력을 생성합니다.
형식화된 숫자에서 통화 기호 스타일링하기
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);
// Output: "<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);
// Output: "1,234<span class="text-gray-500">.50</span>"
이 패턴은 소수 부분이 더 작거나 더 밝게 표시되는 가격 표시에서 일반적입니다.
형식화된 날짜를 부분으로 분할하기
DateTimeFormat 포매터는 형식화된 날짜와 시간을 분해하기 위한 formatToParts()를 제공합니다.
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
console.log(parts);
다음과 같은 객체 배열이 출력됩니다.
[
{ type: "month", value: "January" },
{ type: "literal", value: " " },
{ type: "day", value: "15" },
{ type: "literal", value: ", " },
{ type: "year", value: "2025" }
]
month 유형은 월 이름 또는 숫자를 나타냅니다. day 유형은 일을 나타냅니다. year 유형은 연도를 나타냅니다. literal 유형은 포매터가 삽입한 공백, 구두점 또는 기타 텍스트를 나타냅니다.
형식화된 날짜에서 월 이름 스타일링하기
숫자와 동일한 패턴을 사용하여 날짜 구성 요소에 사용자 정의 스타일을 적용할 수 있습니다.
월 이름을 굵게 만들기:
const formatter = new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long",
day: "numeric"
});
const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
const html = parts
.map(part => {
if (part.type === "month") {
return `<strong>${part.value}</strong>`;
}
return part.value;
})
.join("");
console.log(html);
// Output: "<strong>January</strong> 15, 2025"
여러 날짜 구성 요소 스타일링하기:
const formatter = new Intl.DateTimeFormat("en-US", {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric"
});
const date = new Date(2025, 0, 15);
const parts = formatter.formatToParts(date);
const html = parts
.map(part => {
switch (part.type) {
case "weekday":
return `<span class="font-bold">${part.value}</span>`;
case "month":
return `<span class="text-blue-600">${part.value}</span>`;
case "year":
return `<span class="text-gray-500">${part.value}</span>`;
default:
return part.value;
}
})
.join("");
console.log(html);
// Output: "<span class="font-bold">Wednesday</span>, <span class="text-blue-600">January</span> 15, <span class="text-gray-500">2025</span>"
이러한 세밀한 제어를 통해 각 구성 요소에 대한 정확한 스타일링이 가능합니다.
형식화된 목록을 부분으로 분할하기
ListFormat 포매터는 형식화된 목록을 분해하기 위한 formatToParts()를 제공합니다.
const formatter = new Intl.ListFormat("en-US", {
style: "long",
type: "conjunction"
});
const items = ["apples", "oranges", "bananas"];
const parts = formatter.formatToParts(items);
console.log(parts);
이것은 객체 배열을 출력합니다:
[
{ type: "element", value: "apples" },
{ type: "literal", value: ", " },
{ type: "element", value: "oranges" },
{ type: "literal", value: ", and " },
{ type: "element", value: "bananas" }
]
element 타입은 목록의 각 항목을 나타냅니다. literal 타입은 포매터에 의해 추가된 구분 기호와 접속사를 나타냅니다.
목록 항목을 개별적으로 스타일링하기
동일한 패턴을 사용하여 목록 요소에 사용자 정의 스타일을 적용할 수 있습니다.
목록 항목을 굵게 만들기:
const formatter = new Intl.ListFormat("en-US", {
style: "long",
type: "conjunction"
});
const items = ["apples", "oranges", "bananas"];
const parts = formatter.formatToParts(items);
const html = parts
.map(part => {
if (part.type === "element") {
return `<strong>${part.value}</strong>`;
}
return part.value;
})
.join("");
console.log(html);
// Output: "<strong>apples</strong>, <strong>oranges</strong>, and <strong>bananas</strong>"
특정 목록 항목 스타일링하기:
const formatter = new Intl.ListFormat("en-US", {
style: "long",
type: "conjunction"
});
const items = ["apples", "oranges", "bananas"];
const parts = formatter.formatToParts(items);
let itemIndex = 0;
const html = parts
.map(part => {
if (part.type === "element") {
const currentIndex = itemIndex++;
if (currentIndex === 0) {
return `<span class="text-green-600">${part.value}</span>`;
}
return part.value;
}
return part.value;
})
.join("");
console.log(html);
// Output: "<span class="text-green-600">apples</span>, oranges, and bananas"
이 접근 방식을 사용하면 적절한 로케일별 형식을 유지하면서 특정 항목을 강조 표시할 수 있습니다.
형식화된 상대 시간을 부분으로 분할하기
RelativeTimeFormat 포매터는 상대 시간 표현을 분해하기 위한 formatToParts()를 제공합니다.
const formatter = new Intl.RelativeTimeFormat("en-US", {
numeric: "auto"
});
const parts = formatter.formatToParts(-1, "day");
console.log(parts);
이것은 객체 배열을 출력합니다:
{/* CODE_PLACEHOLDER_abd978769d38434511f86f02f249689b */
숫자 상대 시간의 경우:
const formatter = new Intl.RelativeTimeFormat("en-US", {
numeric: "always"
});
const parts = formatter.formatToParts(-3, "day");
console.log(parts);
// [
// { type: "integer", value: "3" },
// { type: "literal", value: " days ago" }
// ]
integer 타입은 숫자 값을 나타냅니다. literal 타입은 상대 시간 단위와 방향을 나타냅니다.
형식화된 기간을 부분으로 분할하기
DurationFormat 포매터는 형식화된 기간을 분해하기 위한 formatToParts()를 제공합니다.
const formatter = new Intl.DurationFormat("en-US", {
style: "long"
});
const parts = formatter.formatToParts({
hours: 2,
minutes: 30,
seconds: 15
});
console.log(parts);
이것은 다음과 유사한 객체 배열을 출력합니다:
[
{ type: "integer", value: "2" },
{ type: "literal", value: " hours, " },
{ type: "integer", value: "30" },
{ type: "literal", value: " minutes, " },
{ type: "integer", value: "15" },
{ type: "literal", value: " seconds" }
]
integer 타입은 숫자 값을 나타냅니다. literal 타입은 단위 이름과 구분 기호를 나타냅니다.
형식화된 부분에서 HTML 생성하기
부분을 처리하고 스타일 규칙을 일관되게 적용하는 재사용 가능한 함수를 만들 수 있습니다.
function formatWithStyles(parts, styleMap) {
return parts
.map(part => {
const style = styleMap[part.type];
if (style) {
return `<span class="${style}">${part.value}</span>`;
}
return part.value;
})
.join("");
}
const numberFormatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
});
const parts = numberFormatter.formatToParts(1234.56);
const html = formatWithStyles(parts, {
currency: "font-bold text-gray-700",
integer: "text-2xl",
fraction: "text-sm text-gray-500"
});
console.log(html);
// Output: "<span class="font-bold text-gray-700">$</span><span class="text-2xl">1</span>,<span class="text-2xl">234</span>.<span class="text-sm text-gray-500">56</span>"
이 패턴은 스타일 규칙을 형식화 로직에서 분리하여 유지 관리와 재사용을 더 쉽게 만듭니다.
로케일별 부분 순서 이해하기
부분 배열은 로케일별 형식화 규칙을 자동으로 유지합니다. 로케일마다 구성 요소를 다른 순서로 배치하고 다른 형식을 사용하지만, 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: "€" }
// ]
독일어 형식화는 통화를 숫자 뒤에 공백과 함께 배치합니다. 그룹 구분 기호는 마침표이고 소수점 구분 기호는 쉼표입니다. 스타일 코드는 로케일에 관계없이 동일한 방식으로 부분 배열을 처리하며, 형식화는 자동으로 조정됩니다.
접근 가능한 형식화된 표시 만들기
formatToParts()를 사용하여 형식화된 출력에 접근성 속성을 추가할 수 있습니다. 이는 스크린 리더가 값을 올바르게 읽도록 돕습니다.
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
});
function formatAccessible(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(formatAccessible(1234.56));
// Output: "<span aria-label="1234.56 US dollars">$1,234.56</span>"
이렇게 하면 스크린 리더가 형식화된 표시 값과 기본 숫자 값을 적절한 컨텍스트와 함께 읽습니다.
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>
);
}
이 컴포넌트는 모든 로케일과 통화에 대해 적절한 형식을 유지하면서 각 부분에 다른 스타일을 적용합니다.
formatToParts를 사용해야 하는 경우
format()는 사용자 정의 없이 간단한 형식화된 문자열이 필요할 때 사용하세요. 이는 대부분의 표시 시나리오에서 일반적인 경우입니다.
formatToParts()는 다음이 필요할 때 사용하세요:
- 형식화된 출력의 각 부분에 다른 스타일 적용
- 형식화된 콘텐츠로 HTML 또는 JSX 구축
- 특정 컴포넌트에 속성 또는 메타데이터 추가
- 형식화된 출력을 복잡한 레이아웃에 통합
- 형식화된 출력을 프로그래밍 방식으로 처리
- 세밀한 제어가 필요한 맞춤형 시각적 디자인 생성
formatToParts() 메서드는 단일 문자열 대신 객체 배열을 생성하기 때문에 format()보다 약간 더 많은 오버헤드가 있습니다. 이 차이는 일반적인 애플리케이션에서는 무시할 수 있지만, 초당 수천 개의 값을 형식화하는 경우 format()가 더 나은 성능을 제공합니다.
대부분의 애플리케이션에서는 성능 문제보다는 스타일링 요구 사항에 따라 선택하세요. 출력을 사용자 정의할 필요가 없다면 format()를 사용하세요. 맞춤형 스타일링이나 마크업이 필요하다면 formatToParts()를 사용하세요.
포매터 간 공통 파트 타입
각 포매터는 서로 다른 파트 타입을 생성하지만, 여러 포매터에서 공통적으로 나타나는 타입도 있습니다:
literal: 형식화에 의해 추가되는 공백, 구두점 또는 기타 텍스트. 날짜, 숫자, 목록 및 기간에 나타납니다.integer: 정수 자릿수. 숫자, 상대 시간 및 기간에 나타납니다.decimal: 소수점 구분 기호. 숫자에 나타납니다.fraction: 소수 자릿수. 숫자에 나타납니다.
포맷터별 타입은 다음과 같습니다:
- 숫자:
currency,group,percentSign,minusSign,plusSign,unit,compact,exponentInteger - 날짜:
weekday,era,year,month,day,hour,minute,second,dayPeriod,timeZoneName - 목록:
element - 상대 시간: 숫자 값은
integer로 표시되고, 텍스트는literal로 표시됩니다
이러한 타입을 이해하면 모든 포맷터 출력을 올바르게 처리하는 스타일링 코드를 작성하는 데 도움이 됩니다.