오라클 PL/SQL에서 IN, OUT, IN OUT 파라미터 모드에 대해 알아보는 시간입니다. 파라미터 모드란, 오라클 서브프로그램을 호출할 때 파라미터 이름과 데이터타입 사이에 들어가는 IN, OUT, IN OUT을 의미합니다. 모드 이름만으로 유추해 볼 때 IN은 입력 기능만 담당하고, OUT은 출력 역할만 할 것 같고, IN OUT은 입출력 모두를 제어할 것으로 추측합니다. 과연 파라미터 모드가 어떠한 역할을 하는지 한 번 살펴보도록 합시다.
우선 시작하기에 앞서 매개변수의 종류에 대해 안내해드리겠습니다. 매개변수는 형식 매개변수와 실제 매개변수가 있습니다. 각각 영어로 Formal Parameter, Actual Parameter라고 합니다. 형식 매개변수는 펑션, 프로시저와 같은 서브프로그램에 할당된 매개변수를 의미하고, 실질 매개변수는 서브프로그램을 호출할 때 할당하는 매개변수입니다.
예를 들면 FUNCTION F_TEST(P_V_IN IN VARCHAR2) RETURN VARCHAR2 ~ 에서 할당된 P_V_IN 이 형식 매개변수입니다. F_TEST('IN') 에서 매개변수 'IN' 이 실제 매개변수입니다.
IN 매개변수 모드를 살펴봅시다. IN MODE의 특징 중 하나는 형식 매개변수의 식은 피할당자로 사용될 수 없습니다. 상기의 예제 자료에서 보면 P_V_IN := 'IN2'; 를 할당하는 과정에서 오류가 발생하였습니다.
이를 오라클 원서에서는 다음과 같이 표현하고 있습니다. Formal parameter cannot be assigned a value.
Actual, Formal Parameter?
이제 포멀 파라미터와 액츄얼 파라미터의 구분은 정확하게 하실 수 있을 것입니다.
오라클 IN, OUT, IN OUT 파라미터 모드
서브프로그램의 흐름대로 설명을 진행하겠습니다. 예시 자료는 익명블록에서 펑션을 호출하며 변수를 할당해 보는 테스트입니다. => SUBPROGRAM으로 함수를 호출하였으며 함수 내부에서 변수 설정 이전, 이후 값을 보여드립니다.
우선 각각의 변수는 다음과 같이 초기 값이 할당되어 있습니다.
L_V_IN VARCHAR2 (100) := 'IN1';
L_V_OUT1 VARCHAR2 (100) := 'OUT1_1';
L_V_OUT2 VARCHAR2 (100) := 'OUT2_1';
L_V_INOUT VARCHAR2 (100) := 'INOUT1';
[1] 초기 값 결과
L_V_IN = IN1
L_V_OUT1 = OUT1_1
L_V_OUT2 = OUT2_1
L_V_INOUT = INOUT1
그리하여 초기 아웃풋은 변수 그대로 각각 'IN1', 'OUT1_1', 'OUT2_1', 'INOUT1' 값을 출력합니다. 즉, 초기 값은 DECLARE 부분 에서 선언한 값을 그대로 출력합니다.
이제 2차 펑션 내부로 이동합니다.
[2] 함수 내부 변수 설정 이전 결과
P_V_IN = IN1
P_V_OUT1 =
P_V_OUT2 =
P_V_INOUT = INOUT1
실제 파라미터는 위와 같았으나 펑션 내부의 형식 파라미터는 IN 모드는 그대로 'IN1'이 유지되었으나 OUT 모드는 NULL인 것을 볼 수 있습니다. 반면 IN OUT 모드 또한 IN 과 같이 'INOUT1' 이 피할당 되어 있습니다. 즉, OUT 모드의 포멀 파라미터는 호출하는 실제 매개변수의 값을 무시하고 NULL 인 것을 확인할 수 있습니다.
[3] 함수 내부 변수 설정 이후 결과
P_V_IN = IN1
P_V_OUT1 = OUT1_2
P_V_OUT2 =
P_V_INOUT = INOUT2
3차 시도는 펑션 내부에서 각 매개변수에 값을 할당하도록 시도해보겠습니다. 함수 내에서 OUT1 변수는 OUT1_2 라는 값을 할당하였고 OUT2 변수는 어떠한 값도 할당하지 않았습니다. IN 모드는 피할당이 불가합니다. 할당을 시도하려는 경우 본문 상단에서 본 것처럼 오류가 발생하며 내용은 다음과 같습니다. OUT 모드와 IN OUT 모드는 모두 할당이 가능합니다. 그리하여 P_V_OUT1 := 'OUT1_2'; P_V_INOUT := 'INOUT2'; 값이 그대로 출력이 됩니다. IN OUT 모드는 IN 과 같이 포멀 파라미터 할당도 되고 OUT 처럼 피할당이 가능하다는 것을 확인할 수 있습니다.
ORA-06550: PLS-00363: 식은 피할당자로 사용될 수 없습니다.
ORA-06550: PLS-00363: cannot be used as an assignment target
[4] 함수 호출 이후 CALLER
L_V_IN = IN1
L_V_OUT1 = OUT1_2
L_V_OUT2 =
L_V_INOUT = INOUT2
4차 시도입니다. 내부 함수를 벗어나 호출자(CALLER) 부분으로 돌아왔습니다. OUT1은 함수 내에서 설정했던 'OUT1_2' 값을 보여드리며 OUT2는 할당을 하지 않아서 NULL 값을 보여드립니다. (NOCOPY 기준) 예기치 않은 결과를 가져올 수 있으므로 OUT MODE를 사용하실 때는 변수 설정을 꼭 해주셔야 합니다. (OUT mode: Formal parameter must be assigned a value)
출력을 해보면 IN 모드는 변함이 없으며 OUT 모드와 IN OUT 모드는 펑션에서 할당한 값을 유지합니다. OUT2 같은 경우에는 펑션에서 피할당이 없었기에 NULL 값입니다.
[5] CALLER 변수 설정 이후
L_V_IN = IN3
L_V_OUT1 = OUT1_3
L_V_OUT2 = OUT2_3
L_V_INOUT = INOUT3
5차 시도입니다. 호출자에서 변수 값을 다음과 같이 할당해 봅니다. L_V_IN := 'IN3'; L_V_OUT1 := 'OUT1_3'; L_V_OUT2 := 'OUT2_3'; L_V_INOUT := 'INOUT3';
당연히 해당 피할당 값을 그대로 출력합니다.
IN, OUT, IN OUT 파라미터 모드 정리
IN 모드 => 서브프로그램에서 변수 피할당이 불가합니다. 실제 파라미터가 포멀 파라미터 값으로 작동하며 상수과 같이 작동한다고 볼 수 있습니다 (act like constant)
OUT 모드 => 서브프로그램에서 매개하는 최초 변수 할당값이 포멀 파라미터로 할당되지 않습니다. OUT 매개변수는 각 데이터타입의 최초 기본값으로 설정됩니다. 예시 자료는 VARCHAR2이기에 NULL 이 기본값입니다. 그리고 서브프로그램에서 할당한 값이 호출자로 돌아갑니다.
IN OUT 모드 => IN 모드와 같이 초기 변수 피할당이 가능합니다. (act like Initialized variable) 그리고 OUT 모드와 같이 서브프로그램에서 피할당 된 값이 호출자에게 반환됩다.
다음의 오라클 다큐먼트를 참고하시면 더욱 도움이 될 것입니다. https://docs.oracle.com/cd/B28359_01/appdev.111/b28370/subprograms.htm#LNPLS008
SET SERVEROUTPUT ON
DECLARE
L_V_IN VARCHAR2 (100) := 'IN1';
L_V_OUT1 VARCHAR2 (100) := 'OUT1_1';
L_V_OUT2 VARCHAR2 (100) := 'OUT2_1';
L_V_INOUT VARCHAR2 (100) := 'INOUT1';
L_V_TMP VARCHAR2 (1);
FUNCTION F_TEST (P_V_IN IN VARCHAR2,
P_V_OUT1 OUT VARCHAR2,
P_V_OUT2 OUT VARCHAR2,
P_V_INOUT IN OUT VARCHAR2)
RETURN VARCHAR2
IS
L_V_RTN VARCHAR2 (100);
BEGIN
DBMS_OUTPUT.PUT_LINE (
'[2] 함수 내부 변수 설정 이전(OUT MODE는 해당 변수의 기본값으로 초기화 합니다)');
DBMS_OUTPUT.PUT_LINE ('P_V_IN = ' || P_V_IN);
DBMS_OUTPUT.PUT_LINE ('P_V_OUT1 = ' || P_V_OUT1);
DBMS_OUTPUT.PUT_LINE ('P_V_OUT2 = ' || P_V_OUT2);
DBMS_OUTPUT.PUT_LINE ('P_V_INOUT = ' || P_V_INOUT);
-- P_V_IN := 'IN2';
P_V_OUT1 := 'OUT1_2';
P_V_INOUT := 'INOUT2';
DBMS_OUTPUT.PUT_LINE ('[3] 함수 내부 변수 설정 이후');
DBMS_OUTPUT.PUT_LINE ('P_V_IN = ' || P_V_IN);
DBMS_OUTPUT.PUT_LINE ('P_V_OUT1 = ' || P_V_OUT1);
DBMS_OUTPUT.PUT_LINE ('P_V_OUT2 = ' || P_V_OUT2);
DBMS_OUTPUT.PUT_LINE ('P_V_INOUT = ' || P_V_INOUT);
RETURN L_V_RTN;
END;
BEGIN
DBMS_OUTPUT.PUT_LINE (
'[1] 초기값 (OUT1은 호출함수에서 변수할당을 할 예정이며, OUT2는 하지 않을 것입니다.');
DBMS_OUTPUT.PUT_LINE ('L_V_IN = ' || L_V_IN);
DBMS_OUTPUT.PUT_LINE ('L_V_OUT1 = ' || L_V_OUT1);
DBMS_OUTPUT.PUT_LINE ('L_V_OUT2 = ' || L_V_OUT2);
DBMS_OUTPUT.PUT_LINE ('L_V_INOUT = ' || L_V_INOUT);
L_V_TMP :=
F_TEST (L_V_IN,
L_V_OUT1,
L_V_OUT2,
L_V_INOUT);
DBMS_OUTPUT.PUT_LINE ('[4] 함수 호출 이후 CALLER');
DBMS_OUTPUT.PUT_LINE ('L_V_IN = ' || L_V_IN);
DBMS_OUTPUT.PUT_LINE ('L_V_OUT1 = ' || L_V_OUT1);
DBMS_OUTPUT.PUT_LINE ('L_V_OUT2 = ' || L_V_OUT2);
DBMS_OUTPUT.PUT_LINE ('L_V_INOUT = ' || L_V_INOUT);
L_V_IN := 'IN3';
L_V_OUT1 := 'OUT1_3';
L_V_OUT2 := 'OUT2_3';
L_V_INOUT := 'INOUT3';
DBMS_OUTPUT.PUT_LINE ('[5] CALLER 변수 설정(3으로) 이후');
DBMS_OUTPUT.PUT_LINE ('L_V_IN = ' || L_V_IN);
DBMS_OUTPUT.PUT_LINE ('L_V_OUT1 = ' || L_V_OUT1);
DBMS_OUTPUT.PUT_LINE ('L_V_OUT2 = ' || L_V_OUT2);
DBMS_OUTPUT.PUT_LINE ('L_V_INOUT = ' || L_V_INOUT);
END;