Oracle ORA-00942: SELECT는 되는데 프로시저 컴파일만 실패하는 이유

다른 계정의 테이블을 프로시저 SQL에 추가한 뒤 PL/SQL: ORA-00942: 테이블 또는 뷰가 존재하지 않습니다가 발생했다면, 객체가 실제로 없는 경우뿐 아니라 프로시저 소유자에게 직접 권한이 없는 경우를 먼저 확인해야 합니다. 일반적인 definer’s rights 프로시저라면 테이블 소유 계정에서 GRANT SELECT ON 다른계정.테이블명 TO 프로시저소유계정;을 실행한 뒤 다시 컴파일하면 해결됩니다.

핵심 답변

SQL 실행 화면에서는 조회되는데 저장 프로시저만 ORA-00942로 컴파일되지 않는다면, 역할(Role)을 통해 받은 권한인지 확인하세요. 기본 방식인 definer’s rights 저장 프로시저에서는 참조 객체 권한이 프로시저 소유자에게 직접 부여되어야 합니다.

LEFT JOIN 한 줄을 추가했는데 ORA-00942가 발생한 상황

실제 작업에서는 기존 프로시저의 조회문에 다른 스키마의 테이블을 LEFT JOIN으로 연결했습니다. 테이블은 분명히 존재했고 일반 조회도 가능한 상황이었지만, 프로시저를 컴파일하자 두 위치에서 오류가 잡혔습니다.

PL/SQL: SQL Statement ignored
PL/SQL: ORA-00942: 테이블 또는 뷰가 존재하지 않습니다
오류: 컴파일러 로그를 확인하십시오.

첨부된 컴파일러 로그에는 207/8의 SQL Statement ignored와 290/35의 ORA-00942가 표시되어 있습니다. 보안을 위해 프로시저명은 가려져 있으며, 글에서도 실제 계정명·테이블명·프로시저명을 복원하거나 추정하지 않았습니다.

ORA-00942 오류와 권한 부여 후 프로시저 컴파일 완료 메시지가 함께 보이는 컴파일러 로그
다른 계정 테이블을 참조한 프로시저의 ORA-00942 오류와 직접 권한 부여 후 컴파일 결과

권한을 직접 부여한 뒤에는 같은 컴파일 작업에서 대상 프로시저들이 컴파일되었다는 메시지가 확인됐습니다. 이 화면은 원인을 모든 환경에 일반화하는 증거라기보다, 이번 사례에서 직접 객체 권한 부여 후 컴파일이 완료된 결과로 사용했습니다.

왜 테이블이 존재하는데 “존재하지 않는다”고 나올까

ORA-00942는 이름 그대로 테이블이나 뷰가 없을 때 발생하지만, 현재 사용자가 그 객체를 볼 권한이 없을 때도 나타납니다. Oracle 공식 오류 안내도 다른 스키마의 객체를 참조할 때 올바른 스키마를 지정하고 필요한 접근 권한이 부여됐는지 확인하라고 설명합니다.

여기에 PL/SQL의 권한 규칙이 하나 더 붙습니다. 별도로 AUTHID CURRENT_USER를 지정하지 않은 저장 프로시저는 기본적으로 definer’s rights, 즉 정의자 권한으로 실행됩니다. Oracle은 이 방식의 명명된 PL/SQL 블록에서 일반 사용자에게 부여된 Role을 비활성화합니다. 따라서 Role을 통해 테이블을 조회할 수 있어도 프로시저를 컴파일할 때는 그 권한이 인정되지 않을 수 있습니다.

확인 상황 가능한 결과 이유
SQL 창에서 직접 SELECT성공현재 세션에서 활성화된 Role 권한을 사용할 수 있음
기본 저장 프로시저 컴파일ORA-00942definer’s rights 프로시저에서는 Role이 아니라 직접 권한이 필요함
직접 SELECT 권한 부여 후 컴파일성공 가능프로시저 소유자의 객체 권한으로 참조 객체를 해석할 수 있음

해결 SQL은 테이블 소유 계정에서 실행한다

예를 들어 DATA_OWNER가 테이블을 소유하고 APP_OWNER가 프로시저를 소유한다면, 권한 부여는 다음 형태입니다.

GRANT SELECT ON DATA_OWNER.TARGET_TABLE
TO APP_OWNER;

실행 목적과 권한

DATA_OWNER.TARGET_TABLE을 조회할 객체 권한을 APP_OWNER에게 직접 부여합니다. 테이블 소유자 또는 해당 객체 권한을 부여할 수 있는 권한을 가진 관리 계정에서 실행해야 합니다. 단순 조회와 LEFT JOIN만 필요하다면 SELECT만 부여하고 INSERT·UPDATE·DELETE까지 넓히지 않습니다.

프로시저 안에서는 스키마를 명시하면 어느 객체를 참조하는지 분명해집니다.

SELECT a.business_key,
       a.status_code,
       b.phone_number
  FROM APP_OWNER.MAIN_TABLE a
  LEFT JOIN DATA_OWNER.TARGET_TABLE b
    ON b.business_key = a.business_key
 WHERE a.status_code = :status_code;

