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

ORA-01006
2026년 06월 26일 | DBMS Error 가이드

이 글에서 다루는 내용

ORA-01006 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.

ORA-01006 bind variable does not exist 는?

ORA-01006 에러는 Oracle 데이터베이스에서 SQL 또는 PL/SQL 코드 실행 시 바인드 변수(Bind Variable)가 존재하지 않거나 올바르게 정의되지 않았을 때 발생하는 오류입니다. 주로 동적 SQL(Dynamic SQL)을 사용하는 환경, Pro*C, OCI(Oracle Call Interface), JDBC, Python cx_Oracle 등의 애플리케이션 계층에서 SQL 문에 선언된 바인드 변수의 수와 실제로 바인딩된 변수의 수가 불일치할 때 자주 나타납니다. 이 에러는 런타임(Runtime) 시점에 발생하기 때문에 컴파일 단계에서 사전에 감지하기 어려워 운영 중인 시스템에서 예기치 않게 장애를 유발할 수 있어 각별한 주의가 필요합니다.


주요 발생 원인

  • 바인드 변수 개수 불일치 (SQL 문과 바인딩 변수 수 미스매치)

가장 빈번하게 발생하는 원인으로, SQL 문 내에 선언된 :변수명 플레이스홀더의 수와 실제 애플리케이션 코드에서 바인딩(binding)한 변수의 수가 맞지 않을 때 발생합니다. 예를 들어 SQL에는 :1, :2, :3 세 개의 바인드 변수를 사용했지만, 실제 코드에서는 두 개만 바인딩한 경우가 대표적입니다. 이는 코드 수정 중 SQL 문은 업데이트했지만 바인딩 로직을 함께 수정하지 않았을 때 특히 자주 발생합니다.

  • EXECUTE IMMEDIATE 또는 DBMS_SQL 사용 시 바인드 변수 누락

PL/SQL의 동적 SQL 처리 방식인 EXECUTE IMMEDIATEDBMS_SQL 패키지를 사용할 때 SQL 문자열 내에 바인드 변수를 포함시켰지만, USING 절이나 DBMS_SQL.BIND_VARIABLE 호출을 누락하거나 잘못 작성한 경우에 발생합니다. 동적으로 생성되는 SQL 문자열이 조건에 따라 변할 때 바인드 변수의 유무가 달라지는 상황에서 이 문제가 빈번하게 발생합니다. 특히 복잡한 동적 WHERE 절을 구성할 때 바인드 변수 관리가 어려워 실수가 발생하기 쉽습니다.

  • 외부 애플리케이션(JDBC, OCI, cx_Oracle 등)에서의 파라미터 바인딩 오류

Java JDBC, Python의 cx_Oracle 또는 python-oracledb, C/C++의 OCI 같은 외부 드라이버에서 SQL PreparedStatement나 커서(Cursor)를 사용할 때 파라미터 인덱스 또는 이름이 잘못 지정되어 발생합니다. JDBC에서 파라미터 인덱스가 1부터 시작하는데 0부터 설정하거나, Named Bind Variable의 이름을 SQL 문과 다르게 지정하는 경우가 대표적입니다. 프레임워크나 ORM(MyBatis, Hibernate 등)을 사용할 때는 매핑 파일(XML) 또는 어노테이션에서 바인드 변수명이 실제 SQL과 불일치하는 경우에도 발생합니다.


해결 방법

원인 1 해결: 바인드 변수 개수 맞추기

SQL 문에 선언된 바인드 변수 플레이스홀더 수와 실제 바인딩하는 변수 수를 정확히 일치시켜야 합니다.

잘못된 예 (에러 발생)

-- SQL 문에 바인드 변수 3개 선언
DECLARE
  v_sql VARCHAR2(500);
  v_result NUMBER;
BEGIN
  v_sql := 'SELECT COUNT(*) FROM orders WHERE customer_id = :1 AND status = :2 AND order_date > :3';
  -- USING 절에 변수 2개만 제공 -> ORA-01006 발생
  EXECUTE IMMEDIATE v_sql INTO v_result USING 1001, 'ACTIVE';
END;
/

올바른 예 (수정 후)

DECLARE
  v_sql       VARCHAR2(500);
  v_result    NUMBER;
  v_cust_id   NUMBER := 1001;
  v_status    VARCHAR2(20) := 'ACTIVE';
  v_order_dt  DATE := SYSDATE - 30;
