개요 및 핵심 포인트
ABAP의 RAISE EXCEPTION 명령은 단순히 오류를 던지는 것 이상의 의미를 가집니다. TYPE으로 어떤 예외 클래스를 발생시킬지, MESSAGE로 사용자에게 보여줄 메시지를 어떻게 첨부할지, RESUMABLE로 호출자가 실행을 이어갈 수 있도록 허용할지를 결합하여 결정해야 합니다. 세 가지 패턴을 제대로 이해하면 트랜잭션 로직, RAP 동작 구현, BAdI 구현체 어디에서도 일관된 오류 처리 코드를 작성할 수 있습니다.
학습 체크리스트
cx_root–cx_static_check/cx_dynamic_check/cx_no_check계층 구분RAISE EXCEPTION TYPE ... EXPORTING ...문법으로 속성 주입RAISE EXCEPTION TYPE ... MESSAGE ID ... TYPE ... NUMBER ... WITH ...T100 연결RAISE RESUMABLE EXCEPTION과CATCH BEFORE UNWIND ... RESUME흐름PREVIOUS를 이용한 예외 체인(wrapping) 구현
사전에 알아두면 좋은 배경
클래식 ABAP의 RAISE / MESSAGE ... RAISING은 함수 모듈의 EXCEPTIONS 인터페이스에 의존하는 옛 방식입니다. 반면 클래스 기반 예외(class-based exceptions)는 cx_root를 루트로 하는 객체 인스턴스를 생성·전파합니다. TRY ... CATCH ... ENDTRY 블록, RAISING 절, 상속 기반 다형성을 이용한 다단계 처리가 가능해야 본 문서의 패턴이 의미를 가지므로 객체 지향 ABAP 기본 문법은 미리 익혀두는 것이 좋습니다.
실습 환경 및 버전
- ABAP 언어 버전: 7.50 이상 (RESUMABLE은 7.0부터 지원되나 인라인 선언 등은 7.40 SP08+ 필요)
- 플랫폼: SAP S/4HANA 2020 이상 또는 SAP BTP ABAP Environment
- 개발 도구: ABAP Development Tools(ADT) for Eclipse 권장
- 예외 클래스 생성: SE24 또는 ADT의 New ABAP Class 마법사 → With Message Class 옵션
- 메시지 클래스: SE91에서 T100 메시지 등록(예:
ZTUT_MSG)
주의: SAP BTP ABAP Environment(Steampunk)에서는 일부 옛 메시지 호출이 제한되므로 클래스 기반 예외 + MESSAGE 절 사용이 일반적으로 권장됩니다.
핵심 개념과 동작 원리
예외 클래스 계층 비유
ABAP의 예외 계층은 회사 보고 체계에 비유할 수 있습니다. cx_root는 모든 예외의 CEO이며, 그 아래에 세 가지 부서가 있습니다.
cx_static_check: 컴파일러가RAISING선언을 강제로 검사하는 "정적 결재" 부서. 호출자는 반드시 처리하거나 다시 선언해야 합니다.cx_dynamic_check: 런타임에만 검증되는 "동적 결재" 부서. 보통 비즈니스 로직에서 가장 많이 사용됩니다.cx_no_check: 어디서나 자유롭게 던질 수 있는 "프리패스" 부서. 시스템 예외가 여기에 해당합니다.
RAISE EXCEPTION의 동작 흐름
RAISE EXCEPTION TYPE cx_my_error는 다음 순서로 동작합니다.
- 지정된 예외 클래스의 인스턴스를 자동으로
CREATE OBJECT EXPORTING에 적힌 값이 인스턴스 속성에 주입MESSAGE절이 있으면 T100 메시지 ID/번호/변수가 인스턴스에 저장- 호출 스택을 거슬러 올라가며 매칭되는
CATCH를 탐색 RESUMABLE이면 스택을 즉시 풀지 않고BEFORE UNWIND핸들러를 우선 시도
get_text vs MESSAGE
예외 메시지는 두 가지 경로로 표시됩니다. oref->get_text( )는 OTR(Online Text Repository) 텍스트 또는 첨부된 T100 텍스트를 반환합니다. MESSAGE oref TYPE 'E'는 예외에 저장된 T100 정보를 ABAP 메시지 시스템으로 직접 출력합니다. 두 방식은 동일한 데이터를 다른 채널로 흘려보내는 것입니다.
실전 코드
1단계 — TYPE 옵션: 가장 기본적인 예외 발생
먼저 SE24 또는 ADT에서 ZCX_DIVISION_ERROR 예외 클래스를 만듭니다. 슈퍼클래스는 cx_static_check로 지정하고, 속성으로 divisor TYPE i를 추가합니다. 그 후 다음 코드를 실행합니다.
CLASS zcl_calc DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
METHODS divide
IMPORTING iv_num1 TYPE i
iv_num2 TYPE i
RETURNING VALUE(rv_res) TYPE i
RAISING zcx_division_error.
ENDCLASS.
CLASS zcl_calc IMPLEMENTATION.
METHOD divide.
IF iv_num2 = 0.
RAISE EXCEPTION TYPE zcx_division_error
EXPORTING
divisor = iv_num2.
ENDIF.
rv_res = iv_num1 / iv_num2.
ENDMETHOD.
ENDCLASS.
핵심은 EXPORTING divisor = iv_num2 부분입니다. ABAP 런타임이 자동으로 인스턴스를 만들어 속성에 값을 채워주므로, 호출자는 CATCH에서 lo_ex->divisor로 어떤 값이 문제였는지 정확히 알 수 있습니다.
2단계 — MESSAGE 절: T100 메시지 첨부와 로깅
실무에서는 단순한 속성 전달을 넘어 사용자에게 다국어 메시지를 보여주어야 합니다. 먼저 SE91에서 메시지 클래스 ZTUT_MSG의 001번에 "&1 항목 처리 중 오류: &2"를 등록한 뒤 아래처럼 사용합니다.
METHOD post_invoice.
TRY.
validate_amount( iv_amount ).
CATCH zcx_amount_invalid INTO DATA(lo_prev).
RAISE EXCEPTION TYPE zcx_invoice_error
EXPORTING
textid = zcx_invoice_error=>invalid_amount
previous = lo_prev
MESSAGE ID 'ZTUT_MSG' TYPE 'E' NUMBER '001'
WITH 'INVOICE' lo_prev->get_text( ).
ENDTRY.
ENDMETHOD.
" 호출자 측
TRY.
lo_service->post_invoice( iv_amount = -100 ).
CATCH zcx_invoice_error INTO DATA(lo_err).
" 화면 메시지 출력
MESSAGE lo_err TYPE 'I' DISPLAY LIKE 'E'.
" Application Log 기록
cl_bali_message_setter=>create(
severity = if_bali_constants=>c_severity_error
text = lo_err->get_text( ) ).
ENDTRY.
MESSAGE 절은 호출자가 get_text( )를 호출하거나 MESSAGE lo_err TYPE 'E'를 사용할 때 SE91에 등록된 텍스트를 자동으로 조립해줍니다. WITH 뒤의 값들은 메시지 placeholder &1~&4에 순서대로 매핑됩니다.
3단계 — RESUMABLE: 실행 재개가 가능한 예외
일괄 처리에서 일부 레코드가 실패해도 전체를 중단하지 않고 계속 진행해야 할 때 RESUMABLE이 유용합니다. 예외 클래스를 만들 때 Exception can be resumed 옵션이 활성화되어야 하며, 호출 쪽에서는 RAISING RESUMABLE(...)로 시그니처를 선언합니다.
METHODS process_row
IMPORTING is_row TYPE zsales_row
RAISING RESUMABLE(zcx_row_warning).
METHOD process_row.
IF is_row-amount < 0.
RAISE RESUMABLE EXCEPTION TYPE zcx_row_warning
EXPORTING row_id = is_row-id.
ENDIF.
" 정상 처리 로직 (RESUME 후에도 여기까지 실행됨)
insert_row( is_row ).
ENDMETHOD.
" 배치 호출자
LOOP AT lt_rows INTO DATA(ls_row).
TRY.
lo_service->process_row( is_row = ls_row ).
CATCH BEFORE UNWIND zcx_row_warning INTO DATA(lo_warn).
log_warning( lo_warn->row_id ).
RESUME. " process_row 내부 RAISE 다음 줄부터 재개
ENDTRY.
ENDLOOP.
CATCH BEFORE UNWIND가 핵심입니다. 일반 CATCH는 스택을 풀어버려 RAISE 위치로 돌아갈 수 없지만, BEFORE UNWIND는 스택을 보존해 RESUME으로 RAISE 명령 바로 다음 줄로 점프할 수 있게 합니다. 단, 같은 TRY 블록에서 BEFORE UNWIND와 일반 CATCH를 함께 쓸 때 우선순위 규칙을 반드시 숙지해야 합니다.
실전 패턴 — 예외 래핑(PREVIOUS 체인)
저수준 예외를 그대로 상위 계층까지 노출하면 추상화가 깨집니다. 일반적으로 권장되는 방법은 PREVIOUS 파라미터로 원래 예외를 감싸 새 예외를 던지는 것입니다.
METHOD load_customer.
TRY.
SELECT SINGLE * FROM kna1 INTO @DATA(ls_kna1)
WHERE kunnr = @iv_kunnr.
IF sy-subrc <> 0.
RAISE EXCEPTION TYPE cx_sy_itab_line_not_found.
ENDIF.
CATCH cx_sy_itab_line_not_found INTO DATA(lo_root).
RAISE EXCEPTION TYPE zcx_customer_not_found
EXPORTING
kunnr = iv_kunnr
previous = lo_root. " 원본 보존
ENDTRY.
ENDMETHOD.
호출자는 lo_err->previous를 따라가며 원인을 추적할 수 있고, get_text( ) 호출 시 자동으로 체인 정보를 활용할 수 있습니다. 이는 분산 트랜잭션이나 RAP 동작에서 비즈니스 예외로 변환할 때 표준적으로 사용되는 기법입니다.
흔한 실수와 트러블슈팅
FAQ 1. "RAISE RESUMABLE EXCEPTION ... is not allowed here" 컴파일 오류
예외 클래스에 resumable 옵션이 켜져 있어도, 메서드 시그니처가 RAISING zcx_xxx(일반)으로 선언되어 있으면 컴파일되지 않습니다. 반드시 RAISING RESUMABLE(zcx_xxx)로 적어야 하며, 호출 체인 전체가 동일하게 선언되어야 RESUME이 동작합니다.
FAQ 2. get_text( )가 메시지 클래스 ID만 반환
MESSAGE 절을 사용한 예외가 빈 텍스트 또는 이상한 코드만 반환한다면, 예외 클래스 속성에 if_t100_dyn_msg 또는 if_t100_message 인터페이스가 누락된 경우가 많습니다. 7.50+ 에서는 클래스 생성 마법사의 With Message Class를 체크하면 자동으로 msgid/msgno/msgv1~4 속성과 인터페이스가 추가됩니다.
FAQ 3. CATCH BEFORE UNWIND 후 RESUME을 호출하지 않으면?
RESUME을 호출하지 않고 CATCH 블록이 끝나면 일반 CATCH처럼 스택이 풀리고, 컨트롤이 ENDTRY 다음으로 이동합니다. 의도적으로 "경고는 무시하고 계속"이 아니라 "한 번 더 검사하고 진행/중단을 결정"하는 경우에는 명시적으로 RESUME 또는 다시 RAISE를 호출하는 것이 권장됩니다.
기타 디버깅 팁
- ADT 디버거의 Exceptions 뷰에서
previous체인을 펼쳐볼 수 있습니다. cl_demo_output=>display( lo_err )로 예외 객체 전체를 빠르게 시각화할 수 있습니다.- 운영 환경에서는 ST22 short dump가 아니라 Application Log(SLG1)에 기록되도록 catch-and-log 패턴을 사용하는 것이 일반적입니다.
다음 단계 / 관련 주제
- RAP Managed Behavior의
FAILED/REPORTED구조와 예외 매핑 - cl_bali_* 클래스 기반 신규 Application Log API (BAL 후속)
- ABAP Unit의
cl_abap_unit_assert=>fail과 예외 검증 패턴 - Clean ABAP 가이드의 "Throw class-based exceptions" 규칙 학습
- OData/RAP 서비스에서
/IWBEP/CX_MGW_BUSI_EXCEPTION으로의 변환 전략
참고 자료
- help.sap.com — RAISE EXCEPTION, Class-Based Exceptions
- help.sap.com — Exception Handling (개념 가이드)
- help.sap.com — CATCH BEFORE UNWIND / RESUME
- help.sap.com — Class-Based Exceptions and T100 Messages
- help.sap.com — Exception Categories (static/dynamic/no check)
- SAP Clean ABAP — Error Handling 챕터
- SAP Community 블로그 — ABAP Exception Handling 태그
댓글 0
아직 댓글이 없습니다.