ABAP

VBAK 없이 판매 오더 조회하는 3가지 #shorts #SAP #ABAP

▶ YouTube에서 보기

개요와 이 글에서 얻어갈 것

S/4HANA 환경에서 판매 오더 헤더 데이터를 조회할 때 여전히 VBAK 테이블을 직접 SELECT 하는 코드를 자주 보게 됩니다. 하지만 SAP는 S/4HANA 이후로 판매 오더 헤더 정보를 표준 CDS 뷰인 I_SalesOrder를 통해 노출하도록 아키텍처를 재정의했습니다. 이 글에서는 왜 VBAK 직접 조회가 더 이상 권장되지 않는지, I_SalesOrder가 제공하는 부가 가치는 무엇인지, 그리고 실제 리포트/오데이터/RAP 서비스에서 어떻게 활용하는지를 단계별로 정리합니다.

  • VBAK 조회의 한계와 CDS 뷰 전환 배경 파악
  • I_SalesOrder의 어소시에이션 구조와 어노테이션 활용법 이해
  • 기본 SELECT부터 필터/조인/예외 처리까지 실전 코드 3단계 습득
  • ODATA/RAP 서비스 연계 및 성능 튜닝 포인트 확인

미리 알아두면 좋은 것

이 글은 ABAP 기본 문법(SELECT, INTO TABLE, LOOP)과 CDS 뷰의 기초 개념(DEFINE VIEW, association)을 알고 있다는 전제하에 진행합니다. S/4HANA on-premise 또는 Cloud 환경에서 ADT(ABAP Development Tools)를 사용해본 경험이 있다면 이해가 훨씬 수월합니다. 판매 오더 프로세스(SD 모듈)의 기본 흐름(견적 → 오더 → 납품 → 청구)에 대한 기초 지식도 예제 해석에 도움이 됩니다.

대상 버전과 준비 환경

이 글의 예제는 다음 환경을 기준으로 작성되었습니다. 버전에 따라 I_SalesOrder의 필드 구성이나 어노테이션이 일부 다를 수 있으므로, 반드시 자신의 시스템에서 ADT → Data Preview로 실제 필드를 확인하시기 바랍니다.

  • SAP S/4HANA: 2021 이상 (on-premise) 또는 SAP S/4HANA Cloud Public Edition
  • ABAP Platform: 7.55 이상 (New ABAP SQL 문법 지원)
  • 개발 도구: ADT for Eclipse 최신 버전 권장
  • 권한: S_RS_AUTH, S_DEVELOP, 판매 조직 접근 권한
  • CDS 뷰 위치: I_SalesOrder (헤더), I_SalesOrderItem (아이템), I_SalesOrderScheduleLine (스케줄 라인)

ABAP Cloud(Steampunk, RAP) 환경에서는 VBAK에 직접 접근하는 것 자체가 릴리스 대상이 아니므로, 사실상 I_SalesOrder 계열만 사용 가능합니다.

왜 VBAK 대신 I_SalesOrder인가

VBAK은 R/3 시절부터 존재한 물리 테이블입니다. 판매 오더 헤더 원시 데이터가 저장되지만, 여기에는 몇 가지 구조적 한계가 있습니다.

비유하자면 VBAK은 창고 안 원자재 상자이고, I_SalesOrder는 창고 관리자가 라벨을 붙이고 관련 부품(고객, 통화, 판매조직 텍스트)까지 함께 묶어 내놓은 완제품 패키지입니다.

VBAK 직접 조회의 문제점

  • 텍스트 결합 필요: 고객명은 KNA1, 판매조직 텍스트는 TVKOT, 통화 단위는 TCURC 등 별도 조인이 필수
  • 클라우드 비호환: ABAP Cloud/Steampunk에서 물리 테이블 직접 접근은 릴리스 계약(C1) 대상이 아님
  • 권한 체크 수동 처리: AUTHORITY-CHECK를 별도로 넣어야 함
  • Fiori/UI 어노테이션 부재: 라벨, 검색 도움, 필드 유형 등 UI 메타데이터가 없음

I_SalesOrder가 제공하는 부가 가치

I_SalesOrder는 SAP가 정의한 인터페이스(Interface) 계층 CDS 뷰입니다. 접두어 I_가 인터페이스 뷰임을 의미하며, 릴리스 계약 C1(안정 계약)으로 보호되어 향후 릴리스에서도 하위 호환이 일반적으로 유지됩니다.

  • association으로 _SoldToParty, _SalesOrganization, _Currency 등 관련 엔티티에 즉시 접근
  • DCL(Data Control Language)로 판매 조직 기반 권한이 자동 적용
  • @Semantics.amount.currencyCode 같은 어노테이션으로 금액-통화 연계 처리
  • Analytical/Transactional 어노테이션으로 Fiori Elements와 즉시 결합 가능

정리하자면, VBAK은 "데이터가 있는 곳"이고 I_SalesOrder는 "데이터를 안전하고 편리하게 쓸 수 있는 곳"입니다.

