SAP/Abap

[Modularization] Subroutine

i009727 2022. 1. 17. 11:01

[Subroutine]

특정 스크립트의 모듈화, 구조화를 통한 재사용을 목적으로 구성된 구문으로 프로그램 내/외부에서 모두 호출이 가능하다.
* Subroutine 호출부
PERFORM write_data.

* Subroutine 정의부
FORM write_data.
  WRITE :/ 'Subroutine TEST'.
ENDFORM.

 

 

[Subroutine: Parameter(1)]

Parameter: Subroutine의 정의부와 호출부 사이에서 주고받는 데이터로 모든 data type, field symbol, internal table 등이 사용 가능하며, subroutine 내에서는 일반적인 local 변수처럼 사용된다.
   - Actual parameter: Subroutine 호출 시 사용되는 parameter.
   - Formal parameter: Subroutine에서 사용되는 parameter.

0) Parameter의 전달방법

call-by-value actual parameter와 formal parameter가 서로 다른 메모리 영역에 존재  => value 복사 개념
call-by-reference actual parameter와 formal parameter가 서로 같은 메모리 영역에 존재  => object 참조 개념
call-by-value_and_result actual parameter와 formal parameter가 서로 다른 메모리 영역에 존재, 작업 성공시에만 값 반환

 

1) Call-by-Value

PERFORM subr [USING | CHANGING] <ap>
FORM subr USING VALUE(<fp>)
REPORT ZA04_02.

DATA : gv_val TYPE c LENGTH 20 VALUE 'Call by Value test'.

PERFORM call_byvalue USING gv_val.
WRITE :/ 'gv_val: ', gv_val.

FORM call_byvalue USING VALUE(p_val).
  WRITE :/ 'p_val: ', p_val.
  p_val = 'Data changed'.
  WRITE :/ 'p_val: ', p_val.
ENDFORM.

 

2) Call-by-Reference

PERFROM subr [USING | CHANGING] <ap>
FORM subr [USING | CHANGING] <fp>
REPORT ZA04_03.

DATA : gv_val TYPE c LENGTH 30 VALUE 'Call by Reference test'.

PERFORM call_byref CHANGING gv_val.
WRITE :/ 'gv_val: ', gv_val.

FORM call_byref CHANGING p_val.
  WRITE :/ 'p_val: ', p_val.
  p_val = 'Data changed'.
  WRITE :/ 'p_val: ', p_val.
ENDFORM.

 

3) Call-by-Value_and_Result

Call-by-reference와 비슷한 의미를 갖는다.
해당 subroutine이 정상종료될 경우 actual parameter값이 formal parameter값으로 변경된다.
PERFORM subr [USING | CHANGING] <ap>
FORM subr CHANGING VALUE(<fp>)
REPORT ZA04_04.

DATA : gv_val1    TYPE i VALUE 2,
       gv_val2    TYPE i VALUE 3,
       gv_sum     TYPE i.

PERFORM sum_data USING     gv_val1        " call_by_value
                           gv_val2        " call_by_value
                 CHANGING  gv_sum.        " call_by_reference

WRITE :/ 'Result: ', gv_sum.


FORM sum_data USING       value(p_val1)   " call_by_value
                          value(p_val2)   " call_by_value
              CHANGING    value(p_sum).   " call_by_value_and_result
  p_sum = p_val1 + p_val2.
ENDFORM.

 

4) 정리

  • PERFORM(호출부) 구문에서는 USING, CHANGING이 모두 사용 가능하지만, 의미를 다르게 하기위해 나눠 사용한다.
    • USING: 값을 사용한다는 의미               => 메모리가 별도로 존재
    • CHANGING: 값을 변경한다는 의미       => 동일한 메모리를 참조
  • FORM(정의부) 구분에서는 USING VALUE, CHANGING VALUE에 따라 동작이 달라진다.
    • USING VALUE (<fp>): formal parameter의 메모리가 할당되고, 값이 복사되어 전달받는다. => Call-by-Value
    • CHANGING VALUE (<fp>): actual parameter의 주소값을 전달받는다. => Call-by-Reference | Call-by-ValueAndResult

 

