ABAP

ABAP 동기 처리 아직도 써요? cl_background_jobs로 비동기 전환하기 #shorts #SAP #ABAP

▶ YouTube에서 보기

개요 및 이 글이 답하는 질문

SAP ABAP에서 대량 데이터 처리, 외부 시스템 호출, 장시간 실행 리포트를 동기(Synchronous)로 수행하면 사용자는 SAP GUI 앞에서 무한정 기다려야 하고, 다이얼로그 작업의 최대 실행 시간(rdisp/max_wprun_time, 일반적으로 600초) 제한에 걸려 TIME_OUT 덤프가 발생할 수 있습니다. 이 글은 cl_bgmc_* 계열과 함께 자주 쓰이는 백그라운드 잡(Background Job) 등록·모니터링 패턴을 표준 클래스/함수 모듈 관점에서 정리합니다.

  • 동기 호출의 한계와 비동기 처리 필요성 이해
  • JOB_OPEN / JOB_SUBMIT / JOB_CLOSE 함수 흐름 숙지
  • 최신 ABAP의 cl_bgmc_process_factory(bgRFC/bgMC) 비교
  • BP_JOB_STATUS_GET 등 상태 조회 API 활용
  • 에러 처리, 로깅, 잡 변형(Variant) 처리

이 글을 보기 전에

이 글의 코드를 실습하려면 다음 사전 지식이 권장됩니다.

  • ABAP 객체지향 기본 문법(클래스, 메서드, 예외 클래스)
  • 리포트 프로그램(REPORT) 작성과 SUBMIT ... VIA JOB 구문
  • SAP GUI 트랜잭션 SM36(잡 정의), SM37(잡 모니터링)
  • 다이얼로그 vs 백그라운드 워크 프로세스 차이

환경, 버전, 준비물

아래 예제는 SAP S/4HANA 2022 / NetWeaver AS ABAP 7.55 이상 기준입니다. ABAP Cloud(Steampunk) 환경에서는 일부 함수 모듈 직접 호출이 제한되며, 그 경우 bgRFC 또는 Background Processing Framework(bgPF)를 사용하는 것이 권장됩니다.

  • SAP NetWeaver AS ABAP 7.55+ (S/4HANA 2022 권장)
  • ADT(ABAP Development Tools) for Eclipse 또는 SE80
  • 권한 객체: S_BTCH_JOB, S_BTCH_ADM(잡 등록/관리)
  • 관련 트랜잭션: SM36, SM37, SM50, SM21
  • 대상 변형(Variant) 또는 실행할 리포트 프로그램
참고: ABAP Cloud 또는 RAP(Restful ABAP Programming) 모델에서는 cl_bgmc_process_factory(bgMC) 사용이 일반적으로 권장됩니다. 본 예제는 On-Premise 표준 ABAP을 기준으로 합니다.

핵심 개념

동기 처리(Synchronous)는 호출자가 함수가 끝날 때까지 블로킹됩니다. 사용자가 SAP GUI에서 버튼을 누르면 응답이 올 때까지 화면이 멈춰 있고, 다이얼로그 워크 프로세스가 점유됩니다. 반면 비동기(Asynchronous) 처리는 작업을 별도 워크 프로세스에 위임하고 호출자는 즉시 다음 코드로 진행합니다.

비유: 카페 주문

동기 호출은 "주문 후 카운터 앞에 서서 음료가 나올 때까지 기다리는" 방식입니다. 비동기 잡은 "진동벨을 받고 자리로 돌아가 다른 일을 하다가 벨이 울리면 받으러 가는" 방식입니다. JOB_OPEN으로 진동벨을 받고, JOB_SUBMIT으로 주문 접수, JOB_CLOSE로 결제 완료, BP_JOB_STATUS_GET으로 진동벨이 울렸는지 확인합니다.

ABAP 백그라운드 잡 흐름

  1. JOB_OPEN: 잡 컨테이너 생성 → JOBCOUNT 반환
  2. SUBMIT ... VIA JOB: 실행할 리포트를 잡에 등록
  3. JOB_CLOSE: 시작 조건(즉시/예약) 설정 후 스케줄러에 제출
  4. BP_JOB_STATUS_GET 또는 테이블 TBTCO 조회로 상태 확인

잡 상태 코드

  • P — Planned(예약됨)
  • S — Released(릴리스, 실행 대기)
  • R — Running(실행 중)
  • F — Finished(정상 종료)
  • A — Cancelled(취소/실패)

실전 코드 3단계

1단계: 기본 예제 — 동기 호출의 문제와 잡 등록 첫걸음

먼저 문제 상황을 봅니다. 다음 코드는 사용자가 화면에서 버튼을 누르면 1만 건의 자재를 처리하는 동기 호출입니다.

