본문 바로가기
ABAP 문법 & 기법

[SAP ABAP] TRY / CATCH / ENDTRY — 덤프 방지 + cx_root / get_source_position 으로 에러 위치 추적

by Song.sh 2026. 5. 15.

SAP 운영 중 가장 피해야 할 상황은 숏 덤프(Short Dump) — 사용자 화면에 빨갛게 ABAP RUNTIME ERROR 가 뜨면서 트랜잭션이 끊기는 사고입니다. 중복 키 INSERT, 0 나누기, 숫자 오버플로우, 0건 결과를 첫 행으로 접근, 형변환 실패 등 — 사소한 케이스에서도 덤프는 너무 쉽게 발생합니다.

 

해결은 ABAP 표준 예외 처리 구문 TRY ... CATCH ... ENDTRY. 예외가 발생할 수 있는 코드를 TRY 블록 안에 두고, 발생할 수 있는 예외 클래스를 CATCH 로 잡아 처리하면 덤프 대신 메시지·로그·재시도 같은 정상 흐름으로 빠질 수 있습니다. 추가로 get_source_position( ) 으로 에러가 정확히 어느 프로그램·인클루드·줄에서 났는지까지 끌어낼 수 있어 디버깅에도 강력합니다.

 

이 글은 TRY/CATCH/ENDTRY 의 기본 문법 + cx_root 만능 catch + get_text 메시지 추출 + get_source_position 위치 추적 + 함수형 EXCEPTIONS 와의 결합 까지 한 번에 정리한 메모입니다.


핵심 원리

덤프가 발생하는 흐름과 TRY/CATCH 로 가로채는 흐름 비교.

시점 TRY 없이 그냥 실행 TRY/CATCH 로 감쌌을 때
예외 발생 즉시 ABAP RUNTIME ERROR — 덤프 CATCH 블록으로 점프 — 정상 흐름 유지
사용자 화면 덤프 페이지 — 트랜잭션 끊김 개발자가 만든 메시지 출력 + 정상 종료
데이터 상태 부분 처리 가능성 (불일치 위험) 롤백/보상 로직 직접 수행 가능
로그·추적 ST22 덤프 로그에서 사후 확인 실시간 메시지·위치 추적 가능 (get_source_position)

핵심 트레이드오프: 모든 코드를 TRY 로 감싸는 건 과함. 외부 시스템 호출·동적 SQL·형변환·DB INSERT 처럼 실패 가능성이 명확한 지점만 감싸는 게 표준입니다. 가능성도 없는 단순 대입을 TRY 로 감싸면 성능 손해이자 코드 가독성 저해.


1단계 — TRY/CATCH/ENDTRY 기본 + cx_root

가장 단순한 형태. 예외가 어떤 종류든 다 잡고 싶으면 모든 예외의 부모 클래스 cx_root 로 catch.

SELECT * INTO TABLE @DATA(lt_sflight)
  FROM sflight
 WHERE carrid = 'AA'.

TRY.
    INSERT sflight FROM TABLE lt_sflight.   " 중복키 → 덤프 위험

  CATCH cx_root INTO DATA(oref).
    WRITE: / oref->get_text( ).             " 덤프 대신 메시지만 출력
ENDTRY.

 

핵심 포인트:

  • cx_root모든 예외의 최상위 부모 — 종류 불문 catch
  • INTO DATA(oref) 로 예외 객체 참조 받아 메서드 호출 가능
  • ENDTRY. 까지 반드시 닫기

2단계 — 예외 클래스 계층 + 자주 쓰는 종류

ABAP 예외 클래스는 트리 구조입니다. 자주 만나는 클래스 정리.

클래스 발생 케이스
cx_root 모든 예외 — 최상위 부모 (catch-all)
cx_sy_zerodivide 0 으로 나누기
cx_sy_conversion_overflow 숫자 오버플로우 (값이 변수 크기 초과)
cx_sy_conversion_no_number 문자 → 숫자 변환 실패 ('ABC' → integer)
cx_sy_itab_line_not_found READ TABLE 결과 없음 (특히 inline)
cx_sy_arithmetic_overflow 사칙연산 결과가 범위 초과
cx_sy_open_sql_db Open SQL 실행 오류 (중복키·제약위반 등)
cx_sql_exception ADBC(Native SQL) 실행 오류
cx_sy_no_handler 잡지 못한 예외(함수형 EXCEPTIONS 미처리 등) 메타-catch

여러 CATCH 를 나열해서 종류별 다르게 처리할 수도 있습니다.

