Skip to content

fix(rte.rdt): 버전 비교가 자리별 수가 아니라 문자열 순서로 이뤄져 업데이트 판정이 뒤집히는 문제 수정 - #155

Merged
eGovFrameSupport merged 2 commits into
eGovFramework:mainfrom
EricSeokgon:patch-5
Sep 18, 2026
Merged

eGovFrameSupport merged 2 commits into
eGovFramework:mainfrom
EricSeokgon:patch-5

Conversation

@EricSeokgon

Copy link
Copy Markdown
Contributor

수정 사유 Reason for modification

소스를 수정한 사유가 무엇인지 체크해 주세요. Please check the reason you modified the source. ([X] X는 대문자여야 합니다.)

  • 버그수정 Bug fixes
  • 기능개선 Enhancements
  • 기능추가 Adding features
  • 기타 Others

수정된 소스 내용 Modified source

문제

Version.compareTo() 가 realVersion.compareTo(...) 로 문자열 사전순 비교를 합니다. 버전 자리가 두 자리 이상이 되면 순서가 뒤집힙니다.

public int compareTo(Version o) {
    return this.realVersion.compareTo(o.realVersion);   // "6.2.9" > "6.2.11"
}

이 비교는 Version.isOlderThan() → TableList.isOlderThanMaster() → TableList.isUpdate(Service) 로 그대로 이어지므로, RDT 서비스 목록의 업데이트 가능 여부 판정이 잘못됩니다.

재현 (플러그인이 배포하는 master pom 기준)

egovframework.rte.rdt/src/main/resources/meta/pom_master.xml 의 spring.framework.version 은 현재 6.2.11 입니다.

프로젝트 pom(설치) master pom 기대 현재 결과
6.2.9 6.2.11 업데이트 필요(true) false — 업데이트가 목록에 뜨지 않음
6.2.11 6.2.9 아님(false) true — 다운그레이드를 업데이트로 안내
3.9.0 3.10.0 true false
4.3.0-SNAPSHOT 4.3.0 true false
4.3.0 4.3.0-SNAPSHOT false true

master pom 안의 버전 중 두 자리 이상 자리를 가진 것이 26개라 드문 경우가 아닙니다.

수정

compareTo() 가 버전을 . - _ 로 나눈 뒤 자리별로 비교하도록 했습니다.

  • 숫자 자리끼리는 수의 크기로 비교합니다(9 < 11). 앞의 0 을 없앤 뒤 자릿수·사전순으로 비교하므로 자리 값이 커도 넘침이 없습니다.
  • 한쪽에만 있는 자리는, 값이 0 이거나 정식 릴리스 한정자(GA/Final/RELEASE)면 없는 것과 같게 봅니다. 4.3 = 4.3.0 = 4.3.0.GA, 5.6.15.Final = 5.6.15.
  • 그 밖의 한정자(SNAPSHOT, alpha, RC1 등)가 뒤에 붙으면 더 낮은 버전으로 봅니다. 4.3.0-SNAPSHOT < 4.3.0.
  • 숫자 자리는 한정자 자리보다 높은 버전으로 봅니다.

isOlderThan() 의 기존 방어(버전이 없거나 프로퍼티를 해석하지 못하면 판단 유보)는 그대로 두었고, compareTo 의 시그니처와 호출부는 바뀌지 않았습니다.

JUnit 테스트 JUnit tests

  • JUnit 테스트 JUnit tests
  • 수동 테스트 Manual testing
  1. 기존 테스트 전부 통과 — VersionTest 15건, DependencyTest 5건, PomObjectTest 3건, SecureSAXBuilderTest 7건이 그대로 통과합니다.
  2. 결함 재현 테스트 4건 추가 — 수정 전 코드에서는 4건 모두 실패(Tests run: 19, Failures: 4), 수정 후 19건 전부 통과합니다.
  3. Maven 기준 대조 — pom_master.xml 에 실제로 들어 있는 버전 31종의 모든 조합 961쌍을 Maven 의 org.apache.maven.artifact.versioning.ComparableVersion 과 대조했고 불일치 0건 입니다.

compareToKeepsLexicalOrderForLiterals 는 더 이상 사전순이 아니므로 compareToOrdersLiteralVersions 로 이름만 바꿨습니다. 단언은 그대로입니다.

알려진 한계

위 961쌍 밖에서, 한정자끼리 순서가 갈리는 두 경우는 Maven 과 다르게 판정합니다.

  • 2.0.0-M2 vs 2.0-RC1 — 자리 수가 서로 달라 정렬이 어긋납니다.
  • 1.0.0.RELEASE vs 1.0.0 — Maven 은 RELEASE 를 릴리스 별칭으로 보지 않아 더 높은 버전으로 판정합니다. 이 플러그인의 용도(설치본이 master 보다 낮은지 판정)에서는 Spring 계열의 x.y.z.RELEASE 를 같은 버전으로 보는 편이 안전하다고 판단해 의도적으로 같게 두었습니다.

다르게 보시는 편이 맞다면 알려주시면 맞추겠습니다.

테스트 브라우저 Test Browser

  • 기타 Others