실전 예제 1단계: 기본 조회

가장 단순한 형태로 특정 판매 조직의 최근 100건 오더 헤더를 가져와 봅니다. VBAK 시절에는 조인이 필요했던 통화 코드와 고객 정보가 어소시에이션 한 줄로 해결됩니다.

REPORT z_so_header_basic.

SELECT SalesOrder,
       SalesOrderType,
       SalesOrganization,
       SoldToParty,
       TotalNetAmount,
       TransactionCurrency,
       CreationDate
  FROM I_SalesOrder
  WHERE SalesOrganization = '1710'
    AND CreationDate     >= @( cl_abap_context_info=>get_system_date( ) - 30 )
  ORDER BY CreationDate DESCENDING
  INTO TABLE @DATA(lt_recent_orders)
  UP TO 100 ROWS.

LOOP AT lt_recent_orders INTO DATA(ls_row).
  WRITE: / ls_row-salesorder,
           ls_row-soldtoparty,
           ls_row-totalnetamount,
           ls_row-transactioncurrency.
ENDLOOP.

주목할 점은 필드명이 VBAK 시절의 이름(VBELN, VKORG)이 아닌 SalesOrder, SalesOrganization으로 의미가 명확하다는 것입니다. 이는 CDS 뷰 표준 명명 규칙에 따른 것이며, 협업과 유지보수 시 큰 장점이 됩니다.

실전 예제 2단계: 어소시에이션과 예외 처리

실무에서는 헤더만으로 끝나지 않습니다. 고객명, 아이템 개수, 로그 남기기까지 함께 처리하는 형태가 일반적입니다. 아래는 특정 판매 조직의 오더를 조회해 아이템 수를 계산하고, 예외 상황을 처리하는 예제입니다.

CLASS zcl_so_reader DEFINITION PUBLIC FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    TYPES: BEGIN OF ty_report,
             sales_order    TYPE i_salesorder-salesorder,
             customer_name  TYPE string,
             item_count     TYPE i,
             net_amount     TYPE i_salesorder-totalnetamount,
             currency       TYPE i_salesorder-transactioncurrency,
           END OF ty_report.

    METHODS fetch_orders
      IMPORTING iv_sales_org TYPE vkorg
      RETURNING VALUE(rt_report) TYPE STANDARD TABLE OF ty_report
      RAISING   cx_sy_open_sql_db.
ENDCLASS.

CLASS zcl_so_reader IMPLEMENTATION.
  METHOD fetch_orders.
    TRY.
        SELECT so~SalesOrder,
               cust~CustomerName    AS customer_name,
               so~TotalNetAmount    AS net_amount,
               so~TransactionCurrency AS currency
          FROM I_SalesOrder AS so
          LEFT OUTER JOIN I_Customer AS cust
                 ON cust~Customer = so~SoldToParty
          WHERE so~SalesOrganization = @iv_sales_org
          INTO TABLE @DATA(lt_raw).

        LOOP AT lt_raw INTO DATA(ls_raw).
          SELECT COUNT(*) FROM I_SalesOrderItem
            WHERE SalesOrder = @ls_raw-salesorder
            INTO @DATA(lv_count).

          APPEND VALUE #(
            sales_order   = ls_raw-salesorder
            customer_name = ls_raw-customer_name
            item_count    = lv_count
            net_amount    = ls_raw-net_amount
            currency      = ls_raw-currency
          ) TO rt_report.
        ENDLOOP.

      CATCH cx_sy_open_sql_db INTO DATA(lx_db).
        MESSAGE lx_db->get_text( ) TYPE 'E'.
        RAISE EXCEPTION lx_db.
    ENDTRY.
  ENDMETHOD.
ENDCLASS.

여기서 눈여겨봐야 할 부분은 두 가지입니다. 첫째, I_Customer와의 조인을 통해 KNA1을 직접 건드리지 않고도 고객명을 얻습니다. 둘째, cx_sy_open_sql_db 예외를 명시적으로 잡아 상위 호출자가 판단할 수 있도록 재발생시킵니다.

실전 예제 3단계: 사용자 정의 뷰와 단위 테스트

실제 운영 환경에서는 성능, 테스트, 보안을 모두 고려해야 합니다. 아래는 미결 오더만 노출하는 사용자 정의 뷰 ZC_MyOpenSalesOrder와 이를 검증하는 단위 테스트입니다.

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Open Sales Order Header View'
@Metadata.allowExtensions: true
define view entity ZC_MyOpenSalesOrder
  as select from I_SalesOrder as so

  association [0..1] to I_Customer         as _Customer
    on $projection.SoldToParty = _Customer.Customer

  association [0..1] to I_SalesOrganization as _SalesOrg
    on $projection.SalesOrganization = _SalesOrg.SalesOrganization
{
  key so.SalesOrder,
      so.SalesOrderType,
      so.SalesOrganization,
      so.SoldToParty,
      so.OverallSDProcessStatus,

      @Semantics.amount.currencyCode: 'TransactionCurrency'
      so.TotalNetAmount,
      so.TransactionCurrency,
      so.CreationDate,

      _Customer,
      _SalesOrg
}
where so.OverallSDProcessStatus <> 'C';
CLASS ltcl_open_orders DEFINITION FINAL
  FOR TESTING RISK LEVEL HARMLESS DURATION SHORT.
  PRIVATE SECTION.
    METHODS: only_open_orders FOR TESTING RAISING cx_static_check.