TRY.
    DATA(lv_result) = lv_a / lv_b.
    INSERT zsome_table FROM ls_data.

  CATCH cx_sy_zerodivide       INTO DATA(ox_div).
    MESSAGE '0 으로 나눌 수 없습니다' TYPE 'E'.

  CATCH cx_sy_open_sql_db      INTO DATA(ox_db).
    MESSAGE |DB 오류: { ox_db->get_text( ) }| TYPE 'E'.

  CATCH cx_root                INTO DATA(ox_etc).
    MESSAGE |기타 예외: { ox_etc->get_text( ) }| TYPE 'E'.
ENDTRY.

CATCH 는 위에서부터 매칭 — 더 구체적인 클래스를 먼저 두고, cx_root 같은 catch-all 은 맨 아래.


3단계 — get_text( ) 로 메시지 추출

예외 객체에서 사람이 읽을 수 있는 메시지를 꺼내는 표준 메서드.

TRY.
    " 위험한 코드
  CATCH cx_root INTO DATA(oref).
    DATA(lv_msg) = oref->get_text( ).      " "Short text" 추출
    MESSAGE lv_msg TYPE 'I'.

    " 더 자세한 정보가 필요하면
    DATA(lv_long) = oref->get_longtext( ). " "Long text" 추출 (있을 때만)
ENDTRY.

get_text( ) 는 예외 클래스에 등록된 Text(메시지 클래스 연결) 를 사용자 언어로 반환합니다. 운영 로그·MESSAGE 출력에 그대로 쓰면 됩니다.


4단계 — get_source_position( ) 으로 위치 추적

복잡한 시스템에서 "어디서 터졌는지" 추적할 때 강력합니다.

TRY.
    DATA(lv_waers)  = 'KRW'.
    DATA(lv_amount) = 10000000000.       " 의도적 오버플로우

    CALL FUNCTION 'CURRENCY_AMOUNT_SAP_TO_DISPLAY'
      EXPORTING
        currency        = lv_waers
        amount_internal = lv_amount
      IMPORTING
        amount_display  = lv_amount
      EXCEPTIONS
        internal_error  = 1
        OTHERS          = 2.

  CATCH cx_sy_no_handler INTO DATA(hid).

    IF hid->classname = 'CX_SY_CONVERSION_OVERFLOW'.
      WRITE: hid->classname.
      DATA(lv_text) = hid->get_text( ).
      WRITE: lv_text.

      " ★ 프로그램명/인클루드명/소스 라인 추출
      hid->get_source_position(
        IMPORTING
          program_name = DATA(program_name)
          include_name = DATA(include_name)
          source_line  = DATA(source_line)
      ).

      DATA(lv_pos) = |{ program_name }/{ include_name } @ { source_line }|.
      WRITE: / '예외 위치:', lv_pos.
    ENDIF.

ENDTRY.

핵심 포인트:

  • get_source_positioncx_root 의 표준 메서드 — 모든 예외 객체에 있음
  • 운영 로그 테이블에 program_name·include_name·source_line 을 같이 저장해두면 ST22 안 봐도 어디서 터졌는지 즉시 확인
  • hid->classname 으로 실제 예외 클래스명도 확인 가능

5단계 — 함수형 EXCEPTIONS 와 cx_sy_no_handler

레거시 SAP 표준 함수(CALL FUNCTION) 는 클래스 기반이 아닌 숫자 코드 기반 예외(EXCEPTIONS) 를 씁니다.

CALL FUNCTION 'CONVERT_DATE_TO_INTERNAL'
  EXPORTING
    date_external            = '2026/02/29'
  IMPORTING
    date_internal            = lv_date
  EXCEPTIONS
    date_external_is_invalid = 1
    OTHERS                   = 2.

IF sy-subrc <> 0.
  " sy-subrc 로 분기
ENDIF.

이 패턴은 sy-subrc 로 처리하는 게 정석. 단, 함수 내부에서 처리 안 된 클래스형 예외가 올라올 수 있는데, 그게 바로 cx_sy_no_handler:

TRY.
    CALL FUNCTION 'CURRENCY_AMOUNT_SAP_TO_DISPLAY'
      EXPORTING
        currency        = 'KRW'
        amount_internal = 99999999999999.
      " EXCEPTIONS 절 없음 → 함수 내부 예외가 그대로 올라옴

  CATCH cx_sy_no_handler INTO DATA(hid).
    " 함수 안에서 발생한 예외(예: cx_sy_conversion_overflow) 의 메타 wrapper
    DATA(actual_class) = hid->classname.        " 실제 클래스명
    WRITE: / '함수 내부 예외:', actual_class.
