ABAP

VBAP 직접 조회 — ABAP 개발자 90% 실수 #shorts #SAP #ABAP

▶ YouTube에서 보기

1. 이 글에서 다루는 범위와 도달 지점

S/4HANA 환경에서 판매 오더 아이템 정보를 조회할 때, 더 이상 VBAP 테이블을 직접 SELECT하는 것은 권장되지 않습니다. 대신 SAP가 제공하는 가상 데이터 모델(Virtual Data Model, VDM) 계층의 I_SalesOrderItem CDS View를 사용해야 합니다. 이 글은 I_SalesOrderItem의 구조와 키 필드, 연관(Association) 사용법을 짚고, 자재(Material)·수량(Quantity)·단위(Unit) 정보를 안전하게 읽어내는 실전 예제를 다룹니다.

  • VBAP와 I_SalesOrderItem의 의미적 차이 이해
  • 키 필드 SalesOrder + SalesOrderItem 조합 사용법 숙지
  • Association을 통한 헤더·자재 마스터 조인 패턴 학습
  • 대량 데이터 처리 시 성능·권한 체크 포인트 점검
  • RAP/Fiori 연계 시 주의해야 할 노출 범위 이해

2. 사전에 익혀두면 좋은 사항

이 글은 ABAP CDS View의 기본 문법(define view, association, composition)을 알고 있다고 가정합니다. 또한 SD(Sales & Distribution) 모듈의 VBAK(헤더)·VBAP(아이템)·VBEP(스케줄 라인) 테이블 관계, S/4HANA의 Annotation 기반 모델링, 그리고 ABAP Development Tools(ADT) 사용 경험이 있으면 이해가 빠릅니다. @Semantics.quantity.unitOfMeasure 같은 시맨틱 어노테이션의 의미도 미리 살펴두면 좋습니다.

3. 동작 환경과 준비 조건

이 글의 코드는 다음 환경을 기준으로 작성되었습니다.

  • SAP S/4HANA 2022 FPS02 이상 (On-Premise) 또는 SAP S/4HANA Cloud Public Edition 2023
  • ABAP Platform 2022 이상, ABAP Development Tools 3.40 이상
  • HANA DB 2.0 SPS06 이상 권장
  • 릴리스 컨트랙트: I_SalesOrderItem은 일반적으로 C1(Cloud Development) 및 C2 컨트랙트로 릴리스되어 있어 RAP/UI 서비스에서 직접 참조 가능
  • 권한 객체: V_VBAK_AAT, V_VBAK_VKO 등 SD 표준 권한 부여 필요

On-Premise 환경에서는 SE11 또는 ADT의 Data Preview 기능으로 뷰의 메타데이터를 먼저 확인하길 권장합니다. Cloud 환경에서는 Released Object 목록을 통해 사용 가능 여부를 검증해야 합니다.

4. 핵심 개념 — VDM 계층과 I_SalesOrderItem의 위상

SAP의 가상 데이터 모델은 크게 세 계층으로 나뉩니다. 기초(Basic) 뷰는 I_ 접두어를 가지며 원시 테이블에 1:1로 가까운 의미 모델을 제공합니다. 복합(Composite) 뷰는 C_ 접두어로 여러 Basic 뷰를 결합해 비즈니스 시나리오에 맞는 데이터를 만듭니다. 마지막으로 소비(Consumption) 뷰는 OData/UI 서비스 노출용입니다.

I_SalesOrderItem은 이 중 가장 기초가 되는 Interface View로, 물리 테이블 VBAP을 의미적으로 재정의한 객체입니다. 직접 VBAP을 읽는 것과 비교하면 다음과 같은 차이가 있습니다.

구분VBAP (테이블)I_SalesOrderItem (CDS)
필드명VBELN, POSNR, MATNRSalesOrder, SalesOrderItem, Material
수량/단위 매핑수동으로 KWMENG ↔ VRKME 결합Semantic Annotation으로 자동 연결
통화/환율 변환직접 함수 호출 필요@Semantics.amount.currencyCode 기반 자동
연관 객체 접근JOIN 수동 작성_SalesOrder, _Material Association
업그레이드 안정성필드 변경 위험릴리스 컨트랙트로 호환성 보장 수준 향상

비유하자면 VBAP이 "원자재 창고의 라벨 없는 상자"라면, I_SalesOrderItem은 "품명·중량·재고단위가 명확히 적힌 진열대 상품"에 가깝습니다. 같은 내용물이라도 소비자(개발자)가 안전하게 다룰 수 있는 형태로 가공되어 있다는 뜻입니다.

