본문 바로가기
IT

ORA-01861 literal does not match format string — 날짜 형식 불일치 에러 원인·해결·예방

by 샤나엘 2026. 5. 18.
반응형

 

ORA-01861은 오라클이 문자열을 날짜·시간 타입으로 변환할 때 입력 리터럴과 포맷 마스크의 구조가 일치하지 않을 때 발생한다. TO_DATE의 두 번째 인자(마스크)가 첫 번째 인자(리터럴)의 길이·구분자·자릿수와 어긋나면 즉시 던져진다. 같은 SQL이 어제는 멀쩡히 돌다가 오늘 갑자기 실패하는 일도 있는데, 보통은 코드가 아니라 세션 NLS_DATE_FORMAT이 바뀌었기 때문이다. 본 글은 ORA-01861의 정확한 의미, 다섯 가지 핵심 원인, 진단 흐름, 그리고 NLS에 의존하지 않는 견고한 해결 패턴까지 정리한다.

ORA-01861

한눈에 보기
 
에러 의미 / 문자열 리터럴이 지정한 날짜·시간 포맷 마스크와 구조적으로 일치하지 않음
주요 원인 / TO_DATE 마스크 불일치, 암시적 변환, NLS_DATE_FORMAT 차이, RR/MM/HH24 혼동, NLS_DATE_LANGUAGE 영향
근본 해법 / TO_DATE 명시, ANSI DATE 리터럴(DATE '2026-05-15'), NLS 옵션 명시

목차

01ORA-01861 정확한 의미와 에러 메시지
02발생 원인 다섯 가지 (마스크·암시적·NLS·포맷요소·언어)
03재현 시나리오 — 같은 SQL이 환경 따라 다르게 실패
04해결 방법 — 명시 변환, DATE 리터럴, NLS 옵션
05ORA-01843·ORA-01830·ORA-01847 인접 에러와의 구분
Q&A자주 묻는 질문 5선
06예방 체크리스트와 결론
01 / ORA-01861의 정확한 의미
오라클 공식 메시지는 "literal does not match format string" ━ 한국어로는 "리터럴이 형식 문자열과 일치하지 않음"이다. 같은 메시지가 11g부터 23ai까지 동일하게 유지되어 있다. 원인 설명은 "입력 리터럴이 (선행 공백을 제외하고) 포맷 마스크의 길이·구조와 같지 않음"이다.

즉 ORA-01861은 값의 유효성이 아니라 구조의 정합성을 검사한 결과다. '2026년 13월 32일'처럼 값이 비현실적인 경우는 ORA-01843, ORA-01847 같은 다른 에러로 분리된다. ORA-01861은 단순히 "글자 수·구분자·자릿수가 안 맞다"고 알려주는 1차 방어선이다.

1. ORA-01861이 던져지는 상황

ORA-01861이 발생하려면 두 가지 요소가 모두 충족돼야 한다. 첫째, 문자열을 날짜·시간 타입으로 변환하는 시점이 있어야 한다. 둘째, 그 변환에 사용된 포맷 마스크가 입력 리터럴과 구조적으로 어긋나야 한다.

가장 단순한 예시는 다음과 같다.

-- 실패: 입력 '2026-05-15'는 10자리, 마스크 'YYYYMMDD'는 8자리 기대
SELECT TO_DATE('2026-05-15', 'YYYYMMDD') FROM dual;
-- ORA-01861: literal does not match format string

-- 성공
SELECT TO_DATE('2026-05-15', 'YYYY-MM-DD') FROM dual;

문자열에 하이픈이 있는데 마스크는 하이픈을 비워뒀다. 길이와 구분자 모두 어긋난다. 오라클은 첫 번째 자리부터 마스크를 따라 읽다가 4번째 글자(-)가 5번째 숫자가 와야 할 자리에서 등장하자 즉시 ORA-01861을 던진다.

2. 발생 원인 다섯 가지

원인 1 / TO_DATE 마스크 불일치

가장 흔한 원인이다. 개발자가 입력 문자열의 실제 형식과 다른 마스크를 적었을 때 발생한다.

