2026년 08월 08일 | DBMS Error 가이드
이 글에서 다루는 내용
ORA-02030 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
ORA-02030 can only select from fixed tables/views 는?
ORA-02030 에러는 Oracle 데이터베이스에서 고정 테이블(Fixed Tables) 또는 고정 뷰(Fixed Views)에 대해 SELECT 이외의 DML 작업(INSERT, UPDATE, DELETE)을 시도하거나, 해당 객체에 허용되지 않는 방식으로 접근할 때 발생합니다. 고정 테이블과 뷰는 X$ 접두사로 시작하는 내부 메모리 구조 테이블이나 V$, GV$ 동적 성능 뷰(Dynamic Performance Views)처럼 Oracle 커널이 직접 관리하는 읽기 전용 객체입니다. 이 에러는 주로 일반 테이블과 동일하게 DML을 수행하려 하거나, 권한 없이 내부 객체에 접근하려 할 때 발생하며, DBA나 개발자가 스크립트를 잘못 작성했을 때 빈번하게 나타납니다.
주요 발생 원인
1. X$ 테이블 또는 V$ 뷰에 DML 시도
가장 흔한 원인으로, X$BH, X$KCBWH 같은 내부 고정 테이블이나 V$SESSION, V$SQL 같은 동적 성능 뷰에 INSERT, UPDATE, DELETE를 시도하는 경우입니다. 이 객체들은 Oracle SGA(System Global Area) 메모리 구조를 직접 참조하는 읽기 전용 객체이기 때문에, 어떠한 데이터 변경도 허용하지 않습니다. DBA가 모니터링 스크립트를 작성하다가 실수로 UPDATE 문을 사용하거나, 애플리케이션 개발자가 일반 테이블로 착각하고 DML을 수행할 때 발생합니다.
2. 불충분한 권한으로 고정 뷰에 접근 시도
V$ 뷰나 X$ 테이블은 기본적으로 SYS 계정만 직접 접근 가능하며, 일반 사용자는 V_$ 동의어(Synonym)를 통해 SELECT 권한이 부여된 경우에만 조회가 가능합니다. 권한이 없는 사용자가 직접 X$ 테이블에 접근하거나, DBA가 권한 부여 시 잘못된 객체 이름을 사용하면 이 에러가 발생할 수 있습니다. 특히 Oracle 버전 업그레이드 후 권한 구조가 변경되어 기존 스크립트가 동작하지 않는 경우에도 나타납니다.
3. SYNONYM 또는 VIEW 생성 시 고정 테이블을 기반으로 DML 허용 시도
개발자가 X$ 테이블이나 V$ 뷰를 기반으로 일반 뷰(View)나 동의어(Synonym)를 생성한 후, 해당 객체를 통해 DML을 시도하는 경우입니다. 원본 객체가 고정 테이블이기 때문에 뷰나 동의어를 통해서도 동일하게 ORA-02030 에러가 발생합니다. INSTEAD OF 트리거를 이용한 우회도 X$ 테이블에는 적용되지 않으므로 주의가 필요합니다.
해결 방법
원인 1 해결: DML 대신 SELECT만 사용
고정 테이블과 뷰는 오직 SELECT만 허용됩니다. 기존 DML 문을 제거하고 조회 전용으로 변경해야 합니다.
-- ❌ 잘못된 사용 예 (ORA-02030 발생)
UPDATE V$SESSION
SET STATUS = 'INACTIVE'
WHERE SID = 100;
-- ❌ 잘못된 사용 예 (ORA-02030 발생)
DELETE FROM V$SQL
WHERE LAST_ACTIVE_TIME < SYSDATE - 7;
-- ✅ 올바른 사용 예 (SELECT만 허용)
SELECT SID, SERIAL#, USERNAME, STATUS, LAST_CALL_ET
FROM V$SESSION
WHERE STATUS = 'ACTIVE'
AND USERNAME IS NOT NULL
ORDER BY LAST_CALL_ET DESC;
-- ✅ X$ 테이블 조회 예 (SYS 계정 필요)
SELECT ADDR, INDX, INST_ID, FILE#, BLOCK#, STATUS
FROM X$BH
WHERE STATUS != 'free'
AND ROWNUM <= 20;
원인 2 해결: 적절한 권한 부여
일반 사용자에게 V$ 뷰 접근 권한을 올바르게 부여합니다.
-- SYS 계정으로 실행: 특정 사용자에게 V$ 뷰 권한 부여
GRANT SELECT ON V_$SESSION TO app_user;
GRANT SELECT ON V_$SQL TO app_user;
GRANT SELECT ON V_$SQLAREA TO app_user;
-- 또는 DBA 권한이 있는 역할 부여 (운영 환경에서는 신중히 사용)
GRANT SELECT_CATALOG_ROLE TO app_user;
-- 권한 부여 확인
SELECT GRANTEE, PRIVILEGE, TABLE_NAME
FROM DBA_TAB_PRIVS
WHERE TABLE_NAME IN ('V_$SESSION', 'V_$SQL')
AND GRANTEE = 'APP_USER';
-- 현재 사용자의 V$ 접근 가능 여부 확인
SELECT * FROM SESSION_PRIVS
WHERE PRIVILEGE LIKE '%SELECT%';
원인 3 해결: 고정 뷰 기반 뷰 설계 수정
고정 테이블 기반의 뷰에서 DML을 시도하는 구조를 변경합니다.
-- 고정 뷰를 기반으로 일반 테이블을 만들어 데이터를 복사하는 방식으로 해결
-- 스냅샷 테이블 생성
CREATE TABLE session_snapshot AS
SELECT SID, SERIAL#, USERNAME, STATUS, MACHINE, PROGRAM,
LAST_CALL_ET, LOGON_TIME, SYSDATE AS SNAP_TIME
FROM V$SESSION
WHERE 1=0; -- 구조만 생성
-- 데이터 정기적으로 수집 (스케줄러 활용)
INSERT INTO session_snapshot
SELECT SID, SERIAL#, USERNAME, STATUS, MACHINE, PROGRAM,
LAST_CALL_ET, LOGON_TIME, SYSDATE
FROM V$SESSION
WHERE USERNAME IS NOT NULL;
COMMIT;
-- 이제 session_snapshot 테이블에서 DML 가능
UPDATE session_snapshot
SET STATUS = 'REVIEWED'
WHERE SNAP_TIME < SYSDATE - 1;
-- V$ 뷰를 직접 사용하는 조회용 뷰는 SELECT 전용으로 명시적 설계
CREATE OR REPLACE VIEW active_sessions_vw AS
SELECT SID, SERIAL#, USERNAME, STATUS,
MACHINE, PROGRAM, LAST_CALL_ET
FROM V$SESSION
WHERE STATUS = 'ACTIVE'
AND USERNAME IS NOT NULL;
-- 위 뷰는 SELECT만 허용됨을 문서화
-- COMMENT ON TABLE active_sessions_vw IS 'READ-ONLY: Based on V$SESSION fixed view';
에러 발생 객체 확인 방법
-- 고정 테이블 목록 확인 (X$ 테이블)
SELECT NAME, TYPE
FROM V$FIXED_TABLE
WHERE NAME LIKE 'X$%'
ORDER BY NAME;
-- 동적 성능 뷰 목록 확인
SELECT NAME, TYPE
FROM V$FIXED_TABLE
WHERE NAME LIKE 'V$%'
ORDER BY NAME;
-- GV$ 뷰 목록 확인 (RAC 환경)
SELECT NAME, TYPE
FROM V$FIXED_TABLE
WHERE NAME LIKE 'GV$%'
ORDER BY NAME;
-- 특정 고정 뷰의 컬럼 구조 확인
SELECT COLUMN_NAME, DATA_TYPE, DATA_LENGTH
FROM V$FIXED_VIEW_DEFINITION
WHERE VIEW_NAME = 'V$SESSION';
예방 방법
1. 객체 유형 사전 검증 및 코딩 표준 수립
스크립트 작성 전 반드시 해당 객체가 일반 테이블인지 고정 뷰인지 확인하는 습관을 들이고, 팀 내 코딩 표준에 V$/X$ 객체에는 SELECT만 허용한다는 규칙을 명시합니다. 아래 쿼리를 통해 객체 유형을 사전에 확인하여 실수를 방지할 수 있습니다.
-- 객체 유형 사전 확인 쿼리
SELECT OBJECT_NAME, OBJECT_TYPE, STATUS
FROM DBA_OBJECTS
WHERE OBJECT_NAME = UPPER('&object_name');
-- 고정 테이블 여부 확인
SELECT COUNT(*) AS is_fixed_table
FROM V$FIXED_TABLE
WHERE NAME = UPPER('&object_name');
2. 성능 데이터 수집은 전용 스냅샷 테이블 활용
V$, X$ 객체의 데이터를 분석하거나 이력을 관리해야 할 경우, 직접 DML을 시도하는 대신 AWR(Automatic Workload Repository)이나 커스텀 스냅샷 테이블을 활용합니다. Oracle의 DBMS_WORKLOAD_REPOSITORY 패키지나 DBMS_SCHEDULER를 활용해 주기적으로 일반 테이블에 데이터를 수집하면 DML이 필요한 모든 작업을 안전하게 수행할 수 있습니다.
-- DBMS_SCHEDULER를 이용한 자동 스냅샷 수집 예
BEGIN
DBMS_SCHEDULER.CREATE_JOB(
job_name => 'COLLECT_SESSION_SNAPSHOT',
job_type => 'PLSQL_BLOCK',
job_action => '
BEGIN
INSERT INTO session_snapshot
SELECT SID, SERIAL#, USERNAME, STATUS, MACHINE,
PROGRAM, LAST_CALL_ET, LOGON_TIME, SYSDATE
FROM V$SESSION
WHERE USERNAME IS NOT NULL;
COMMIT;
END;',
start_date => SYSTIMESTAMP,
repeat_interval => ''FREQ=MINUTELY;INTERVAL=30'',
enabled => TRUE,
comments => ''Collect V$SESSION data every 30 minutes''
);
END;
/
관련 에러
- ORA-01031 (insufficient privileges): V$ 뷰나 X$ 테이블에 대한 SELECT 권한조차 없을 때 발생하며, ORA-02030과 함께 자주 나타납니다.
- ORA-00942 (table or view does not exist): 고정 테이블에 대한 접근 권한이 전혀 없는 일반 사용자가 X$ 테이블을 직접 조회할 때 발생할 수 있습니다.
- ORA-04063 (view has errors): V$ 기반 뷰가 깨져 있거나 재컴파일이 필요할 때 관련하여 발생합니다.
- ORA-00257 (archiver error): V$ARCHIVE_DEST 등의 뷰에서 상태를 확인할 때 연관되어 나타나는 에러로, ORA-02030과 혼용되어 잘못된 수정 시도를 유발하기도 합니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.