-
SimpleDateFormat, 언제일지 모른다Java & Kotlin 2026. 3. 1. 05:30반응형
1) 서론
캘린더에 일정을 등록하고 잘 관리하고 계신가요?
과거에는 매년 새해가 되면 수첩과 미니 종이 달력을 구매해서 볼펜으로 작성하고 했었는데요. 직접 작성하다 보니 실수할 여지가 없었습니다. 쓰면서 스스로 읽으면서 작성하게 되고, 잘 못 된 것은 곧바로 알게 되어서 수정 테이프로 수정하곤 했었습니다.
하지만 최근 5년이내에 종이로 된 문서에 볼펜으로 직접 기록을 하는 경우는 없었던 것 같습니다. 대부분의 사람들이 모든 메모와 일정 관리는 모바일 앱으로 하고 있고 있을 것 같습니다.
하지만 모바일 앱으로 일정을 관리하면 간혹 실수하는 경우가 생깁니다. 개인 일정을 회사 계정으로 로그인된 캘린더에 작성하거나, 일자를 잘 못 클릭해서 작성해두거나 하는 경우가 종종 있는데요. 일정을 지키는 것은 상대방과의 약속이기도 하기 때문에 매우 중요합니다. 만약 다른 날로 일정이 잘 못되어 있다면 꽤 억울할 것입니다.
이번에 파일송수신 업무를 하며 오래된 로직에서 잘못된 날짜를 반환하는 버그가 있었습니다. 범인은 Java의 SimpleDateFormat 객체였으며 파악 과정을 공유드리겠습니다.
2) 오늘 날짜로 입력했어요. 그런데 어제로 되어있던데요?
SimpleDateFormat을 이용해서 특정 날짜를 구하는 함수가 있었습니다. 기준 날짜와 숫자를 계산해서 오늘 혹은 과거 일자를 yyyyMMdd 포맷으로 반환하는 함수입니다.

오래된 레거시 로직 해당 함수를 이용해서 파일명_YYYYMMDD 포멧으로 생성된 파일을 이동시켜야 합니다. 그리고 사용자는 항상 오늘자 파일을 이동시키는 것을 가정하고 있습니다. 하지만 어떠한 이유에서인지 간헐적으로 다른 날짜의 파일을 조회하는 문제가 발생했습니다.

간단하게 테스트해봅니다.

모든 테스트 케이스가 잘 동작합니다.

3) 문제는 늘 그렇듯, 동시성 문제
동일한 함수를 멀티스레드 환경에서 테스트해보겠습니다.
멀티스레드 환경에서 하루 전, 이틀 전을 동시에 요청합니다. 그리고 의도한 대로 결과를 반환했는지 확인합니다. 만약 의도한 날짜와 다르다면 corrupted를 증가시킵니다.


하지만 테스트는 실패하고 총 2,000개 중 134개의 값(6.7%)이 일치하지 않습니다.

이유는 SimpleDateFormat format() 함수의 문제입니다. 내부적으로 calendar 필드를 참조하고 있는데요. 입력된 날짜를 해당 객체에 저장하고 여러 곳에서 변경합니다.
문제는 calendar는 클래스의 인스턴스 필드입니다. 즉 여러곳에서 공유되는 데이터인데요. 오래된 레거시 로직에서는 SimpleDateFormat를 static instacne field로 생성 후 공유해서 사용하고 있었습니다. 그래서 멀티스레드 환경에서는 어떠한 날짜가 주어지던 한 번 생성되고 공유되는 SimpleDateFormat가 동시성 문제를 일으킵니다.
private static final SimpleDateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");

그렇다면 어떻게 수정할 수 있을까요?
다행히 SimpleDateFormat 클래스의 JavaDoc을 보면 동시성 문제가 있으니 DateTimeFormatter를 사용하도록 하고 있습니다. DateTimeFormatter을 이용해서 코드를 변경해 보겠습니다.

DateTimeFormatter는 내부적으로 상태를 공유하는 instance field가 없습니다. 요청마다 DateTimeParseContext를 새롭게 생성하고 모든 값은 local variable로서 stack memory를 사용합니다. 그래서 동시성 문제가 발생하지 않습니다.



4) 결론
SimpleDateFormat을 static instance field로 생성해 두고 공유해서 사용하면 큰 문제가 발생하는 것을 알 수 있습니다.
오래전 과거에 작성된 레거시 코드에 있고, 간헐적으로 날짜 생성이 잘 못 된다면 파악하기도 어렵습니다. 특히 단순 계산을 담당하는 util 이름의 클래스에서 동시성 문제가 있을 것이라고는 예상하기 더욱더 어렵습니다.
단순한 함수더라도 항상 동시성 문제를 고려해야 하며 테스트 코드 작성 시에도 고려되어야 하는 것을 배울 수 있습니다.
반응형'Java & Kotlin' 카테고리의 다른 글
Java ArrayList 동시성 문제와 성능 (1) 2025.02.03 인터페이스와 제네릭을 이용한 공통 로직 관리 (1) 2024.01.01 ExecutorService 그리고 maxThreadPool (1) 2023.11.15 JVM OOM 발생 및 원인 분석하기 (0) 2023.09.22 UTF-8, EUC-KR 인코딩 파일 읽어들이기 (0) 2023.09.03