발단
회사의 동료 개발자가 퇴직을 함에 따라, 동료 개발자가 들고 있었던 유지보수 프로젝트 하나가 나에게 넘어왔다. 입사 초, 프로젝트 구축 초기에 잠시 참여해 포인트, 출석체크 기능을 만든 적이 있었던 프로젝트인데 이후에는 별다른 작업에 참여하지 않아 히스토리를 전혀 모르는 상황이었다.
며칠 전, 클라이언트로부터 게시판의 고정글 기능이 제대로 작동하지 않는 것 같다는 문의가 왔다. 우리 회사 고정글 기능은 특정 날짜를 입력하면 그 날짜에 속하는 일자에 고정글이 게시되고, 그 일자 이전 혹은 이후에는 고정글이 내려가도록 되어 있었는데 확인을 해보니 해당 기능이 동작하지 않았다. 회사 cms에 기본적으로 탑재되어 있는 기능인데 왜 동작을 하지 않을까 의문을 가지며 디버깅을 시작했다.
문제의 대략적인 형태
해당 프로젝트는 MyBatis로 영속성 계층을 잡아주고 있었고, Cron4j라는 라이브러리를 이용해 매일 새벽 2시마다 게시판의 고정글 여부를 판단해 고정글을 내리고, 올리는 기능을 가지고 있었다.
즉,
- 매일 새벽 2시
- cron4j 동작
- xml 파일에 등록되어 있는 sql이 실행되며 데이터베이스로 sql이 날아감
- 고정글 여부와 고정글 기간에 따라 게시판의 고정글 상태가 바뀜
의 차례를 가지고 있었던 것.
이 기능은 회사 cms에 기본적으로 탑재되어 있는 기능이라 전에 프로젝트를 맡으셨던 개발자분께서 해당 기능을 커스텀한 것일까 확인했는데, 또 아니었다. 기본 상태 그대로 있었다.
다른 프로젝트에서는 정상적으로 돌아가는 코드가 왜..?
문제 파악
프로젝트를 로컬에서 붙어 작업을 하다보니 이상한 부분을 발견했다. 우리 회사 같은 경우 보통 로컬은 데이터베이스를 따로 만들지 않고 개발 db에 붙어 작업을 하는데, 개발 db의 데이터 정보와 로컬에서 빌드한 프로젝트에서 뷰단에 출력되는 데이터 정보가 서로 다른 것을 발견한 것이었다. 바로 globals.properties에 들어가 데이터베이스의 설정을 보니 로컬의 데이터베이스 설정이 개발서버 데이터베이스를 바라보는 것이 아니라 운영서버 데이터베이스를 바라보고 있는 것을 발견했다. (아직 런칭되지 않은 프로젝트라 운영서버 데이터베이스에도 테스트 데이터들밖에 없어 전 개발자분께서 운영 디비에서 작업을 하신 것 같았다.)
globals.properties를 개발서버의 데이터베이스 설정으로 교체한 후, 프로젝트를 빌드해 해당 기능을 테스트 해 보았다. 정상적으로 동작하는 것을 확인했다. 다시 globals.properties를 운영서버의 데이터베이스 설정으로 교체한 후 같은 기능을 테스트해 봤다. 어라, 에러가 나오네?
### Error updating database. Cause: java.sql.SQLException: Incorrect datetime value: ' 00:00:00' for function str_to_date
...
### Cause: java.sql.SQLException: Incorrect datetime value: ' 00:00:00' for function str_to_date
; uncategorized SQLException for SQL []; SQL state [HY000]; error code [1411]; Incorrect datetime value: ' 00:00:00' for function str_to_date; nested exception is java.sql.SQLException: Incorrect datetime value: ' 00:00:00' for function str_to_date
java.sql.SQLException: Incorrect datetime value: ' 00:00:00' for function str_to_date
왜 개발에서는 잘 되는 것이 운영에서는 되지 않을까를 파악하기 위해 각각의 데이터베이스를 보니 운영과 개발이 서로 다른 rdbms를 사용하는 것을 발견했다. 정상 동작하는 개발서버는 MySQL을, 정상적으로 동작하지 않는 운영서버는 MariaDB를 사용하고 있었던 것이었다.
해결
문제가 되었던 sql은 아래 부분이었다.
AND STR_TO_DATE(CONCAT(IFNULL(PO_STICKY_SDATE, '2000-01-01'), ' 00:00:00'), '%Y-%m-%d %H:%i:%s') > NOW()
...
아무리 봐도 문제가 없는데 뭐지 싶어 구글링을 하던 도중, 00:00:00과 같은 형태를 사용하면 좋지 않을 수 있다는 게시글을 하나 발견했다.
https://jaehoney.tistory.com/120
뭔가 데이터베이스의 설정 차이 때문에 발생한 문제일 수 있겠구나 하는 생각이 들었다. 같은 rdbms라도 버전에 따라 지원하는 문법이나 내부 설정들이 달라 오류를 뱉을 때가 있을 수 있다는 사실을 실무를 경험하며 여러번 느꼈었기 때문이었다. 뭔가 mysql에서는 지원을 하는데, mariadb에서는 지원하지 않는 무언가가 있는 것일까 하는 생각이 들어 위의 게시글을 토대로 시간 부분을 조작하며 테스트를 진행했다. 그리고 결국, 아래와 같은 sql에서 mariadb가 에러를 내뱉지 않고 성공적으로 결과를 출력한다는 사실을 알게 되었다.
AND STR_TO_DATE(IFNULL(PO_STICKY_SDATE, '2000-01-01'), '%Y-%m-%d %H:%i:%s') > NOW()
느낀 점
oracle, mysql, mariadb들이 언뜻 보기에는 서로 비슷해 보이고 사용법도 같아 보이지만 실제로는 내부 메커니즘도 다르고 기능상에 많은 차이가 있다는 부분을 다시 한 번 체감한 경험이 되었다. 그만큼 개발을 진행하며 로컬, 개발, 운영서버의 개발환경을 동기화하는 것이 얼만큼 중요한 지 다시 한 번 느꼈던 경험이었던 것 같다. 사실 운영과 개발의 데이터베이스 세팅이 같았다면, 어쩌면 이 이슈는 개발 단계에서 이미 수정되어 제거되었을 수도 있었을테니 말이다(혹은 어쩌면 아예 이슈 자체가 발생하지 않았을 수도 있다). 지금은 클라이언트가 피드백을 요청해서 이슈가 있음을 인지하게 된 상황이라 만약 프로젝트 런칭이 끝난 후, 사용자 단에서 해당 버그가 발견되었다면 얼마나 개발자로서 부끄러운 일이었을까를 생각했던 것 같다.
추가적으로 데이터베이스는 역시 하나의 전문영역이구나를 느꼈던 또 한번의 순간이었던 것 같다. 사실 이렇게 저렇게 시도해보다 얼떨결에 이슈가 해결된 것이지 아직 나는 mysql과 mariadb가 어떻게 달라서 해당 이슈가 생겼으며 어떠한 이유에서 두 데이터베이스가 다른지 모르기 때문이다. 어쩌다 이슈를 잡았으니 망정이지, '어쩌다'라는 요행이 없었다면 나는 아직도 해당 이슈를 잡으려고 머리를 싸맸을지 모른다.
'메모 & 삽질기록보관소' 카테고리의 다른 글
[자바] MXBean already registered 에러 핸들링 (0) | 2023.06.15 |
---|---|
MQTT 공부 중 참고할만한 문헌 모아두기 (5) | 2023.06.11 |
[strapi] NginX 리버스 프록싱 관련 이슈 (0) | 2022.07.07 |
[JPA] 스프링이 엔티티를 인식하지 않는 것 같습니다! (0) | 2022.06.18 |
[윈도우 11?] Mybatis - Password Not Matched!!! (0) | 2022.03.18 |