2026년 7월 3일 | 개발 깨알 상식_Tips
릴리스 파일을 훑다가 model-1.10.tar.gz가 model-1.2.tar.gz보다 먼저 보이면 잠깐 멈추게 된다. 파일 이름만 보면 1.10이 더 뒤 버전인데, 그냥 문자열 정렬을 걸면 문자를 왼쪽부터 비교하니 1.10의 1 다음에 오는 점과 숫자 조각이 기대와 다르게 읽힌다. 나는 이런 목록에서 최신 파일을 고를 때 정렬 명령을 먼저 믿지 않고, 어떤 비교 규칙을 썼는지부터 확인하는 편이 낫다는 쪽으로 습관을 바꿨다.
특히 다운로드 디렉터리, 모델 체크포인트, 마이그레이션 파일, 로그 archive처럼 파일명 안에 숫자 조각이 섞이면 문제가 조용히 들어온다. 화면에는 그럴듯하게 정렬된 줄이 나오지만, 자동화가 첫 줄이나 마지막 줄을 집어 가는 순간 결과가 달라진다. 이때 GNU Coreutils의 sort -V, 즉 --version-sort는 작은 안전장치가 된다.
문자열 정렬이 틀린 게 아니라 기준이 다른 것
기본 정렬은 대체로 사전식 비교에 가깝다. 사람이 버전을 읽을 때처럼 숫자 덩어리를 하나의 값으로 묶어 보지 않는다. 그래서 아래처럼 이름을 넣으면 1.10이 1.2보다 앞에 올 수 있다.
printf '%s
' model-1.2.tar.gz model-1.10.tar.gz model-1.9.tar.gz | sort
model-1.10.tar.gz
model-1.2.tar.gz
model-1.9.tar.gz
이 출력은 명령이 고장 난 게 아니다. 비교 기준이 “버전”이 아니라 “문자열”인 것이다. 반대로 같은 입력에 sort -V를 쓰면 숫자로 보이는 구간을 버전 조각처럼 비교한다.
printf '%s
' model-1.2.tar.gz model-1.10.tar.gz model-1.9.tar.gz | sort -V
model-1.2.tar.gz
model-1.9.tar.gz
model-1.10.tar.gz
GNU Coreutils 매뉴얼도 이 기능을 ls -v, ls --sort=version, sort -V, sort --version-sort에서 제공한다고 설명한다. 이름은 version sort지만, 실전 감각으로는 “숫자가 섞인 파일명을 사람이 기대하는 순서에 가깝게 놓는 정렬”이라고 기억해도 충분했다.
최신 파일 하나를 고를 때 특히 위험하다
목록 전체를 눈으로 볼 때는 사람이 금방 이상함을 알아챈다. 문제는 자동화다. 예를 들어 릴리스 파일을 정렬한 뒤 마지막 줄을 가져오거나, 가장 큰 체크포인트를 골라 다음 단계로 넘기는 스크립트에서는 한 줄 차이가 그대로 입력 파일 차이가 된다.
find releases -maxdepth 1 -type f -name 'model-*.tar.gz' | sort -V | tail -n 1
여기서 중요한 건 tail이 아니라 그 앞의 정렬 기준이다. 마지막 줄을 고르는 방식 자체는 단순하지만, 그 줄이 “최신”이라는 의미를 갖으려면 먼저 버전 순서가 맞아야 한다. 나는 이런 스크립트 옆에는 가능하면 작은 샘플 입력과 예상 순서를 같이 남긴다. 나중에 파일명이 rc1, 2026.07, v2-alpha처럼 바뀌었을 때도 기준을 다시 볼 수 있기 때문이다.
숫자 정렬과 SemVer 해석은 아니다
sort -V를 숫자 정렬 -n의 확장판처럼 보면 헷갈린다. -n은 줄 앞이나 지정한 key에서 숫자를 비교하는 쪽에 가깝고, -V는 문자열 안의 숫자와 구두점을 버전 이름처럼 처리한다. model-1.10.tar.gz 같은 파일명 전체에는 -n보다 -V가 자연스럽다.
반대로 이것이 완전한 SemVer 판정기라는 뜻도 아니다. prerelease, build metadata, 배포판별 버전 규칙까지 정확히 해석해야 한다면 패키지 매니저나 전용 라이브러리의 비교 함수를 써야 한다. sort -V는 셸 파이프라인에서 “대충 보이는 순서”를 사람이 읽는 버전 순서에 맞추는 도구이지, 의존성 해석기를 대신하지 않는다.
다른 줄 비교 도구에 그대로 넘기지 않는다
한 가지 함정도 있다. 두 목록의 차이를 comm으로 볼 때는 입력이 같은 정렬 기준으로 정렬돼 있어야 한다. 그런데 comm은 version sort를 이해하고 비교하는 도구가 아니다. 그래서 집합 비교용 중간 파일을 만들 때는 기존처럼 LC_ALL=C sort -u 기준을 유지하고, sort -V는 사람이 보는 릴리스 목록이나 최신 후보 선택 단계에 따로 쓰는 편이 안전하다.
내가 실제로는 이렇게 나눠 둔다. 사람이 볼 후보 목록은 sort -V로 만들고, 그 결과에서 고른 파일명은 별도 로그에 그대로 남긴다. 그리고 삭제·복사·업로드처럼 되돌리기 어려운 단계로 넘어가기 전에는 선택된 파일 하나의 크기, checksum, 생성 시각을 다시 확인한다. 정렬은 후보를 좁혀 주지만, 최종 검증까지 대신해 주지는 않는다.
또 하나는 파일명 규칙을 너무 느슨하게 두지 않는 것이다. model-final.tar.gz, model-1.10.tar.gz, model-20260703.tar.gz가 같은 디렉터리에 섞이면 어떤 정렬을 써도 해석이 애매해진다. 버전 기반으로 고를 파일은 prefix와 확장자를 맞추고, 날짜 기반 archive는 날짜 기반으로만 고르는 식으로 목록 자체를 먼저 좁히는 편이 낫다.
팀 작업에서는 이 차이를 리뷰 코멘트로도 자주 남긴다. “정렬했음”이라고만 쓰면 나중에 어떤 정렬인지 다시 추적해야 한다. 반대로 version sort로 후보를 정렬했고, 선택된 파일은 model-1.10.tar.gz처럼 적어 두면 같은 디렉터리에 새 파일이 추가됐을 때도 재현이 쉽다. 자동화 로그에서 제일 아까운 정보는 대개 명령 이름이 아니라, 그 명령을 어떤 의미로 썼는지에 대한 한 줄이다.
정리하면 기준은 간단하다. 파일명 안의 숫자 조각 때문에 1.10, 1.2, 1.9 같은 순서가 의미를 갖는다면 표시용·선택용 목록에는 sort -V를 먼저 의심한다. 특히 최신 checkpoint 하나를 고르거나, 릴리스 노트를 시간순으로 훑거나, 오래된 archive를 지울 후보를 만드는 순간에는 이 작은 차이가 바로 운영 결과로 이어진다. 반대로 정확한 집합 연산, locale 고정, 패키지 규칙 검증이 목적이면 그 목적에 맞는 정렬과 비교 도구를 분리한다. 작은 옵션 하나지만, 릴리스 파일을 잘못 집어 드는 종류의 버그는 이런 곳에서 꽤 쉽게 줄어든다.
'[개발 깨알 상식_Tips]' 카테고리의 다른 글
| 임시 디렉터리 잔여 파일과 테스트 착시 (0) | 2026.07.04 |
|---|---|
| 빈 환경변수가 덮어쓴 API 기본 URL (0) | 2026.07.03 |
| 삭제된 로그 파일이 붙잡은 디스크 용량 (0) | 2026.07.02 |
| 체크섬 manifest로 잡는 깨진 다운로드 (0) | 2026.07.01 |
| 첫 실패 기준으로 줄인 pytest 로그 (0) | 2026.06.30 |