1. 개요 및 이 글에서 다룰 것
ABAP에서 견고한 비즈니스 로직을 작성하기 위해서는 예외 처리(Exception Handling)가 필수입니다. RFC 호출, 데이터베이스 조회, 형변환, 외부 시스템 연동 등 실패 가능성이 있는 코드를 그대로 두면, 단 한 번의 런타임 오류가 전체 트랜잭션을 무너뜨릴 수 있습니다. ABAP의 TRY...CATCH...CLEANUP...ENDTRY 구문은 이러한 위험을 통제하는 핵심 도구입니다.
이 글에서 다룰 것:
- TRY 블록으로 위험한 코드를 격리하는 방법
- CATCH 절에서
cx_static_check,cx_dynamic_check,cx_no_check세 가지 예외 카테고리를 구분하여 처리하는 방법 - CLEANUP 절을 통해 예외 발생 여부와 무관하게 락(Lock), 파일 핸들, 연결 등의 리소스를 안전하게 해제하는 패턴
- 실무에서 자주 마주치는 함정과 트러블슈팅 방법
2. 사전 가정
이 글은 다음을 사전에 가정합니다.
- ABAP 기본 문법(DATA, METHOD, CLASS, LOOP 등)에 익숙
- 객체 지향 ABAP의 클래스/인스턴스 개념 이해
- SE80 또는 ADT(ABAP Development Tools) 사용 경험
- SELECT, UPDATE 등 Open SQL 기본 사용 경험
예외 클래스(CX_ROOT) 계층을 처음 접한다면, 사전에 SE24에서 CX_ROOT 계층 구조를 한 번 훑어보길 권장합니다.
3. 환경 / 버전 / 준비물
예제는 다음 환경에서 동작을 확인했습니다. 일반적으로 NetWeaver 7.40 이상이면 모든 구문이 동일하게 사용 가능합니다.
- SAP NetWeaver AS ABAP 7.50 이상 또는 SAP S/4HANA 2022 / 2023
- ABAP Cloud (BTP, ABAP Environment) — Steampunk 환경에서도 동일 패턴 사용 가능
- 개발 도구: ADT (Eclipse 2024-03 + ABAP Development Tools) 또는 SE80
- 권한: SE38/SE80 개발 권한, 사용자 정의 클래스 생성 권한(SU01)
준비물로는 테스트용 패키지($TMP도 무방), 그리고 RAISING 절을 시험할 수 있는 빈 클래스 하나면 충분합니다. CDS, RAP 등 상위 프레임워크 없이도 본문의 모든 코드는 그대로 실행 가능합니다.
4. 핵심 개념
ABAP의 예외 처리를 이해하기 위해서는 예외 카테고리 3종을 구분하는 것이 가장 중요합니다. 자판기에 비유하면 이렇습니다.
- cx_static_check (정적 점검) — "동전 투입구 막힘". 호출자가 반드시 미리 알고 RAISING 절에 선언해야 하는 예외. 컴파일 타임에 점검되며, 처리하지 않으면 신택스 에러가 발생합니다. 예: 잘못된 키 입력으로 인한 비즈니스 오류.
- cx_dynamic_check (동적 점검) — "동전이 끼었음". 발생 가능성은 있지만 매번 선언을 강제하지는 않습니다. RAISING에 적지 않아도 컴파일은 되지만, 처리하지 않으면 런타임에 미처리 예외로 종료됩니다. 가장 많이 쓰이는 카테고리입니다.
- cx_no_check (점검 없음) — "전기 차단". 시스템 레벨 오류(메모리 부족 등). 어디서든 발생할 수 있으며, RAISING 선언이 불가능합니다.
구문 구조는 다음과 같이 4개 블록으로 구성됩니다.
TRY.
" (1) 위험 코드 영역
CATCH cx_xxx INTO DATA(lo_ex).
" (2) 예외 발생 시 처리
CLEANUP.
" (3) 예외 발생 시에만, 외부로 전파되기 전 정리
ENDTRY.
중요한 차이는 CLEANUP은 CATCH로 잡히지 않은 예외가 상위로 전파될 때만 실행된다는 점입니다. 즉, 같은 TRY 블록 내 CATCH로 잡힌 경우에는 CLEANUP이 실행되지 않습니다. 이 동작 특성을 잘못 이해하면 락이 풀리지 않거나, 반대로 두 번 해제되는 버그가 발생합니다.
예외 클래스 계층은 다음과 같이 정리할 수 있습니다.
CX_ROOT
|-- CX_STATIC_CHECK " 명시적 선언 강제
|-- CX_DYNAMIC_CHECK " 가장 많이 사용
| |-- CX_SY_ZERODIVIDE
| |-- CX_SY_CONVERSION_NO_NUMBER
| |-- CX_SY_OPEN_SQL_DB ...
|-- CX_NO_CHECK " 시스템 레벨
5. 실전 코드 (TRY/CATCH/CLEANUP 각각)
1단계: 기본 예제 — 형변환 오류 잡기
가장 흔한 시나리오인 문자열을 숫자로 변환하는 코드입니다. CX_SY_CONVERSION_NO_NUMBER는 cx_dynamic_check 계열로, 잡지 않으면 덤프(short dump)로 이어집니다.
REPORT z_demo_try_basic.
DATA: lv_input TYPE string VALUE 'ABC123',
lv_number TYPE i.
TRY.
lv_number = lv_input.
WRITE: / 'Converted:', lv_number.
CATCH cx_sy_conversion_no_number INTO DATA(lo_conv_ex).
WRITE: / 'Conversion failed:', lo_conv_ex->get_text( ).
ENDTRY.
get_text( ) 메서드는 예외의 다국어 메시지를 반환합니다. get_longtext( )는 상세 설명을 제공하므로 로그 기록 시 유용합니다.
2단계: 실무 시나리오 — RFC 호출 + 로깅 + 다중 CATCH
외부 시스템 RFC 호출은 통신 장애, 시스템 장애, 비즈니스 오류 등 다양한 예외를 발생시킵니다. 각각을 구분하여 다른 로직으로 대응해야 합니다.
CLASS zcl_partner_service DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
METHODS get_partner_name
IMPORTING iv_partner_id TYPE bu_partner
RETURNING VALUE(rv_name) TYPE string
RAISING cx_uuid_error " static_check
cx_sy_dyn_call_error. " dynamic_check
ENDCLASS.
CLASS zcl_partner_service IMPLEMENTATION.
METHOD get_partner_name.
DATA lv_log TYPE string.
TRY.
CALL FUNCTION 'BAPI_BUPA_CENTRAL_GETDETAIL'
DESTINATION 'CRM_PROD'
EXPORTING businesspartner = iv_partner_id
IMPORTING centraldata = DATA(ls_data)
EXCEPTIONS system_failure = 1 MESSAGE lv_log
communication_failure = 2 MESSAGE lv_log
OTHERS = 3.
IF sy-subrc <> 0.
" 클래식 EXCEPTIONS을 클래스 기반으로 승격
RAISE EXCEPTION TYPE cx_sy_dyn_call_error
EXPORTING textid = cx_sy_dyn_call_error=>cntl_error.
ENDIF.
rv_name = ls_data-name1.
CATCH cx_sy_dyn_call_error INTO DATA(lo_rfc_ex).
" 통신 오류는 재시도 큐에 적재
MESSAGE i001(zfi) WITH 'RFC fail:' lo_rfc_ex->get_text( ).
RAISE EXCEPTION lo_rfc_ex.
CATCH cx_root INTO DATA(lo_root_ex).
" 예상 못한 예외는 애플리케이션 로그로
cl_demo_output=>write( |Unexpected: { lo_root_ex->get_text( ) }| ).
RAISE EXCEPTION TYPE cx_uuid_error.
ENDTRY.
ENDMETHOD.
ENDCLASS.
핵심 포인트는 세 가지입니다. 첫째, 구체적인 예외부터 잡고 마지막에 cx_root로 안전망을 둡니다. 둘째, 잡은 예외를 그대로 무시(swallow)하지 않고 로깅 후 재발생(RAISE EXCEPTION lo_rfc_ex)시킵니다. 셋째, 클래식 EXCEPTIONS는 클래스 기반 예외로 변환하여 일관성을 유지합니다.
3단계: 프로덕션 — CLEANUP으로 락/연결 안전 해제
EBAN(구매요청) 테이블을 잠그고 업데이트하는 시나리오입니다. 중간에 예외가 발생하더라도 DEQUEUE가 반드시 실행되어야 합니다. CLEANUP은 해당 TRY 블록에서 CATCH되지 않은 예외가 상위로 전파될 때 동작한다는 점을 활용합니다.
METHOD update_purchase_req.
DATA lo_lock_ex TYPE REF TO cx_root.
CALL FUNCTION 'ENQUEUE_EMEBAN'
EXPORTING banfn = iv_banfn
EXCEPTIONS foreign_lock = 1
system_failure = 2
OTHERS = 3.
IF sy-subrc <> 0.
RAISE EXCEPTION TYPE zcx_lock_failed.
ENDIF.
TRY.
UPDATE eban SET menge = iv_new_qty
WHERE banfn = iv_banfn.
IF sy-subrc <> 0.
RAISE EXCEPTION TYPE zcx_update_failed.
ENDIF.
" 후속 BAPI 호출 (실패 가능)
mo_validator->validate( iv_banfn ).
CATCH zcx_update_failed INTO DATA(lo_upd).
" 잡고 정리 후 재발생
cl_demo_output=>write( lo_upd->get_text( ) ).
ROLLBACK WORK.
RAISE EXCEPTION lo_upd.
CLEANUP.
" 위 CATCH로 못 잡은 예외 (예: zcx_validator)가 전파될 때만 실행
ROLLBACK WORK.
ENDTRY.
" 정상 종료 또는 예외 전파 후 반드시 락 해제
CALL FUNCTION 'DEQUEUE_EMEBAN'
EXPORTING banfn = iv_banfn.
ENDMETHOD.
여기서 락 해제(DEQUEUE)는 ENDTRY 바깥에 두어 정상/예외 경로 모두에서 실행되도록 했습니다. 더 엄격하게는 호출자가 RAISING으로 받은 예외에서도 락이 풀리도록, 메서드 종료 직전 정리를 분리하는 것이 일반적으로 권장됩니다.
6. 흔한 실수 / 트러블슈팅
FAQ 1: CATCH cx_root만 쓰면 안 되나요?
CATCH cx_root로 모든 예외를 일괄 처리하면 코드는 간결해지지만, 비즈니스 오류와 시스템 오류를 구분할 수 없어 잘못된 복구 로직이 실행될 위험이 큽니다. 일반적으로 구체적인 예외를 먼저 잡고 cx_root는 마지막 안전망으로만 두는 패턴이 권장됩니다.
FAQ 2: CLEANUP이 실행되지 않습니다.
가장 흔한 원인은 같은 TRY 블록의 CATCH가 예외를 잡았기 때문입니다. CLEANUP은 "잡히지 않은 예외가 상위로 전파될 때"만 실행됩니다. 정상 종료, CATCH로 처리된 경우 모두 CLEANUP을 건너뜁니다. 무조건 실행되어야 하는 정리 코드는 ENDTRY 뒤에 배치하거나, 별도 메서드로 분리하는 것이 안전합니다.
FAQ 3: "The exception is neither caught nor declared" 신택스 에러가 납니다.
이는 cx_static_check 계열 예외를 받았는데 RAISING에 선언하지도, CATCH로 잡지도 않았을 때 발생합니다. 해결 방법은 두 가지입니다. 첫째, 메서드 시그니처에 RAISING cx_xxx를 추가하여 상위로 전파. 둘째, TRY/CATCH로 직접 처리. 어느 쪽이 옳은지는 "이 메서드가 이 오류를 의미 있게 처리할 수 있는가"로 판단합니다.
기타 자주 발생하는 실수
- 예외를
CATCH후 빈 블록으로 무시(swallow)하여 디버깅 불가능한 상태를 만드는 것 get_text( )결과만 로깅하고 스택 트레이스(get_source_position)는 버리는 것- CLEANUP에서 또 다른 예외를 발생시켜 원본 예외 정보를 잃는 것
- RFC의 클래식
EXCEPTIONS와 클래스 기반 예외를 혼용하여 일관성 깨지는 것
7. 다음 단계 / 관련 주제
예외 처리의 다음 학습 주제로는 다음을 권장합니다.
- 사용자 정의 예외 클래스(ZCX_*) 설계 — SE24에서
CX_STATIC_CHECK상속, T100 메시지 클래스 연동,previous체이닝 - 예외 체이닝(Exception Chaining) —
RAISE EXCEPTION ... EXPORTING previous = lo_ex로 원본 원인 보존 - RESUMABLE 예외 —
RESUMABLE키워드로 예외 발생 지점에서 재개 가능한 패턴 (제한적 사용 권장) - ABAP RAP에서의 예외 — Behavior Implementation에서
reported구조로 비즈니스 오류 반환 - ABAP Unit + 예외 테스트 —
cl_abap_unit_assert=>assert_throws( )로 예외 발생 여부 검증
8. 핵심 한 줄
TRY로 위험을 격리하고, CATCH로 카테고리를 구분해 잡으며, CLEANUP으로 리소스를 보장하라. 이 세 가지 원칙만 지켜도 ABAP 코드의 견고성은 극적으로 올라갑니다.
참고 자료
- SAP Help — Exceptions and Exception Handling (ABAP Keyword Documentation)
- SAP Help — TRY ... CATCH ... CLEANUP ... ENDTRY 구문 레퍼런스
- SAP Help — Exception Categories (static/dynamic/no_check)
- SAP Help — ABAP Platform Documentation Hub
- SAP Community — ABAP 토픽 (실무 사례 및 토론)
- SAP Blogs — ABAP Exception Handling 태그
댓글 0
아직 댓글이 없습니다.