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

ORA-29278
2026년 10월 06일 | DBMS Error 가이드

이 글에서 다루는 내용

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

ORA-29278 SMTP transient error 는?

ORA-29278 에러는 Oracle 데이터베이스에서 UTL_SMTP 패키지를 사용하여 이메일을 전송할 때 발생하는 SMTP 일시적 오류(Transient Error)입니다. 이 에러는 SMTP 서버가 요청을 완전히 거부한 것이 아니라, 일시적인 문제로 인해 메일 전송이 실패했음을 의미하며, SMTP 프로토콜 상에서 4xx 계열의 응답 코드와 함께 반환됩니다. 주로 Oracle 데이터베이스 서버와 SMTP 메일 서버 간의 네트워크 문제, 인증 문제, 또는 SMTP 서버의 일시적인 과부하 상황에서 발생합니다.


주요 발생 원인

1. SMTP 서버 연결 실패 또는 네트워크 문제

Oracle 데이터베이스 서버에서 SMTP 서버로의 네트워크 경로가 불안정하거나 방화벽(Firewall) 정책에 의해 특정 포트(기본 25번)가 차단되어 있을 경우 이 에러가 발생합니다. 또한 SMTP 서버의 호스트명이 잘못 지정되었거나 DNS 해석이 실패할 때도 동일한 에러가 나타납니다. 실무에서는 보안 정책 변경이나 네트워크 장비 교체 이후 갑자기 발생하는 경우가 많으므로, 먼저 네트워크 연결 상태를 확인하는 것이 중요합니다.

2. Oracle ACL(Access Control List) 권한 설정 문제

Oracle 11g 이상에서는 외부 네트워크 리소스에 접근하기 위해 반드시 ACL(Access Control List)을 설정해야 합니다. ACL이 올바르게 설정되지 않거나, 특정 사용자(Schema)에게 SMTP 서버의 호스트 및 포트에 대한 접근 권한이 부여되지 않은 경우 ORA-29278 에러가 발생합니다. 이 경우에는 에러 메시지가 단순히 “transient error”로 표시되어 ACL 문제임을 바로 파악하기 어려울 수 있으므로, 권한 관련 로그를 함께 확인해야 합니다.

3. SMTP 서버 인증(Authentication) 실패 또는 서버 과부하

현대의 SMTP 서버는 대부분 인증을 요구하며, 잘못된 사용자 이름이나 비밀번호를 전달하거나 TLS/SSL 설정이 올바르지 않은 경우 SMTP 서버가 4xx 응답을 반환하면서 ORA-29278이 발생합니다. 또한 SMTP 서버가 과부하 상태이거나 수신 큐(Queue)가 가득 차 있을 때도 일시적으로 연결을 거부하면서 동일한 에러가 발생할 수 있습니다. 이 경우 잠시 후 재시도하면 해결되기도 하지만, 근본적인 원인을 제거하는 것이 중요합니다.


해결 방법

1. 네트워크 연결 및 SMTP 서버 접근 확인

먼저 Oracle 서버에서 SMTP 서버로의 연결이 가능한지 확인합니다. OS 레벨에서 telnet이나 nc 명령어를 사용해 포트 연결을 테스트하고, Oracle 내부에서 UTL_SMTP 패키지를 이용해 연결을 테스트합니다.

-- UTL_SMTP를 이용한 기본 연결 테스트
DECLARE
  v_conn  UTL_SMTP.CONNECTION;
  v_host  VARCHAR2(100) := 'smtp.yourmailserver.com';
  v_port  NUMBER        := 25;
BEGIN
  -- SMTP 서버에 연결 시도
  v_conn := UTL_SMTP.OPEN_CONNECTION(v_host, v_port);
  
  -- HELO 명령으로 서버 응답 확인
  UTL_SMTP.HELO(v_conn, v_host);
  
  DBMS_OUTPUT.PUT_LINE('SMTP 연결 성공!');
  
  -- 연결 종료
  UTL_SMTP.QUIT(v_conn);
