📖 이 글에서 다루는 내용과 목표
S/4HANA 프로젝트에서 화면 하나를 만들 때마다 반복되는 질문이 있습니다. "이건 RAP으로 갈까, 그냥 SE38 리포트로 끝낼까?" 이 글은 RAP(RESTful ABAP Programming Model)과 클래식 리포트(SE38 + ALV)의 아키텍처 차이를 비교하고, 실무 의사결정 기준을 제시합니다. 예제 도메인은 제조 현장의 자재 입고 검수로 창작했습니다.
- RAP과 클래식 리포트의 실행 구조 차이를 설명할 수 있다
- 동일 요구사항을 두 방식으로 구현한 코드를 비교할 수 있다
- 요구사항 유형별로 어떤 방식을 선택할지 판단 기준을 세울 수 있다
📚 읽기 전에 알아두면 좋은 것
ABAP 문법 기본기(SELECT, 내부 테이블, 클래스), CDS View의 기초 개념, ADT(Eclipse 기반 ABAP Development Tools) 사용 경험이 있으면 수월합니다. OData나 Fiori Elements를 몰라도 따라올 수 있도록 필요한 부분에서 짧게 설명합니다.
🔧 실습 환경과 버전 기준
이 글의 RAP 코드는 SAP S/4HANA 2022 이상 (온프레미스) 또는 SAP BTP ABAP Environment를 기준으로 합니다. RAP의 관리형(Managed) 시나리오와 CL_SALV_TABLE은 두 환경에서 동작 범위가 다르다는 점에 유의하세요.
- 클래식 리포트(SE38, ALV): 온프레미스/Private Cloud에서만 유효. BTP ABAP Environment와 Public Cloud(3-tier 확장 모델)에서는 화면 기반 클래식 개발이 제한됩니다.
- RAP: S/4HANA 1909부터 사용 가능하나, 관리형 시나리오의 액션·검증 기능은 2020 이후 버전에서 안정적입니다.
- 도구: 클래식은 SAP GUI(SE38/SE80), RAP은 ADT(Eclipse)가 사실상 필수입니다.
💡 두 모델의 핵심 개념 — 요리사와 프랜차이즈 본사
클래식 리포트는 동네 식당 요리사에 비유할 수 있습니다. 재료 선정(SELECT), 조리(가공 로직), 플레이팅(ALV 출력)까지 개발자가 절차적으로 전부 통제합니다. 자유도가 높고 빠르지만, 화면·로직·데이터 접근이 한 프로그램 안에 섞이기 쉽습니다.
RAP은 프랜차이즈 본사 시스템에 가깝습니다. 데이터 모델(CDS View), 행위 정의(Behavior Definition), 행위 구현(Behavior Implementation), 서비스 노출(Service Definition/Binding)이라는 표준 레이어를 따르면, 트랜잭션 처리(LUW), 잠금, 초안(Draft), ETag 동시성 제어, OData UI 노출까지 프레임워크가 제공합니다. 대신 규칙을 따라야 하고 초기 학습 비용이 있습니다.
실행 구조를 도식으로 비교하면 다음과 같습니다.
# 클래식 리포트
SAP GUI → SE38 프로그램 → SELECT → 내부테이블 가공 → ALV 출력
(상태 관리, 화면, 로직이 한 프로그램에 응집)
# RAP
Fiori UI / OData 클라이언트
→ Service Binding → Service Definition
→ Projection View (ZC_*) → Interface View (ZI_*)
→ Behavior Definition + Implementation → 테이블
(레이어별 관심사 분리, 프레임워크가 트랜잭션 관리)
의사결정 관점에서 본질적 차이는 세 가지입니다. 첫째, 소비 채널 — RAP은 처음부터 OData API로 노출되어 Fiori·외부 시스템·테스트 코드가 동일 로직을 소비합니다. 클래식 리포트는 GUI 화면이 유일한 소비자입니다. 둘째, 상태 변경 여부 — 데이터를 생성·수정·승인하는 트랜잭션 앱이라면 RAP의 관리형 LUW가 잠금·검증·초안을 대신 처리해 줍니다. 셋째, 수명 주기 — SAP의 클라우드 로드맵상 신규 트랜잭션 개발의 권장 모델은 RAP이며, 클래식 화면 기술은 유지 모드에 가깝습니다. 다만 일회성 분석, 배치 검증, 대량 데이터 덤프처럼 "읽고 뿌리면 끝"인 요구사항은 여전히 클래식 리포트가 총비용에서 유리한 경우가 많습니다.
💻 실전 코드로 보는 3단계 비교
시나리오: 입고 검수 로트 테이블 ztmm_inbchk(검수번호, 플랜트, 공급사, 자재코드, 입고수량, 불합격수량, 검수상태)를 조회·처리합니다.
1단계 — 클래식 ALV 조회 리포트 (기본 예제)
일회성 분석 요구라면 아래 코드만으로 30분 내 배포가 가능합니다.
REPORT zmm_r_inbchk_list.
DATA gt_check TYPE STANDARD TABLE OF ztmm_inbchk.
SELECT-OPTIONS: s_plant FOR gt_check-plant_id,
s_vend FOR gt_check-vendor_no.
START-OF-SELECTION.
SELECT * FROM ztmm_inbchk
WHERE plant_id IN @s_plant
AND vendor_no IN @s_vend
INTO TABLE @gt_check.
TRY.
cl_salv_table=>factory(
IMPORTING r_salv_table = DATA(go_alv)
CHANGING t_table = gt_check ).
go_alv->get_functions( )->set_all( abap_true ).
go_alv->display( ).
CATCH cx_salv_msg INTO DATA(gx_msg).
MESSAGE gx_msg->get_text( ) TYPE 'E'.
ENDTRY.
2단계 — RAP 데이터 모델과 행위 정의 (실무 시나리오)
같은 데이터를 "검수 완료 처리"라는 상태 변경까지 포함해 앱으로 만들면, RAP에서는 로직이 레이어별로 분리됩니다. 먼저 인터페이스 CDS View입니다.
@AccessControl.authorizationCheck: #CHECK
define root view entity ZI_MM_InboundCheck
as select from ztmm_inbchk
{
key check_lot as CheckLot,
plant_id as PlantId,
vendor_no as VendorNo,
mat_code as MatCode,
qty_received as QtyReceived,
qty_rejected as QtyRejected,
check_status as CheckStatus, // O=대기, C=완료
@Semantics.systemDateTime.lastChangedAt: true
changed_at as ChangedAt
}
행위 정의에서는 검증(validation)과 액션을 선언합니다. 잠금·ETag·초안은 선언 한 줄로 프레임워크에 위임됩니다.
managed implementation in class zbp_i_mm_inboundcheck unique;
strict ( 2 );
define behavior for ZI_MM_InboundCheck alias InbCheck
persistent table ztmm_inbchk
lock master
authorization master ( instance )
etag master ChangedAt
{
create; update; delete;
field ( readonly ) CheckLot, CheckStatus;
action ( features : instance ) completeCheck result [1] $self;
validation validateQty on save { field QtyReceived, QtyRejected; }
}
3단계 — 행위 구현: 에러 처리와 프로덕션 품질
검증 실패 시 failed/reported 구조로 메시지를 반환하면 Fiori UI가 자동으로 에러를 표시합니다. 클래식에서 MESSAGE ... TYPE 'E'로 처리하던 것과 달리, 이 로직은 UI 없이 OData 호출이나 ABAP Unit 테스트로도 동일하게 검증됩니다.
CLASS lhc_inbcheck IMPLEMENTATION.
METHOD validateQty.
READ ENTITIES OF zi_mm_inboundcheck IN LOCAL MODE
ENTITY InbCheck FIELDS ( QtyReceived QtyRejected )
WITH CORRESPONDING #( keys ) RESULT DATA(lt_lot).
LOOP AT lt_lot INTO DATA(ls_lot)
WHERE QtyRejected > QtyReceived.
APPEND VALUE #( %tky = ls_lot-%tky ) TO failed-inbcheck.
APPEND VALUE #(
%tky = ls_lot-%tky
%msg = new_message_with_text(
severity = if_abap_behv_message=>severity-error
text = '불합격 수량이 입고 수량을 초과할 수 없습니다' )
) TO reported-inbcheck.
ENDLOOP.
ENDMETHOD.
ENDCLASS.
프로덕션 관점 체크포인트: 인터페이스 View의 @AccessControl에 대응하는 DCL(Access Control)로 플랜트 권한을 걸고, 행위 구현은 CL_ABAP_TESTDOUBLE 계열의 Entity Manipulation Language 테스트 더블로 단위 테스트를 작성하는 것이 일반적입니다. 클래식 리포트에서 이 수준의 테스트 자동화를 하려면 로직을 별도 클래스로 추출하는 리팩터링이 선행되어야 합니다 — 이것이 유지보수성 격차의 핵심입니다.
⚠️ 자주 겪는 실수와 트러블슈팅
- Q1. RAP인데 SELECT 후 LOOP로 가공하던 습관대로 짜도 되나요? — 관리형 시나리오에서 데이터 접근은 반드시 EML(
READ ENTITIES,MODIFY ENTITIES)을 경유해야 합니다. 테이블 직접 UPDATE는 잠금·초안 상태를 깨뜨려COMMIT ENTITIES시점에 불일치가 발생합니다. - Q2. 클래식 리포트를 RAP으로 "전환"만 하면 Fiori 앱이 되나요? — 아닙니다. WRITE/ALV 출력, 선택 화면, GUI 상태 코드는 이식 대상이 아니라 재설계 대상입니다. 조회 전용이라면 RAP 대신 CDS View + Fiori Elements List Report(행위 정의 없이)로 충분한 경우가 많습니다.
- Q3.
strict(2)모드에서 문법 오류가 쏟아집니다. — 클라우드 호환 검사(비허용 구문, 누락된 필드 제어)가 원인인 경우가 대부분입니다. 온프레미스 전용이라도 strict 모드 유지를 권장합니다. 나중에 클라우드 이관 비용이 크게 줄어듭니다. - Q4. 배치 잡도 RAP으로 해야 하나요? — 대량 데이터를 읽어 파일로 내리거나 야간 검증만 하는 잡이라면 클래식 리포트(백그라운드 실행)가 여전히 실용적입니다. 단, 상태를 변경하는 배치라면 RAP 액션을 EML로 호출하는 실행용 클래스를 두는 편이 로직 중복을 막습니다.
🚀 이어서 살펴볼 주제
의사결정 기준을 세웠다면 다음 주제로 확장해 보세요. RAP 비관리형(Unmanaged) 시나리오로 기존 BAPI/함수 모듈 로직 재사용하기, Draft 활성화로 장문 입력 UX 개선하기, ABAP Cloud 3-tier 확장 모델에서 클래식 자산 격리 전략, 그리고 CDS Analytical Query로 조회성 리포트를 대체하는 패턴이 자연스러운 다음 코스입니다.
댓글 0
아직 댓글이 없습니다.