한글 이름을 sort()로 정렬했더니 순서가 이상했던 이유
문제 발생
이름 목록을 정렬했는데 사전 순서와 달랐고, 숫자가 섞인 파일명은 더 이상하게 나왔습니다.
["파일10", "파일2", "파일1"].sort();
// ['파일1', '파일10', '파일2'] — 10이 2보다 앞원인 분석
인자 없는 Array.prototype.sort()는 원소를 문자열로 바꾼 뒤 유니코드 코드 포인트 순서로 비교합니다. 언어의 정렬 규칙을 전혀 모릅니다.
그래서 두 종류의 문제가 생깁니다.
언어별 규칙. 독일어에서 ä는 a의 변형으로 취급되지만 스웨덴어에서는 z 뒤에 옵니다. 코드 포인트 순서는 둘 다 표현하지 못합니다.
new Intl.Collator("de").compare("ä", "z"); // -1
new Intl.Collator("sv").compare("ä", "z"); // 1숫자가 섞인 문자열. "파일10"과 "파일2"는 문자 단위로 비교되므로 1 < 2에서 순서가 정해집니다.
Intl.Collator는 언어별 정렬 규칙(CLDR)을 담고 있어 두 문제를 모두 다룹니다.
해결 방안
- 비교 함수로 Collator를 넘깁니다.
const collator = new Intl.Collator("ko");
names.sort(collator.compare);- 숫자를 숫자로 비교하려면
numeric옵션을 켭니다.
const natural = new Intl.Collator("ko", { numeric: true });
["파일10", "파일2", "파일1"].sort(natural.compare);
// ['파일1', '파일2', '파일10']- Collator를 만들어 재사용합니다. MDN이 명시하는 성능 권고입니다 — 큰 배열을 정렬할 때
localeCompare를 매번 호출하면 비교마다 인스턴스 생성 비용이 듭니다.
// 권장
const collator = new Intl.Collator("ko");
array.sort(collator.compare);
// 비교마다 인스턴스가 만들어진다
array.sort((a, b) => a.localeCompare(b, "ko"));- 검색·중복 판정에는
sensitivity를 조절합니다. 대소문자나 악센트를 무시하고 같은 것으로 보려면base나accent를 씁니다.
new Intl.Collator("ko", { sensitivity: "base" }).compare("Cafe", "café"); // 0- DB 정렬과 화면 정렬을 섞지 않습니다. 목록을 DB에서
ORDER BY로 정렬해 내려주면 그 순서는 DB의 콜레이션을 따릅니다. 클라이언트에서 다시 정렬하면 두 규칙이 충돌해 페이지마다 순서가 달라 보일 수 있습니다. 한쪽에서만 정렬합니다. - 반환값은 부호만 보장됩니다. 명세가 정하는 것은 음수·0·양수뿐이고 구체적인 값은 구현마다 다를 수 있으므로,
=== -1같은 비교를 하지 않습니다.
댓글0
댓글을 남기려면 로그인이 필요해요. 로그인
아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.