2026년 08월 31일 | DBMS Error 가이드
이 글에서 다루는 내용
ORA-06535 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
ORA-06535 statement string in EXECUTE IMMEDIATE is NULL or 0 length 는?
ORA-06535 에러는 Oracle PL/SQL에서 EXECUTE IMMEDIATE 구문을 사용할 때, 실행하려는 동적 SQL 문자열이 NULL이거나 길이가 0인 빈 문자열일 경우 발생하는 에러입니다. 동적 SQL은 런타임에 문자열을 조합하여 SQL을 실행하는 방식이기 때문에, 문자열이 올바르게 생성되지 않으면 Oracle 엔진이 실행할 쿼리를 찾지 못해 이 에러를 발생시킵니다. 특히 조건 분기 로직이 복잡하거나 외부 입력값에 의존하는 동적 SQL 구현 시 자주 마주치게 되는 에러입니다.
주요 발생 원인
1. 동적 SQL 문자열 변수가 초기화되지 않아 NULL인 경우
PL/SQL에서 변수를 선언만 하고 값을 할당하지 않으면 기본값은 NULL입니다. 동적 SQL을 구성하는 변수에 적절한 SQL 문자열이 할당되기 전에 EXECUTE IMMEDIATE를 호출하면 이 에러가 발생합니다. 복잡한 로직에서 특정 분기가 실행되지 않아 변수가 여전히 NULL로 남아 있는 경우가 대표적입니다.
-- 에러 발생 예시
DECLARE
v_sql VARCHAR2(1000); -- 초기화되지 않음 (NULL 상태)
BEGIN
-- 어떠한 조건도 충족되지 않아 v_sql에 값이 할당되지 않음
EXECUTE IMMEDIATE v_sql; -- ORA-06535 발생
END;
/
2. 문자열 연산 과정에서 NULL이 포함되어 결과가 NULL이 되는 경우
Oracle에서는 NULL과 문자열을 연결(||)하면 NULL이 아니라 상대 문자열이 반환되지만, 변수 자체가 NULL인 상태에서 조건부 로직 없이 문자열 조합을 잘못 설계하면 최종 결과가 NULL이 될 수 있습니다. 특히 CASE, DECODE, 또는 함수 반환값이 NULL을 리턴하는 경우 동적 SQL 문자열 전체가 NULL이 될 가능성이 있습니다.
-- 에러 발생 예시: 함수가 NULL을 반환하는 경우
DECLARE
v_table_name VARCHAR2(100) := NULL; -- 외부에서 NULL로 전달됨
v_sql VARCHAR2(1000);
BEGIN
v_sql := 'SELECT * FROM ' || v_table_name;
-- v_table_name이 NULL이면 v_sql은 'SELECT * FROM ' (길이는 있지만)
-- 완전히 NULL 변수를 그대로 전달하는 경우라면 에러 발생
EXECUTE IMMEDIATE v_sql;
END;
/
3. 빈 문자열(”) 또는 공백만 포함된 문자열이 전달되는 경우
Oracle은 빈 문자열('')을 NULL로 처리하기 때문에, 조건 분기 결과로 빈 문자열이 할당된 경우에도 동일하게 ORA-06535가 발생합니다. 문자열 길이 검사 없이 동적 SQL을 바로 실행하거나, 외부 파라미터로부터 빈 값이 전달되는 상황에서 자주 발생합니다.
-- 에러 발생 예시: 빈 문자열 전달
DECLARE
v_sql VARCHAR2(1000) := ''; -- Oracle에서 ''는 NULL과 동일
BEGIN
EXECUTE IMMEDIATE v_sql; -- ORA-06535 발생
END;
/
해결 방법
원인 1 해결: 변수 초기화 및 NULL 체크 추가
EXECUTE IMMEDIATE 실행 전에 반드시 SQL 문자열 변수가 NULL인지 확인하는 방어 코드를 추가해야 합니다.
DECLARE
v_sql VARCHAR2(1000);
v_result NUMBER;
BEGIN
-- 동적 SQL 구성
v_sql := 'SELECT COUNT(*) FROM emp WHERE deptno = 10';
-- NULL 체크 후 실행
IF v_sql IS NOT NULL AND LENGTH(TRIM(v_sql)) > 0 THEN
EXECUTE IMMEDIATE v_sql INTO v_result;
DBMS_OUTPUT.PUT_LINE('결과: ' || v_result);
ELSE
DBMS_OUTPUT.PUT_LINE('실행할 SQL이 없습니다. SQL 문자열을 확인하세요.');
END IF;
END;
/
원인 2 해결: NVL 또는 COALESCE를 이용한 NULL 방어 처리
외부에서 전달받은 값이 NULL일 가능성이 있는 경우, NVL() 또는 COALESCE()를 활용하여 기본값을 보장합니다.
DECLARE
v_table_name VARCHAR2(100) := NULL; -- 외부 입력값
v_condition VARCHAR2(200) := NULL;
v_sql VARCHAR2(1000);
v_count NUMBER;
BEGIN
-- NVL로 NULL 방어
v_table_name := NVL(v_table_name, 'EMP'); -- NULL이면 기본 테이블 사용
v_sql := 'SELECT COUNT(*) FROM ' || v_table_name;
-- 조건절이 있을 경우에만 WHERE 추가
IF v_condition IS NOT NULL THEN
v_sql := v_sql || ' WHERE ' || v_condition;
END IF;
DBMS_OUTPUT.PUT_LINE('실행 SQL: ' || v_sql);
EXECUTE IMMEDIATE v_sql INTO v_count;
DBMS_OUTPUT.PUT_LINE('Count: ' || v_count);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('에러 발생: ' || SQLERRM);
END;
/
원인 3 해결: 동적 SQL 빌더 패턴 적용
복잡한 동적 SQL을 구성할 때는 별도의 함수나 프로시저로 SQL 빌더 패턴을 구현하여 항상 유효한 SQL이 생성되도록 보장합니다.
CREATE OR REPLACE FUNCTION build_dynamic_sql(
p_table_name IN VARCHAR2,
p_where_clause IN VARCHAR2 DEFAULT NULL
) RETURN VARCHAR2 IS
v_sql VARCHAR2(4000);
BEGIN
-- 테이블명 유효성 검사
IF p_table_name IS NULL OR LENGTH(TRIM(p_table_name)) = 0 THEN
RAISE_APPLICATION_ERROR(-20001, '테이블명은 필수입니다.');
END IF;
v_sql := 'SELECT * FROM ' || DBMS_ASSERT.SQL_OBJECT_NAME(p_table_name);
IF p_where_clause IS NOT NULL AND LENGTH(TRIM(p_where_clause)) > 0 THEN
v_sql := v_sql || ' WHERE ' || p_where_clause;
END IF;
RETURN v_sql;
END build_dynamic_sql;
/
-- 사용 예시
DECLARE
v_sql VARCHAR2(4000);
TYPE t_cur IS REF CURSOR;
v_cursor t_cur;
BEGIN
v_sql := build_dynamic_sql('EMP', 'DEPTNO = 10');
DBMS_OUTPUT.PUT_LINE('생성된 SQL: ' || v_sql);
OPEN v_cursor FOR v_sql;
-- 커서 처리 로직
CLOSE v_cursor;
END;
/
예방 방법
1. 동적 SQL 실행 전 유효성 검사 래퍼(Wrapper) 프로시저 표준화
팀 내 코딩 표준으로 동적 SQL을 실행하는 공통 래퍼 프로시저를 만들어 모든 EXECUTE IMMEDIATE 호출이 해당 프로시저를 통하도록 강제합니다. 이렇게 하면 NULL 체크, 길이 검사, 로깅이 일관성 있게 적용되어 ORA-06535뿐 아니라 SQL Injection과 같은 보안 문제도 예방할 수 있습니다.
CREATE OR REPLACE PROCEDURE exec_dynamic_sql(
p_sql IN VARCHAR2,
p_context IN VARCHAR2 DEFAULT 'UNKNOWN'
) IS
BEGIN
-- 유효성 검사
IF p_sql IS NULL THEN
RAISE_APPLICATION_ERROR(-20100,
'[' || p_context || '] 동적 SQL이 NULL입니다. 로직을 확인하세요.');
END IF;
IF LENGTH(TRIM(p_sql)) = 0 THEN
RAISE_APPLICATION_ERROR(-20101,
'[' || p_context || '] 동적 SQL이 빈 문자열입니다.');
END IF;
-- 실행 로그 (개발/운영 환경에서 디버깅 용이)
DBMS_OUTPUT.PUT_LINE('[SQL EXEC][' || p_context || ']: ' || SUBSTR(p_sql, 1, 200));
EXECUTE IMMEDIATE p_sql;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('[ERROR][' || p_context || ']: ' || SQLERRM);
RAISE;
END exec_dynamic_sql;
/
2. 코드 리뷰 및 단위 테스트에서 경계값(NULL, 빈 문자열) 테스트 의무화
동적 SQL을 포함하는 PL/SQL 코드를 개발할 때는 반드시 NULL 입력값과 빈 문자열에 대한 단위 테스트 케이스를 작성하고 코드 리뷰 체크리스트에 포함시킵니다. CI/CD 파이프라인에 utPLSQL과 같은 PL/SQL 테스트 프레임워크를 연동하면 회귀 테스트 자동화가 가능하여 배포 전 사전에 문제를 발견할 수 있습니다.
관련 에러
- ORA-06512: PL/SQL 에러 발생 위치(라인 번호)를 나타내며, ORA-06535와 함께 스택 트레이스에서 자주 같이 출력됩니다.
- ORA-00900:
EXECUTE IMMEDIATE에 전달된 SQL 문자열이 유효하지 않은 SQL일 때 발생하는 “invalid SQL statement” 에러로, NULL이 아닌 잘못된 문자열이 원인입니다. - ORA-06550: PL/SQL 컴파일 에러로, 동적 SQL 문법 오류가 런타임에 감지될 때 ORA-06535와 함께 발생할 수 있습니다.
- ORA-01403: 동적 SQL의
INTO절에서 데이터가 없을 때 발생하는NO_DATA_FOUND에러로, 동적 쿼리 처리 시 함께 방어 코드를 작성해야 합니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.