또한 Association은 데이터베이스 JOIN과 달리 "지연 평가(lazy evaluation)"되므로, SELECT 절에서 실제 참조하지 않으면 JOIN이 발생하지 않습니다. 이 점이 성능 최적화의 핵심입니다.

5. 단계별 실전 예제

5-1단계 — 기본 조회: 특정 판매 오더의 아이템 목록 읽기

가장 단순한 시나리오로, 판매 오더 번호를 입력받아 해당 오더의 모든 아이템과 자재·수량 정보를 출력합니다.

REPORT zr_so_item_basic.

PARAMETERS: p_vbeln TYPE vbeln_va OBLIGATORY.

SELECT salesorder,
       salesorderitem,
       material,
       salesorderitemtext,
       requestedquantity,
       requestedquantityunit,
       netamount,
       transactioncurrency
  FROM i_salesorderitem
  WHERE salesorder = @p_vbeln
  INTO TABLE @DATA(lt_result).

LOOP AT lt_result INTO DATA(ls_item).
  WRITE: / ls_item-salesorderitem,
           ls_item-material,
           ls_item-requestedquantity,
           ls_item-requestedquantityunit,
           ls_item-netamount,
           ls_item-transactioncurrency.
ENDLOOP.

여기서 RequestedQuantity는 고객 요청 수량이며, 단위는 RequestedQuantityUnit(VRKME) 필드에 의미적으로 묶여 있습니다. 통화 또한 NetAmountTransactionCurrency와 자동으로 연동됩니다.

5-2단계 — 실무 시나리오: Association 활용 + 예외 처리·로깅

실제 업무에서는 아이템 정보뿐 아니라 자재 마스터의 자재 그룹, 판매 오더 헤더의 판매 조직까지 한 번에 필요한 경우가 많습니다. 이때 Association을 사용하면 JOIN을 명시적으로 쓰지 않아도 됩니다.

CLASS zcl_so_item_reader DEFINITION PUBLIC FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    TYPES: BEGIN OF ty_item_enriched,
             sales_order      TYPE vbeln_va,
             item_no          TYPE posnr_va,
             material         TYPE matnr,
             material_group   TYPE matkl,
             req_qty          TYPE kwmeng,
             req_qty_unit     TYPE vrkme,
             sales_org        TYPE vkorg,
           END OF ty_item_enriched.

    METHODS read_items
      IMPORTING iv_sales_order TYPE vbeln_va
      RETURNING VALUE(rt_items) TYPE TABLE OF ty_item_enriched
      RAISING   cx_sy_open_sql_db.
ENDCLASS.

CLASS zcl_so_item_reader IMPLEMENTATION.
  METHOD read_items.
    TRY.
        SELECT it~salesorder       AS sales_order,
               it~salesorderitem   AS item_no,
               it~material         AS material,
               mat~materialgroup   AS material_group,
               it~requestedquantity AS req_qty,
               it~requestedquantityunit AS req_qty_unit,
               hdr~salesorganization AS sales_org
          FROM i_salesorderitem AS it
            LEFT OUTER JOIN i_product   AS mat ON mat~product = it~material
            LEFT OUTER JOIN i_salesorder AS hdr ON hdr~salesorder = it~salesorder
          WHERE it~salesorder = @iv_sales_order
          INTO CORRESPONDING FIELDS OF TABLE @rt_items.
      CATCH cx_sy_open_sql_db INTO DATA(lx_db).
        RAISE EXCEPTION lx_db.
    ENDTRY.
  ENDMETHOD.
ENDCLASS.

이 코드에서는 I_ProductI_SalesOrder를 명시적으로 LEFT OUTER JOIN하여 자재 그룹과 판매 조직을 함께 가져옵니다. Association 표기(_Material.MaterialGroup)를 path 형태로 사용할 수도 있지만, 가독성과 SQL 트레이싱 편의를 위해 명시적 JOIN을 선호하는 팀도 많습니다.

5-3단계 — 프로덕션 패턴: 페이징·권한·단위 테스트

대량 데이터 처리 시 한 번에 모든 아이템을 메모리로 올리면 단기 메모리를 초과할 수 있습니다. 페이징과 권한 객체 점검, 그리고 ABAP Unit 테스트까지 포함한 형태가 안전합니다.

