박종훈 기술블로그
close menu

JDK-8381330 기여기: 기여는 실패했지만, 메인테이너도 몰랐던 i18n 잔재를 발견했다

JDK-8381330을 보고했다. 결과적으로 이슈는 Duplicate로 닫혔지만, 이 과정에서 OpenJDK 메인테이너도 인지하지 못하고 있던 문제가 수면 위로 올라왔다.

발단: 내 맥북에서만 발생하는 테스트 실패

OpenJDK를 빌드하고 테스트를 돌려보면 항상 몇몇 테스트가 실패하였다. hotspot/jtreg/runtime/verifier/TestANewArray.java 테스트가 그 중 하나이다.

아래와 같은 에러가 발생된다.

STDERR:
Running with cfv: 49, test_Dimension_254
Running with cfv: 49, test_Dimension_255
 stdout: [];
 stderr: [\uc624\ub958: \uc774 \uc560\ud50c\ub9ac\ucf00\uc774\uc158\uc744 \uc2e4\ud589\ud558\ub294 \ub370 \ud544\uc694\ud55c JavaFX \ub7f0\ud0c0\uc784 \uad6c\uc131\uc694\uc18c\uac00 \ub204\ub77d\ub418\uc5c8\uc2b5\ub2c8\ub2e4.
]
 exitValue = 1
java.lang.RuntimeException: 'java.lang.VerifyError' missing from stdout/stderr

테스트는 java.lang.VerifyError가 출력에 포함되어 있는지를 확인하는데, 해당 문자열이 포함되지 않았다.

분석: 왜 실패하는가.

우선 해당 유니코드를 디코드 해보면 다음과 같이 나온다.

오류: 이 애플리케이션을 실행하는 데 필요한 JavaFX 런타임 구성요소가 누락되었습니다.

처음 생각한 해결책

처음에는 단순히 현재 시스템의 언어가 무엇인지에 따라 결과가 달라지나 생각하였다.

내 환경은 Apple M1 Max, macOS 14.6.1(23G93)이었고, 시스템 언어가 한국어로 설정되어 있었다.

그래서 테스트 코드에서 서브프로세스를 실행할 때 -Duser.language=en 옵션을 추가하면 해결될 것이라고 생각했다.

그리고 실제로 위 옵션을 추가하면 테스트가 통과가 되었다.

그래서 이 내용으로 이슈를 등록했다.

메인테이너의 반응

메인테이너인 David Holmes와 이슈를 확인해나갔다.

처음에는 그도 OpenJDK 에 localized 된 에러메시지 같은건 없다며, 내가 JDK를 빌드할 때 뭔가 실수 한게 아니냐고 물어보았다.

하지만 곧 흥미로운 점을 지적했다.

한국어 에러 메시지를 다시 확인해 보면 아래와 아래와 같았다.

오류: 이 애플리케이션을 실행하는 데 필요한 JavaFX 런타임 구성요소가 누락되었습니다.

이것은 영어로 하면 다음과 같다.

Error: A JavaFX runtime component required to run this application is missing

그런데 이 메시지는 실제로 발생해야 하는 에러 메시지가 아니었다. 원래 출력되어야 할 메시지는 다음과 같다.

Error: Unable to initialize main class classCName_52_255 Caused by: java.lang.VerifyError: Illegal anewarray instruction, array has more than 255 dimensions

영어 메시지 템플릿은 Error: Unable to initialize main class {0}\nCaused by: {1}: {2} 형식으로, {1} 자리에 java.lang.VerifyError가 들어간다. 하지만 한국어 번역 파일(launcher_ko.properties)의 해당 템플릿에는 {1} 파라미터가 포함되어 있지 않았고, 엉뚱한 JavaFX 관련 메시지가 출력되고 있었던 것이다.

즉, 문제의 본질은 테스트가 아니라 오래된 한국어 번역 리소스에 있었다.

더 큰 문제: 관리되지 않는 번역 리소스

David Holmes가 이 문제의 배경을 설명해 주었다.