BEGIN
  v_sql := 'SELECT COUNT(*) FROM orders WHERE customer_id = :1 AND status = :2 AND order_date > :3';
  -- USING 절에 바인드 변수 3개 모두 제공
  EXECUTE IMMEDIATE v_sql INTO v_result USING v_cust_id, v_status, v_order_dt;
  DBMS_OUTPUT.PUT_LINE('주문 건수: ' || v_result);
END;
/

원인 2 해결: DBMS_SQL 패키지 바인드 변수 올바르게 처리하기

DBMS_SQL 패키지를 사용할 때는 SQL 문에 선언된 모든 바인드 변수에 대해 반드시 DBMS_SQL.BIND_VARIABLE을 호출해야 합니다.

잘못된 예 (에러 발생)

DECLARE
  v_cursor  INTEGER;
  v_sql     VARCHAR2(500);
  v_rows    INTEGER;
BEGIN
  v_sql := 'UPDATE employees SET salary = :new_sal, department_id = :dept_id WHERE employee_id = :emp_id';
  v_cursor := DBMS_SQL.OPEN_CURSOR;
  DBMS_SQL.PARSE(v_cursor, v_sql, DBMS_SQL.NATIVE);
  -- :new_sal 만 바인딩하고 나머지 누락 -> ORA-01006 발생
  DBMS_SQL.BIND_VARIABLE(v_cursor, ':new_sal', 75000);
  v_rows := DBMS_SQL.EXECUTE(v_cursor);
  DBMS_SQL.CLOSE_CURSOR(v_cursor);
END;
/

올바른 예 (수정 후)

DECLARE
  v_cursor  INTEGER;
  v_sql     VARCHAR2(500);
  v_rows    INTEGER;
BEGIN
  v_sql := 'UPDATE employees SET salary = :new_sal, department_id = :dept_id WHERE employee_id = :emp_id';
  v_cursor := DBMS_SQL.OPEN_CURSOR;
  DBMS_SQL.PARSE(v_cursor, v_sql, DBMS_SQL.NATIVE);

  -- SQL 문에 선언된 모든 바인드 변수를 빠짐없이 바인딩
  DBMS_SQL.BIND_VARIABLE(v_cursor, ':new_sal',  75000);
  DBMS_SQL.BIND_VARIABLE(v_cursor, ':dept_id',  10);
  DBMS_SQL.BIND_VARIABLE(v_cursor, ':emp_id',   200);

  v_rows := DBMS_SQL.EXECUTE(v_cursor);
  DBMS_OUTPUT.PUT_LINE('업데이트된 행 수: ' || v_rows);
  DBMS_SQL.CLOSE_CURSOR(v_cursor);
EXCEPTION
  WHEN OTHERS THEN
    IF DBMS_SQL.IS_OPEN(v_cursor) THEN
      DBMS_SQL.CLOSE_CURSOR(v_cursor);
    END IF;
    RAISE;
END;
/

원인 3 해결: 동적 SQL에서 조건부 바인드 변수 안전하게 관리하기

조건에 따라 WHERE 절이 달라지는 동적 SQL의 경우, 바인드 변수 리스트도 함께 동적으로 관리해야 합니다.

DECLARE
  v_sql         VARCHAR2(1000);
  v_dept_id     NUMBER := 10;
  v_job_id      VARCHAR2(20) := NULL;  -- 선택적 조건
  v_result      SYS_REFCURSOR;
  v_emp_id      employees.employee_id%TYPE;
  v_emp_name    employees.last_name%TYPE;
BEGIN
  v_sql := 'SELECT employee_id, last_name FROM employees WHERE department_id = :dept_id';

  -- 조건이 있을 때만 바인드 변수 추가
  IF v_job_id IS NOT NULL THEN
    v_sql := v_sql || ' AND job_id = :job_id';
    EXECUTE IMMEDIATE v_sql || ' ORDER BY employee_id'
      OPEN v_result USING v_dept_id, v_job_id;  -- 2개 바인딩
  ELSE
    EXECUTE IMMEDIATE v_sql || ' ORDER BY employee_id'
      OPEN v_result USING v_dept_id;  -- 1개 바인딩
  END IF;

  LOOP
    FETCH v_result INTO v_emp_id, v_emp_name;
    EXIT WHEN v_result%NOTFOUND;
    DBMS_OUTPUT.PUT_LINE(v_emp_id || ' - ' || v_emp_name);
  END LOOP;
  CLOSE v_result;
END;
/

JDBC (Java)에서의 올바른 바인드 변수 처리 예시

-- Oracle DB 쪽 테스트 테이블 준비
CREATE TABLE test_orders (
  order_id    NUMBER PRIMARY KEY,
  cust_id     NUMBER,
  amount      NUMBER,
  order_date  DATE
);
// Java JDBC 올바른 예시
String sql = "INSERT INTO test_orders (order_id, cust_id, amount, order_date) " +
             "VALUES (:1, :2, :3, :4)";

