2026년 09월 03일 | DBMS Error 가이드
이 글에서 다루는 내용
ORA-06571 에러의 원인 분석, 해결 SQL, 예방 방법을 실무 관점에서 정리합니다.
ORA-06571 function does not guarantee not to update database 는?
ORA-06571 에러는 SQL 쿼리 내에서 호출된 PL/SQL 함수가 데이터베이스를 수정할 가능성이 있을 때 Oracle이 이를 거부하며 발생시키는 오류입니다. Oracle은 SQL 문장 실행 중 데이터 일관성을 보장하기 위해, SELECT 쿼리나 제약 조건 등에서 호출되는 함수가 DML(INSERT, UPDATE, DELETE) 작업을 수행하지 않는다는 것을 명시적으로 선언하도록 요구합니다. 즉, 함수 정의 시 PRAGMA RESTRICT_REFERENCES 또는 DETERMINISTIC, WNDS(Write No Database State) 등의 순수성(purity) 선언이 누락되었거나, 함수 내부 로직이 실제로 DML을 포함하고 있을 때 이 에러가 발생합니다.
주요 발생 원인
- 함수 내부에 DML 문장이 포함된 경우
가장 흔한 원인으로, 개발자가 SELECT 쿼리에서 호출하는 함수 안에 INSERT, UPDATE, DELETE 또는 MERGE 문을 포함시킨 경우입니다. Oracle의 SQL 엔진은 쿼리 실행 중 데이터 변경이 발생하면 읽기 일관성(Read Consistency)을 보장할 수 없기 때문에, 이러한 함수의 호출을 허용하지 않습니다. 예를 들어, 로그 테이블에 기록을 남기는 함수를 SELECT 절에서 호출하면 이 에러가 발생합니다.
- PRAGMA RESTRICT_REFERENCES 선언 누락 또는 잘못된 선언
패키지 함수의 경우, 패키지 스펙(specification)에서 PRAGMA RESTRICT_REFERENCES를 통해 함수의 순수성을 선언해야 합니다. 이 선언이 없거나, 선언된 purity 레벨(WNDS, RNDS, WNPS, RNPS)이 실제 함수 본문의 동작과 일치하지 않으면 컴파일 또는 실행 시점에 ORA-06571이 발생합니다. 특히 Oracle 8i 이전 버전에서 작성된 레거시 패키지에서 자주 나타나는 패턴입니다.
- 함수가 호출하는 서브프로그램(Subprogram)이 DML을 수행하는 경우
함수 자체에는 DML이 없더라도, 해당 함수 내부에서 호출하는 다른 프로시저나 함수가 DML을 수행하는 경우에도 동일한 에러가 발생합니다. Oracle은 호출 체인 전체를 분석하여 데이터 수정 가능성을 판단하므로, 간접적인 DML 호출도 허용되지 않습니다. 복잡한 패키지 구조에서 의도치 않게 이러한 상황이 발생하는 경우가 많습니다.
해결 방법
원인 1 해결: DML을 함수에서 제거하거나 프로시저로 분리
함수 내 DML 로직을 별도의 프로시저로 분리하고, 함수는 순수하게 값을 반환하는 용도로만 사용합니다.
-- 문제가 있는 함수 (SELECT에서 호출 시 ORA-06571 발생)
CREATE OR REPLACE FUNCTION get_employee_salary(p_emp_id IN NUMBER)
RETURN NUMBER IS
v_salary NUMBER;
BEGIN
SELECT salary INTO v_salary FROM employees WHERE employee_id = p_emp_id;
-- DML이 포함되어 있어 SQL에서 호출 불가
INSERT INTO audit_log (emp_id, access_time) VALUES (p_emp_id, SYSDATE);
COMMIT;
RETURN v_salary;
END;
/
-- 해결책: DML을 함수에서 제거, 순수 함수로 변경
CREATE OR REPLACE FUNCTION get_employee_salary(p_emp_id IN NUMBER)
RETURN NUMBER IS
v_salary NUMBER;
BEGIN
SELECT salary INTO v_salary FROM employees WHERE employee_id = p_emp_id;
RETURN v_salary;
END;
/
-- 감사 로그는 별도 프로시저로 분리
CREATE OR REPLACE PROCEDURE log_employee_access(p_emp_id IN NUMBER) IS
BEGIN
INSERT INTO audit_log (emp_id, access_time) VALUES (p_emp_id, SYSDATE);
COMMIT;
END;
/
원인 2 해결: PRAGMA RESTRICT_REFERENCES 올바르게 선언
패키지 스펙에서 함수의 순수성을 명시적으로 선언합니다.
-- 패키지 스펙에서 PRAGMA RESTRICT_REFERENCES 선언
CREATE OR REPLACE PACKAGE emp_pkg IS
FUNCTION get_salary(p_emp_id IN NUMBER) RETURN NUMBER;
-- WNDS: Write No Database State (DML 수행 안 함)
-- RNDS: Read No Database State (SELECT 수행 안 함, 필요 시 제외)
-- WNPS: Write No Package State (패키지 변수 수정 안 함)
-- RNPS: Read No Package State (패키지 변수 읽기 안 함)
PRAGMA RESTRICT_REFERENCES(get_salary, WNDS, WNPS);
END emp_pkg;
/
-- 패키지 바디 구현
CREATE OR REPLACE PACKAGE BODY emp_pkg IS
FUNCTION get_salary(p_emp_id IN NUMBER) RETURN NUMBER IS
v_salary NUMBER;
BEGIN
SELECT salary INTO v_salary
FROM employees
WHERE employee_id = p_emp_id;
RETURN v_salary;
END get_salary;
END emp_pkg;
/
-- 이제 SQL에서 정상 호출 가능
SELECT employee_id, emp_pkg.get_salary(employee_id) AS salary
FROM employees
WHERE department_id = 10;
원인 3 해결: 호출 체인 내 DML 제거 및 AUTONOMOUS_TRANSACTION 활용
감사 로그처럼 반드시 DML이 필요한 경우, PRAGMA AUTONOMOUS_TRANSACTION을 사용하여 독립적인 트랜잭션으로 처리합니다. 단, 이 방법은 주의해서 사용해야 합니다.
-- AUTONOMOUS_TRANSACTION을 활용한 감사 로그 함수
CREATE OR REPLACE FUNCTION get_salary_with_log(p_emp_id IN NUMBER)
RETURN NUMBER IS
PRAGMA AUTONOMOUS_TRANSACTION; -- 독립 트랜잭션 선언
v_salary NUMBER;
BEGIN
-- 감사 로그 DML (독립 트랜잭션으로 처리)
INSERT INTO audit_log (emp_id, access_time, action)
VALUES (p_emp_id, SYSDATE, 'SALARY_QUERY');
COMMIT; -- 반드시 AUTONOMOUS TRANSACTION 내에서 COMMIT/ROLLBACK 필요
-- 급여 조회
SELECT salary INTO v_salary
FROM employees
WHERE employee_id = p_emp_id;
RETURN v_salary;
END;
/
-- SQL에서 호출 테스트
SELECT employee_id,
get_salary_with_log(employee_id) AS salary
FROM employees
WHERE department_id = 20;
-- 함수의 purity 레벨 확인 방법
SELECT object_name, object_type, status
FROM user_objects
WHERE object_name = 'GET_SALARY_WITH_LOG';
예방 방법
- 함수 설계 단계에서 순수 함수(Pure Function) 원칙 준수
SQL에서 호출될 가능성이 있는 함수는 처음부터 DML을 포함하지 않는 순수 함수로 설계해야 합니다. 함수는 입력값을 받아 결과값을 반환하는 용도로만 사용하고, 데이터 변경이 필요한 로직은 반드시 프로시저로 분리하는 코딩 컨벤션을 팀 전체에 적용하세요. 코드 리뷰 체크리스트에 “SQL 호출 함수 내 DML 여부 확인” 항목을 추가하는 것을 권장합니다.
- 정기적인 코드 감사(Code Audit)와 DETERMINISTIC 키워드 활용
Oracle 8i 이후부터는 PRAGMA RESTRICT_REFERENCES 대신 DETERMINISTIC 키워드를 사용하여 함수의 순수성을 선언할 수 있습니다. 또한 정기적으로 DBA_ARGUMENTS, USER_SOURCE 뷰를 조회하여 SQL에서 호출되는 함수들이 DML을 포함하지 않는지 감사하는 스크립트를 운영 환경에 도입하는 것이 좋습니다.
-- SQL에서 호출되는 함수의 소스 코드 내 DML 키워드 탐지 스크립트
SELECT DISTINCT name, type
FROM user_source
WHERE type = 'FUNCTION'
AND (UPPER(text) LIKE '%INSERT%'
OR UPPER(text) LIKE '%UPDATE%'
OR UPPER(text) LIKE '%DELETE%'
OR UPPER(text) LIKE '%MERGE%')
ORDER BY name;
관련 에러
- ORA-06572:
PRAGMA RESTRICT_REFERENCES에서 선언한 purity 레벨과 실제 함수 동작이 충돌할 때 발생합니다. - ORA-14551: 쿼리 실행 중 DML 작업을 수행할 수 없음을 나타내며, ORA-06571과 유사한 맥락에서 발생합니다.
- ORA-04091: 트리거 내에서 변이 테이블(Mutating Table)을 참조할 때 발생하며, DML 제한과 관련된 에러입니다.
- ORA-06576: 유효하지 않은 함수 또는 프로시저 이름으로 호출 시 발생하는 에러로, 함수 선언 문제와 연관됩니다.
주요 DBMS error code를 정리하는 시리즈입니다.
블로그 홈에서 다른 에러도 확인하세요.
본 포스트는 AI가 생성한 기술 가이드입니다. 운영 환경 적용 전 충분한 검토를 권장합니다.