1HOON
논리적 코딩
1HOON
전체 방문자
오늘
어제
  • HOME (186)
    • ☕️ JAVA (28)
      • WhiteShip Java LIVE Study (6)
      • Effective JAVA (10)
    • 🔮 KOTLIN (4)
    • 🌱 SPRING (51)
      • 스프링 인 액션 (22)
      • JPA (18)
    • ☕️ JAVASCRIPT (6)
    • 📃 DATABASE (40)
      • ORACLE (37)
      • MSSQL (2)
    • 🐧 LINUX (4)
    • 🐳 DOCKER (5)
    • 🐙 KUBERNETES (4)
    • 🏗️ ARCHITECTURE (8)
    • 📦 ETC (26)
      • TOY PROJECT (5)
      • RECRUIT (1)
      • 그냥 쓰고 싶어서요 (14)
    • 🤿 DEEP DIVE (1)
    • 🚽 Deprecated (9)
      • PYTHON (3)
      • AWS (2)
      • HTTP 완벽가이드 (3)
      • WEB (1)

블로그 메뉴

  • 홈
  • 방명록
  • 관리

인기 글

최근 글

티스토리

hELLO · Designed By 정상우.
1HOON

논리적 코딩

[PL/SQL] 예외처리(3) : 효율적인 예외처리
📃 DATABASE/ORACLE

[PL/SQL] 예외처리(3) : 효율적인 예외처리

2018. 1. 26. 14:01

 예외처리(1) : 예외처리란?

 예외처리(2) : 시스템 예외와 사용자 정의 예외

 예외처리(3) : 효율적인 예외처리


효율적인 예외처리 방법


1. 시스템 예외인 경우는 OTHERS를 사용하자

모든 시스템 예외를 알 수 없기 때문에, 모든 시스템 예외를 잡아주는 OTHERS와 예외의 정보를 알려주는 SQLCODE, SQLERRM을 적극 사용하자.


2. 예외처리 루틴을 공통 모듈화하고, 발생된 예외 로그를 남기자

발생한 예외에 대한 로그 테이블을 만들어 예외가 발생할 때 만다 로그 테이블에 기록하자.


3. 사용자 정의 예외도 별도의 테이블을 미리 만들어 관리한다.

동일한 예외 코드의 중복을 막기위해서


예제

먼저, 예외 로그를 남길 테이블을 만든다.

1
2
3
4
5
6
7
8
9
CREATE TABLE ERROR_LOG
(
    ERROR_SEQ       NUMBER,                 -- 에러 시퀀스
    PROG_NAME       VARCHAR2(80),           -- 프로그램명
    ERROR_CODE      NUMBER,                 -- 에러 코드
    ERROR_MESSAGE   VARCHAR2(300),          -- 에러 메시지
    ERROR_LINE      VARCHAR2(100),          -- 에러 라인
    ERROR_DATE      DATE    DEFAULT SYSDATE -- 에러 발생일
);
Colored by Color Scripter
cs


ERROR_LOG 테이블의 에러 시퀀스를 만든다.

1
2
3
4
5
6
7
CREATE SEQUENCE ERROR_SEQ
INCREMENT BY    1
START WITH      1
MINVALUE        1
MAXVALUE        999999
NOCYCLE
NOCACHE;
cs


ERROR_LOG 테이블에 데이터를 INSERT하는 프로시저를 만든다. 

이 프로시저를 이용하면 다른 곳에서 예외 처리 후 로그를 남길 때, INSERT문을 작성하지 않고 이 프로시저를 실행시키면 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
CREATE OR REPLACE PROCEDURE ERROR_LOG_PROC
(   P_PROG_NAME     ERROR_LOG.PROG_NAME%TYPE,
    P_ERROR_CODE    ERROR_LOG.ERROR_CODE%TYPE,
    P_ERROR_MESSAGE ERROR_LOG.ERROR_MESSAGE%TYPE,
    P_ERROR_LINE    ERROR_LOG.ERROR_LINE%TYPE       )
IS
 
BEGIN
    INSERT  INTO ERROR_LOG (ERROR_SEQ, PROG_NAME, ERROR_CODE, ERROR_MESSAGE, ERROR_LINE)
            VALUES (ERROR_SEQ.NEXTVAL, P_PROG_NAME, P_ERROR_CODE, P_ERROR_MESSAGE, P_ERROR_LINE);
    
    COMMIT;