-- 흔한 실수 모음
TO_DATE('2026/05/15', 'YYYY-MM-DD')        -- 슬래시 vs 하이픈
TO_DATE('26-05-15',   'YYYY-MM-DD')        -- 2자리 연도 vs 4자리 마스크
TO_DATE('2026-05-15 14:30', 'YYYY-MM-DD')  -- 시간 포함 리터럴 vs 날짜만 마스크

원인 2 / 암시적 변환의 함정

오라클은 문자열을 DATE 컬럼과 비교하거나 DATE 컬럼에 INSERT할 때 자동으로 TO_DATE를 호출한다. 이때 사용되는 마스크는 세션의 NLS_DATE_FORMAT이다. 코드에는 TO_DATE가 한 줄도 없는데 ORA-01861이 발생하는 경우 대부분 여기에 해당한다.

-- DB 기본 NLS_DATE_FORMAT이 'DD-MON-RR'인 경우
INSERT INTO emp(hire_date) VALUES ('2026-05-15');
-- 오라클: TO_DATE('2026-05-15', 'DD-MON-RR') 시도 → ORA-01861

암시적 변환의 무서운 점은 같은 코드가 환경마다 다르게 동작한다는 것이다. 개발 DB 세션 NLS는 YYYY-MM-DD인데 운영 DB는 DD-MON-RR이라면, 개발에서는 잘 돌던 쿼리가 운영에서만 실패한다.

원인 3 / NLS_DATE_FORMAT 차이

오라클 12c 이후 NLS_DATE_FORMAT 기본값은 NLS_TERRITORY 설정에서 파생된다. KOREA 영토는 RR/MM/DD, AMERICA 영토는 DD-MON-RR이다. 같은 DB라도 접속 클라이언트의 OS 로케일, 환경변수, JDBC URL 옵션에 따라 세션 NLS가 다르게 잡힌다.

원인 4 / RR · MM · HH24 같은 포맷 요소 혼동

  • MM(월 01~12) ↔ MI(분 00~59) ━ M 다음 한 글자를 헷갈리면 의도와 다른 값이 들어간다
  • HH 또는 HH12(1~12, AM/PM 필요) ↔ HH24(0~23) ━ 14시를 HH 마스크로 파싱하면 ORA-01849
  • RR(2자리 연도 라운드: 00~49→20xx, 50~99→19xx) ↔ YY(현재 세기 그대로) ━ 형식 자체는 통과해도 의미가 달라짐
  • D(요일 1~7) ↔ DD(일 01~31) ━ 길이가 같다고 안심하면 안 됨

원인 5 / NLS_DATE_LANGUAGE 영향

MON, MONTH, DY, DAY 같은 텍스트 기반 포맷 요소는 세션의 NLS_DATE_LANGUAGE를 따른다. 한국어 세션에서 '15-JAN-26''DD-MON-YY' 마스크로 파싱하면 오라클은 'JAN'이 아니라 '1월'을 기대하므로 ORA-01843이 발생한다. 영어로 강제하려면 TO_DATE의 세 번째 인자를 지정한다.

-- 한국어 세션에서도 영어 월 이름 파싱
SELECT TO_DATE('15-JAN-26', 'DD-MON-YY',
              'NLS_DATE_LANGUAGE=AMERICAN') FROM dual;

2. 재현 시나리오 — 환경에 따라 다른 결과

다음 SQL은 세션 NLS에 따라 통과와 실패가 갈린다.

-- 케이스 A: NLS_DATE_FORMAT = 'YYYY-MM-DD' (한국 기본)
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD';
SELECT * FROM emp WHERE hire_date = '2026-05-15';
-- 성공

-- 케이스 B: NLS_DATE_FORMAT = 'DD-MON-RR' (미국 기본)
ALTER SESSION SET NLS_DATE_FORMAT = 'DD-MON-RR';
SELECT * FROM emp WHERE hire_date = '2026-05-15';
-- ORA-01861

세션 NLS는 다음 쿼리로 확인한다.

-- 현재 세션 NLS
SELECT parameter, value
FROM nls_session_parameters
WHERE parameter LIKE 'NLS_DATE%';

