새소식

Welcome to the tech blog of Junior Backend Developer Myoungji Kim!

Database/MySQL

LocalDateTime 데이터의 MySQL datetime 반올림 이슈

  • -

요즘 한창 SMS 발송 결과 조회 프로세스를 개선 중이다.


단순히 SMS 발송뿐만 아니라, 카카오 알림톡 발송 실패 시 SMS 대체 발송하는 컨슈머까지 함께 개선하게 되었는데,
컨슈머에서 SMS 발송 요청 내역을 상점 DB에도 저장해야 한다는 추가 요구사항이 있었다.

 

문제는 API에서는 수신한 '발송요청시각'을 API DB에 저장하고, 컨슈머에서는 API 요청 직후 동일한 값의 '발송요청시각'을 상점 DB에 저장하는데, 간헐적으로 두 DB의 '발송요청시각'이 1초씩 차이 나는 이슈가 있었다.

 

이 미세한 차이로 인해, SMS 발송 내역을 조회할 때 일부 데이터가 누락되는 문제가 있었다.

여기서 "왜 발송 내역을 굳이 두 개의 DB에 저장하지?"라는 의문이 들 수 있는데, 이는 레거시 구조에서 비롯된 이슈고..
지금은 근본적인 문제 해결을 위해 하나의 마스터 DB에만 발송 내역이 적재되도록 전체 구조를 갈아엎는 중이다..!
그래서 의문점은 일단 패스하길!

 

🚨 원인: MySQL의 datetime 반올림

🔗 https://dev.mysql.com/doc/refman/5.7/en/fractional-seconds.html

 

MySQL :: MySQL 5.7 Reference Manual :: 11.2.7 Fractional Seconds in Time Values

11.2.7 Fractional Seconds in Time Values MySQL has fractional seconds support for TIME, DATETIME, and TIMESTAMP values, with up to microseconds (6 digits) precision: To define a column that includes a fractional seconds part, use the syntax type_name(fsp)

dev.mysql.com

문제의 원인은 MySQL의 datetime 반올림 처리였다.

 

위 공식 문서에 따르면, MySQL의 TIME, DATETIME, TIMESTAMP 타입은 최대 마이크로초(6자리)까지 fractional seconds(소수점 이하 시각)를 지원하지만, 기본값은 소수점 없음(fsp = 0) 으로 설정되어 있다. 만약 DATETIME 타입 컬럼에 LocalDateTime처럼 나노초 단위의 값을 저장하려 하면, MySQL은 이를 반올림하여 초 단위로 저장하게 된다.

 

ex. 2024-04-12T10:15:30.999999999 => 2024-04-12 10:15:31

해결: 나노초 제거 후 저장

해결 방법은 생각보다 간단했다..!

Kotlin의 LocalDateTime에서 나노초 이하 값을 잘라낸 후 저장하도록 수정했다. 🙆‍♀️

smsLog.sendDt.truncatedTo(ChronoUnit.SECONDS))

 

이렇게 하면 2024-04-20T10:15:30.999999999도 MySQL에 저장될 때 2024-04-20 10:15:30으로 정확하게 일치하게 된다.
반올림 없이 그대로 잘리기 때문에, 이제 상점 DB에도 API DB와 동일한 초로 저장이 가능해졌다!

(참고, API 쪽에서는 처음부터 @JsonFormat으로 나노초를 제거하고 받았기 때문에 애초에 반올림 이슈가 없었던 것으로 확인했다.)

이슈 원인 찾자마자 개발팀에도 호다닥 공유했고, 다들 처음 알았다는 반응을 보면서 이제 같은 이슈는 안 생기겠구나(?) 하는 안도감이 들었다..ㅎㅎ

 

DATETIME은 워낙 일반적으로 많이 쓰는 타입이라 그동안 아무 생각 없이 사용했는데,,
이번 이슈를 계기로 필드 타입의 특성과 처리 방식에 대해서도 꼼꼼히 공부해야겠다는 생각이 들었다 📝

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.