EXCEPTION
  WHEN UTL_SMTP.TRANSIENT_ERROR THEN
    DBMS_OUTPUT.PUT_LINE('SMTP 일시적 오류 발생: ' || SQLERRM);
  WHEN UTL_SMTP.PERMANENT_ERROR THEN
    DBMS_OUTPUT.PUT_LINE('SMTP 영구적 오류 발생: ' || SQLERRM);
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE('기타 오류: ' || SQLERRM);
END;
/

2. ACL(Access Control List) 설정 및 권한 부여

Oracle 11g 이상에서는 외부 네트워크 접근을 위한 ACL 설정이 필수입니다. 아래 스크립트를 DBA 권한으로 실행하여 ACL을 생성하고 사용자에게 권한을 부여합니다.

-- Oracle 11g/12c 이상: SMTP 서버 접근을 위한 ACL 설정
-- 반드시 DBA 권한으로 실행

BEGIN
  -- ACL 생성 (이미 존재하면 에러 발생하므로 예외 처리)
  DBMS_NETWORK_ACL_ADMIN.CREATE_ACL(
    acl         => 'smtp_acl.xml',
    description => 'ACL for SMTP Mail Server Access',
    principal   => 'MAIL_USER',   -- 메일 발송할 스키마명
    is_grant    => TRUE,
    privilege   => 'connect'
  );
  COMMIT;
END;
/

-- ACL에 resolve 권한 추가 (DNS 조회용)
BEGIN
  DBMS_NETWORK_ACL_ADMIN.ADD_PRIVILEGE(
    acl       => 'smtp_acl.xml',
    principal => 'MAIL_USER',
    is_grant  => TRUE,
    privilege => 'resolve'
  );
  COMMIT;
END;
/

-- ACL을 SMTP 서버 호스트 및 포트에 할당
BEGIN
  DBMS_NETWORK_ACL_ADMIN.ASSIGN_ACL(
    acl  => 'smtp_acl.xml',
    host => 'smtp.yourmailserver.com',
    lower_port => 25,
    upper_port => 25
  );
  COMMIT;
END;
/

-- Oracle 12c 이상에서는 아래 방식도 사용 가능 (권장)
BEGIN
  DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE(
    host       => 'smtp.yourmailserver.com',
    lower_port => 25,
    upper_port => 587,
    ace        => xs$ace_type(
                    privilege_list => xs$name_list('connect', 'resolve'),
                    principal_name => 'MAIL_USER',
                    principal_type => xs_acl.ptype_db
                  )
  );
  COMMIT;
END;
/

-- ACL 설정 확인 쿼리
SELECT host, lower_port, upper_port, acl
FROM   dba_network_acls
ORDER BY host;

SELECT acl, principal, privilege, is_grant
FROM   dba_network_acl_privileges
ORDER BY acl, principal;

3. 완전한 이메일 전송 프로시저 예제 (인증 포함)

실무에서 바로 사용할 수 있는 이메일 전송 프로시저로, 예외 처리와 재시도 로직을 포함합니다.

-- 실무용 이메일 전송 프로시저
CREATE OR REPLACE PROCEDURE send_email (
  p_from     IN VARCHAR2,
  p_to       IN VARCHAR2,
  p_subject  IN VARCHAR2,
  p_body     IN VARCHAR2,
  p_smtp_host IN VARCHAR2 DEFAULT 'smtp.yourmailserver.com',
  p_smtp_port IN NUMBER   DEFAULT 25
)
AS
  v_conn       UTL_SMTP.CONNECTION;
  v_reply      UTL_SMTP.REPLY;
  v_message    VARCHAR2(32767);
  v_retry_cnt  NUMBER := 0;
  v_max_retry  NUMBER := 3;