예제 읽는 법

계정과 테이블 이름은 설명용 가상 이름입니다. 필요한 컬럼만 명시했고 ANSI LEFT JOIN을 사용했습니다. 실제 프로시저에서는 바인드 변수나 PL/SQL 매개변수 이름을 기존 코드에 맞춰 적용해야 합니다.

권한을 줬는지 확인하고 다시 컴파일하는 순서

먼저 프로시저 소유 계정에 SELECT가 직접 부여됐는지 확인합니다. 조회 권한 범위에 따라 DBA_TAB_PRIVS 또는 ALL_TAB_PRIVS를 사용할 수 있습니다.

SELECT table_schema AS owner,
       table_name,
       grantee,
       privilege
  FROM all_tab_privs
 WHERE table_schema      = 'DATA_OWNER'
   AND table_name = 'TARGET_TABLE'
   AND grantee    = 'APP_OWNER'
   AND privilege  = 'SELECT';

대문자로 생성한 일반 객체는 데이터 딕셔너리에서 대문자로 조회합니다. 따옴표로 만든 혼합 대소문자 객체라면 실제 이름과 정확히 맞아야 합니다.

권한 행이 확인되면 프로시저 소유 계정에서 다시 컴파일하고 오류를 읽습니다.

ALTER PROCEDURE APP_OWNER.PROCEDURE_NAME COMPILE;

SELECT line,
       position,
       text
  FROM all_errors
 WHERE owner = 'APP_OWNER'
   AND name  = 'PROCEDURE_NAME'
   AND type  = 'PROCEDURE'
 ORDER BY sequence;

ALL_ERRORS 결과가 없고 객체 상태가 VALID라면 컴파일 오류가 해소된 것입니다.

SELECT owner,
       object_name,
       object_type,
       status
  FROM all_objects
 WHERE owner       = 'APP_OWNER'
   AND object_name = 'PROCEDURE_NAME'
   AND object_type = 'PROCEDURE';

동의어만 만들면 권한 문제까지 해결되지는 않는다

스키마명을 생략하려고 private synonym을 만들 수는 있습니다. 하지만 동의어는 객체 이름의 별칭일 뿐, 대상 테이블의 SELECT 권한을 대신 부여하지 않습니다.

CREATE SYNONYM APP_OWNER.TARGET_TABLE
FOR DATA_OWNER.TARGET_TABLE;

이 동의어를 쓰더라도 APP_OWNER에 대한 직접 SELECT 권한은 별도로 필요합니다. 같은 이름의 로컬 테이블이나 동의어가 이미 있으면 이름 해석이 예상과 달라질 수 있으므로, 운영 프로시저에서는 스키마를 명시하는 편이 원인 파악에 유리합니다.

AUTHID CURRENT_USER로 바꾸기 전에 실행 주체를 생각한다

AUTHID CURRENT_USER를 사용한 invoker’s rights 프로시저는 호출자의 권한과 이름 해석 규칙을 따릅니다. Role 사용 방식도 definer’s rights와 다릅니다. 그렇다고 ORA-00942를 피하려고 기존 프로시저를 곧바로 invoker’s rights로 바꾸면 안 됩니다.

호출자마다 보이는 객체와 권한이 달라질 수 있고, 이미 운영 중인 권한 경계도 변합니다. 기존 프로시저가 기본 definer’s rights로 설계됐다면 필요한 테이블에 최소 SELECT 권한을 직접 부여하는 변경이 보통 더 작고 예측하기 쉽습니다.

보안을 위해 최소 권한과 회수 방법을 함께 준비한다

이 사례는 다른 계정 테이블을 읽기만 하므로 SELECT 하나만 부여했습니다. 편의를 위해 SELECT ANY TABLE 같은 넓은 시스템 권한을 주거나 불필요한 DML 권한을 함께 주는 방식은 피해야 합니다.

권한 회수와 영향

기능을 되돌릴 때는 REVOKE SELECT ON DATA_OWNER.TARGET_TABLE FROM APP_OWNER;로 회수할 수 있습니다. 다만 그 테이블을 참조하는 프로시저가 다시 INVALID 상태가 되거나 실행에 실패할 수 있으므로, 먼저 의존 객체와 사용 중인 배치를 확인해야 합니다.

REVOKE SELECT ON DATA_OWNER.TARGET_TABLE
FROM APP_OWNER;

이번 사례에서는 다른 계정 테이블을 LEFT JOIN에 추가한 뒤 ORA-00942가 발생했고, 테이블 소유 계정에서 프로시저 소유 계정으로 SELECT 권한을 직접 부여하자 컴파일이 완료됐습니다. 같은 증상이라도 오타, 잘못된 스키마명, 깨진 동의어, 실제 객체 부재가 원인일 수 있으므로 객체 존재 여부와 직접 권한을 함께 확인하는 순서가 안전합니다.

공식 문서

댓글 남기기