자연 정렬(Natural Sort), item2가 item10보다 왜 먼저 와야 하나
파일 목록이나 표를 정렬했는데 "item1, item10, item2, item20, item3" 순서로 뒤죽박죽 나온 경험이 있을 겁니다. 프로그램이 고장 난 게 아니라, 기본 정렬 방식(사전식 정렬)이 원래 그렇게 동작하도록 설계돼 있기 때문입니다. 이 가이드는 왜 이런 일이 생기는지, 그리고 사람이 기대하는 순서로 정렬하는 "자연 정렬(natural sort)"이 내부적으로 어떤 방식으로 이 문제를 해결하는지 구조를 뜯어봅니다.
1. 사전식 정렬은 숫자를 숫자로 보지 않는다
컴퓨터의 기본 문자열 정렬은 문자를 왼쪽에서 오른쪽으로 한 글자씩 비교합니다. "item10"과 "item2"를 비교하면 앞의 네 글자 "item"까지는 동일하고, 다섯 번째 글자에서 '1'과 '2'를 비교하게 됩니다. 유니코드 코드표에서 '1'은 '2'보다 앞선 코드값을 가지므로, 정렬 알고리즘은 뒤에 남은 '0'이라는 글자를 아예 쳐다보지도 않고 "item10"이 "item2"보다 앞선다고 결론 내립니다. 즉 "10"을 십(十)이라는 크기로 이해한 게 아니라, '1' 다음 '0'이 온 문자열로만 취급한 것입니다. 이것이 자연어 정렬처럼 보이지 않는 근본 원인입니다.
2. 자연 정렬의 해법: 문자열을 청크 단위로 쪼갠다
자연 정렬은 문자열 전체를 한 글자씩 비교하는 대신, 먼저 문자열을 "숫자로만 이루어진 덩어리"와 "숫자가 아닌 덩어리"로 분리합니다. 예를 들어 "item10-v2"라는 문자열은 정규식으로 ["item", "10", "-v", "2"]처럼 숫자/비숫자가 번갈아 나오는 청크 배열로 쪼개집니다. 그다음 같은 위치의 청크끼리 비교하되, 두 청크가 모두 숫자면 문자열이 아니라 parseInt로 변환한 정수값으로 비교하고, 하나라도 문자면 일반 문자열 비교(대소문자 옵션에 따라 localeCompare)를 적용합니다. 이렇게 하면 "10"과 "2"를 비교할 때 십과 이라는 실제 크기로 비교되어 2가 10보다 앞에 오는, 사람이 기대하는 순서가 나옵니다.
3. 실제 도구 코드로 확인한 구현
텍스트 줄 정렬기의 소스를 열어 확인해 보면, "숫자순" 옵션을 선택했을 때 호출되는 naturalCompare 함수가 정확히 이 방식으로 구현되어 있습니다. 정규식 /(\d+|\D+)/g로 문자열을 숫자 청크와 비숫자 청크로 분리한 뒤, 두 줄의 같은 위치 청크를 순서대로 비교합니다. 두 청크가 모두 순수 숫자 패턴(/^\d+$/)이면 정수로 변환해 크기를 비교하고, 그렇지 않으면 대소문자 구분 옵션에 따라 문자열로 비교합니다. 이 도구는 일반 정렬(A→Z, Z→A)과 자연 정렬(숫자순)을 별도의 버튼으로 명확히 구분해 제공하므로, 파일명이나 항목 번호가 섞인 텍스트를 정렬할 때는 반드시 "숫자순" 모드를 선택해야 원하는 결과를 얻습니다. A→Z 모드는 순수 사전식 비교(localeCompare)라서 자연 정렬과는 다른 결과가 나옵니다.
| 정렬 방식 | 결과 순서 |
|---|---|
| A → Z (사전식) | item1, item10, item2, item20, item3 |
| 숫자순 (자연 정렬) | item1, item2, item3, item10, item20 |
같은 데이터, 같은 도구인데도 옵션 하나 차이로 결과가 완전히 달라집니다. 정렬 후 목록을 확인하지 않고 바로 사용하면 순서가 뒤바뀐 채로 문서에 붙여넣는 실수가 자주 발생하니, 숫자가 포함된 텍스트는 항상 "숫자순" 옵션을 켰는지 확인하는 습관이 필요합니다.
4. 자연 정렬도 만능은 아니다
자연 정렬은 정수 크기 비교에는 강하지만, 소수점이 포함된 버전 번호에는 그대로 적용하면 오히려 헷갈릴 수 있습니다. 예를 들어 "1.9"와 "1.10"을 소프트웨어 버전으로 보면 1.10이 더 최신 버전이지만, 자연 정렬 알고리즘은 "."을 비숫자 청크로 취급하고 "9"와 "10"이라는 두 개의 독립된 숫자 청크로 비교하기 때문에 실제로는 정확히 1.9 < 1.10 순서가 나옵니다. 반면 "v1.02"처럼 앞에 불필요한 0이 붙은 경우도 정수로 변환되는 순간 자릿수 정보가 사라지므로 문제없이 처리됩니다. 다만 한글 조사가 숫자 뒤에 바로 붙는 "10개", "2개" 같은 표현은 청크 분리 자체는 정상 동작하지만, 자연 정렬은 정렬 기준일 뿐 형태소를 이해하는 것은 아니므로 문맥적 의미까지 반영하지는 못한다는 한계가 있습니다.
자주 묻는 질문
Q. 사전식 정렬과 자연 정렬 중 어느 쪽이 기본값이어야 하나요?
숫자가 텍스트 안에 섞여 있고 그 크기를 순서에 반영하고 싶다면 자연 정렬(숫자순)을, 순수하게 문자 코드 기준으로 정렬해야 하는 경우(예: 프로그래밍 언어의 기본 정렬 동작 재현)라면 사전식 정렬을 선택해야 합니다. 용도에 따라 다르며 둘 다 "정답"인 상황이 존재합니다.
Q. 엑셀이나 파일 탐색기의 정렬도 같은 원리인가요?
네. 대부분의 OS 파일 탐색기(Windows 탐색기, macOS Finder)는 파일명을 자연 정렬로 보여줘서 "file2"가 "file10"보다 먼저 나오지만, 프로그래밍 언어의 기본 배열 정렬 함수나 데이터베이스의 문자열 컬럼 정렬은 대부분 사전식이 기본값입니다.
Q. 음수나 소수는 자연 정렬로 올바르게 처리되나요?
정규식 기반 자연 정렬은 연속된 숫자 문자(0-9)만 하나의 청크로 묶기 때문에 마이너스 기호(-)나 소수점(.)은 별도의 비숫자 청크로 분리됩니다. 즉 "-5"는 부호가 있는 정수 하나가 아니라 "-"와 "5"로 쪼개져 비교되므로, 음수가 섞인 목록에서는 기대와 다른 순서가 나올 수 있습니다.
Q. 대소문자 구분 옵션은 자연 정렬에도 적용되나요?
네. 청크가 숫자가 아닌 문자일 때는 대소문자 구분 옵션에 따라 비교 방식이 달라집니다. 옵션을 끄면 "Item2"와 "item2"를 같은 우선순위로 취급하고, 켜면 대문자와 소문자를 다른 문자로 구분해 비교합니다.