BEGIN
  -- 메일 메시지 구성
  v_message := 'Date: ' || TO_CHAR(SYSDATE, 'DD Mon YYYY HH24:MI:SS') || UTL_TCP.CRLF
            || 'From: ' || p_from || UTL_TCP.CRLF
            || 'To: ' || p_to || UTL_TCP.CRLF
            || 'Subject: ' || p_subject || UTL_TCP.CRLF
            || 'MIME-Version: 1.0' || UTL_TCP.CRLF
            || 'Content-Type: text/plain; charset=UTF-8' || UTL_TCP.CRLF
            || UTL_TCP.CRLF
            || p_body;

  -- 재시도 로직 포함
  WHILE v_retry_cnt < v_max_retry LOOP
    BEGIN
      -- SMTP 서버 연결
      v_conn := UTL_SMTP.OPEN_CONNECTION(p_smtp_host, p_smtp_port, 30);
      UTL_SMTP.HELO(v_conn, p_smtp_host);
      
      -- 발신자/수신자 설정
      UTL_SMTP.MAIL(v_conn, p_from);
      UTL_SMTP.RCPT(v_conn, p_to);
      
      -- 메일 본문 전송
      UTL_SMTP.DATA(v_conn, v_message);
      UTL_SMTP.QUIT(v_conn);
      
      DBMS_OUTPUT.PUT_LINE('메일 전송 성공: ' || p_to);
      RETURN; -- 성공 시 프로시저 종료
      
    EXCEPTION
      WHEN UTL_SMTP.TRANSIENT_ERROR THEN
        v_retry_cnt := v_retry_cnt + 1;
        DBMS_OUTPUT.PUT_LINE('일시적 오류, 재시도 ' || v_retry_cnt || '/' || v_max_retry);
        
        -- 연결이 열려 있으면 닫기 시도
        BEGIN
          UTL_SMTP.QUIT(v_conn);
        EXCEPTION
          WHEN OTHERS THEN NULL;
        END;
        
        IF v_retry_cnt >= v_max_retry THEN
          RAISE_APPLICATION_ERROR(-20001, 
            'SMTP 재시도 한도 초과. 최종 오류: ' || SQLERRM);
        END IF;
        
        -- 재시도 전 잠시 대기 (5초)
        DBMS_LOCK.SLEEP(5);
        
      WHEN UTL_SMTP.PERMANENT_ERROR THEN
        RAISE_APPLICATION_ERROR(-20002, 
          'SMTP 영구 오류 발생: ' || SQLERRM);
    END;
  END LOOP;

EXCEPTION
  WHEN OTHERS THEN
    -- 로그 테이블에 에러 기록
    INSERT INTO mail_error_log (error_date, error_msg, mail_to, mail_subject)
    VALUES (SYSDATE, SQLERRM, p_to, p_subject);
    COMMIT;
    RAISE;
END send_email;
/

-- 이메일 오류 로그 테이블 생성
CREATE TABLE mail_error_log (
  log_id      NUMBER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
  error_date  DATE          DEFAULT SYSDATE,
  error_msg   VARCHAR2(4000),
  mail_to     VARCHAR2(500),
  mail_subject VARCHAR2(1000)
);

-- 실행 예제
BEGIN
  send_email(
    p_from      => 'sender@yourdomain.com',
    p_to        => 'recipient@target.com',
    p_subject   => '[Oracle] 테스트 메일',
    p_body      => '안녕하세요. Oracle UTL_SMTP 테스트 메일입니다.',
    p_smtp_host => 'smtp.yourmailserver.com',
    p_smtp_port => 25
  );
END;
/

4. 현재 ACL 및 네트워크 설정 진단 쿼리

-- 현재 설정된 ACL 목록 확인
SELECT acl, host, lower_port, upper_port
FROM   dba_network_acls
WHERE  host LIKE '%smtp%' OR host LIKE '%mail%'
ORDER BY host;

-- 특정 사용자의 ACL 권한 확인
SELECT a.acl, a.host, p.principal, p.privilege, p.is_grant
FROM   dba_network_acls a
JOIN   dba_network_acl_privileges p ON a.acl = p.acl
WHERE  p.principal = 'MAIL_USER'  -- 확인할 사용자명
ORDER BY a.host;