END;
Colored by Color Scripter
cs


마지막으로, 테스트용 프로시저를 만든다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
CREATE OR REPLACE PROCEDURE INS_EMP2_PROC
(   P_EMP_NAME      EMPLOYEES.EMP_NAME%TYPE,
    P_DEPARTMENT_ID DEPARTMENTS.DEPARTMENT_ID%TYPE,
    P_HIRE_MONTH    VARCHAR2                        )
IS
    VN_EMPLOYEE_ID  EMPLOYEES.EMPLOYEE_ID%TYPE;
    VD_CURR_DATE    DATE := SYSDATE;
    VN_CNT          NUMBER := 0;
 
    EX_INVALID_DEPID    EXCEPTION;
    PRAGMA EXCEPTION_INIT(EX_INVALID_DEPID, -20000);
    
    EX_INVALID_MONTH    EXCEPTION;
    PRAGMA EXCEPTION_INIT(EX_INVALID_MONTH, -1843);
    
    V_ERR_CODE  ERROR_LOG.ERROR_CODE%TYPE;
    V_ERR_MSG   ERROR_LOG.ERROR_MESSAGE%TYPE;
    V_ERR_LINE  ERROR_LOG.ERROR_LINE%TYPE;
BEGIN
    SELECT  COUNT(*)
    INTO    VN_CNT
    FROM    DEPARTMENTS
    WHERE   DEPARTMENT_ID = P_DEPARTMENT_ID;
    
    IF  VN_CNT = 0  THEN
        RAISE EX_INVALID_DEPID;
    END IF;
    
    IF  SUBSTR(P_HIRE_MONTH, 5, 2) NOT BETWEEN '01' AND '12' THEN
        RAISE EX_INVALID_MONTH;
    END IF;
    
    SELECT  MAX(EMPLOYEE_ID) + 1
    INTO    VN_EMPLOYEE_ID
    FROM    EMPLOYEES;
    
    INSERT  INTO EMPLOYEES (EMPLOYEE_ID, EMP_NAME, HIRE_DATE, DEPARTMENT_ID)
            VALUES (VN_EMPLOYEE_ID, P_EMP_NAME, TO_DATE(P_HIRE_MONTH || '01'), P_DEPARTMENT_ID);
    
    COMMIT;
EXCEPTION
    WHEN    EX_INVALID_DEPID    THEN
            V_ERR_CODE  := SQLCODE;
            V_ERR_MSG   := '해당 부서가 없습니다.';
            V_ERR_LINE  := DBMS_UTILITY.FORMAT_ERROR_BACKTRACE;
            ROLLBACK;
            ERROR_LOG_PROC ('INS_EMP2_PROC', V_ERR_CODE, V_ERR_MSG, V_ERR_LINE);
    WHEN    EX_INVALID_MONTH    THEN
            V_ERR_CODE  := SQLCODE;
            V_ERR_MSG   := SQLERRM;
            V_ERR_LINE  := DBMS_UTILITY.FORMAT_ERROR_BACKTRACE;
            ROLLBACK;
            ERROR_LOG_PROC ('INS_EMP2_PROC', V_ERR_CODE, V_ERR_MSG, V_ERR_LINE);
    WHEN    OTHERS    THEN
            V_ERR_CODE  := SQLCODE;
            V_ERR_MSG   := SQLERRM;
            V_ERR_LINE  := DBMS_UTILITY.FORMAT_ERROR_BACKTRACE;
            ROLLBACK;
            ERROR_LOG_PROC ('INS_EMP2_PROC', V_ERR_CODE, V_ERR_MSG, V_ERR_LINE);
END;
Colored by Color Scripter
cs


코드 길이가 좀 있지만, 찬찬히 보면 충분히 해석 가능한 길이다.


실제로 테스트를 진행해보자.

1
2
3
4
5
EXEC INS_EMP2_PROC ('HONG', 1000, '201801');
EXEC INS_EMP2_PROC ('HONG', 100, '201813');
 
SELECT  *
FROM    ERROR_LOG;
cs