[Subroutine: Parameter(2)]

1) Parameter 타입 정의

FORM 구문 내의 formal parameter는 TYPE, LIKE구문을 사용해 모든 abap data type을 사용할 수 있다.
  • Generic type: 특정 type을 명시적으로 지정하지 않으면 actual parameter의 기술적 속성을 상속받게 된다.
    • 이때는 actual param, formal param 간의 type conversion이 가능해야한다.
# 1. Generic type 사용
FORM subr CHANGING p_val.
# 2. Abap 기본 data type 사용
FORM subr CHANGING p_val TYPE d.
# 3. Actual parameter와 같은 type 사용.
FORM subr CHANGING p_val LIKE gv_val.
  • Generic type을 사용할때, actual param, formal param간 type conversion이 불가능하다면 구문에러가 발생한다.
REPORT ZA04_05.

DATA : gv_val TYPE c.                      " actual param: Type c

PERFORM call_byref CHANGING gv_val.

WRITE :/ gv_val.


FORM call_byref CHANGING p_val TYPE i.     " formal param: Type i
  p_val = 'Value is changed'.
ENDFORM.

 

 

2) Parameter와 구조체

FORM subr CHANGING <param> [TYPE | LIKE | STRUCTURE] <str>
REPORT ZA04_06.

DATA : BEGIN OF gs_str,
  col1 VALUE 'A',
  col2 VALUE 'B',
END OF gs_str.

PERFORM write_data CHANGING gs_str.


FORM write_data USING ps_str STRUCTURE gs_str.
  WRITE : ps_str-col1, ps_str-col2.
ENDFORM.
  • Parameter type으로 구조체를 사용할때 명시적으로 type을 지정하지 않으면 구조체 내 컬럼을 찾을수 없으므로 에러가 발생한다.
    • 이후 배울 필드 심볼을 통해 명시적 type 지정 없이 사용이 가능하다.

 

 

3) Paramter와 인터널 테이블

  • USING 및 CHANGING 구문: TYPE 및 LIKE를 사용한다.
FORM subr CHANGING <param> [TYPE | LIKE] <itab>
REPORT ZA04_07.

TYPES : BEGIN OF t_str,
  col1 TYPE c,
  col2 TYPE i,
END OF t_str.

TYPES : t_itab TYPE TABLE OF t_str.

DATA : gs_str     TYPE t_str,
       gt_itab    TYPE t_itab.

gs_str-col1 = 'A'.
gs_str-col2 = 1.
APPEND gs_str TO gt_itab.

gs_str-col1 = 'B'.
gs_str-col2 = 2.
APPEND gs_str TO gt_itab.

PERFORM test_itab USING gt_itab.


FORM test_itab USING pt_itab TYPE t_itab.
  READ TABLE pt_itab WITH KEY col1 = 'A' INTO gs_str.
  IF sy-subrc EQ 0.
    WRITE :/ gs_str-col1, gs_str-col2.
  ENDIF.
ENDFORM.
  • TABLES 구문: 이전 프로그램에서 USING, CHANGING 대신 사용되던 구문이지만 호환성 문제로 현재도 사용되고 있다.
REPORT ZA04_08.

TYPES : BEGIN OF t_str,
  col1 TYPE c,
  col2 TYPE i,
END OF t_str.

TYPES : t_itab TYPE TABLE OF t_str.

DATA : gt_itab    TYPE t_itab.

PERFORM test_itab  TABLES gt_itab.
PERFORM write_data TABLES gt_itab.


FORM test_itab TABLES pt_itab TYPE t_itab.
  DATA ls_str TYPE t_str.
  ls_str-col1 = 'A'.
  ls_str-col2 = 1.
  APPEND ls_str TO pt_itab.

  ls_str-col1 = 'B'.
  ls_str-col2 = 2.
  APPEND ls_str TO pt_itab.
ENDFORM.

FORM write_data TABLES pt_itab LIKE gt_itab.
  DATA ls_str TYPE t_str.
  LOOP AT pt_itab INTO ls_str.
    WRITE :/ ls_str-col1, ls_str-col2.
  ENDLOOP.