Eclipse 플러그인(RCP) 코드라 브라우저가 관여하지 않습니다. JDK 21 · JUnit 4.7 로 테스트했습니다.

테스트 스크린샷 또는 캡처 영상 Test screenshots or captured video

UI 화면이 아닌 순수 판정 로직이라 스크린샷 대신 테스트 실행 결과로 갈음합니다.

수정 전(main) 코드 + 새 테스트
1) compareToComparesNumericSegmentsAsNumbers
2) isOlderThanDetectsUpdateOfTwoDigitSegment
3) trailingZeroAndReleaseQualifierDoNotChangeVersion
4) snapshotIsOlderThanSameRelease
Tests run: 19,  Failures: 4

수정 후
OK (19 tests)

Version.compareTo() 가 realVersion 을 문자열 사전순으로 비교해 자리가 두 자리 이상이면 순서가 뒤집혔다.
이 비교는 isOlderThan() → TableList.isUpdate() 로 이어지므로 설치본 6.2.9 는 master 6.2.11 로의
업데이트가 목록에 뜨지 않고, 반대로 6.2.11 에는 6.2.9 로의 다운그레이드가 업데이트로 안내됐다.

버전을 . - _ 로 나눠 자리별로 비교하고, 숫자 자리는 수의 크기로 비교하도록 했다.
한쪽에만 있는 자리는 값이 0 이거나 정식 릴리스 한정자(GA/Final/RELEASE)면 없는 것과 같게 보고,
SNAPSHOT 같은 한정자면 더 낮은 버전으로 본다.
두 자리 이상 세그먼트(6.2.9 vs 6.2.11), 뒤따르는 0 자리와 릴리스 한정자, SNAPSHOT 순서를 검증한다.
수정 전 Version 에서는 4건 모두 실패한다.

compareToKeepsLexicalOrderForLiterals 는 더 이상 사전순이 아니므로 compareToOrdersLiteralVersions 로 이름을 바꿨다. 단언은 그대로다.
@eGovFrameSupport
eGovFrameSupport merged commit a23f1d5 into eGovFramework:main Sep 18, 2026
1 check passed
swanpark8538 added a commit that referenced this pull request Sep 18, 2026
한정자끼리는 통째로 문자열 비교를 해 RC9 를 RC10 보다, M2 를 M10 보다 새 버전으로
판정했다. 한정자를 이름과 번호로 나눠 이름이 같으면 번호를 수의 크기로 비교한다.

릴리스보다 뒤에 나오는 SP 한정자도 SNAPSHOT 과 같은 릴리스 이전 한정자로 묶여
1.0.0-SP1 이 1.0.0 보다 낮게 판정됐다. 한정자에 등급(릴리스 이전 < 릴리스 < 릴리스
이후)을 두어 SP 가 붙으면 더 높은 버전으로 본다.

compareNumeric 은 BigInteger 비교로 단순화하고, compareTo 가 equals 와 일관되지 않는다는
점을 Javadoc 에 적었다. 한정자 비교와 compareTo 의 반대칭성·추이성 테스트 5건을 추가했다.

Refs: #155
@swanpark8538

swanpark8538 commented Sep 18, 2026 •

Copy link
Copy Markdown
Collaborator

기여해 주셔서 감사합니다. 재현 표와 Maven ComparableVersion 대조까지 첨부해 주셔서 검토하기가 수월했습니다. 이 PR은 squash 방식으로 머지했습니다(a23f1d5).

머지한 뒤에, 리뷰 과정에서 확인한 경계 조건들을 main에 후속 커밋 2건으로 보완했습니다. 본문의 "알려진 한계"에 적어 주신 경우도 여기에 포함되어 있어서 내용을 공유드립니다.

후속 커밋 1: 7ad067a (한정자에 붙은 번호, 서비스 팩 한정자)

한정자 안의 숫자가 다시 사전순으로 비교되던 문제

한정자끼리는 compareToIgnoreCase로 통째로 비교하고 있어서, 이 PR이 숫자 자리에서 고친 것과 같은 문제가 한정자 안에 남아 있었습니다.

비교 수정 전 수정 후
2.0.0-RC9 vs 2.0.0-RC10 RC9 가 더 새 버전 RC10 이 더 새 버전
2.0.0-M2 vs 2.0.0-M10 M2 가 더 새 버전 M10 이 더 새 버전

한정자를 이름과 번호로 나눈 뒤, 이름이 같으면 번호를 수의 크기로 비교하도록 했습니다(compareQualifier). RC01과 rc1은 같은 버전으로 판정됩니다.

릴리스보다 뒤에 나오는 한정자(SP)가 릴리스보다 낮게 판정되던 문제

compareWithAbsent가 릴리스 한정자가 아닌 것을 모두 -1로 처리해서, 1.0.0-SP1이 1.0.0보다 낮게 판정되었습니다. 한정자에 등급(릴리스 이전 -1 / 릴리스 0 / 릴리스 이후 +1)을 두었고(qualifierRank), 이제 1.0.0 < 1.0.0-SP1 < 1.0.1 순서가 됩니다. special처럼 sp로 시작하기만 하는 한정자는 릴리스 이전 등급으로 남습니다.