JDK는 JDK 10까지 11개 언어의 UI 번역을 지원했다. 하지만 JDK-8204973을 통해 JDK 11부터 공식 지원 언어가 영어(en), 일본어(ja), 중국어 간체(zh_CN)로 축소되었고, 이후 독일어(de)가 추가되었다.

문제는 지원이 중단된 언어의 번역 파일들이 레포지토리에 그대로 남아있었다는 것이다. 한국어, 프랑스어, 이탈리아어, 포르투갈어, 스페인어, 스웨덴어 등의 .properties 파일이 JDK 10 시절의 내용 그대로 유지되고 있었다.

JDK 22에서 JDK-8315458로 launcher의 에러 메시지 템플릿이 변경되었는데, 영어와 공식 지원 언어의 번역 파일은 함께 업데이트되었지만 한국어를 포함한 비지원 언어의 파일은 업데이트되지 않았다. 그 결과 영어 템플릿과 한국어 템플릿 사이에 불일치가 발생했고, 이것이 테스트 실패의 원인이었다.

내가 로컬에서 한국어 launcher_ko.properties의 해당 템플릿을 영어 버전에 맞게 수정했더니 테스트가 정상적으로 통과했다. 또한 macOS의 기본 언어를 영어로 변경했을 때도 문제없이 통과했다. 이를 통해 문제의 원인이 오래된 번역 리소스에 있다는 것이 확인되었다.

JDK-8381436: 폐기된 번역 리소스 일괄 제거

이 문제를 계기로 JDK-8381436이 등록되었다.

Since those resources are no longer maintained, they have become stale over time and are now causing issues. One example is JDK-8381330 which was caused by a mismatch between the English resource and the stale Korean resource. To prevent similar problems, it would be preferable to remove these obsolete resources from the repository, unless another organization is willing to take over their maintenance.

공식 지원이 중단된 언어의 번역 리소스를 레포지토리에서 모두 제거하자는 것이다. 유지보수되지 않는 번역 파일이 남아있으면 시간이 지남에 따라 영어 리소스와의 불일치가 쌓이고, 이번처럼 예상치 못한 문제를 일으킬 수 있기 때문이다.

이 이슈에 대한 PR(openjdk/jdk#30524)이 이미 제출되어 리뷰 중이다.

내가 보고한 JDK-8381330은 JDK-8381436의 Duplicate로 닫기로 결정되었다.

마무리

이번 기여 시도 자체는 “실패”했다. 내가 제안한 -Duser.language=en 수정은 적용되지 않았고, 이슈는 Duplicate로 닫혔다.

하지만 이 과정이 의미 없었다고는 생각하지 않는다. 한국어 macOS에서 테스트를 돌리지 않았다면, 이 문제가 발견되기까지 더 오래 걸렸을 수도 있다. 실제로 메인테이너인 David Holmes도 한국어 번역 파일이 이런 상태라는 것을 몰랐고, 이슈를 통한 대화 과정에서 문제의 본질이 드러났다.

결과적으로 JDK 10 이후 6년 넘게 방치되어 있던 폐기된 번역 리소스를 정리하는 작업(JDK-8381436)으로 이어졌다. 한국어뿐만 아니라 프랑스어, 이탈리아어 등 관리되지 않는 모든 번역 리소스를 제거하는, 내가 처음 생각했던 것보다 훨씬 큰 범위의 정리 작업이다.

OpenJDK처럼 거대한 프로젝트에서도, 메인테이너도 인지하지 못하는 사각지대가 존재할 수 있다는 것을 다시 한 번 느꼈다. 이전 기여에서도 2007년부터 약 20년간 남아있던 정의 없는 함수 선언을 발견한 적이 있는데, 이번에는 JDK 10 이후 관리되지 않던 번역 리소스가 그런 사각지대였다. 기여가 항상 코드 변경으로 이어지지는 않지만, 문제를 발견하고 보고하는 것만으로도 프로젝트에 기여할 수 있다는 것을 느꼈다.

categories: 개발

tags: JDK , OpenJDK , i18n , l10n , 오픈소스 기여