METHOD read_items_paged.
  CONSTANTS lc_page_size TYPE i VALUE 500.

  AUTHORITY-CHECK OBJECT 'V_VBAK_VKO'
    ID 'VKORG' FIELD iv_sales_org
    ID 'ACTVT' FIELD '03'.
  IF sy-subrc <> 0.
    RAISE EXCEPTION TYPE zcx_authorization.
  ENDIF.

  DATA(lv_offset) = 0.
  DO.
    SELECT salesorder, salesorderitem, material,
           requestedquantity, requestedquantityunit
      FROM i_salesorderitem
      WHERE salesorganization = @iv_sales_org
        AND creationdate     >= @iv_from_date
      ORDER BY salesorder, salesorderitem
      INTO TABLE @DATA(lt_chunk)
      OFFSET @lv_offset
      UP TO @lc_page_size ROWS.

    IF lt_chunk IS INITIAL. EXIT. ENDIF.
    APPEND LINES OF lt_chunk TO rt_items.
    lv_offset = lv_offset + lc_page_size.
  ENDDO.
ENDMETHOD.

단위 테스트는 cl_osql_test_environment를 사용해 CDS View 자체를 모킹할 수 있습니다.

CLASS ltc_reader DEFINITION FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
  PRIVATE SECTION.
    DATA mo_env TYPE REF TO if_osql_test_environment.
    METHODS setup.
    METHODS test_read FOR TESTING.
ENDCLASS.

CLASS ltc_reader IMPLEMENTATION.
  METHOD setup.
    mo_env = cl_osql_test_environment=>create(
      i_dependency_list = VALUE #( ( 'I_SALESORDERITEM' ) ) ).
  ENDMETHOD.

  METHOD test_read.
    mo_env->insert_test_data( VALUE i_salesorderitem_t = (
      ( salesorder = '0000010001' salesorderitem = '000010'
        material = 'MAT-PUMP-001' requestedquantity = '5.000'
        requestedquantityunit = 'PC' ) ) ).
  ENDMETHOD.
ENDCLASS.

6. 자주 마주치는 함정과 FAQ

Q1. RequestedQuantity를 더했더니 단위가 섞여 합계가 이상합니다.

A1. 한 오더 안에서도 아이템별로 단위(PC, KG, L 등)가 다를 수 있습니다. 합계를 내려면 반드시 RequestedQuantityUnit 별로 GROUP BY 하거나, 단위 변환 함수 UNIT_CONVERSION으로 기준 단위로 정규화한 뒤 합산하세요.

Q2. ADT에서 Data Preview가 비어 있는데 SE16에서는 VBAP에 데이터가 있습니다.

A2. I_SalesOrderItem은 권한 체크 어노테이션이 일반적으로 활성화되어 있습니다. 판매 조직 권한이 없으면 결과가 비어 보입니다. 사용자에게 V_VBAK_VKO가 부여되었는지 확인하세요.

Q3. I_SalesOrderItem이 Cloud 시스템에서 보이지 않습니다.

A3. Public Cloud 에디션은 릴리스된(Released) 객체만 사용 가능합니다. Released Objects 앱이나 ABAP Repository Tree에서 C1/C2 컨트랙트 여부를 확인해야 합니다.

Q4. Association _SalesOrder를 썼는데 SQL 트레이스에 JOIN이 두 번 나타납니다.

A4. SELECT 절과 WHERE 절에서 같은 Association을 path 형태로 두 번 참조하면 옵티마이저가 중복 JOIN을 생성할 수 있습니다. 명시적 JOIN으로 바꾸는 것이 일반적으로 더 안정적입니다.

7. 이어서 탐색하면 좋은 주제

  • I_SalesOrderScheduleLine — 납기 일정과 확약 수량 분석
  • I_SalesOrderItemPartner — 아이템 레벨의 파트너 역할(Ship-to, Bill-to)
  • I_SalesOrderPricingElement — 가격 결정 절차의 조건 유형 분해
  • RAP Behavior Definition을 통한 판매 오더 변경 트랜잭션 구현
  • CDS View Entity(define view entity) 문법으로의 마이그레이션

8. 더 깊이 살펴볼 수 있는 자료

  • SAP S/4HANA Virtual Data Model (VDM) — help.sap.com
  • ABAP CDS — Defining View Entities — help.sap.com
  • Released CDS Views for Sales — help.sap.com
  • SAP Business Accelerator Hub — API_SALES_ORDER_SRV
  • SAP Community — CDS Views 태그 블로그
  • ABAP Open SQL Test Double Framework — help.sap.com

댓글 0

아직 댓글이 없습니다.