ENDTRY.

cx_sy_no_handlerclassname 속성에 진짜 발생한 예외 클래스 이름이 들어옵니다. 함수형 코드에서 덤프 방지 + 실제 원인 추적을 같이 잡고 싶을 때 이 패턴.


흔히 빠뜨리는 함정

ENDTRY 누락

TRY 만 쓰고 ENDTRY 안 쓰면 구문 에러. SE80 의 들여쓰기로 보면 단번에 보임 — 항상 짝 맞춰 작성.

CATCH 순서 잘못 — 자식이 부모 아래에

CATCH cx_root 를 맨 위에 두고 그 아래에 CATCH cx_sy_zerodivide 를 두면 후자에 절대 도달하지 못함. 구체적인 자식 클래스 먼저, catch-all 부모는 맨 아래.

CATCH 안 한 클래스형 예외 → cx_sy_no_handler 덤프

호출한 함수/메서드가 RAISING cx_xxx 로 클래스형 예외를 던지는데 호출 측에서 안 잡으면 cx_sy_no_handler 로 변환되어 그대로 덤프. 메서드 시그니처의 RAISING 절을 확인 하고 CATCH.

cx_root 만 남발 → 진짜 원인 가려짐

모든 걸 cx_root 로만 받으면 어떤 예외인지 구분 불가. 최소한 classname 이라도 로그에 남길 것.

TRY 안에서 발생한 SQL 의 부분 INSERT 가 살아남음

INSERT FROM TABLE 중 한 행에서 오류 나도 그 전까지의 행은 DB 캐시에 남아있을 수 있음(ACCEPTING DUPLICATE KEYS 없으면). CATCH 안에서 ROLLBACK WORK 명시.

TRY/CATCH 가 성능 비용 = 0 이라는 착각

예외가 발생하지 않을 때 는 거의 무비용. 하지만 발생 시점 의 스택 언와인딩은 무겁습니다. 정상 흐름 분기에 예외를 쓰면(예: 결과 없음을 cx_sy_itab_line_not_found 로 처리) 안 좋습니다 — sy-subrc / IS NOT INITIAL 등 분기로 처리.

get_text( ) 만 보고 끝내기

운영 트러블슈팅은 classname + get_text( ) + get_source_position( ) 3종 세트로 로그를 남겨야 빠릅니다. 메시지만 남기면 어디서 터졌는지 모름.

CATCH 안에서 또 예외 발생

CATCH 블록 내부에서 다시 예외가 나면 cx_sy_no_handler 덤프. CATCH 안에서는 단순한 로그·메시지만, 추가 작업이 필요하면 별도 TRY 로 한 번 더 감싸기.


전체 코드 — 복사용 통합본

위 단계를 하나의 ABAP 프로그램으로 합친 통합본입니다. SE38 에 그대로 복사해 활성화하면 동작합니다.

*&---------------------------------------------------------------------*
*& TRY / CATCH / ENDTRY 덤프 방지 패턴 모음
*&---------------------------------------------------------------------*
REPORT zexample_try_catch NO STANDARD PAGE HEADING.

PARAMETERS: p_mode TYPE c LENGTH 1 DEFAULT '1'.
" 1 = cx_root 만능 catch
" 2 = 클래스별 분기 catch
" 3 = get_source_position 위치 추적
" 4 = cx_sy_no_handler 함수형 메타 catch

START-OF-SELECTION.

  CASE p_mode.

* ---------------------------------------------------------
* 1) 만능 catch — cx_root + get_text( )
* ---------------------------------------------------------
    WHEN '1'.
      SELECT * INTO TABLE @DATA(lt_sflight)
        FROM sflight
       WHERE carrid = 'AA'.

      TRY.
*         이미 존재하는 키 → DUPLICATE KEY 예외
          INSERT sflight FROM TABLE lt_sflight.

        CATCH cx_root INTO DATA(oref).
          WRITE: / 'cx_root:', oref->get_text( ).
*         부분 반영 방지
          ROLLBACK WORK.
      ENDTRY.

