이 글의 목적과 도달점
SAP S/4HANA 환경에서 생산 오더 데이터를 조회할 때, 과거 ECC 시절부터 이어져 온 AUFK · AFKO · AFPO 테이블 직접 SELECT 방식은 여러 부작용을 낳습니다. 조인이 길어지고, 권한 체크가 누락되며, 릴리스 업그레이드 시 필드 변경에 취약합니다. 이 글에서는 SAP가 권장하는 VDM(Virtual Data Model) CDS 뷰인 I_ProductionOrder를 활용해 자동차 부품 생산 공장의 생산 오더를 안전하고 효율적으로 조회하는 방법을 실전 예제 3단계로 다룹니다.
- AUFK 직접 조회 방식의 리스크와 한계 파악
I_ProductionOrder의 구조·핵심 필드·Association 이해- ABAP Open SQL + CDS로 성능·권한·유지보수를 동시에 잡는 패턴 습득
- OData 서비스 노출까지 연결되는 엔드투엔드 흐름 체득
알고 시작하면 좋은 배경 지식
이 글은 ABAP 개발 경험 1년 이상, Open SQL SELECT ... FROM 구문에 익숙하며, CDS View의 기본 개념(annotation, association, @AccessControl.authorizationCheck)을 한 번쯤 접해본 중급 개발자를 대상으로 합니다. PP 모듈의 생산 오더 도메인 용어(오더 타입, 플랜트, 라우팅, 컴포넌트) 정도만 알고 있어도 예제를 따라오는 데 무리가 없습니다.
실습 환경과 준비물
다음 환경을 기준으로 코드를 작성했습니다. On-Premise 기준이며, RISE with SAP 및 Public Cloud 에디션에서도 뷰 이름과 필드 이름은 대체로 동일하게 유지되지만 릴리스별 릴리스 노트를 확인하는 것을 권장합니다.
- SAP S/4HANA 2022 (FPS02) 또는 2023 On-Premise
- ABAP Development Tools for Eclipse 3.34 이상
- SAP GUI 800 (SE80/SE16N 병행 확인용)
- 플랜트
KR01, 생산 오더 타입PP01이 설정되어 있어야 합니다 - 테스트 사용자에게
C_AFKO_AWK등 생산 오더 관련 권한 오브젝트가 할당되어 있어야 합니다
Public Cloud 에디션이라면 SE16N 대신 View Browser(F2)에서 I_ProductionOrder를 검색해 미리보기로 데이터 존재 여부를 확인하는 방식을 권장합니다.
핵심 개념 · 왜 AUFK를 직접 건드리면 안 되는가
생산 오더는 물리적으로 여러 테이블에 흩어져 있습니다. AUFK는 오더 마스터(오더 번호, 오더 타입, 컨트롤링 영역), AFKO는 헤더(자재, 수량, 시작/종료일), AFPO는 아이템(WBS, 스톡 세그먼트), AFVC는 오퍼레이션을 담고 있습니다. ECC 시절 개발자는 이 네 개를 INNER JOIN으로 엮고, 사용자 권한은 별도 AUTHORITY-CHECK로 처리했습니다.
이 접근법에는 세 가지 근본적 문제가 있습니다. 첫째, 조인 폭발. AFKO의 AUFNR과 AUFK의 AUFNR은 같아 보여도 오더 카테고리(AUFK-AUTYP) 필터를 놓치면 유지보수 오더(PM), 내부 오더(CO)까지 함께 딸려옵니다. 둘째, 권한 누락. 개발자가 명시적으로 권한 체크를 넣지 않으면 사용자가 볼 수 없는 플랜트의 오더까지 리턴되는 보안 홀이 생깁니다. 셋째, 업그레이드 리스크. S/4HANA 전환 시 일부 필드가 확장되면 커스텀 코드가 무더기로 깨집니다.
SAP는 이 문제를 해결하기 위해 VDM 계층을 도입했습니다. I_ProductionOrder는 그중 Basic 뷰(I 접두어 = Interface View)로서, AUFK/AFKO/AFPO의 조인·필드 매핑·권한 체크를 이미 내장하고 있습니다. 개발자는 그냥 SELECT FROM I_ProductionOrder만 쓰면 됩니다.
실전 예제 1단계 · 기본 조회
자동차 부품 공장의 특정 플랜트에서 진행 중인 생산 오더 목록을 조회하는 가장 단순한 형태입니다. AUFK를 몰라도 됩니다.
REPORT z_prod_order_basic.
DATA: lt_prod_orders TYPE STANDARD TABLE OF i_productionorder,
ls_prod_order TYPE i_productionorder.
CONSTANTS: lc_plant_code TYPE werks_d VALUE 'KR01',
lc_order_type TYPE aufart VALUE 'PP01'.
SELECT productionorder,
productionordertype,
production_plant,
material,
totalquantity,
productionunit,
mfgorderplannedstartdate,
mfgorderplannedenddate,
orderisreleased
FROM i_productionorder
WHERE production_plant = @lc_plant_code
AND productionordertype = @lc_order_type
AND mfgorderplannedstartdate GE @( cl_abap_context_info=>get_system_date( ) )
INTO TABLE @lt_prod_orders
UP TO 100 ROWS.
LOOP AT lt_prod_orders INTO ls_prod_order.
WRITE: / ls_prod_order-productionorder,
ls_prod_order-material,
ls_prod_order-totalquantity,
ls_prod_order-mfgorderplannedstartdate.
ENDLOOP.
주목할 점은 WHERE 절에 AUTYP 같은 카테고리 구분자를 넣지 않아도 I_ProductionOrder가 이미 생산 오더 카테고리(카테고리 10)만 필터링해 노출한다는 것입니다. 또한 @AccessControl.authorizationCheck: #CHECK가 뷰에 걸려 있어, 사용자 권한에 없는 플랜트 데이터는 자동으로 걸러집니다.
실전 예제 2단계 · Association과 오퍼레이션 조인
실무에서는 헤더만 보는 경우가 드뭅니다. 각 생산 오더의 오퍼레이션(공정)과 컴포넌트(투입 자재)를 함께 조회해야 하는데, VDM은 이를 Association으로 미리 정의해 두고 있습니다. _ProductionOrderOperation, _ProductionOrderComponent 등이 대표적입니다.
REPORT z_prod_order_with_ops.
TYPES: BEGIN OF ty_order_with_op,
productionorder TYPE i_productionorder-productionorder,
material TYPE i_productionorder-material,
operationnumber TYPE i_productionorderoperation-operationinternalid,
workcenter TYPE i_productionorderoperation-workcenterinternalid,
operationdesc TYPE i_productionorderoperation-operationdescription,
END OF ty_order_with_op.
DATA: lt_result TYPE STANDARD TABLE OF ty_order_with_op.
TRY.
SELECT po~productionorder,
po~material,
op~operationinternalid AS operationnumber,
op~workcenterinternalid AS workcenter,
op~operationdescription AS operationdesc
FROM i_productionorder AS po
INNER JOIN i_productionorderoperation AS op
ON op~productionorder = po~productionorder
WHERE po~production_plant = 'KR01'
AND po~productionordertype = 'PP01'
AND po~orderisreleased = 'X'
INTO TABLE @lt_result
UP TO 500 ROWS.
IF sy-subrc <> 0.
MESSAGE |조회 결과가 없습니다. 플랜트: KR01| TYPE 'I'.
ENDIF.
CATCH cx_sy_open_sql_db INTO DATA(lx_sql).
MESSAGE lx_sql->get_text( ) TYPE 'E'.
ENDTRY.
여기서 두 가지 실무 포인트가 있습니다. 첫째, orderisreleased = 'X' 필터로 릴리스된 오더만 필터링해 개발자가 STATU/JEST 테이블을 직접 뒤질 필요가 없게 했습니다. 둘째, CATCH cx_sy_open_sql_db로 DB 예외를 잡아 상위 로깅으로 넘깁니다. 실전에서는 MESSAGE 대신 SLG1(Application Log)에 기록해 야간 배치 실패도 추적 가능하게 만드는 것을 권장합니다.
실전 예제 3단계 · 페이징·성능·OData 노출
수십만 건의 생산 오더가 존재하는 환경에서는 페이징과 인덱스 힌트, 그리고 OData 서비스로의 노출까지 고려해야 합니다. 다음은 CDS Consumption View를 만들어 OData로 노출하는 패턴입니다.
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: '자동차 부품 생산 오더 조회 - 소비 뷰'
@Metadata.allowExtensions: true
@OData.publish: true
@Search.searchable: true
define view entity ZC_AutoPartProdOrder
as select from I_ProductionOrder as ProdOrder
association [0..*] to I_ProductionOrderOperation as _Operation
on _Operation.ProductionOrder = ProdOrder.ProductionOrder
{
key ProdOrder.ProductionOrder,
ProdOrder.ProductionOrderType,
ProdOrder.Production_Plant as PlantCode,
ProdOrder.Material,
@Semantics.quantity.unitOfMeasure: 'ProductionUnit'
ProdOrder.TotalQuantity as OrderQuantity,
ProdOrder.ProductionUnit,
ProdOrder.MfgOrderPlannedStartDate as PlannedStart,
ProdOrder.MfgOrderPlannedEndDate as PlannedEnd,
ProdOrder.OrderIsReleased,
_Operation
}
where ProdOrder.ProductionOrderType = 'PP01';
이 뷰를 저장하고 액티베이션하면 @OData.publish: true 덕분에 트랜잭션 /IWFND/MAINT_SERVICE에서 서비스를 활성화해 Fiori Elements나 외부 시스템에서 바로 소비 가능합니다. ABAP 측에서는 대용량 조회 시 다음처럼 커서 기반 페이징을 사용합니다.
DATA: lv_offset TYPE i VALUE 0,
lv_page TYPE i VALUE 200.
DO.
SELECT productionorder, material, totalquantity
FROM zc_autopartprodorder
WHERE plantcode = 'KR01'
ORDER BY productionorder
INTO TABLE @DATA(lt_page)
OFFSET @lv_offset
UP TO @lv_page ROWS.
IF lines( lt_page ) = 0. EXIT. ENDIF.
PERFORM process_chunk USING lt_page.
lv_offset = lv_offset + lv_page.
ENDDO.
자주 마주치는 함정과 FAQ
실무에서 I_ProductionOrder를 도입할 때 반복적으로 발생하는 이슈들입니다.
- Q1. AUFK에는 있는데 I_ProductionOrder에서 안 나옵니다. A. 대부분 오더 카테고리(
AUTYP) 문제입니다.I_ProductionOrder는 카테고리 10(PP 생산 오더)만 노출합니다. 프로세스 오더(카테고리 40)는I_ProcessOrder, 유지보수 오더는I_MaintenanceOrder를 사용해야 합니다. - Q2. 권한이 있는데도 데이터가 안 보입니다. A. DCL(Data Control Language)에 정의된 Access Control이 우선합니다.
C_AFKO_AWK의 플랜트/오더 타입 조합이 사용자 역할에 정확히 있어야 합니다. SU53으로 확인하세요. - Q3. 성능이 오히려 SE16보다 느립니다. A. VDM 뷰는 여러 조인을 포함하므로
WHERE절에 반드시 인덱스 필드(ProductionOrder,Production_Plant,MfgOrderPlannedStartDate)를 넣어야 합니다. 필드 하나 없이 전체 스캔하면 당연히 느려집니다. ST05로 실행 계획을 확인하는 습관을 권장합니다. - Q4. ABAP Unit 테스트는 어떻게 작성합니까? A.
cl_osql_test_environment로I_ProductionOrder의 테스트 더블을 생성해 실제 DB 없이도 로직 검증이 가능합니다. CI 파이프라인 통합 시 매우 유용합니다.
또 하나 빠지기 쉬운 함정은 필드명 대소문자입니다. CDS 뷰 정의에서는 CamelCase이지만 ABAP Open SQL에서는 소문자로 써야 합니다(productionorder). Eclipse ADT의 Code Completion(Ctrl+Space)을 적극 활용하세요.
댓글 0
아직 댓글이 없습니다.