REPORT zsync_problem.

START-OF-SELECTION.
  DATA(lt_matnr) = VALUE matnr_t( ).

  " 1만 건 처리 - 다이얼로그 워크 프로세스 점유, 600초 초과 시 TIME_OUT 덤프
  SELECT matnr FROM mara INTO TABLE @lt_matnr UP TO 10000 ROWS.

  LOOP AT lt_matnr INTO DATA(lv_matnr).
    " 무거운 처리 (외부 시스템 RFC, 가격 계산 등)
    PERFORM heavy_processing USING lv_matnr.
  ENDLOOP.

  WRITE: / '처리 완료. 사용자는', sy-uzeit, '까지 기다렸습니다.'.

이 문제를 해결하기 위해 백그라운드 잡으로 전환합니다.

REPORT zbgjob_basic.

DATA: lv_jobcount LIKE tbtcjob-jobcount,
      lv_jobname  LIKE tbtcjob-jobname.

START-OF-SELECTION.
  lv_jobname = 'ZJOB_MATERIAL_PROCESS'.

  " 1) 잡 컨테이너 오픈
  CALL FUNCTION 'JOB_OPEN'
    EXPORTING
      jobname  = lv_jobname
    IMPORTING
      jobcount = lv_jobcount
    EXCEPTIONS
      cant_create_job  = 1
      invalid_job_data = 2
      jobname_missing  = 3
      OTHERS           = 4.

  IF sy-subrc <> 0.
    MESSAGE 'JOB_OPEN 실패' TYPE 'E'.
  ENDIF.

  " 2) 실행할 리포트 등록 (Variant 이용 권장)
  SUBMIT zreport_material_process
    WITH VARIANT 'STANDARD'
    VIA JOB lv_jobname NUMBER lv_jobcount
    AND RETURN.

  " 3) 잡 클로즈 - 즉시 시작
  CALL FUNCTION 'JOB_CLOSE'
    EXPORTING
      jobcount             = lv_jobcount
      jobname              = lv_jobname
      strtimmed            = abap_true
    EXCEPTIONS
      cant_start_immediate = 1
      invalid_startdate    = 2
      jobname_missing      = 3
      job_close_failed     = 4
      OTHERS               = 8.

  IF sy-subrc = 0.
    WRITE: / '잡 등록 완료. JOBCOUNT =', lv_jobcount.
  ENDIF.

2단계: 실무 시나리오 — 잡 상태 확인과 에러 로깅

잡을 등록한 뒤 결과를 알아야 한다면 폴링(polling)으로 상태를 조회합니다. 표준 함수 BP_JOB_STATUS_GET(또는 SHOW_JOBSTATE)을 사용합니다.

CLASS zcl_bg_job_runner DEFINITION
  PUBLIC FINAL CREATE PUBLIC.

  PUBLIC SECTION.
    METHODS:
      submit_and_track
        IMPORTING iv_report   TYPE syrepid
                  iv_variant  TYPE raldb_vari DEFAULT 'STANDARD'
        EXPORTING ev_jobname  TYPE tbtcjob-jobname
                  ev_jobcount TYPE tbtcjob-jobcount
        RAISING   cx_bgmc_failed,

      get_status
        IMPORTING iv_jobname TYPE tbtcjob-jobname
                  iv_jobcount TYPE tbtcjob-jobcount
        RETURNING VALUE(rv_status) TYPE btcstatus.
ENDCLASS.

CLASS zcl_bg_job_runner IMPLEMENTATION.
  METHOD submit_and_track.
    " 유니크한 잡 이름 생성
    ev_jobname = |Z_{ iv_report }_{ sy-datum }_{ sy-uzeit }|.

    CALL FUNCTION 'JOB_OPEN'
      EXPORTING
        jobname  = ev_jobname
      IMPORTING
        jobcount = ev_jobcount
      EXCEPTIONS
        OTHERS   = 1.
    IF sy-subrc <> 0.
      RAISE EXCEPTION TYPE cx_bgmc_failed.
    ENDIF.

    SUBMIT (iv_report)
      WITH VARIANT iv_variant
      VIA JOB ev_jobname NUMBER ev_jobcount
      AND RETURN.

    CALL FUNCTION 'JOB_CLOSE'
      EXPORTING
        jobcount  = ev_jobcount
        jobname   = ev_jobname
        strtimmed = abap_true
      EXCEPTIONS
        OTHERS    = 1.

    IF sy-subrc <> 0.
      " 애플리케이션 로그(BAL_LOG_*) 기록 권장
      MESSAGE i001(zbg) WITH ev_jobname INTO DATA(lv_msg).
      RAISE EXCEPTION TYPE cx_bgmc_failed.
    ENDIF.
  ENDMETHOD.

  METHOD get_status.
    CALL FUNCTION 'BP_JOB_STATUS_GET'
      EXPORTING
        jobcount       = iv_jobcount
        jobname        = iv_jobname
      IMPORTING
        status         = rv_status
      EXCEPTIONS
        job_doesnt_exist = 1
        OTHERS           = 2.

    IF sy-subrc <> 0.
      rv_status = 'A'. " Cancelled로 간주
    ENDIF.
  ENDMETHOD.
