제10조가 제2조 앞에 오는 정렬 버그, 조문 번호 natural sort로 고치기
법령 조문 목록에서 제10조가 제2조 앞에 오는 문자열 정렬 버그를 Intl.Collator numeric 옵션과 '조의2' 파싱으로 해결한 프론트엔드 구현기예요.
법령 조문 목록을 화면에 뿌릴 때 가장 먼저 걸리는 게 정렬이에요. 플랜엘(plan-l)의 조문 목록 컴포넌트를 직접 만들어 보니, 서버에서 받은 배열을 그대로 sort()에 넣자 제1조 다음에 제10조가 오고, 제2조는 한참 뒤로 밀려 있었어요.
왜 제10조가 제2조 앞에 올까요
자바스크립트의 기본 sort()는 요소를 문자열로 바꿔 한 글자씩 비교해요. '제10조'와 '제2조'를 비교하면 '제'는 같고, 다음 글자 '1'과 '2'를 비교하는데 '1'이 작으니 '제10조'가 먼저 와요. 숫자 10이 아니라 문자 '1'로 본 거죠. 조문이 9개 이하면 티가 안 나다가, 10개를 넘는 순간 화면이 뒤섞여요.
문자열 정렬 '제1조·제10조·제2조' vs 자연 정렬 '제1조·제2조·제10조'
가장 짧은 수정: numeric 옵션
적용해 봤더니 수정은 1줄이면 충분했어요. Intl.Collator에 numeric: true를 주면 연속된 숫자를 하나의 수로 비교해요.
const collator = new Intl.Collator("ko", { numeric: true });
const sorted = [...articles].sort((a, b) => collator.compare(a.no, b.no));Collator 인스턴스는 컴포넌트 밖에서 한 번만 만들고, 정렬은 useMemo로 감싸 두면 리렌더마다 다시 정렬하지 않아요. 원본 배열을 건드리지 않고 복사본을 정렬하는 것도 중요해요. 상태로 들고 있는 배열을 제자리 정렬하면 React가 변경을 알아채지 못하거든요.
'조의2'는 어떻게 되나
법령에는 '제2조의2'처럼 가지 번호가 붙은 조문이 있어요. 테스트해 보면 numeric 옵션만으로도 '제2조 → 제2조의2 → 제3조' 순서가 나와요. '제2조'가 '제2조의2'의 접두어라 먼저 오고, '의2'와 '의10'도 숫자로 비교되니까요.
문제는 조문 번호 뒤에 제목이 붙어 올 때예요. '제2조(정의)'처럼 괄호나 공백이 섞이면 번호가 아닌 글자가 비교에 끼어들어요. 표기가 조금만 달라도 순서를 보장하기 어려워서, 돌려 보니 번호를 먼저 뽑아 두 개의 키로 비교하는 쪽이 안전했어요.
const parse = (s: string) => {
const m = s.match(/제(\d+)조(?:의(\d+))?/);
return [Number(m?.[1] ?? 0), Number(m?.[2] ?? 0)];
};
const byArticle = (a: string, b: string) => {
const [x1, y1] = parse(a), [x2, y2] = parse(b);
return x1 - x2 || y1 - y2;
};정규식으로 조 번호와 가지 번호를 뽑고, 조 번호가 같을 때만 가지 번호로 비교해요. 매칭이 안 되는 값은 0으로 보내 맨 앞에 모아 두면 데이터 이상을 화면에서 바로 발견할 수 있어요.
화면에서 확인한 것
테스트 케이스는 6개를 두었어요. 제1조·제2조·제10조 기본 순서, 제2조와 제2조의2, 제2조의2와 제2조의10, 제목 괄호가 붙은 경우, 번호가 없는 항목, 빈 배열이에요. 이 6개가 통과하면 조문 목록 컴포넌트는 데이터 개수가 늘어도 순서가 바뀌지 않아요.
정렬이 맞아야 '제N조로 이동' 같은 목차 UI도 제대로 동작해요. 플랜엘(plan-l)은 국가법령정보센터와 대법원 데이터를 기반으로 월 3건까지 무료로 검색할 수 있는데, 조문 목록이 이 순서대로 나와야 사용자가 스크롤 위치를 예측할 수 있어요.
관련 글
댓글
아직 댓글이 없어요. 첫 댓글을 남겨주세요.