Oracle ORA-06535 오류 원인과 해결 방법 완벽 가이드

ORA-06535
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 에러 코드 시리즈

주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.

본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.

댓글 남기기