첫 번째 라인은 존재하지 않는 부서번호를, 두번째 라인은 2018년 13월을 매개변수로 넘겼다.



두 개의 예외 모두 로그 테이블에 기록된 것을 확인할 수 있다.



다음으로, 사용자 정의 예외목록 테이블을 만들고 그 예외들도 로그 테이블에 남겨보자.

사용자 정의 예외목록 테이블을 이용하면 사용자 정의 예외들을 효율적으로 관리할 수 있고, 코드의 중복을 줄일 수 있다.


사용자 정의 예외목록 테이블을 만들자. 기본키는 에러코드다.

1
2
3
4
5
6
7
CREATE TABLE APP_USER_DEFINE_ERROR
(
    ERROR_CODE      NUMBER,
    ERROR_MESSAGE   VARCHAR2(300),
    CREATE_DATE     DATE    DEFAULT SYSDATE,
    PRIMARY KEY (ERROR_CODE)
);
Colored by Color Scripter
cs


그리고 사용할 예외 정보를 사용자 정의 예외목록 테이블에 입력한다.

1
2
3
4
INSERT  INTO APP_USER_DEFINE_ERROR (ERROR_CODE, ERROR_MESSAGE)
        VALUES (-1843, '지정한 월이 부적합합니다.');
INSERT  INTO APP_USER_DEFINE_ERROR (ERROR_CODE, ERROR_MESSAGE)
        VALUES (-20000, '해당 부서가 없습니다.');
cs


이 전에 사용했던 예외 로그 기록 프로시저를 수정한다. (변경되는 내용은 굵은 글씨)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
CREATE OR REPLACE PROCEDURE ERROR_LOG_PROC
(   P_PROG_NAME     ERROR_LOG.PROG_NAME%TYPE,
    P_ERROR_CODE    ERROR_LOG.ERROR_CODE%TYPE,
    P_ERROR_MESSAGE ERROR_LOG.ERROR_MESSAGE%TYPE,
    P_ERROR_LINE    ERROR_LOG.ERROR_LINE%TYPE       )
IS
    VN_ERROR_CODE       ERROR_LOG.ERROR_CODE%TYPE       := P_ERROR_CODE;
    VN_ERROR_MESSAGE    ERROR_LOG.ERROR_MESSAGE%TYPE    := P_ERROR_MESSAGE;
BEGIN
    
    BEGIN
        SELECT  ERROR_MESSAGE
        INTO    VN_ERROR_MESSAGE
        FROM    APP_USER_DEFINE_ERROR
        WHERE   ERROR_CODE = VN_ERROR_CODE;
    EXCEPTION
        WHEN    NO_DATA_FOUND   THEN
                VN_ERROR_MESSAGE := P_ERROR_MESSAGE;
    END;
 
    INSERT  INTO ERROR_LOG (ERROR_SEQ, PROG_NAME, ERROR_CODE, ERROR_MESSAGE, ERROR_LINE)
            VALUES (ERROR_SEQ.NEXTVAL, P_PROG_NAME, VN_ERROR_CODE, VN_ERROR_MESSAGE, P_ERROR_LINE);
    
    COMMIT;
END;
Colored by Color Scripter
cs


테스트용 프로시저도 다음과 같이 수정한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
CREATE OR REPLACE PROCEDURE INS_EMP2_PROC
(   P_EMP_NAME      EMPLOYEES.EMP_NAME%TYPE,
    P_DEPARTMENT_ID DEPARTMENTS.DEPARTMENT_ID%TYPE,
    P_HIRE_MONTH    VARCHAR2                        )
IS
    VN_EMPLOYEE_ID  EMPLOYEES.EMPLOYEE_ID%TYPE;
    VD_CURR_DATE    DATE := SYSDATE;
    VN_CNT          NUMBER := 0;
 
    EX_INVALID_DEPID    EXCEPTION;
    PRAGMA EXCEPTION_INIT(EX_INVALID_DEPID, -20000);
    
    EX_INVALID_MONTH    EXCEPTION;
    PRAGMA EXCEPTION_INIT(EX_INVALID_MONTH, -1843);
    
    V_ERR_CODE  ERROR_LOG.ERROR_CODE%TYPE;
    V_ERR_MSG   ERROR_LOG.ERROR_MESSAGE%TYPE;
    V_ERR_LINE  ERROR_LOG.ERROR_LINE%TYPE;