-- UTL_SMTP 관련 패키지 실행 권한 확인
SELECT grantee, privilege, admin_option
FROM   dba_sys_privs
WHERE  grantee = 'MAIL_USER'
UNION ALL
SELECT grantee, privilege, admin_option
FROM   dba_tab_privs
WHERE  grantee = 'MAIL_USER'
  AND  table_name IN ('UTL_SMTP', 'UTL_TCP', 'UTL_MAIL');

예방 방법

1. 이메일 전송 모니터링 및 자동 알림 체계 구축

이메일 전송 실패를 추적할 수 있는 로그 테이블을 운영하고, 주기적인 배치 잡(DBMS_SCHEDULER)을 통해 실패 건수가 임계치를 초과할 경우 DBA에게 자동 알림이 가도록 설정합니다. 또한 SMTP 서버의 상태를 주기적으로 체크하는 헬스체크 프로시저를 작성해두면 장애 발생 시 빠른 대응이 가능합니다.

-- DBMS_SCHEDULER를 이용한 SMTP 헬스체크 잡 예시
BEGIN
  DBMS_SCHEDULER.CREATE_JOB(
    job_name        => 'SMTP_HEALTH_CHECK',
    job_type        => 'PLSQL_BLOCK',
    job_action      => '
      DECLARE
        v_conn UTL_SMTP.CONNECTION;
      BEGIN
        v_conn := UTL_SMTP.OPEN_CONNECTION(''smtp.yourmailserver.com'', 25, 10);
        UTL_SMTP.HELO(v_conn, ''smtp.yourmailserver.com'');
        UTL_SMTP.QUIT(v_conn);
        -- 정상: 로그 업데이트
        UPDATE smtp_health_log SET last_ok = SYSDATE WHERE smtp_host = ''smtp.yourmailserver.com'';
        COMMIT;
      EXCEPTION
        WHEN OTHERS THEN
          INSERT INTO smtp_health_log (smtp_host, last_fail, error_msg)
          VALUES (''smtp.yourmailserver.com'', SYSDATE, SQLERRM);
          COMMIT;
      END;',
    start_date      => SYSTIMESTAMP,
    repeat_interval => 'FREQ=MINUTELY;INTERVAL=10',  -- 10분마다 실행
    enabled         => TRUE,
    comments        => 'SMTP 서버 헬스체크 잡'
  );
END;
/

2. 네트워크 변경 시 사전 검증 프로세스 표준화

방화벽 정책 변경, 네트워크 장비 교체, SMTP 서버 이전 등 인프라 변경 작업 시 반드시 Oracle에서 SMTP 연결 테스트를 변경 전/후로 수행하는 체크리스트를 표준화합니다. 또한 Oracle 패치 적용이나 업그레이드 시 ACL 설정이 초기화되는 경우가 있으므로, ACL 설정 스크립트를 형상 관리(Git 등)에 보관하고 변경 이력을 관리합니다.


관련 에러

  • ORA-29279: SMTP permanent error — ORA-29278이 일시적 오류(4xx)인 반면, ORA-29279는 영구적 오류(5xx)로 수신 거부나 인증 완전 실패 시 발생합니다.
  • ORA-24247: Network access denied by access control list (ACL) — ACL 설정이 누락되거나 잘못된 경우 발생하며, ORA-29278 이전에 먼저 확인해야 할 에러입니다.
  • ORA-29276: Transfer timeout — SMTP 서버가 응답하지 않아 타임아웃이 발생한 경우로, 네트워크 불안정이나 서버 과부하와 관련됩니다.
  • ORA-06512: PL/SQL 스택 정보 에러로, UTL_SMTP 관련 에러 발생 시 콜 스택 정보와 함께 표시됩니다.
DBMS 에러 코드 시리즈

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

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

댓글 남기기