-- DB 기본 NLS
SELECT parameter, value
FROM nls_database_parameters
WHERE parameter LIKE 'NLS_DATE%';
디버깅 흐름
ORA-01861이 발생하면 다음 순서로 확인한다. 첫째, 변환이 일어나는 모든 지점에서 TO_DATE가 명시적으로 호출되는지 본다. 둘째, TO_DATE가 없다면 어떤 컬럼·바인드 변수가 암시적 변환을 일으키는지 확인한다. 셋째, 변환에 사용된 마스크(명시적이면 TO_DATE 2번째 인자, 암시적이면 NLS_DATE_FORMAT)를 출력해 입력 문자열과 비교한다. 넷째, 환경 간 차이가 의심되면 NLS_SESSION_PARAMETERS를 비교한다.

4. 해결 방법 — 세 가지 패턴

패턴 A / TO_DATE 명시 변환

가장 직관적인 해법이다. 모든 문자열 → 날짜 변환을 TO_DATE로 명시하고, 마스크를 입력 형식에 정확히 맞춘다.

-- 비교 조건
SELECT * FROM emp
WHERE hire_date = TO_DATE('2026-05-15', 'YYYY-MM-DD');

-- INSERT
INSERT INTO emp(emp_id, hire_date)
VALUES (1001, TO_DATE('2026-05-15', 'YYYY-MM-DD'));

-- TIMESTAMP는 TO_TIMESTAMP
INSERT INTO log_t(log_time)
VALUES (TO_TIMESTAMP('2026-05-15 14:30:00.123',
                       'YYYY-MM-DD HH24:MI:SS.FF3'));

패턴 B / ANSI DATE·TIMESTAMP 리터럴

상수 값이라면 ANSI 표준 리터럴이 가장 안전하다. NLS 설정과 완전히 독립적이며, 항상 YYYY-MM-DD 또는 YYYY-MM-DD HH24:MI:SS[.FFFFFFFFF] 형식을 사용한다.

-- ANSI DATE 리터럴 (NLS와 무관)
SELECT DATE '2026-05-15' FROM dual;

-- ANSI TIMESTAMP 리터럴
SELECT TIMESTAMP '2026-05-15 14:30:00' FROM dual;

-- 비교 조건에도 사용 가능
SELECT * FROM emp
WHERE hire_date >= DATE '2026-01-01';

패턴 C / NLS_DATE_FORMAT 명시 설정

레거시 코드를 한꺼번에 못 고친다면 세션 진입 시점에 NLS_DATE_FORMAT을 고정한다. JDBC라면 oracle.jdbc.defaultDateFormat 같은 드라이버 옵션 또는 커넥션 풀 init SQL을 사용한다.

-- 세션 시작 시 NLS 통일
ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';
ALTER SESSION SET NLS_TIMESTAMP_FORMAT = 'YYYY-MM-DD HH24:MI:SS.FF3';
ALTER SESSION SET NLS_DATE_LANGUAGE = 'AMERICAN';

이 방법은 응급 대응이며, 장기적으로는 모든 변환을 명시적으로 만드는 것이 안전하다. NLS는 언제든 환경 변경으로 다시 흔들릴 수 있다.

5. 인접 에러와의 구분

ORA-01861은 형식 검사 에러다. 값 검사에서 걸리는 에러들과 메시지가 비슷해 헷갈리기 쉽다.

에러 코드 검사 단계 전형적 입력
ORA-01861 형식·구조 불일치 '2026/05/15' + 'YYYY-MM-DD'
ORA-01843 월 값이 유효하지 않음 '2026-13-01' + 'YYYY-MM-DD'
ORA-01847 일 값이 월 범위를 벗어남 '2026-02-31' + 'YYYY-MM-DD'
ORA-01830 마스크가 리터럴보다 짧음 '2026-05-15 14:30' + 'YYYY-MM-DD'
ORA-01840 리터럴이 마스크보다 짧음 '2026-5' + 'YYYY-MM-DD'
ORA-01858 숫자 자리에 비숫자 등장 '2026-AB-15' + 'YYYY-MM-DD'

대부분의 경우 해결 패턴은 같다 ━ TO_DATE를 명시하고, 마스크와 리터럴을 끝까지 1:1로 맞추고, NLS·언어 의존을 제거한다.

