Why need to not use SimpleDateFormat?

들어가기에 앞서

제목이 좀 자극적이었나요? 전혀 자극적이지 않습니다.

엄밀하게는, production 에 SimpleDateFormat 을 쓰면 안되는 이유입니다.

더 엄밀하게는, multi-thread 환경에서 사용하면 안되는 이유입니다.

 

SimpleDateFormat 이란?

Java7 에서 제공하는 (locale sensitive 한) SimpleDateFormat 클래스는 다음과 같은 역할을 합니다.

  • String 을 파싱하여 Date 객체를 생성합니다.
  • Date 객체를 formatting 하여. String 화 합니다.
  • python datetime 패키지의 strftime, strptime 과 같은 역할을 합니다.

 

그런데 왜 사용하지말아야 할까요?

해답은 oracle javadocs 에 있습니다.

https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html

Synchronization

Date formats are not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.

See Also:
Java Tutorial, Calendar, TimeZone, DateFormat, DateFormatSymbols, Serialized Form

synchronized 하지 않기 때문입니다.

 

근데 이게 왜?

일반적으로 SimpleDateFormat 을 사용하시는 분들이 대부분,

public static final 로 선언하고 static 객체를 참조하기 때문이죠.

 

우리팀의 Legacy 코드에도 비슷한 문제를 가지고 있었습니다. (이 아티클을 작성하는 주된 이유이기도 합니다)

HTTP 요청을 받는 레이어는 thread pool 로 connection 을 관리합니다.

다시 말해, WEB, WAS 는 기본적으로 multi-thread 환경이란 뜻입니다.

절대로 절대로 절대로 SimpleDateFormat 을 public static 객체로 선언해놓고 여러곳에서 참조해서는 안됩니다.

 

 

정말로 문제가 될까?

네 문제가 됩니다.

아래 테스트 코드를 예시로 들겠습니다.

 

Utilization Method

util 성 method 먼저 설명을 드리겠습니다.

  • generateRandomTimestamp
    • 사전에 지정한 범위 내에서 random 한 unix timestamp 를 생성하여 long 형태로 반환한다.
  • dateToFormattedString1
    • 주어진 Date 객체를 SimpleDateFormatter 를 이용해 formatting 한다.
  • dateToFormattedString2
    • 주어진 Date 객체를 DateTimeFormatter 를 이용해 formatting 한다.

 

Test Flow

generateRandomTimestamp 로 생성한 long 값으로 Date 를 객체를 만듭니다.

만든 Date 객체를 dateToFormattedString1, dateToFormattedString2 메서드를 통해 stringify 하고,

두 문자열 값을 Assertion 합니다.

 

Test Method
  • it_is_look_like_thread_safe_simpledateformat
    • sequential 하게 iterative 한 loop 문을 돌면서 Test Flow 를 진행합니다.
  • why_thread_unsafe_simpledateformat
    • Parallel stream 을 이용해 병렬로 Test Flow 를 진행합니다.

 

Test Result

이런 이런, 정말로 why_thread_unsafe_simpledateformat 테스트 메서드에서 실패를 했습니다.

 

좀 더 자세하게 실패 사유를 보면, 정말로 stringify 된 두 값이 달라 에러를 발생하고 있습니다.

 

 

해결 방법

두 가지 안이 있습니다.

 

  • SimpleDateFormat 을 사용해야하는 로직안에서 new
  • apache common lang 패키지의 FastDateFormat 을 사용
  • Java8 에서 지원하는 DateTimeFormatter 로 전환

SimpleDateFormat 을 public static 으로 만들고, 여러 곳에서 참조한다면 당연히 thread un-safe 합니다.

따라서 사용되는 로직에서 new SimpleDateFormat(“format”) 하면 ‘심플’ 하게 해결이 됩니다.

하지만 비즈니스 로직안에 new SimpleDateFormat() 을 하고 싶은 개발자분은 없으리라 믿습니다.

가급적 2안 혹은, (진행하는 프로젝트가 Java8+ 라면,) 3안을 선택하여 리팩토링을 진행하시기 바랍니다.

 

 

 


댓글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다

This site uses Akismet to reduce spam. Learn how your comment data is processed.