BEGIN
    SELECT  COUNT(*)
    INTO    VN_CNT
    FROM    DEPARTMENTS
    WHERE   DEPARTMENT_ID = P_DEPARTMENT_ID;
    
    IF  VN_CNT = 0  THEN
        RAISE EX_INVALID_DEPID;
    END IF;
    
    IF  SUBSTR(P_HIRE_MONTH, 5, 2) NOT BETWEEN '01' AND '12' THEN
        RAISE EX_INVALID_MONTH;
    END IF;
    
    SELECT  MAX(EMPLOYEE_ID) + 1
    INTO    VN_EMPLOYEE_ID
    FROM    EMPLOYEES;
    
    INSERT  INTO EMPLOYEES (EMPLOYEE_ID, EMP_NAME, HIRE_DATE, DEPARTMENT_ID)
            VALUES (VN_EMPLOYEE_ID, P_EMP_NAME, TO_DATE(P_HIRE_MONTH || '01'), P_DEPARTMENT_ID);
    
    COMMIT;
EXCEPTION
    WHEN    EX_INVALID_DEPID    THEN
            V_ERR_CODE  := SQLCODE;
            V_ERR_LINE  := DBMS_UTILITY.FORMAT_ERROR_BACKTRACE;
            ROLLBACK;
            ERROR_LOG_PROC ('INS_EMP2_PROC', V_ERR_CODE, V_ERR_MSG, V_ERR_LINE);
    WHEN    EX_INVALID_MONTH    THEN
            V_ERR_CODE  := SQLCODE;
            V_ERR_LINE  := DBMS_UTILITY.FORMAT_ERROR_BACKTRACE;
            ROLLBACK;
            ERROR_LOG_PROC ('INS_EMP2_PROC', V_ERR_CODE, V_ERR_MSG, V_ERR_LINE);
    WHEN    OTHERS    THEN
            V_ERR_CODE  := SQLCODE;
            V_ERR_MSG   := SQLERRM;
            V_ERR_LINE  := DBMS_UTILITY.FORMAT_ERROR_BACKTRACE;
            ROLLBACK;
            ERROR_LOG_PROC ('INS_EMP2_PROC', V_ERR_CODE, V_ERR_MSG, V_ERR_LINE);
END;
Colored by Color Scripter
cs


사용자 정의 예외의 경우, 예외 메시지가 이미 테이블상에 존재하기 때문에 따로 매개변수에 초기화 하는 작업이 필요없다.


다시 예외를 발생시켜보자.

1
2
EXEC INS_EMP2_PROC ('HONG', 1000, '201801');
EXEC INS_EMP2_PROC ('HONG', 100, '201813');
cs


그리고 예외 로그 기록 테이블을 조회하면 다음과 같은 결과가 나온다.

1
2
SELECT  *
FROM    ERROR_LOG;
cs



1,2행은 사용자 정의 예외 테이블을 만들기 전, 3,4행은 만든 이후이다.



 예외처리(1) : 예외처리란?

 예외처리(2) : 시스템 예외와 사용자 정의 예외

 예외처리(3) : 효율적인 예외처리

반응형
저작자표시 비영리 변경금지 (새창열림)

'📃 DATABASE > ORACLE' 카테고리의 다른 글

[PL/SQL] 커서(CURSOR) (1) : 명시적 커서와 묵시적 커서  (4) 2018.02.01
[PL/SQL] 트랜잭션  (0) 2018.01.29
[PL/SQL] 예외처리(2) : 시스템 예외와 사용자정의 예외  (0) 2018.01.25
[PL/SQL] 예외처리 (1) : 예외처리란?  (0) 2018.01.24
[PL/SQL] 프로시저(PROCEDURE)  (0) 2018.01.23
    '📃 DATABASE/ORACLE' 카테고리의 다른 글
    • [PL/SQL] 커서(CURSOR) (1) : 명시적 커서와 묵시적 커서
    • [PL/SQL] 트랜잭션
    • [PL/SQL] 예외처리(2) : 시스템 예외와 사용자정의 예외
    • [PL/SQL] 예외처리 (1) : 예외처리란?
    1HOON
    1HOON

    티스토리툴바