Q & A — 자주 묻는 다섯 가지
Q1. 같은 SQL이 어제는 됐는데 오늘 ORA-01861이 발생합니다.
세션 NLS_DATE_FORMAT이 변경됐을 가능성이 가장 높다. SQL 자체는 그대로지만 암시적 변환에 사용되는 마스크가 바뀌면 결과가 달라진다. NLS_SESSION_PARAMETERS를 확인하고, 가능하면 모든 변환을 TO_DATE로 명시화한다.

Q2. DATE 리터럴이 항상 더 좋은가요?
상수 값이라면 그렇다. NLS와 무관하고, 형식이 표준화돼 있어 가독성·이식성이 가장 높다. 다만 바인드 변수나 동적 값에는 적용할 수 없으므로, 그때는 TO_DATE를 쓴다.

Q3. 자바·파이썬 등 애플리케이션에서 호출할 때는 어떻게 하나요?
문자열로 넘기지 말고 드라이버의 Date·Timestamp 타입으로 바인딩한다. JDBC의 PreparedStatement.setDate, Python cx_Oracle의 datetime.date 객체 바인딩이 권장된다. 이 경우 드라이버가 직접 DATE 타입으로 전송하므로 NLS 의존이 사라진다.

Q4. WHERE 절에서 인덱스 활용까지 고려하면?
컬럼 쪽에 TO_CHAR를 씌우면 인덱스를 못 탄다. 반드시 비교 값 쪽에 TO_DATE 또는 DATE 리터럴을 둬서 컬럼은 원본 그대로 두는 것이 좋다. 함수 기반 인덱스가 별도로 있는 경우가 아니라면 이 원칙은 변하지 않는다.

Q5. 23ai에서도 같은 동작인가요?
ORA-01861의 의미와 동작은 11g부터 23ai까지 동일하다. 클라우드 환경(ATP·ADW)도 동일하다. NLS 기본값이 환경마다 다를 수 있다는 점만 유의하면 된다.

6. 예방 체크리스트와 결론

ORA-01861은 SQL 표현의 일관성과 NLS 비의존성으로 거의 100% 차단할 수 있다. 새 쿼리·새 컬럼·새 모듈을 작성할 때 다음 항목을 점검한다.

예방 체크리스트
  • 모든 문자열 → 날짜 변환에 TO_DATE·TO_TIMESTAMP를 명시적으로 사용
  • 상수 값은 ANSI DATE 'YYYY-MM-DD' / TIMESTAMP 'YYYY-MM-DD HH24:MI:SS' 리터럴 사용
  • 커넥션 풀 초기 SQL에서 NLS_DATE_FORMAT·NLS_TIMESTAMP_FORMAT·NLS_DATE_LANGUAGE 명시
  • 애플리케이션은 문자열이 아닌 Date·Timestamp 타입으로 바인딩
  • MMMI, HHHH24, YYRR 차이를 코드 리뷰 항목에 포함
  • MON·MONTH 마스크 사용 시 NLS_DATE_LANGUAGE=AMERICAN 명시
  • WHERE 절 비교 시 컬럼 쪽이 아닌 값 쪽에서 변환 수행 (인덱스 보존)
  • 운영·개발·테스트 환경의 NLS_SESSION_PARAMETERS 동기화 점검

ORA-01861은 본질적으로 "암시적 동작에 의존하지 말라"는 경고다. 오라클이 친절하게 문자열을 날짜로 바꿔주는 것처럼 보이지만, 그 친절은 세션 NLS라는 가변 변수에 묶여 있다. 한 번이라도 환경이 바뀌면 즉시 에러로 되돌아온다. 답은 단순하다 ━ 모든 변환을 명시화하고, 상수는 ANSI 리터럴을 쓰고, 바인드는 타입을 정확히 맞춘다. 이 세 가지만 지키면 ORA-01861은 다시 만날 일이 거의 없는 에러가 된다.

 

#ORA01861 #literaldoesnotmatch #오라클에러 #TO_DATE #날짜형식 #NLS #DATE리터럴 #TO_TIMESTAMP #암시적변환 #NLSDATEFORMAT #NLSDATELANGUAGE #ORA01843 #ORA01830 #SQL날짜 #DB트러블슈팅

반응형

댓글