1. RAP 디버깅이 왜 유독 까다로운가
ABAP RESTful Application Programming Model(RAP)은 CDS 뷰, Behavior Definition(BDEF), Behavior Implementation(BIL) 클래스, Service Definition, Service Binding까지 여러 계층이 얽혀서 하나의 OData 요청을 처리합니다. 단순 리포트 프로그램이라면 /h 명령이나 F5로 흐름을 잡을 수 있지만, RAP에서는 요청이 어느 핸들러(For Modify / For Read / For Determination / For Validation)로 진입하는지 눈으로 확인하기 어렵습니다. 특히 프레임워크가 내부적으로 트랜잭션 버퍼(transactional buffer)를 관리하고, Save Sequence(ADJUST_NUMBERS → FINALIZE → CHECK_BEFORE_SAVE → SAVE)를 자동으로 호출하기 때문에, 실무 개발자는 "왜 이 Determination이 두 번 호출되는가", "왜 Validation 결과가 반영되지 않는가" 같은 문제에 부딪히곤 합니다.
이 글은 SAP BTP ABAP Environment(2402 릴리스 기준) 및 S/4HANA 2023 On-Premise 환경에서 ADT(ABAP Development Tools) for Eclipse를 사용해 RAP Behavior Implementation을 3단계로 디버깅하는 방법을 정리합니다. 아래 체크리스트를 하나씩 만족시키는 것이 목표입니다.
- External Breakpoint와 Session Breakpoint의 차이를 이해하고 상황에 맞게 선택
- Managed / Unmanaged 시나리오에서 Handler 클래스에 진입하는 브레이크포인트 배치
- ADT Debugger의 Variables, Stack, Breakpoints 뷰를 활용해 흐름 추적
- Fiori Elements Preview / RAP Generator / Postman 요청과 디버깅 세션 연결
- Save Sequence 각 단계에서 트랜잭션 버퍼 상태 검증
2. ADT 디버거 핵심 개념 — 동작 원리 이해
이 글의 예제를 따라가려면 CDS View Entity와 Behavior Definition 문법에 대한 최소한의 이해가 필요합니다. Managed BO와 Unmanaged BO의 차이, EML(Entity Manipulation Language)의 MODIFY ENTITIES·READ ENTITIES·COMMIT ENTITIES 구문, 그리고 ABP_BEHV_IMPL·BASE_BUILDER 같은 프레임워크 클래스의 존재 정도는 알고 있어야 합니다. Eclipse ADT의 Project Explorer / ABAP Development Perspective / Debug Perspective 전환에 익숙하지 않다면 먼저 ADT 기본 사용법을 익힌 뒤 이 글로 돌아오는 것을 권장합니다.
테스트는 아래 환경 조합에서 검증했습니다. 릴리스가 다르면 메뉴 위치가 조금씩 다를 수 있으므로 주의합니다.
- Eclipse IDE for Enterprise Java and Web Developers 2024-03 (4.31)
- ABAP Development Tools 3.42 이상 — Help > Install New Software에서
https://tools.hana.ondemand.com/latest리포지토리 사용 - 백엔드: SAP BTP ABAP Environment 2402 또는 S/4HANA 2023 FPS01 (ABAP Platform 2023)
- 권한:
S_DEVELOP(개발 오브젝트 변경),S_DEBUG(디버깅 활성화 필요 — 프로덕션 시스템에서는 일반적으로 제한됨) - 테스트 요청 도구: RAP Service Binding의 Preview 버튼, 또는 Postman/Bruno로 CSRF 토큰과 함께 OData 요청 전송
백엔드 계정이 다이얼로그 사용자(Dialog User)여야 External Breakpoint가 정상적으로 걸립니다. Communication User나 Business User(BTP ABAP Environment의 경우)는 별도 매핑이 필요합니다. 클라우드 환경에서는 Custom Communication Scenario를 만들고 자신의 사용자 ID를 Bound Service Instance에 매핑해두어야 서비스 요청이 자기 세션으로 들어옵니다.
3. 브레이크포인트 유형별 설정법
ADT 디버거는 SAP GUI의 New ABAP Debugger를 Eclipse UI로 감싼 원격 디버거입니다. 개념적으로는 두 개의 세션이 필요합니다. 하나는 실제 사용자 코드를 실행하는 Debuggee 세션(예: OData 요청을 처리하는 워크 프로세스), 다른 하나는 개발자가 코드를 훑어보는 Debug Control 세션(Eclipse가 열어주는 뷰). 두 세션은 Breakpoint 테이블을 통해 연결됩니다.
브레이크포인트는 크게 세 가지로 나뉩니다. 비유하자면 다음과 같습니다.
- Session Breakpoint — "내 방 안에서만 유효한 포스트잇". 같은 SAP GUI 로그온 세션 안에서만 살아있고 세션이 끝나면 사라집니다. 리포트를 직접 실행할 때 편리합니다.
- External Breakpoint — "내 이름표가 붙은 형광 표식". 사용자 ID 기준으로 저장되어, 웹 요청(RAP OData, HTTP, RFC, ICF 등) 같은 다른 세션이 같은 코드에 진입해도 걸립니다. RAP 디버깅에서는 사실상 기본값입니다.
- Conditional / Watchpoint — "특정 조건에서만 울리는 알람". 반복 호출되는 Determination에서 특정 키 값일 때만 멈추게 하고 싶을 때 유용합니다.
RAP Behavior Implementation은 프레임워크가 CREATE OBJECT로 인스턴스화하고 FOR MODIFY 메서드를 호출하는 구조라 요청마다 새 세션에서 실행됩니다. 그래서 개인 개발 시스템이 아니라면 반드시 External Breakpoint(Eclipse 아이콘의 오른쪽 상단에 사람 모양 표시)를 사용해야 합니다. Eclipse에서 왼쪽 여백을 더블 클릭해 만든 브레이크포인트는 기본이 External이며, 오른쪽 클릭 > Breakpoint Properties에서 Session으로 전환할 수 있습니다.
실행 흐름은 개략적으로 다음과 같습니다.
"[OData PATCH /SalesOrder(1000)] 요청 도착
"↓
"Service Adapter (Gateway) → Behavior Pool 인스턴스화
"↓
"lhc_SalesOrder->CHANGE_SalesOrder( ) " FOR MODIFY 핸들러
"↓
"Determination (early/late) 자동 호출
"↓
"Validation (on save) 호출
"↓
"ADJUST_NUMBERS → FINALIZE → CHECK_BEFORE_SAVE → SAVE
"↓
"트랜잭션 커밋 및 응답
각 화살표 지점이 곧 브레이크포인트 후보이며, 프레임워크가 어느 메서드를 호출했는지 Stack 뷰에서 역추적하는 것이 디버깅의 핵심 기술입니다.
4. RAP BO 디버깅 실전 세팅 — Handler·Validation·Determination
가상의 SalesOrder BO를 예로 듭니다. Behavior Definition은 Managed 구현으로, Behavior Pool 클래스는 ZBP_R_SALESORDER_TP, Local Handler는 lhc_SalesOrder라고 가정합니다.
먼저 총액이 0보다 커야 한다는 Validation을 구현합니다. Eclipse Project Explorer에서 Behavior Implementation 클래스를 열고, 왼쪽 여백(Line Ruler)을 더블 클릭해 브레이크포인트를 만듭니다.
METHOD validateTotalAmount.
READ ENTITIES OF z_r_salesorder IN LOCAL MODE
ENTITY SalesOrder
FIELDS ( TotalAmount CurrencyCode )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_orders).
LOOP AT lt_orders INTO DATA(ls_order).
IF ls_order-TotalAmount <= 0. "<-- 여기에 External Breakpoint
APPEND VALUE #(
%tky = ls_order-%tky
) TO failed-salesorder.
APPEND VALUE #(
%tky = ls_order-%tky
%msg = new_message(
id = 'ZSO_MSG'
number = '001'
severity = if_abap_behv_message=>severity-error )
%element-TotalAmount = if_abap_behv=>mk-on
) TO reported-salesorder.
ENDIF.
ENDLOOP.
ENDMETHOD.
Service Binding에서 Preview 버튼을 누르면 Fiori Elements Preview가 뜹니다. 총액을 0으로 바꿔 저장하면 Eclipse Debug Perspective가 자동으로 활성화되고, 방금 걸어둔 라인에서 실행이 멈춥니다. Variables 뷰에서 ls_order를 펼치면 실제 키와 필드값을 즉시 확인할 수 있습니다.
5. 실행 흐름 추적 — 스택 프레임·변수 검사
실무에서는 값이 자동으로 채워지는 Determination이 예상대로 동작하지 않는 경우가 더 자주 발생합니다. 예를 들어 OrderStatus가 신규 저장 시 항상 NEW로 세팅되어야 한다고 가정합시다.
METHOD setDefaultStatus.
READ ENTITIES OF z_r_salesorder IN LOCAL MODE
ENTITY SalesOrder
FIELDS ( OrderStatus OrderDate )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_orders).
DATA lt_update TYPE TABLE FOR UPDATE z_r_salesorder\SalesOrder.
LOOP AT lt_orders INTO DATA(ls_order) WHERE OrderStatus IS INITIAL.
APPEND VALUE #(
%tky = ls_order-%tky
OrderStatus = 'NEW'
OrderDate = cl_abap_context_info=>get_system_date( )
%control-OrderStatus = if_abap_behv=>mk-on
%control-OrderDate = if_abap_behv=>mk-on
) TO lt_update.
" 디버깅용 진단 로그 (App Log / BAL 활용)
cl_ba_log=>get_instance( )->add_msg(
iv_process = 'ZRAP_DET'
iv_msg = |Set default status for { ls_order-SalesOrderID }| ).
ENDLOOP.
MODIFY ENTITIES OF z_r_salesorder IN LOCAL MODE
ENTITY SalesOrder
UPDATE FIELDS ( OrderStatus OrderDate )
WITH lt_update
REPORTED DATA(ls_reported).
ENDMETHOD.
이 메서드가 예상보다 많이 호출되는지 확인하려면 Determination 시점(on modify vs on save)이 중요합니다. Breakpoint를 걸어둔 뒤 Debug > Step Return(F7)으로 프레임을 하나씩 빠져나오면서 Stack 뷰의 상위 프레임을 보면, 프레임워크가 어떤 트리거로 호출했는지 ABP_BEHV_ACT_EXEC, DETERMINE_TRIGGERS 같은 내부 클래스 이름으로 드러납니다. 실무에서는 여기에 Application Log(CL_BALI_*)를 곁들여 어떤 요청 배치에서 몇 건이 처리됐는지 남기면, 브레이크포인트를 걷어낸 뒤에도 사후 분석이 가능합니다.
6. 실전 시나리오: SalesOrder BO 조건부 브레이크포인트 실습
대량 데이터 흐름에서는 모든 반복 호출에 멈추면 세션이 타임아웃되기 쉽습니다. Eclipse에서 브레이크포인트를 오른쪽 클릭 > Breakpoint Properties > Conditions에 아래처럼 입력하면, 특정 주문에만 정지합니다.
ls_order-SalesOrderID = '4711'
또한 저장 시퀀스의 각 단계 진입 여부를 감지하려면 Save 메서드에 진입한 순간 Watchpoint를 이용해 트랜잭션 버퍼(예: mt_buffer)의 라인 수 변화를 감시합니다. 프로덕션에 반영 전에는 Unit Test Class(CL_CI_TEST_ABAP_UNIT 기반)에서 EML로 시나리오를 재현해 자동 검증을 붙여두는 편이 훨씬 안전합니다.
METHOD validate_total_positive.
MODIFY ENTITIES OF z_r_salesorder
ENTITY SalesOrder
CREATE FIELDS ( SalesOrderID TotalAmount CurrencyCode )
WITH VALUE #( ( %cid = 'C1' SalesOrderID = '9001'
TotalAmount = 0 CurrencyCode = 'EUR' ) )
FAILED DATA(failed)
REPORTED DATA(reported).
cl_abap_unit_assert=>assert_not_initial( act = reported-salesorder ).
ENDMETHOD.
보안 관점에서는, 프로덕션 시스템의 다이얼로그 디버깅은 대부분 제한됩니다. 반드시 QAS/DEV 시스템에서 재현하고, 데이터 마스킹이 필요한 필드(개인정보)는 Variables 뷰에서도 노출되므로 스크린 캡처 시 주의합니다.
7. 자주 겪는 함정과 해결법
실무에서 반복적으로 목격되는 문제를 FAQ 형태로 정리합니다.
Q1. 브레이크포인트에 초록 체크가 보이는데 요청을 보내도 멈추지 않는다.
가장 흔한 원인은 요청을 보내는 사용자와 External Breakpoint에 등록된 사용자가 다른 경우입니다. Window > Preferences > ABAP Development > Debug에서 "User for External Debugging"이 자신의 백엔드 사용자 ID와 일치하는지 확인합니다. BTP ABAP Environment라면 Communication Arrangement가 개인 사용자로 매핑되어야 합니다.
Q2. Save 메서드에 진입하지 않는다.
Managed 시나리오에서SAVE는 프레임워크가 자동 처리하므로, 개발자가saver클래스를 명시적으로 구현하지 않았다면 진입할 코드가 없습니다. Additional Save나 Unmanaged Save를 사용하지 않는 이상,ADJUST_NUMBERS나FINALIZE에 브레이크포인트를 걸어야 저장 시퀀스가 잡힙니다.
Q3. Draft 활성화 후 두 번씩 호출된다.
Draft-Enabled BO는 Draft 인스턴스와 Active 인스턴스에 대해 Determination/Validation이 각각 트리거될 수 있습니다.%is_draft값을 Variables 뷰에서 확인해 어느 흐름인지 구분하고, 필요하면 조건부 브레이크포인트로 특정 상태만 잡습니다.
이 외에도 Preview URL은 열리지만 브레이크포인트가 무시된다면 브라우저가 캐시된 서비스 정의를 사용 중일 수 있습니다. Service Binding을 다시 게시하거나 브라우저 세션을 새로 시작합니다. Postman으로 테스트할 때는 CSRF 토큰(x-csrf-token: Fetch)을 먼저 받아오고, 동일 세션 쿠키를 유지해야 브레이크포인트가 걸립니다.
8. 디버깅 효율을 높이는 ADT 단축키
이 글에서 다룬 3단계 디버깅을 몸에 익힌 뒤에는 Draft Handling(Edit Draft, Activate, Discard), Managed with Additional Save 시나리오, Unmanaged BO에서 saver 메서드 구현, RAP BO Test Double Framework(CDS_TEST_ENVIRONMENT, BEHAVIOR_TEST_ENVIRONMENT)로 디버깅 없이도 회귀 방지할 수 있는 자동화, ADT의 ABAP Profiler로 RAP 요청 성능 병목을 시각화하는 방법 등으로 시야를 넓혀보면 좋습니다. 이 모든 도구를 효율적으로 활용하려면 아래 ADT 단축키를 손에 익혀두는 것이 선행 조건입니다.
- F5 Step Into / F6 Step Over / F7 Step Return / F8 Resume — 디버깅의 4대 기본기
- Ctrl+Shift+B 현재 라인 브레이크포인트 토글
- Ctrl+Shift+A ADT 오브젝트 검색(클래스/BDEF 빠르게 열기)
- F3 정의로 이동 — 프레임워크 내부 메서드 추적 시 필수
- Ctrl+F7 Debug Perspective와 편집 Perspective 사이 전환
- Ctrl+Shift+I 선택한 표현식 값을 즉시 평가(Inspect)
댓글 0
아직 댓글이 없습니다.