3-5 또는 100-200과 같은 범위 형식을 지정하는 방법
JavaScript를 사용하여 로케일에 적합한 형식으로 숫자 범위 표시하기
소개
숫자 범위는 사용자 인터페이스 전반에 걸쳐 나타납니다. 가격 범위는 $100-$200으로, 페이지 번호는 1-10으로, 수량 추정치는 3-5개 항목으로 표시됩니다. 이러한 범위는 값이 두 끝점 사이에 있음을 전달하여 사용자에게 단일 정확한 숫자가 아닌 경계가 있는 정보를 제공합니다.
범위 값 사이의 구분 기호를 하드코딩하면 모든 사용자가 동일한 타이포그래피 규칙을 따른다고 가정하게 됩니다. 영어 사용자는 일반적으로 범위에 하이픈이나 en 대시를 사용하지만 다른 언어는 다른 기호나 단어를 사용합니다. 독일어는 숫자 사이에 "bis"를 사용하며 일부 언어는 구분 기호 주위에 공백을 배치합니다.
JavaScript는 Intl.NumberFormat에서 formatRange() 메서드를 제공하여 범위 포맷팅을 자동으로 처리합니다. 이 메서드는 숫자와 구분 기호 모두에 로케일별 규칙을 적용하여 전 세계 사용자에게 범위가 올바르게 표시되도록 보장합니다.
숫자 범위에 로케일별 형식이 필요한 이유
서로 다른 문화권에서는 범위를 표현하기 위한 서로 다른 규칙을 개발했습니다. 이러한 규칙에는 구분 기호와 그 주위의 간격이 모두 포함됩니다.
미국 영어에서 범위는 일반적으로 공백 없이 en 대시를 사용합니다: 3-5, 100-200. 일부 스타일 가이드에서는 대시 주위에 공백이 나타납니다: 3 - 5. 정확한 규칙은 컨텍스트와 출판 표준에 따라 다릅니다.
독일어에서 범위는 종종 "bis"를 구분 기호로 사용합니다: 3 bis 5, 100 bis 200. 이러한 단어 기반 접근 방식은 구두점에 의존하지 않고 범위 관계를 명시적으로 만듭니다.
스페인어에서 범위는 영어처럼 대시를 사용하거나 "a"라는 단어를 사용할 수 있습니다: 3-5 또는 3 a 5. 선택은 특정 스페인어 사용 지역과 컨텍스트에 따라 달라집니다.
통화나 단위를 포함하는 범위를 형식화할 때는 복잡성이 증가합니다. 가격 범위는 미국 영어에서는 $100-$200으로 표시될 수 있지만, 독일어에서는 100 €-200 € 또는 기호가 한 번만 나타나는 100-200 €로 표시될 수 있습니다. 로케일마다 통화 기호를 다르게 배치하고 반복을 다르게 처리합니다.
수동 범위 형식화는 이러한 규칙을 알고 로케일별 로직을 구현해야 합니다. Intl API는 이러한 지식을 캡슐화하여 로케일에 따라 적절한 형식을 적용합니다.
formatRange를 사용하여 숫자 범위 형식화하기
formatRange() 메서드는 두 개의 숫자를 받아 범위를 나타내는 포맷된 문자열을 반환합니다. 원하는 로케일과 옵션으로 Intl.NumberFormat 인스턴스를 생성한 다음 시작 값과 종료 값으로 formatRange()를 호출하세요.
const formatter = new Intl.NumberFormat("en-US");
console.log(formatter.formatRange(3, 5));
// Output: "3–5"
console.log(formatter.formatRange(100, 200));
// Output: "100–200"
console.log(formatter.formatRange(1000, 5000));
// Output: "1,000–5,000"
포매터는 범위의 두 숫자에 천 단위 구분 기호를 적용하고 그 사이에 적절한 구분 기호를 사용합니다. 미국 영어의 경우 공백 없는 en 대시입니다.
로케일 식별자를 변경하여 동일한 범위를 다른 로케일로 형식화할 수 있습니다.
const usFormatter = new Intl.NumberFormat("en-US");
console.log(usFormatter.formatRange(100, 200));
// Output: "100–200"
const deFormatter = new Intl.NumberFormat("de-DE");
console.log(deFormatter.formatRange(100, 200));
// Output: "100–200"
const esFormatter = new Intl.NumberFormat("es-ES");
console.log(esFormatter.formatRange(100, 200));
// Output: "100-200"
각 로케일은 구분 기호와 간격에 대한 자체 규칙을 적용합니다. API는 로케일의 타이포그래피 표준에 따라 이러한 세부 사항을 자동으로 처리합니다.
통화 범위 형식화하기
범위 형식화는 통화를 포함한 모든 숫자 형식화 옵션과 함께 작동합니다. 통화 범위를 형식화할 때 포매터는 통화 기호 배치와 범위 구분 기호를 모두 처리합니다.
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
maximumFractionDigits: 0
});
console.log(formatter.formatRange(100, 200));
// Output: "$100 – $200"
console.log(formatter.formatRange(1000, 5000));
// Output: "$1,000 – $5,000"
포매터는 범위의 각 숫자 앞에 통화 기호를 배치합니다. 이렇게 하면 두 값이 모두 통화 금액을 나타낸다는 것이 명확해집니다.
로케일마다 통화 기호를 다르게 배치합니다.
const usFormatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
maximumFractionDigits: 0
});
console.log(usFormatter.formatRange(100, 200));
// Output: "$100 – $200"
const deFormatter = new Intl.NumberFormat("de-DE", {
style: "currency",
currency: "EUR",
maximumFractionDigits: 0
});
console.log(deFormatter.formatRange(100, 200));
// Output: "100–200 €"
독일어 포매터는 각 숫자 앞이 아닌 범위 뒤에 유로 기호를 배치합니다. 이는 통화 범위에 대한 독일어 타이포그래피 규칙을 따릅니다.
범위 값이 거의 같을 때 발생하는 현상
시작 값과 끝 값이 형식화 후 같은 숫자로 반올림되면 포매터는 범위를 축소하고 근사 기호를 추가할 수 있습니다.
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
maximumFractionDigits: 0
});
console.log(formatter.formatRange(100, 200));
// Output: "$100 – $200"
console.log(formatter.formatRange(100, 120));
// Output: "$100 – $120"
console.log(formatter.formatRange(100.2, 100.8));
// Output: "~$100"
세 번째 예시는 같은 정수로 반올림되는 두 값을 보여줍니다. 범위 정보를 전달하지 못하는 "$100 – $100"을 표시하는 대신, 포매터는 "~$100"을 출력합니다. 물결표 기호는 값이 근사치임을 나타냅니다.
이 동작은 형식화 옵션으로 인해 시작 값과 끝 값이 동일하게 표시될 때 적용됩니다.
const formatter = new Intl.NumberFormat("en-US", {
maximumFractionDigits: 1
});
console.log(formatter.formatRange(2.9, 3.1));
// Output: "~3"
console.log(formatter.formatRange(2.94, 2.96));
// Output: "~2.9"
포매터는 필요할 때만 근사 기호를 삽입합니다. 값이 서로 다른 숫자로 반올림되면 표준 범위로 표시합니다.
소수 자릿수가 있는 범위 형식화
범위 형식화는 포매터 옵션의 소수 자릿수 설정을 유지합니다. 범위의 두 값 모두에 대해 정밀도를 제어할 수 있습니다.
const formatter = new Intl.NumberFormat("en-US", {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
console.log(formatter.formatRange(3.5, 5.7));
// Output: "3.50–5.70"
console.log(formatter.formatRange(100, 200));
// Output: "100.00–200.00"
포매터는 범위의 두 숫자 모두에 소수 자릿수 설정을 적용합니다. 이를 통해 전체 범위 표시에서 일관된 정밀도를 보장합니다.
소수 형식화를 통화 또는 다른 스타일과 결합할 수 있습니다.
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
console.log(formatter.formatRange(99.99, 199.99));
// Output: "$99.99 – $199.99"
다양한 언어로 숫자 범위 형식화
범위 형식화는 각 로케일의 숫자, 구분 기호 및 간격에 대한 규칙에 맞게 조정됩니다.
const enFormatter = new Intl.NumberFormat("en-US");
console.log(enFormatter.formatRange(1000, 5000));
// Output: "1,000–5,000"
const deFormatter = new Intl.NumberFormat("de-DE");
console.log(deFormatter.formatRange(1000, 5000));
// Output: "1.000–5.000"
const frFormatter = new Intl.NumberFormat("fr-FR");
console.log(frFormatter.formatRange(1000, 5000));
// Output: "1 000–5 000"
const jaFormatter = new Intl.NumberFormat("ja-JP");
console.log(jaFormatter.formatRange(1000, 5000));
// Output: "1,000~5,000"
영어는 천 단위 구분 기호로 쉼표를 사용하고 범위에는 en 대시를 사용합니다. 독일어는 천 단위에 마침표를 사용하고 en 대시를 사용합니다. 프랑스어는 천 단위에 공백을 사용하고 en 대시를 사용합니다. 일본어는 천 단위에 쉼표를 사용하고 범위에는 물결 대시(~)를 사용합니다.
이러한 차이는 통화 형식화에도 적용됩니다.
const enFormatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD"
});
console.log(enFormatter.formatRange(100, 200));
// Output: "$100.00 – $200.00"
const deFormatter = new Intl.NumberFormat("de-DE", {
style: "currency",
currency: "EUR"
});
console.log(deFormatter.formatRange(100, 200));
// Output: "100,00–200,00 €"
const jaFormatter = new Intl.NumberFormat("ja-JP", {
style: "currency",
currency: "JPY"
});
console.log(jaFormatter.formatRange(100, 200));
// Output: "¥100~¥200"
각 로케일은 통화 기호 배치, 소수점 구분 기호 및 범위 구분 기호에 대한 자체 규칙을 적용합니다. API는 이러한 모든 변형을 자동으로 처리합니다.
formatRange와 간결한 표기법 결합하기
범위 형식 지정은 간결한 표기법과 함께 작동하여 1K-5K 또는 1M-5M과 같은 범위를 표시할 수 있습니다.
const formatter = new Intl.NumberFormat("en-US", {
notation: "compact"
});
console.log(formatter.formatRange(1000, 5000));
// Output: "1K–5K"
console.log(formatter.formatRange(1000000, 5000000));
// Output: "1M–5M"
console.log(formatter.formatRange(1200, 4800));
// Output: "1.2K–4.8K"
포매터는 범위의 두 값 모두에 간결한 표기법을 적용합니다. 이를 통해 범위 정보를 전달하면서도 출력을 간결하게 유지합니다.
범위가 서로 다른 크기 수준에 걸쳐 있을 때 포매터는 각 값을 적절하게 처리합니다.
const formatter = new Intl.NumberFormat("en-US", {
notation: "compact"
});
console.log(formatter.formatRange(500, 1500));
// Output: "500–1.5K"
console.log(formatter.formatRange(900000, 1200000));
// Output: "900K–1.2M"
시작 값은 간결한 표기법을 사용하지 않을 수 있지만 끝 값은 사용하거나, 서로 다른 크기 표시기를 사용할 수 있습니다. 포매터는 각 값의 크기에 따라 이러한 결정을 내립니다.
사용자 정의 스타일링을 위한 formatRangeToParts 사용하기
formatRangeToParts() 메서드는 포맷된 범위의 구성 요소를 나타내는 객체 배열을 반환합니다. 이를 통해 범위의 개별 구성 요소를 스타일링하거나 조작할 수 있습니다.
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
maximumFractionDigits: 0
});
const parts = formatter.formatRangeToParts(100, 200);
console.log(parts);
출력은 각각 type, value, source 속성을 가진 객체 배열입니다.
[
{ type: "currency", value: "$", source: "startRange" },
{ type: "integer", value: "100", source: "startRange" },
{ type: "literal", value: " – ", source: "shared" },
{ type: "currency", value: "$", source: "endRange" },
{ type: "integer", value: "200", source: "endRange" }
]
type 속성은 해당 부분이 무엇을 나타내는지 식별합니다: 통화 기호, 정수, 소수점 구분 기호 또는 리터럴 텍스트. value 속성은 포맷된 텍스트를 포함합니다. source 속성은 해당 부분이 시작 값, 종료 값에 속하는지 또는 둘 사이에 공유되는지를 나타냅니다.
이러한 부분을 사용하여 다양한 구성 요소에 대해 서로 다른 스타일링을 적용한 사용자 정의 HTML을 만들 수 있습니다.
const formatter = new Intl.NumberFormat("en-US", {
style: "currency",
currency: "USD",
maximumFractionDigits: 0
});
const parts = formatter.formatRangeToParts(100, 200);
let html = "";
parts.forEach(part => {
if (part.type === "currency") {
html += `<span class="currency-symbol">${part.value}</span>`;
} else if (part.type === "integer") {
html += `<span class="amount">${part.value}</span>`;
} else if (part.type === "literal") {
html += `<span class="separator">${part.value}</span>`;
} else {
html += part.value;
}
});
console.log(html);
// Output: <span class="currency-symbol">$</span><span class="amount">100</span><span class="separator"> – </span><span class="currency-symbol">$</span><span class="amount">200</span>
이 기법을 사용하면 올바른 로케일별 형식을 유지하면서 CSS 클래스를 적용하거나, 툴팁을 추가하거나, 기타 사용자 정의 동작을 구현할 수 있습니다.
formatRange로 엣지 케이스 처리하기
formatRange() 메서드는 잘못된 입력에 대한 오류 처리를 포함합니다. 매개변수 중 하나가 undefined이면 TypeError가 발생합니다. 매개변수 중 하나가 NaN이거나 숫자로 변환할 수 없으면 RangeError가 발생합니다.
const formatter = new Intl.NumberFormat("en-US");
try {
console.log(formatter.formatRange(100, undefined));
} catch (error) {
console.log(error.name);
// Output: "TypeError"
}
try {
console.log(formatter.formatRange(NaN, 200));
} catch (error) {
console.log(error.name);
// Output: "RangeError"
}
사용자 입력이나 외부 소스의 데이터를 처리할 때는 formatRange()에 전달하기 전에 값이 유효한 숫자인지 검증하세요.
이 메서드는 숫자, BigInt 값 또는 유효한 숫자를 나타내는 문자열을 허용합니다.
const formatter = new Intl.NumberFormat("en-US");
console.log(formatter.formatRange(100, 200));
// Output: "100–200"
console.log(formatter.formatRange(100n, 200n));
// Output: "100–200"
console.log(formatter.formatRange("100", "200"));
// Output: "100–200"
문자열 입력은 부동 소수점 변환 문제 없이 정밀도를 유지하면서 숫자로 파싱됩니다.
formatRange와 수동 포맷팅 중 언제 무엇을 사용할지
사용자에게 범위를 표시할 때는 formatRange()를 사용하세요. 이는 가격 범위, 수량 범위, 측정 범위, 페이지 번호 또는 기타 경계가 있는 값에 적용됩니다. 이 메서드는 구분 기호 로직을 직접 구현할 필요 없이 올바른 로케일별 형식을 보장합니다.
의미상 범위로 관련되지 않은 여러 개별 값을 표시해야 하는 경우 formatRange()를 사용하지 마세요. 예를 들어 "$100, $150, $200"과 같은 가격 목록을 표시할 때는 범위로 처리하는 대신 각 값에 대해 일반 format() 호출을 사용해야 합니다.
또한 값 간의 관계가 숫자 범위가 아닌 경우 formatRange()를 사용하지 마세요. 비교나 차이를 표시하는 경우 범위 형식 대신 해당 컨텍스트에 적합한 형식을 사용하세요.