ENDCLASS.

호출 측에서 폴링하는 예입니다. 무한 루프 방지를 위해 타임아웃과 슬립 인터벌을 둡니다.

DATA(lo_runner) = NEW zcl_bg_job_runner( ).
DATA: lv_jobname  TYPE tbtcjob-jobname,
      lv_jobcount TYPE tbtcjob-jobcount,
      lv_status   TYPE btcstatus.

TRY.
    lo_runner->submit_and_track(
      EXPORTING iv_report  = 'ZREPORT_MATERIAL_PROCESS'
                iv_variant = 'STANDARD'
      IMPORTING ev_jobname  = lv_jobname
                ev_jobcount = lv_jobcount ).

    DO 60 TIMES. " 최대 5분 (5초 x 60)
      WAIT UP TO 5 SECONDS.
      lv_status = lo_runner->get_status( iv_jobname  = lv_jobname
                                          iv_jobcount = lv_jobcount ).
      IF lv_status = 'F' OR lv_status = 'A'.
        EXIT.
      ENDIF.
    ENDDO.

    WRITE: / '최종 상태:', lv_status.
  CATCH cx_bgmc_failed.
    MESSAGE '잡 제출 실패' TYPE 'E'.
ENDTRY.

3단계: 프로덕션 — 성능, 보안, 모니터링

대량 데이터를 분산 처리하려면 단일 잡에 모두 넣지 말고 잡 분할(Job Splitting)을 적용합니다. 또한 권한, 잡 이름 충돌, 로그 정리도 고려해야 합니다.

CLASS zcl_bg_parallel DEFINITION
  PUBLIC FINAL CREATE PUBLIC.

  PUBLIC SECTION.
    CONSTANTS c_chunk_size TYPE i VALUE 1000.

    METHODS run
      IMPORTING it_keys TYPE matnr_t
      RAISING   cx_bgmc_failed.

  PRIVATE SECTION.
    METHODS submit_chunk
      IMPORTING it_chunk    TYPE matnr_t
                iv_chunk_id TYPE i
      RAISING   cx_bgmc_failed.
ENDCLASS.

CLASS zcl_bg_parallel IMPLEMENTATION.
  METHOD run.
    " 권한 체크 - 잡 등록 권한 확인
    AUTHORITY-CHECK OBJECT 'S_BTCH_JOB'
      ID 'JOBACTION' FIELD 'RELE'
      ID 'JOBGROUP'  FIELD '*'.
    IF sy-subrc <> 0.
      RAISE EXCEPTION TYPE cx_bgmc_failed.
    ENDIF.

    DATA: lv_idx   TYPE i VALUE 0,
          lt_chunk TYPE matnr_t.

    LOOP AT it_keys INTO DATA(lv_key).
      APPEND lv_key TO lt_chunk.
      IF lines( lt_chunk ) >= c_chunk_size.
        ADD 1 TO lv_idx.
        submit_chunk( it_chunk = lt_chunk iv_chunk_id = lv_idx ).
        CLEAR lt_chunk.
      ENDIF.
    ENDLOOP.

    IF lt_chunk IS NOT INITIAL.
      ADD 1 TO lv_idx.
      submit_chunk( it_chunk = lt_chunk iv_chunk_id = lv_idx ).
    ENDIF.
  ENDMETHOD.

  METHOD submit_chunk.
    DATA: lv_jobname  TYPE tbtcjob-jobname,
          lv_jobcount TYPE tbtcjob-jobcount.

    lv_jobname = |Z_MAT_CHUNK_{ iv_chunk_id }_{ sy-uzeit }|.

    CALL FUNCTION 'JOB_OPEN'
      EXPORTING jobname  = lv_jobname
      IMPORTING jobcount = lv_jobcount
      EXCEPTIONS OTHERS  = 1.
    IF sy-subrc <> 0.
      RAISE EXCEPTION TYPE cx_bgmc_failed.
    ENDIF.

    " 메모리 export로 청크 전달 (또는 임시 DB 테이블)
    EXPORT keys = it_chunk TO MEMORY ID lv_jobname.

    SUBMIT zreport_chunk_worker
      WITH p_memid = lv_jobname
      VIA JOB lv_jobname NUMBER lv_jobcount
      AND RETURN.

    CALL FUNCTION 'JOB_CLOSE'
      EXPORTING
        jobcount         = lv_jobcount
        jobname          = lv_jobname
        strtimmed        = abap_true
        targetserver     = ''           " 부하분산: 빈 값 = 자동
      EXCEPTIONS OTHERS  = 1.

    IF sy-subrc <> 0.
      RAISE EXCEPTION TYPE cx_bgmc_failed.
    ENDIF.
  ENDMETHOD.