* ---------------------------------------------------------
* 2) 클래스별 분기 — 구체적인 클래스 먼저
* ---------------------------------------------------------
    WHEN '2'.
      DATA: lv_a TYPE i VALUE 10,
            lv_b TYPE i VALUE 0.

      TRY.
          DATA(lv_div) = lv_a / lv_b.
          WRITE: / lv_div.

        CATCH cx_sy_zerodivide       INTO DATA(ox_div).
          WRITE: / '★ 0으로 나누기:', ox_div->get_text( ).

        CATCH cx_sy_arithmetic_overflow INTO DATA(ox_over).
          WRITE: / '★ 산술 오버플로:', ox_over->get_text( ).

        CATCH cx_root                INTO DATA(ox_etc).
          WRITE: / '★ 기타:', ox_etc->classname, ox_etc->get_text( ).
      ENDTRY.

* ---------------------------------------------------------
* 3) get_source_position — 에러 위치 추적
* ---------------------------------------------------------
    WHEN '3'.
      TRY.
          DATA: lv_str TYPE string VALUE 'NOT_A_NUMBER',
                lv_n   TYPE i.
          lv_n = lv_str.

        CATCH cx_root INTO DATA(ox_pos).
          ox_pos->get_source_position(
            IMPORTING
              program_name = DATA(gv_prog)
              include_name = DATA(gv_incl)
              source_line  = DATA(gv_line)
          ).
          WRITE: / 'class :', ox_pos->classname,
                 / 'text  :', ox_pos->get_text( ),
                 / 'where :', |{ gv_prog } / { gv_incl } @ line { gv_line }|.
      ENDTRY.

* ---------------------------------------------------------
* 4) cx_sy_no_handler — 함수형 메타 catch
* ---------------------------------------------------------
    WHEN '4'.
      TRY.
          DATA: lv_waers  TYPE waers VALUE 'KRW',
                lv_amount TYPE p LENGTH 16 DECIMALS 2 VALUE '10000000000000000'.

          CALL FUNCTION 'CURRENCY_AMOUNT_SAP_TO_DISPLAY'
            EXPORTING
              currency        = lv_waers
              amount_internal = lv_amount
            IMPORTING
              amount_display  = lv_amount.
*         EXCEPTIONS 절 생략 → 함수 내부 예외가 클래스형으로 올라옴

        CATCH cx_sy_no_handler INTO DATA(hid).
          WRITE: / 'wrapper :', hid->classname,
                 / 'actual  :', hid->classname,    " 실제 raised 된 클래스
                 / 'text    :', hid->get_text( ).
      ENDTRY.

    WHEN OTHERS.
      WRITE: / 'p_mode 는 1/2/3/4 중 하나'.
  ENDCASE.

* ※ 참고:
*   - CATCH 순서: 구체적 자식 → catch-all cx_root 는 맨 아래
*   - DB 작업 실패 시 CATCH 안에서 ROLLBACK WORK 명시 권장
*   - 운영 로그는 classname + get_text + get_source_position 3종 세트
*   - 정상 분기 처리에 예외 남용 X (성능 비용)
*   - CATCH 안에서 추가 작업은 별도 TRY 로 감쌀 것

요약

단계 처리 핵심
1 기본 구조 TRY ... CATCH cx_root INTO oref ... ENDTRY
2 클래스 계층 자식(cx_sy_zerodivide 등) 먼저 → cx_root 는 맨 아래
3 메시지 추출 oref->get_text( ) / get_longtext( ) / classname
4 위치 추적 get_source_position 으로 program/include/line 추출
5 함수형 결합 cx_sy_no_handler 메타 catch 로 함수 내부 클래스 예외 추적

ABAP 덤프 방지의 표준은 TRY ... CATCH ... ENDTRY. 모든 예외는 cx_root 의 자식이므로 catch-all 만능 처리가 가능하고, 정밀한 분기가 필요하면 cx_sy_zerodivide·cx_sy_conversion_overflow 같은 자식 클래스를 따로 잡습니다. 운영 트러블슈팅이 빠르려면 메시지(get_text) 외에 classnameget_source_position 까지 로그에 남기는 게 표준 — 어디서·왜·무엇이 라는 3종 정보가 한 줄에 다 들어옵니다.


Disclaimer — 이 포스트는 실무 정리 노트를 바탕으로 AI 보조로 정리되었습니다. TRY·CATCH·ENDTRY·cx_root·cx_sy_no_handler 는 SAP NetWeaver 표준 ABAP 객체지향 예외 처리 문법으로 시스템 버전 의존 없이 동작합니다. get_source_position( ) 은 모든 cx_root 후손 클래스에서 호출 가능합니다. 다만 DB 작업·외부 연동 트랜잭션 처리 시에는 CATCH 블록에서 ROLLBACK WORK 명시·재시도 로직·운영 로그 저장 정책을 함께 설계해야 데이터 무결성이 유지됩니다.