정렬 시 대문자 또는 소문자가 먼저 오는지 제어하는 방법
문자열 정렬 시 대소문자 순서를 결정하기 위해 Intl.Collator의 caseFirst 옵션 사용하기
소개
대소문자만 다른 문자열 배열을 정렬할 때, JavaScript는 어떤 버전이 먼저 오는지 결정해야 합니다. apple이 Apple 앞에 와야 할까요, 아니면 Apple이 apple 앞에 와야 할까요? 이러한 순서에 대해 애플리케이션마다 다른 요구사항이 있습니다.
기본적으로 대소문자만 다른 문자열의 정렬 순서는 로케일과 민감도 설정에 따라 달라집니다. 많은 경우, apple과 Apple 같은 문자열은 동일하게 취급되어 순서가 예측 불가능합니다. 애플리케이션에서 대소문자 순서가 중요한 경우, 대문자나 소문자 중 어느 것이 먼저 정렬되는지에 대한 명시적인 제어가 필요합니다.
Intl.Collator API는 대소문자 순서를 결정하는 caseFirst 옵션을 제공합니다. 대문자를 소문자 앞에 배치하거나, 소문자를 대문자 앞에 배치하거나, 로케일의 기본 동작을 사용하도록 콜레이터를 구성할 수 있습니다. 이 강의에서는 caseFirst 옵션의 작동 방식, 효과가 있는 경우, 그리고 실제 정렬 시나리오에서 이를 사용하는 방법을 설명합니다.
caseFirst 옵션의 기능
caseFirst 옵션은 대소문자만 다른 문자열의 순서를 제어합니다. 이 옵션은 "upper", "lower" 또는 "false" 세 가지 문자열 값을 허용합니다.
"upper"로 설정하면 대문자가 해당 소문자보다 먼저 정렬됩니다. 문자열 Apple이 apple 앞에 옵니다.
"lower"로 설정하면 소문자가 해당 대문자보다 먼저 정렬됩니다. 문자열 apple이 Apple 앞에 옵니다.
"false"(불리언이 아닌 문자열 값)로 설정하면 콜레이터는 로케일의 기본 대소문자 순서를 사용합니다. 이는 옵션을 지정하지 않을 때의 기본 동작입니다.
caseFirst 옵션은 대소문자만 다른 동일한 문자열에만 영향을 미칩니다. 기본 문자가 다른 문자열은 이 설정과 관계없이 일반적인 알파벳 규칙에 따라 정렬됩니다.
기본 대소문자 정렬 방식의 작동 원리
'caseFirst' 옵션을 지정하지 않으면, 콜레이터는 로케일과 민감도 설정에 따라 달라지는 기본 동작을 사용합니다.
const collator = new Intl.Collator('en-US');
const words = ['apple', 'Apple', 'APPLE'];
words.sort(collator.compare);
console.log(words);
// 출력: ['apple', 'Apple', 'APPLE']
출력 순서는 구현 방식과 로케일에 따라 달라집니다. 기본 민감도 설정에서는 콜레이터가 대소문자 차이를 고려하지만, apple, Apple, APPLE의 특정 순서는 다양합니다.
다양한 로케일은 서로 다른 기본 대소문자 정렬 규칙을 가지고 있습니다. 일부 로케일은 전통적으로 소문자를 먼저 배치하고, 다른 로케일은 대문자를 먼저 배치합니다. 기본 동작에 의존하면 다양한 환경에서 일관되지 않은 결과가 발생합니다.
caseFirst upper로 대문자 먼저 배치하기
'caseFirst: "upper"'를 설정하면 정렬된 결과에서 대문자가 항상 해당 소문자보다 먼저 오도록 보장합니다.
const collator = new Intl.Collator('en-US', { caseFirst: 'upper' });
const words = ['apple', 'Apple', 'banana', 'Banana'];
words.sort(collator.compare);
console.log(words);
// 출력: ['Apple', 'apple', 'Banana', 'banana']
콜레이터는 먼저 기본 문자를 기준으로 알파벳 순으로 정렬한 다음, 각 그룹 내에서 대소문자 순서를 적용합니다. 'a'로 시작하는 단어 중에서는 'Apple'이 'apple' 앞에 옵니다. 'b'로 시작하는 단어 중에서는 'Banana'가 'banana' 앞에 옵니다.
이 정렬 방식은 고유 명사나 대문자로 시작하는 제목이 일반 명사보다 눈에 띄게 먼저 나타나야 하는 목록을 표시할 때 유용합니다.
const collator = new Intl.Collator('en-US', { caseFirst: 'upper' });
const names = ['smith', 'Smith', 'jones', 'Jones'];
names.sort(collator.compare);
console.log(names);
// 출력: ['Jones', 'jones', 'Smith', 'smith']
대문자로 시작하는 이름이 소문자 버전보다 먼저 나타나므로 혼합 목록에서 고유 이름과 일반 단어를 더 쉽게 구분할 수 있습니다.
caseFirst lower로 소문자 먼저 배치하기
caseFirst: "lower"를 설정하면 정렬된 결과에서 소문자가 항상 해당 대문자보다 먼저 오도록 보장합니다.
const collator = new Intl.Collator('en-US', { caseFirst: 'lower' });
const words = ['apple', 'Apple', 'banana', 'Banana'];
words.sort(collator.compare);
console.log(words);
// 출력: ['apple', 'Apple', 'banana', 'Banana']
콜레이터는 먼저 알파벳 순으로 정렬한 다음, 각 알파벳 그룹 내에서 소문자 버전을 대문자 버전보다 앞에 배치합니다.
이러한 순서는 소문자 텍스트가 더 표준적이거나 중립적인 것으로 간주되고, 대문자는 강조나 특수한 경우를 위해 예약되는 관례와 일치합니다.
const collator = new Intl.Collator('en-US', { caseFirst: 'lower' });
const tags = ['javascript', 'JavaScript', 'python', 'Python'];
tags.sort(collator.compare);
console.log(tags);
// 출력: ['javascript', 'JavaScript', 'python', 'Python']
소문자 태그가 먼저 나타나는데, 이는 소문자가 표준 형식을 나타내고 대문자 버전이 덜 일반적인 변형인 경우에 유용합니다.
로케일 기본 동작을 위한 caseFirst false 사용
caseFirst: "false"(문자열 값)를 설정하면 로케일의 기본 대소문자 순서를 명시적으로 요청합니다. 이는 옵션을 완전히 생략한 것과 동일한 결과를 생성합니다.
const collator = new Intl.Collator('en-US', { caseFirst: 'false' });
const words = ['apple', 'Apple', 'APPLE'];
words.sort(collator.compare);
console.log(words);
// 출력은 로케일 구현에 따라 다름
출력 순서는 로케일이 표준으로 간주하는 것에 따라 달라집니다. 일부 로케일은 대문자를 먼저, 다른 로케일은 소문자를 먼저 정의하며, 일부는 대소문자가 다른 문자열을 동일하게 취급합니다.
이 옵션은 실수로 구성 선택을 생략하는 대신 로케일 기본값을 사용한다는 것을 명시적으로 표현하고자 할 때 유용합니다.
caseFirst가 효과가 없는 경우
caseFirst 옵션은 감도(sensitivity) 설정이 대소문자 차이를 중요하게 여길 때만 비교에 영향을 미칩니다. sensitivity: "base" 또는 sensitivity: "accent"를 사용하면 대소문자 차이가 완전히 무시되어 caseFirst 옵션이 무관해집니다.
const collator = new Intl.Collator('en-US', {
sensitivity: 'base',
caseFirst: 'upper'
});
const words = ['apple', 'Apple', 'APPLE'];
words.sort(collator.compare);
console.log(words);
// 출력: ['apple', 'Apple', 'APPLE'] (또는 임의의 순서)
기본 감도에서는 콜레이터가 세 문자열을 모두 동일하게 취급합니다. 대소문자가 전혀 고려되지 않기 때문에 caseFirst 옵션은 효과가 없습니다. 정렬 알고리즘은 동일한 항목의 원래 순서를 유지하므로(안정적 정렬) 출력은 입력 순서와 일치합니다.
caseFirst가 효과를 발휘하려면 sensitivity: "case" 또는 sensitivity: "variant" (기본값)를 사용하세요.
const collator = new Intl.Collator('en-US', {
sensitivity: 'variant',
caseFirst: 'upper'
});
const words = ['apple', 'Apple', 'APPLE'];
words.sort(collator.compare);
console.log(words);
// 출력: ['APPLE', 'Apple', 'apple']
변형 감도(대소문자를 포함한 모든 차이를 고려)를 사용하면 caseFirst 옵션이 이러한 대소문자가 다른 문자열의 순서를 결정합니다.
일관된 대소문자 순서로 이름 정렬하기
사용자 이름이나 다른 고유 명사를 정렬할 때, 일관된 대소문자 순서는 애플리케이션 전체에서 예측 가능한 결과를 보장합니다.
const collator = new Intl.Collator('en-US', { caseFirst: 'upper' });
const users = [
{ name: 'alice' },
{ name: 'Alice' },
{ name: 'bob' },
{ name: 'Bob' }
];
users.sort((a, b) => collator.compare(a.name, b.name));
users.forEach(user => {
console.log(user.name);
});
// 출력:
// Alice
// alice
// Bob
// bob
콜레이터는 대문자로 시작하는 이름을 소문자 버전보다 먼저 배치합니다. 이러한 순서 지정은 혼합 데이터셋에서 고유 명사와 일반 단어를 더 쉽게 구분할 수 있게 해줍니다.
제어된 대소문자 순서로 파일 이름 정렬하기
파일 시스템은 대소문자 구분에 있어 다양한 동작 방식을 가집니다. 일부는 대문자와 소문자를 동일하게 취급하고, 다른 일부는 다르게 취급합니다. 명시적인 대소문자 순서 지정은 기본 파일 시스템에 관계없이 정렬된 파일 목록이 사용자의 기대와 일치하도록 보장합니다.
const collator = new Intl.Collator('en-US', { caseFirst: 'lower' });
const files = ['README.md', 'readme.md', 'index.js', 'Index.js'];
files.sort(collator.compare);
console.log(files);
// 출력: ['index.js', 'Index.js', 'readme.md', 'README.md']
소문자 버전이 먼저 나타나는데, 이는 유닉스 계열 시스템에서 소문자 파일 이름이 더 표준적인 관례와 일치합니다.
caseFirst와 숫자 콜레이션 결합하기
caseFirst 옵션은 numeric과 같은 다른 콜레이션 옵션과 함께 작동합니다. 대소문자 순서를 제어하면서 내장된 숫자가 있는 문자열을 정렬할 수 있습니다.
const collator = new Intl.Collator('en-US', {
numeric: true,
caseFirst: 'upper'
});
const items = ['item1', 'Item1', 'item2', 'Item2', 'item10', 'Item10'];
items.sort(collator.compare);
console.log(items);
// 출력: ['Item1', 'item1', 'Item2', 'item2', 'Item10', 'item10']
콜레이터는 숫자 부분에 대해 숫자 비교를 적용하고 문자 부분에 대해 대소문자 순서를 적용합니다. 항목은 먼저 숫자 값(1, 2, 10)으로 정렬된 다음, 각 숫자 그룹 내에서 대소문자별로 정렬됩니다.
caseFirst를 위한 유니코드 확장 키 사용하기
caseFirst 옵션은 kf 키를 사용하여 로케일 식별자의 유니코드 확장을 통해 설정할 수도 있습니다. 확장 값은 upper, lower, 그리고 false입니다.
const collator = new Intl.Collator('en-US-u-kf-upper');
const words = ['apple', 'Apple', 'banana', 'Banana'];
words.sort(collator.compare);
console.log(words);
// 출력: ['Apple', 'apple', 'Banana', 'banana']
로케일 문자열 en-US-u-kf-upper는 대문자 우선 정렬 순서를 가진 미국 영어를 지정합니다. 이는 옵션 객체에 { caseFirst: 'upper' }를 전달하는 것과 동일한 결과를 생성합니다.
유니코드 확장과 옵션 객체 모두 caseFirst를 지정하는 경우, 옵션 객체가 우선합니다.
const collator = new Intl.Collator('en-US-u-kf-upper', {
caseFirst: 'lower'
});
const words = ['apple', 'Apple'];
words.sort(collator.compare);
console.log(words);
// 출력: ['apple', 'Apple']
옵션 객체는 caseFirst: 'lower'를 지정하여 로케일 문자열의 kf-upper 확장을 재정의합니다. 콜레이터는 소문자 우선 정렬을 사용합니다.
콜레이터가 사용 중인 caseFirst 값 확인하기
resolvedOptions() 메서드는 콜레이터가 사용 중인 실제 옵션을 반환하며, 여기에는 caseFirst 설정이 포함됩니다.
const collator = new Intl.Collator('en-US', { caseFirst: 'upper' });
const options = collator.resolvedOptions();
console.log(options.caseFirst);
// 출력: "upper"
이는 디버깅이나 콜레이터가 예상된 구성을 가지고 있는지 확인할 때 유용하며, 특히 유니코드 확장과 옵션 객체를 결합할 때 더욱 그렇습니다.
const collator = new Intl.Collator('en-US-u-kf-lower');
const options = collator.resolvedOptions();
console.log(options.caseFirst);
// 출력: "lower"
옵션 객체가 재정의하지 않을 때 해결된 옵션은 유니코드 확장 설정을 반영합니다.
각 caseFirst 값을 사용해야 하는 경우
caseFirst: "upper"를 사용하는 경우:
- 고유 명사나 제목이 일반 단어보다 먼저 나타나길 원할 때
- 애플리케이션 규칙이 대문자 텍스트를 더 중요하게 취급할 때
- 대문자 버전이 표준인 이름을 정렬할 때
caseFirst: "lower"를 사용하는 경우:
- 소문자 텍스트가 표준 형식으로 먼저 나타나길 원할 때
- 애플리케이션 규칙이 소문자를 기본으로, 대문자를 특별한 것으로 취급할 때
- 소문자가 더 일반적인 기술적 식별자를 정렬할 때
caseFirst: "false"를 사용하는 경우:
- 로케일별 규칙을 따르고 싶을 때
- 대소문자가 다른 문자열의 순서가 애플리케이션에 중요하지 않을 때
- 실수로 옵션을 생략하는 대신 기본값을 사용한다는 것을 명시적으로 표현하고 싶을 때