ENDCLASS.

CLASS ltcl_open_orders IMPLEMENTATION.
  METHOD only_open_orders.
    SELECT SalesOrder, OverallSDProcessStatus
      FROM ZC_MyOpenSalesOrder
      INTO TABLE @DATA(lt_test)
      UP TO 50 ROWS.

    LOOP AT lt_test INTO DATA(ls_test).
      cl_abap_unit_assert=>assert_not_equals(
        act  = ls_test-overallsdprocessstatus
        exp  = 'C'
        msg  = '완료 오더가 결과에 포함되어 있음' ).
    ENDLOOP.
  ENDMETHOD.
ENDCLASS.

성능 관점에서는 @ObjectModel.usageType@AccessControl.authorizationCheck 어노테이션이 핵심입니다. 전자는 옵티마이저에게 뷰의 용도와 크기 힌트를 주고, 후자는 DCL 기반 권한 검사를 자동 적용합니다.

자주 하는 실수와 해결 방법

Q1. I_SalesOrder에 SELECT를 걸었는데 필드가 안 보입니다.

가장 흔한 원인은 필드명을 VBAK 시절의 이름(VBELN, ERDAT)으로 쓴 경우입니다. CDS 뷰는 SalesOrder, CreationDate 같은 CamelCase 논리명을 사용합니다. ADT에서 F2로 뷰를 열어 정확한 필드명을 확인하세요.

Q2. Customizing 필드(Z 필드)를 추가하려면?

I_SalesOrder는 SAP 표준 인터페이스라 직접 수정할 수 없습니다. @Metadata.allowExtensions: true가 있는 Consumption View를 확장하거나, CDS View Extension 기능을 사용해야 합니다.

Q3. 성능이 오히려 VBAK보다 느립니다.

CDS 뷰는 여러 어소시에이션과 조인이 내부에 포함되어 있어 모든 필드SELECT *로 가져오면 오버헤드가 큽니다. 반드시 필요한 필드만 명시적으로 선택하고, WHERE 조건에 인덱스가 존재하는 키 필드(SalesOrganization, SalesOrder 등)를 우선 배치하세요. HANA의 경우 SQL Trace(ST05)Plan Explanation으로 실행 계획을 확인하는 것이 일반적입니다.

Q4. ABAP Cloud에서 VBAK을 참조하니 에러가 납니다.

정상 동작입니다. ABAP Cloud는 C1 릴리스 계약된 오브젝트만 사용 가능하며, VBAK은 대상이 아닙니다. 대체 목록은 SAP Note 및 Released Objects 카탈로그에서 확인할 수 있습니다.

Q5. I_SalesOrder와 A_SalesOrder는 뭐가 다른가요?

I_는 인터페이스(재사용용) 뷰, A_는 API 뷰(OData 서비스용)입니다. 리포트/내부 로직에는 I_, OData/외부 노출에는 A_를 사용하는 것이 권장 패턴입니다.

이어서 살펴볼 만한 주제

이 글에서 다룬 I_SalesOrder는 판매 오더 모델링의 시작점입니다. 다음 단계로 확장하면 좋은 주제들입니다.

  • RAP(RESTful ABAP Programming)로 판매 오더 생성/변경 서비스 구현
  • Behavior DefinitionI_SalesOrderTP(Transactional Processing) 뷰 활용
  • ABAP SQL CTE(Common Table Expression)로 복잡한 판매 리포트 최적화
  • Analytical Query(@Analytics.query: true)로 판매 실적 대시보드 구성
  • Custom Fields & Logic(Fiori Key User Extensibility)로 표준 뷰 확장

더 읽어볼 문서와 링크

  • SAP Help Portal - S/4HANA Cloud: Sales Order CDS Views
  • SAP Help Portal - ABAP CDS Development Guide
  • SAP API Business Hub - Sales Order (A2X) API
  • SAP Community - ABAP Topic Hub

마지막으로 강조하고 싶은 점은, VBAK은 여전히 데이터베이스에 존재하지만 애플리케이션 레이어에서는 CDS 뷰가 표준 접근점이라는 사실입니다. 새로운 로직을 작성할 때는 항상 I_로 시작하는 인터페이스 뷰를 먼저 찾아보는 습관을 들이는 것이 S/4HANA 시대의 ABAP 개발자에게 요구되는 자세입니다.

댓글 0

아직 댓글이 없습니다.