ENDFORM.

 

 

 

[Subroutine 호출]

Subroutine은 internal / external 두가지 방식으로 호출이 가능하다.
# 1. Internal call.
PERFORM subr.
# 2. External call
PERFORM subr (prog) [IF FOUND]

 

1) Internal subroutine call

REPORT ZA04_09.

DATA : gv_val1(10) TYPE c VALUE 'Enjoy',
       gv_val2(10) TYPE c VALUE 'Abap',
       gv_val3(20) TYPE c.

PERFORM concate_string USING     gv_val1 gv_val2
                       CHANGING  gv_val3.


FORM concate_string USING     VALUE(p_val1) VALUE(p_val2)
                    CHANGING  VALUE(p_val3).
  CONCATENATE p_val1 p_val2 INTO p_val3 SEPARATED BY space.
  PERFORM write_data USING p_val3.
ENDFORM.

FORM write_data USING VALUE(p_val).
  WRITE :/ p_val.
ENDFORM.

 

2) External subroutine call

  • 명시된 프로그램에 subroutine이 존재하지 않는다면 덤프에러가 발생한다.

  • [IF FOUND] 키워드 추가시 해당 프로그램에 해당 subroutine이 존재하는 경우에만 실행한다.
    • 해당 프로그램에 해당 subroutine이 존재하지 않으면 해당 subroutine은 호출되지 않는다.
REPORT ZA04_10.

DATA : gv_val1(10) TYPE c VALUE 'External',
       gv_val2(10) TYPE c VALUE 'Call',
       gv_val3(20) TYPE c.

PERFORM concate_string(ZA04_09) IF FOUND USING gv_val1 gv_val2
                                          CHANGING gv_val3.

 

3) Subroutine 동적 호출

  • Subroutine name 동적 설정
    • 동적 호출시에는 프로그램명과 서브루틴명을 반드시 대문자로 지정해야 한다.
PERFORM (fsubr) [IN PROGRAM (fprog)] ...
REPORT ZA04_11.

DATA : gv_first(10)     TYPE c VALUE 'Dynamic',
       gv_second(10)    TYPE c VALUE 'Call',
       gv_result(20)    TYPE c.

DATA : gv_pname(10)     TYPE c VALUE 'za04_09',
       gv_sname(20)     TYPE c VALUE 'concate_string'.

TRANSLATE gv_pname TO UPPER CASE.
TRANSLATE gv_sname TO UPPER CASE.

PERFORM (gv_sname) IN PROGRAM (gv_pname) IF FOUND
                          USING gv_first gv_second
                          CHANGING gv_result.​

 

  • LIST를 이용한 Subroutine 호출
    • 시스템의 index 순서에 따라 subroutine을 호출한다.
    • 오직 Internal subroutine call만 가능하며 parameter 사용이 불가능하다.
PERFORM idx OF subr1 subr2 ...
REPORT ZA04_12.

DO 2 TIMES.
  PERFORM sy-index OF subr1 subr2.
ENDDO.


FORM subr1.
  WRITE :/ '1 subroutine is called'.
ENDFORM.

FORM subr2.
  WRITE :/ '2 subroutine is callled'.
ENDFORM.

 

 

#) Abap 언어의 반복문

3가지 형태의 반복문이 있으며 각 반복문에서의 반복횟수는 SY-INDEX 시스템 변수에 저장된다.

1. DO ~ ENDDO 구문

DO n TIMES.
     ...
ENDDO.

2. WHILE ~ ENDWHILE 구문

WHILE flag = 'X'.
     ...
ENDWHILE.

3. LOOP ~ ENDLOOP 구문

LOOP AT  itab TO  wa.
     ...
ENDLOOP.

 

 

 

[Subroutine 종료]

일반적으로 Subroutine은 ENDFORM문을 만나면 정상 종료되지만, EXIT 또는 CHECK구문을 통해 서브루틴 수행중에 종료할 수 있다.
  • EXIT구문: Subroutine을 바로 종료시킨다.
  • CHECK구문: 해당 구문이 거짓이면 Subroutine을 종료시킨다.
