오라클을 사용하여 개발을 하다보면 literal does not match format string 과 같은 에러가 발생할 때가 있다. 이 에러를 검색해보면 문자열 형식이 맞지 않아 발생하는 에러다. 어떤 값에 대해서 TO_CHAR() 함수를 사용해 값의 형태를 바꿀때 변환하는 값의 형태를 지정해주지 않으면 발생하게 된다.
1. 에러 케이스
위의 에러가 발생하는 경우가 몇가지 있어서 정리해 보았다.
1. TO_CHAR() 함수를 사용해 값의 형태를 바꿀때 변환하는 값의 형태를 지정해주지 않는 경우
2. 서버의 환경 설정 (Lang) 값이 다른 경우
3. 로컬에서는 정상적으로 실행되지만 서버에 배포했을 때 발생하는 경우
이렇게 크게 세가지로 정리할 수 있다.
그런데 나는 3번의 경우로 에러가 발생하면서 해결한 내용을 정리해보려고 한다.
분명 로컬에서 개발할 당시에는 아무 문제가 없었는데, 서버에 배포하고 난 후에 에러가 발생하게 되니 당황스럽지 않을 수 없었다. 억울했지만 항상 그렇듯 로컬에서만 잘 되고 운영에서 안되는건.. 그냥 안되는거다...
2. TO_CHAR() 기본 코드
개발 환경은 Springboot + Mybatis 로 진행하였다.
자, 먼저 어떤 상황에 에러가 발생하는 건지 보도록 하자.
묵시적 형변환 쿼리
SELECT TO_CHAR(date_column) FROM table;
위의 쿼리문을 보면 그냥 간단하게 테이블 조회하면서 날짜 컬럼을 문자열로 바꿔주는 쿼리문이다. 언뜻 보면 크게 문제도 없어보이고 DBeaver와 같은 툴로 실행해보면 별다른 이슈 없이 정상적으로 실행된다.
그리고 Mybatis를 통해서 실행하여도 큰 이슈가 없었다.
그러나, 위의 쿼리문을 빌드 후 서버에 배포하게 되면 바로 literal does not match format string 에러가 발생하는 것이다. 위의 에러는 기본적으로 TO_CHAR() 함수를 사용할 때 날짜 형태의 값을 문자열로 변환하고자 하는 포맷 형태를 지정하지 않으면 발생하는 에러다.
명시적 형변환 쿼리
SELECT TO_CHAR(date_column, ‘YYYY-MM-DD') FROM table;
위의 쿼리문은 에러가 발생했던 쿼리와 다르게 TO_CHAR() 함수에 포맷값을 명시적으로 추가해 주었다. 위와 같이 명시적으로 추가해 준다면 빌드 후 배포를 하게 되더라도 더이상 에러가 발생하지 않는다.
그럼 여기서 두 쿼리의 차이는 포맷 값을 명시적으로 줬는지 아니면 생략했는지로 에러가 발생했다고 볼 수 있다.
그러나 나는 개발할 때 묵시적 코드 방식보다는 명시적으로 작성하는 것을 선호하기 때문에 이 부분은 정상적으로 작성이 되어 있었다. 그럼에도 불구하고 동일한 에러가 발생한 것이다. 어떤 문제때문에 발생했던 건지.. 또 다른 예시를 통해서 정리해 보았다.
3. TO_CHAR() 코드 비교 에러 분석
위에서 TO_CHAR() 함수를 사용할 때 어떤 상황에 에러가 났는지 정리해 보았다. 하지만 명시적으로 포맷 값을 줬는데도 에러기 발생하는 경우가 있다.
날짜 데이터 비교 쿼리
SELECT * FROM table
WHERE create_date = TO_CHAR(#{dateColumn}, 'YYYY-MM-DD')
위의 쿼리는 날짜 컬럼을 기준으로 비교하는 쿼리다. 그냥 보면 아무 문제가 없고, Mybatis나 DB 툴에서 실행하면 에러없이 실행이 잘 된다. 그러나 위의 예시처럼 서버에 빌드 후 배포를 하게 되면 동일한 에러가 발샐하게 된다. 이 부분에서 시간을 많이 썼던 것 같다. 테이블 생성을 잘못한건지, 컬럼이 이상한건지, 개발 환경이 이상한건지 찾아봤지만 아무 문제가 없었다.
그러다.. 쿼리와 테이블의 컬럼을 유심히 보다보니...
비교하고자 하는 날짜 컬럼과
TO_CHAR(#{dateColumn}, 'YYYY-MM-DD')의 데이터 유형이 다르다는 것을 발견하게 되었다.
날짜 데이터 비교 쿼리 (포맷 일치)
SELECT * FROM table
WHERE TO_CHAR(create_date, 'YYYY-MM-DD') = TO_CHAR(#{dateColumn}, 'YYYY-MM-DD')
이 부분이 문제가 됐던 것이다. 로컬 환경에서나 DB 툴에서는 묵시적으로 조회를 해도 자동으로 데이터 포맷을 맞춰 실행을 해주지만 빌드 후 배포를 하게 되면 자동으로 포맷을 맞춰주지 않아 에러가 발생하게 되는 것이다.
프로그래밍의 기본중 하나가 데이터의 형변환을 잘 맞추고 비교하고 처리해야 하는데, 이런 부분까지 신경써야 된다는 것을... 처음 알게 되었다. 앞으로는 데이터 비교할 때 반드시 형변환부터 명시적으로 맞춰놓고 해야되겠다.
예를 들어,
1. 전달받은 파라미터와 날짜 컬럼을 문자열로 동일하게 포맷
WHERE TO_CHAR(create_date, 'YYYY-MM-DD') = TO_CHAR(#{dateColumn}, 'YYYY-MM-DD')
2. 날짜 컬럼의 유형에 맞게 전달받은 파라미터를 날짜 형태로 포맷
WHERE create_date = TO_DATE(#{dateColumn}, 'YYYY-MM-DD')
3. 전달받은 파라미터와 날짜 컬럼 모두 동일한 Date, format 맞추기
WHERE TO_DATE(create_date, 'YYYY-MM-DD') = TO_DATE(#{dateColumn}, 'YYYY-MM-DD')
이렇게 최대한 명시적으로 포맷을 맞춰주는게 좋고, 가급적이면 1, 3번과 같이 비교 대상의 값들을 모두 동일한 포맷으로 맞춰주는게 좋을 것 같다.
추가로 날짜가 아닌 다른 유형의 데이터를 비교할 때에도 동일하게 포맷을 맞춰주는게 좋을 것 같다.
마무리
처음 개발을 배우고 시작할 때에 비하면 이제는 개발을 하면 에러율도 많이 줄어들고 자신감이 생겼지만, 여전히 이런 문제때문에 에러가 발생하고 있다. 개발이라는건 정말 끝도 없이 공부를 해야하는 것 같다.