// OracleNamedParameterJdbcTemplate 또는 PreparedStatement 사용
PreparedStatement pstmt = conn.prepareStatement(
    "INSERT INTO test_orders (order_id, cust_id, amount, order_date) VALUES (?, ?, ?, ?)"
);

// 인덱스는 반드시 1부터 시작
pstmt.setInt(1, 10001);     // order_id
pstmt.setInt(2, 2001);      // cust_id
pstmt.setDouble(3, 15000.50); // amount
pstmt.setDate(4, new java.sql.Date(System.currentTimeMillis())); // order_date
pstmt.executeUpdate();

예방 방법

  • 바인드 변수 관리를 위한 헬퍼 프로시저 또는 래퍼 함수 작성

동적 SQL을 많이 사용하는 환경에서는 바인드 변수와 값을 컬렉션(Collection)으로 묶어 관리하는 래퍼 유틸리티를 작성하면 실수를 줄일 수 있습니다. SQL 문자열을 파싱하여 선언된 바인드 변수의 수를 자동으로 추출하고, 실제 바인딩된 값의 수와 비교하는 검증 로직을 추가하면 런타임 에러를 사전에 방지할 수 있습니다. 아래는 바인드 변수 수를 검증하는 간단한 함수 예시입니다.

“`sql

CREATE OR REPLACE FUNCTION count_bind_vars(p_sql IN VARCHAR2)

RETURN NUMBER

IS

v_count NUMBER := 0;

v_pos NUMBER := 1;

v_char CHAR(1);

BEGIN

— 단순화된 바인드 변수(:변수명) 카운터

WHILE v_pos <= LENGTH(p_sql) LOOP

v_char := SUBSTR(p_sql, v_pos, 1);

IF v_char = ‘:’ THEN

— 다음 문자가 영숫자인 경우 바인드 변수로 판단

IF v_pos < LENGTH(p_sql) AND

REGEXP_LIKE(SUBSTR(p_sql, v_pos + 1, 1), ‘[A-Za-z0-9_]’) THEN

v_count := v_count + 1;

END IF;

END IF;

v_pos := v_pos + 1;

END LOOP;

RETURN v_count;

END count_bind_vars;

/

— 사용 예시

DECLARE

v_sql VARCHAR2(500) := ‘SELECT * FROM emp WHERE deptno = :dept AND sal > :sal’;

v_cnt NUMBER;

BEGIN

v_cnt := count_bind_vars(v_sql);

DBMS_OUTPUT.PUT_LINE(‘바인드 변수 수: ‘ || v_cnt); — 출력: 2

END;

/

“`

  • 코드 리뷰 및 정적 분석 도구 활용, 통합 테스트 강화

SQL 문 변경 시 바인드 변수 개수 변경 여부를 코드 리뷰 체크리스트에 반드시 포함시켜야 합니다. Oracle SQL Developer, Toad, PL/SQL Developer와 같은 IDE의 정적 분석 기능을 활용하면 바인드 변수 불일치를 배포 전에 발견할 수 있습니다. 또한 단위 테스트(Unit Test) 및 통합 테스트(Integration Test)에서 다양한 입력 조건(NULL 값 포함, 선택적 파라미터 등)을 커버하는 테스트 케이스를 작성하여 동적 SQL 경로가 모두 검증되도록 하는 것이 중요합니다.


관련 에러

  • ORA-01008: not all variables bound – ORA-01006과 유사하게 바인드 변수 불일치 시 발생하며, 특히 모든 변수가 바인딩되지 않았을 때 나타납니다. ORA-01006이 변수 자체가 존재하지 않는 경우라면, ORA-01008은 변수는 선언되었으나 값이 제공되지 않은 경우에 발생한다는 차이가 있습니다.
  • ORA-01745: invalid host/bind variable name – 바인드 변수명이 Oracle 명명 규칙에 위배될 때 발생합니다. 바인드 변수명에 특수문자나 예약어를 사용하는 경우 발생할 수 있습니다.
  • ORA-03113: end-of-file on communication channel – 바인드 변수 오류로 인해 세션이 비정상 종료될 때 연쇄적으로 발생할 수 있는 에러입니다.
  • ORA-06512: at line N – PL/SQL 블록에서 ORA-01006 발생 시 스택 트레이스에 함께 출력되어 정확한 에러 발생 위치를 알려주는 보조 에러 코드입니다.

DBMS 에러 코드 시리즈

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

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

댓글 남기기