REPORT ZA04_13.

PARAMETERS : p_val TYPE char10.

PERFORM end_subr USING p_val.


FORM end_subr USING VALUE(p_val).
  CASE p_val.
    WHEN 'EXIT'.
      WRITE :/ 'Subroutine exit'.
      EXIT.
    WHEN 'CHECK'.
      WRITE :/ 'Value check'.
      CHECK p_val NE 'CHECK'.
    WHEN OTHERS.
  ENDCASE.

  WRITE :/ 'Subroutine is nomally end'.
ENDFORM.

 

#) Abap 프로그램의 분기문

1. IF ~ ENDIF 분기문

IF 조건문.
     ...
[ELSEIF 조건문].
     ...
ELSE.
     ...
ENDIF

2. CASE ~ ENDCASE 분기문

CASE 변수.
     WHEN 'val1'.
          ...
     WHEN 'val2'.
          ...
     WHEN OTHERS.
          ...
ENDCASE.

 

 

 

[Temporary Subroutine]

실행중인 프로그램의 메모리에 Subroutine pool을 생성하여 동적으로 subroutine을 정의할 수 있다.
GENERATE SUBROUTINE POOL <itab> NAME <prog>
  • <itab>: 생성할 subroutine의 소스코드를 삽입할 인터널 테이블
  • <prog>: 생성할 subroutine 프로그램
  • 사용자의 입력에 따라 인터널테이블을 동적으로 생성해야 하는 경우 유용하게 사용된다.
    • ex) 매출 조회 프로그램: 입력일에 따라 조회할 데이터(인터널테이블)가 달라진다.
REPORT ZA04_14.

DATA : gt_code(72) OCCURS 10,
       gs_code(72),
       gv_prog(8),
       gv_msg(120).

APPEND 'PROGRAM SUBPOOL.' TO gt_code.
APPEND 'FORM dynamic_subr.' TO gt_code.
APPEND 'WRITE / ''Dynamic Subroutine is called''.' TO gt_code.
APPEND 'ENDFORM.' TO gt_code.

LOOP AT gt_code INTO gs_code.
  WRITE :/ 'gs_code: ', gs_code.
ENDLOOP.

GENERATE SUBROUTINE POOL gt_code NAME gv_prog MESSAGE gv_msg.

IF sy-subrc <> 0.
  WRITE :/ 'Subroutine POOL is failed'.
ELSE.
  WRITE :/ 'Subroutine POOL name: ', gv_prog.
  SKIP 1.
  PERFORM dynamic_subr IN PROGRAM (gv_prog).
ENDIF.

 

 

 

[Perform on Commit]

Subroutine 호출 시 ON [COMMIT | ROLLBACK] 옵션을 사용하면 [COMMIT WORK | ROLLBACK WORK] 구문을 만날때 해당 FORM이 실행된다.
  • PERFORM insert_data ON COMMIT 명령어가 실행되면
    • insert_data subroutine이 실행되지 않는다.
    • 이후 COMMIT WORK 명령어가 실행되야 insert_data subroutine이 실행된다.
REPORT ZA04_15.

DATA : gs_scarr LIKE scarr,
       gv_flg   TYPE c.

SELECT SINGLE *
  FROM scarr
  INTO gs_scarr
  WHERE carrid = 'AA'.

WRITE :/ 'delete_data call'.
PERFORM delete_data USING gs_scarr.

WRITE :/ 'insert_data call ON COMMIT'.
PERFORM insert_data ON COMMIT.

IF gv_flg = 'X'.
  WRITE :/ 'COMMIT WORK'.
  COMMIT WORK.
ENDIF.


FORM delete_data USING value(ps_scarr) TYPE scarr.
  WRITE :/ 'delete_data called'.
  DELETE scarr FROM ps_scarr.
  IF sy-subrc EQ 0.
    gv_flg = 'X'.
  ENDIF.
ENDFORM.

FORM insert_data.
  WRITE :/ 'insert_data called'.
  INSERT scarr FROM gs_scarr.
ENDFORM.

 

 

[Macro]