그 밖의 변경

  • compareNumeric과 stripLeadingZeros를 BigInteger 비교 한 줄로 바꿨습니다. 자릿수 제한이 없고 앞자리 0도 처리하므로 동작은 같습니다.
  • compareTo가 4.3과 4.3.0에 0을 반환하지만 Version은 equals를 재정의하지 않습니다. 이 불일치(TreeSet/TreeMap에 넣으면 두 표기가 하나로 합쳐짐)를 Javadoc에 적었습니다.
  • 테스트 5건 추가: 한정자 번호 비교, 릴리스 이전 한정자의 순서, SP 한정자, 버전 14종의 모든 쌍에 대한 반대칭성·연쇄성, long 범위를 넘는 자리.

후속 커밋 2: 06b1fcf (숫자 자리 수가 다른 버전, 공백이 섞인 버전)

자리를 위치만 맞춰 비교해서 한정자가 상대의 숫자 0과 비교되던 문제

본문에 적어 주신 2.0.0-M2 vs 2.0-RC1 한계와 원인이 같은 경우들입니다.

비교 수정 전 수정 후
4.3.GA vs 4.3.0 -1 0
1.0-SNAPSHOT vs 1.0.0-SNAPSHOT -1 0
2.0-RC1 vs 2.0.0-M2 -1 +1

첫 번째 경우는 4.3.GA = 4.3이고 4.3 = 4.3.0인데 4.3.GA < 4.3.0이 되어, compareTo의 연쇄성도 깨져 있었습니다. compareVersion이 앞쪽의 숫자 자리들을 먼저 비교하고(없는 자리는 0), 결과가 같을 때에만 그 뒤의 한정자 자리들을 비교하도록 바꿨습니다. 아울러 GA, Final, RELEASE는 서로 같은 것으로 판정합니다(기존에는 4.3.GA와 4.3.Final이 이름의 사전순으로 갈렸습니다).

공백이 섞인 버전이 어떤 버전보다도 낮게 판정되던 문제

PomString이 element.getText()를 공백 제거 없이 저장하기 때문에, <version>을 줄을 바꿔 적었거나 앞뒤에 공백이 있으면 첫 세그먼트(" 7")가 숫자로 인식되지 않았습니다. 그래서 " 7.0.0 "이 6.2.11보다 낮게 판정되었습니다. Version.setContent가 realVersion을 정할 때와 프로퍼티를 해석할 때 앞뒤 공백을 제거하도록 했습니다. getContent()의 원본 문자열은 그대로 두었으므로 pom에 적힌 표기는 바뀌지 않습니다. isUnresolvedProperty도 같은 기준으로 맞췄습니다.

테스트 5건 추가: 릴리스 한정자와 끝자리 0의 동등성, 자리 수가 다른 버전의 한정자 정렬, 자리 수가 서로 다른 버전 12종의 연쇄성, 앞뒤 공백, 공백이 섞인 프로퍼티 참조와 프로퍼티 값.

"알려진 한계"에 대한 의견

  • 2.0.0-M2 vs 2.0-RC1: 위 06b1fcf 로 해결되었습니다.
  • 1.0.0.RELEASE vs 1.0.0: 말씀하신 대로 같은 버전으로 두었습니다. 이 플러그인의 용도에서는 Spring 계열의 x.y.z.RELEASE를 같은 버전으로 보는 편이 안전하다는 판단에 동의합니다.

Maven과 의도적으로 다르게 남겨 둔 부분도 참고로 적어 둡니다. cr·milestone 같은 별칭은 지원하지 않고 한정자 이름을 사전순으로 비교하며(alpha < beta < m < rc < snapshot 순서는 Maven과 일치합니다), -jre 같은 분류용 접미사는 릴리스보다 낮게 판정합니다. 기본 pom_master.xml에는 해당하는 버전이 없어서 실제 영향은 없습니다.


전자정부 표준프레임워크에 기여해 주셔서 감사합니다.

swanpark8538 added a commit that referenced this pull request Sep 18, 2026
Version.compareTo 가 자리를 위치만 맞춰 비교해, 숫자 자리 수가 다른 두 버전에서는 한정자가
상대의 숫자 0 과 비교됐다. 4.3.GA 는 4.3 과 같고 4.3 은 4.3.0 과 같은데 4.3.GA 는
4.3.0 보다 낮게 판정돼 추이성이 깨졌고, 1.0-SNAPSHOT 이 1.0.0-SNAPSHOT 보다, 2.0-RC1 이
마스터 pom 에 실제로 있는 2.0.0-M2 보다 낮게 판정됐다. 앞쪽의 숫자 자리들을 먼저
비교하고(없는 자리는 0), 같을 때 그 뒤의 한정자 자리들을 비교한다. GA, Final, RELEASE 는
서로 같은 것으로 본다.

pom 에 줄을 바꾸거나 공백을 두고 적은 버전은 첫 자리가 숫자로 인식되지 않아 어떤 버전보다도
낮게 판정됐다. 원본 내용은 그대로 두고 실제 버전과 프로퍼티 해석에서만 앞뒤 공백을 없앤다.

Refs: #155
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants