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

ORA-01023
2026년 06월 28일 | DBMS Error 가이드

이 글에서 다루는 내용

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

ORA-01023 cursor context not found (Invalid cursor number) 는?

ORA-01023 에러는 Oracle 데이터베이스에서 유효하지 않거나 이미 닫힌 커서(Cursor)를 참조하려 할 때 발생하는 오류입니다. 커서는 SQL 쿼리 결과를 처리하기 위한 메모리 상의 작업 영역인데, 해당 커서가 존재하지 않거나 이미 닫혀 있는 상태에서 페치(FETCH) 또는 기타 커서 관련 작업을 시도할 때 이 에러가 발생합니다. 주로 PL/SQL 블록, Pro*C, OCI(Oracle Call Interface), JDBC 등 다양한 프로그래밍 환경에서 커서를 부적절하게 관리할 때 나타납니다.


주요 발생 원인

1. 이미 닫힌 커서에 접근 (CLOSE 후 FETCH 시도)

가장 흔한 원인으로, 커서를 CLOSE한 이후에 다시 FETCH하거나 %ROWCOUNT, %FOUND 등의 커서 속성을 참조할 때 발생합니다. PL/SQL 코드에서 루프 종료 후 커서를 닫고, 이후 로직에서 실수로 다시 해당 커서를 참조하는 경우가 대표적입니다. 코드가 복잡해질수록 커서의 생명 주기를 추적하기 어려워지므로 특히 주의해야 합니다.

2. 동적 SQL에서의 커서 번호 오류 (DBMS_SQL 패키지 오용)

DBMS_SQL 패키지를 사용하는 동적 SQL 환경에서 OPEN_CURSOR로 발급받은 커서 번호를 잘못 관리할 때 발생합니다. 예를 들어, 커서를 CLOSE_CURSOR로 닫은 후 동일한 커서 번호 변수를 재사용하거나, 커서 번호를 초기화(NULL 처리)하지 않고 다시 EXECUTEFETCH_ROWS를 호출하는 경우입니다. 특히 예외 처리 블록에서 커서를 강제로 닫은 뒤 이후 로직에서 동일 변수를 참조할 때 자주 나타납니다.

3. 멀티 세션 또는 커넥션 풀 환경에서의 커서 공유 문제

커넥션 풀(Connection Pool) 환경에서 특정 세션에 귀속된 커서를 다른 세션이나 스레드에서 참조하려 할 때 발생할 수 있습니다. 커서는 세션 레벨의 자원이기 때문에 세션이 종료되거나 연결이 재사용될 경우 이전 커서 컨텍스트는 무효화됩니다. WAS(Web Application Server)의 커넥션 풀 설정이 부적절하거나 커서를 전역 변수로 선언해 세션 간 공유를 시도하는 잘못된 설계가 원인인 경우가 많습니다.


해결 방법

원인 1 해결: 커서 상태 확인 후 접근

커서를 닫기 전에 커서가 열려 있는지 확인하는 습관을 들이고, %ISOPEN 속성을 적극 활용하세요.

DECLARE
    CURSOR emp_cur IS
        SELECT employee_id, first_name, salary
        FROM employees
        WHERE department_id = 10;
    v_emp employees%ROWTYPE;
BEGIN
    OPEN emp_cur;
    
    LOOP
        FETCH emp_cur INTO v_emp.employee_id, v_emp.first_name, v_emp.salary;
        EXIT WHEN emp_cur%NOTFOUND;
        
        DBMS_OUTPUT.PUT_LINE('ID: ' || v_emp.employee_id || 
                             ', Name: ' || v_emp.first_name ||
                             ', Salary: ' || v_emp.salary);
    END LOOP;
    
    -- %ISOPEN으로 커서 상태 확인 후 안전하게 닫기
    IF emp_cur%ISOPEN THEN
        CLOSE emp_cur;
    END IF;
    
    -- 커서 닫힌 후 접근 시도하지 않음 (ORA-01023 방지)
    -- 아래와 같은 코드는 절대 사용하지 말 것:
    -- FETCH emp_cur INTO v_emp; -- 에러 발생!
    
EXCEPTION
    WHEN OTHERS THEN
        -- 예외 발생 시에도 커서 정리
        IF emp_cur%ISOPEN THEN
            CLOSE emp_cur;
        END IF;
        RAISE;
END;
/

원인 2 해결: DBMS_SQL 패키지 커서 번호 안전하게 관리

DBMS_SQL을 사용할 때는 커서 번호 변수를 NULL로 초기화하고, 예외 처리 블록에서 반드시 커서를 닫도록 구성하세요.

DECLARE
    v_cursor_id   INTEGER;
    v_sql         VARCHAR2(500);
    v_result      INTEGER;
    v_emp_id      NUMBER;
    v_emp_name    VARCHAR2(100);
BEGIN
    -- 커서 번호 초기화
    v_cursor_id := NULL;
    
    v_sql := 'SELECT employee_id, first_name FROM employees WHERE rownum <= 5';
    
    -- 커서 열기
    v_cursor_id := DBMS_SQL.OPEN_CURSOR;
    
    -- SQL 파싱
    DBMS_SQL.PARSE(v_cursor_id, v_sql, DBMS_SQL.NATIVE);
    
    -- 컬럼 정의
    DBMS_SQL.DEFINE_COLUMN(v_cursor_id, 1, v_emp_id);
    DBMS_SQL.DEFINE_COLUMN(v_cursor_id, 2, v_emp_name, 100);
    
    -- 실행
    v_result := DBMS_SQL.EXECUTE(v_cursor_id);
    
    -- 페치 루프
    LOOP
        EXIT WHEN DBMS_SQL.FETCH_ROWS(v_cursor_id) = 0;
        DBMS_SQL.COLUMN_VALUE(v_cursor_id, 1, v_emp_id);
        DBMS_SQL.COLUMN_VALUE(v_cursor_id, 2, v_emp_name);
        DBMS_OUTPUT.PUT_LINE(v_emp_id || ' - ' || v_emp_name);
    END LOOP;
    
    -- 사용 후 반드시 닫기
    IF DBMS_SQL.IS_OPEN(v_cursor_id) THEN
        DBMS_SQL.CLOSE_CURSOR(v_cursor_id);
    END IF;

EXCEPTION
    WHEN OTHERS THEN
        -- 예외 시에도 커서가 열려있으면 닫기 (ORA-01023 예방)
        IF v_cursor_id IS NOT NULL AND DBMS_SQL.IS_OPEN(v_cursor_id) THEN
            DBMS_SQL.CLOSE_CURSOR(v_cursor_id);
        END IF;
        DBMS_OUTPUT.PUT_LINE('에러 발생: ' || SQLERRM);
        RAISE;
END;
/

원인 3 해결: 커넥션 풀 환경에서 커서 범위 제한

커서를 전역 변수나 패키지 변수로 선언하지 말고, 항상 로컬 스코프 내에서 선언하고 사용 후 즉시 닫는 구조를 유지하세요.

-- 잘못된 패턴 (패키지 레벨 커서 - 세션 간 공유 시 ORA-01023 위험)
/*
CREATE OR REPLACE PACKAGE bad_pkg AS
    CURSOR g_emp_cur IS SELECT * FROM employees; -- 위험!
END bad_pkg;
*/

-- 올바른 패턴: 프로시저 내부에서 로컬 커서 사용
CREATE OR REPLACE PROCEDURE get_employees_safe(
    p_dept_id IN NUMBER
) AS
    -- 로컬 커서로 선언 (프로시저 스코프)
    CURSOR l_emp_cur IS
        SELECT employee_id, first_name, salary
        FROM employees
        WHERE department_id = p_dept_id;
    
    v_emp_id    employees.employee_id%TYPE;
    v_emp_name  employees.first_name%TYPE;
    v_salary    employees.salary%TYPE;
BEGIN
    OPEN l_emp_cur;
    
    LOOP
        FETCH l_emp_cur INTO v_emp_id, v_emp_name, v_salary;
        EXIT WHEN l_emp_cur%NOTFOUND;
        
        DBMS_OUTPUT.PUT_LINE(
            'EMP: ' || v_emp_id || 
            ' | ' || v_emp_name || 
            ' | Salary: ' || v_salary
        );
    END LOOP;
    
    CLOSE l_emp_cur;

EXCEPTION
    WHEN OTHERS THEN
        IF l_emp_cur%ISOPEN THEN
            CLOSE l_emp_cur;
        END IF;
        RAISE;
END get_employees_safe;
/

-- 실행 테스트
BEGIN
    get_employees_safe(10);
END;
/

예방 방법

1. 커서 생명 주기 관리 원칙 수립 (OPEN-FETCH-CLOSE 패턴 준수)

모든 PL/SQL 코드에서 커서는 반드시 OPEN → FETCH → CLOSE 순서를 지키고, 예외 처리 블록(EXCEPTION 절)에서도 %ISOPEN 또는 DBMS_SQL.IS_OPEN 체크 후 커서를 닫는 코드를 의무적으로 작성하는 팀 코딩 컨벤션을 수립하세요. 코드 리뷰 시 커서 닫힘 여부를 체크리스트 항목으로 포함시키면 운영 환경에서의 실수를 크게 줄일 수 있습니다.

2. FOR 루프 커서 활용으로 자동 관리

명시적 커서를 직접 관리하는 대신, 가능한 경우 FOR ... IN cursor 구문을 사용하면 Oracle이 자동으로 커서의 OPEN, FETCH, CLOSE를 처리해주므로 ORA-01023과 같은 커서 관련 에러를 원천적으로 방지할 수 있습니다.

-- FOR 루프 사용 시 커서 자동 관리 (권장 방식)
BEGIN
    FOR emp_rec IN (
        SELECT employee_id, first_name, salary
        FROM employees
        WHERE department_id = 20
    ) LOOP
        DBMS_OUTPUT.PUT_LINE(
            emp_rec.employee_id || ' | ' || 
            emp_rec.first_name  || ' | ' || 
            emp_rec.salary
        );
    END LOOP;
    -- 커서 자동으로 닫힘 - ORA-01023 발생 없음
END;
/

관련 에러

  • ORA-01001: invalid cursor — 커서가 유효하지 않을 때 발생하며, ORA-01023과 유사하지만 커서 번호 자체가 잘못된 경우에 해당합니다.
  • ORA-01002: fetch out of sequence — 커서에서 순서에 맞지 않는 FETCH 작업을 시도할 때 발생하며, SELECT FOR UPDATE 커서에서 커밋 후 FETCH 시 자주 나타납니다.
  • ORA-06511: PL/SQL: cursor already open — 이미 열린 커서를 다시 OPEN하려 할 때 발생하는 에러로, 커서 생명주기 관리 실수에서 비롯됩니다.
  • ORA-01000: maximum open cursors exceeded — 세션에서 허용된 최대 커서 수를 초과할 때 발생하며, 커서를 닫지 않고 계속 열 경우 발생합니다. OPEN_CURSORS 파라미터 조정이 필요합니다.
DBMS 에러 코드 시리즈

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

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

댓글 남기기