ENDCLASS.

최신 ABAP(특히 Cloud)에서는 bgMC(Background Processing Framework)로 마이그레이션이 권장됩니다.

" bgMC 예시 (S/4HANA 2021+)
TRY.
    DATA(lo_factory) = cl_bgmc_process_factory=>get_default( ).
    DATA(lo_process) = lo_factory->create( ).

    lo_process->set_name( 'MATERIAL_BATCH' ).
    lo_process->application_set_pft_only(
      iv_class_name = 'ZCL_MAT_BGMC_HANDLER'
      iv_parameter  = lt_chunk_xml ).

    lo_process->save_for_execution( ).
    COMMIT WORK.
  CATCH cx_bgmc INTO DATA(lx_bgmc).
    " 표준 예외 처리
ENDTRY.

흔한 실수와 트러블슈팅

FAQ 1: JOB_CLOSE 후 잡이 시작되지 않습니다

가장 흔한 원인은 백그라운드 워크 프로세스(BGD WP) 부족입니다. SM50에서 BGD 타입 워크 프로세스가 있는지, SM37에서 잡이 Released 상태로 멈춰 있는지 확인하세요. 또한 JOB_CLOSEstrtimmed = abap_true를 지정해야 즉시 실행됩니다. 예약 실행이면 sdlstrtdt(시작일), sdlstrttm(시작시간)을 함께 넘겨야 합니다.

FAQ 2: SUBMIT ... VIA JOB이 동작하지 않고 즉시 실행됩니다

VIA JOB 절을 빠뜨리거나 AND RETURN 없이 단순 SUBMIT만 적으면 동기로 호출됩니다. 반드시 SUBMIT (report) VIA JOB jobname NUMBER jobcount AND RETURN. 형태를 유지하세요. 또한 SUBMIT 대상 리포트가 백그라운드 실행을 지원해야 합니다(화면 입력 의존 금지).

FAQ 3: BP_JOB_STATUS_GET이 상태를 못 가져옵니다

JOBCOUNTJOBNAME이 정확히 매칭되어야 합니다. 일반적으로 잡 이름은 대소문자 구별 없이 저장되지만 잡카운트는 8자리 숫자입니다. 잡이 막 종료되어 TBTCO에서 사라졌을 가능성이 있으면 RSBTCRTE(잡 정리) 주기를 확인하세요. ABAP Cloud에서는 이 함수가 비공개이므로 cl_bgmc_process_factory를 통해 상태를 조회해야 합니다.

기타 주의사항

  • 잡 이름 충돌: 동일 잡 이름이 여러 개 존재해도 시스템은 JOBCOUNT로 구분합니다. 운영 모니터링을 위해서는 타임스탬프나 GUID를 잡 이름에 포함시키는 것이 권장됩니다.
  • 메모리 전달: EXPORT TO MEMORY ID로 데이터를 넘기면 같은 애플리케이션 서버에서만 읽을 수 있습니다. 다른 서버에서 잡이 실행될 수 있다면 DB 테이블이나 SHM(Shared Memory) 객체를 사용하세요.
  • 로그 정리: SM37 잡 로그는 누적되면 시스템 성능에 영향을 줍니다. 표준 잡 SAP_REORG_JOBS로 주기적 정리를 권장합니다.

다음 단계와 관련 주제

백그라운드 잡 기초를 익혔다면 다음 주제로 확장하는 것이 일반적으로 권장됩니다.

  • Parallel Cursor / Parallel Processing: SPTA_PARA_PROCESS_START_2로 RFC 기반 병렬 처리
  • bgRFC(qRFC 후속): 트랜잭션 SBGRFCCONF로 비동기 RFC 큐 설정
  • bgMC(Background Processing Framework): ABAP Cloud 친화 모델, RAP과 통합
  • Job Chain: SM36에서 후속 잡(Subsequent Job) 연결로 워크플로 구성
  • Application Log(SLG1): BAL_LOG_CREATE로 구조화된 로깅

참고 자료

댓글 0

아직 댓글이 없습니다.