ABAP

VBAK 없이 견적 조회 — I_Quotation CDS 3가지 #shorts #SAP #ABAP

▶ YouTube에서 보기

VBAK 직접 조회의 문제점과 한계

SAP ERP 시절부터 SD 모듈을 다뤄본 개발자라면 VBAK 테이블에 익숙할 것입니다. 견적(Quotation), 수주(Sales Order), 계약(Contract) 등 모든 판매 문서의 헤더가 이 하나의 테이블에 저장되기 때문에, VA21로 생성한 견적을 조회할 때도 SELECT * FROM vbak WHERE vbtyp = 'B' 같은 쿼리를 습관적으로 작성해 왔습니다. 그러나 S/4HANA로 넘어오면서 이런 접근 방식은 여러 문제를 안게 되었습니다.

  • 비즈니스 로직 누락: VBAK는 순수 데이터 테이블일 뿐, 통화 환산·상태 판정·조직 권한 필터링 로직이 전혀 포함되어 있지 않습니다.
  • 필드 의미의 불명확성: VBELN, AUART, VKORG 같은 4자리 도메인 코드로 구성되어 있어 가독성이 떨어지고, 신규 개발자에게 매번 필드 사전을 참조하게 만듭니다.
  • 확장 필드 미반영: S/4HANA에서 추가된 표준 확장 필드(예: 지속가능성 관련 필드, 통합 비즈니스 파트너 참조)가 VBAK에는 원시 형태로만 존재합니다.
  • Fiori/OData 재사용 불가: Fiori 앱과 동일한 데이터 계층을 재사용하려면 결국 CDS 뷰를 거쳐야 합니다.

이 글에서는 VA21 견적 데이터를 VBAK 대신 표준 Virtual Data Model(VDM) 뷰인 I_Quotation으로 조회하는 방법을 다룹니다. 이 글을 끝까지 읽으면 VBAK와 I_Quotation의 필드 매핑 관계, 기본 SELECT부터 조인·권한 체크까지 실전 쿼리 작성, ABAP OO 클래스에서 CDS 뷰를 안전하게 소비하는 패턴, 기존 레거시 리포트를 단계적으로 마이그레이션하는 전략을 익힐 수 있습니다.

시작 전 환경 준비

실습을 재현하려면 다음 환경이 필요합니다.

  • SAP 시스템: S/4HANA 2021 이상 (On-Premise 또는 Private Cloud Edition 권장). Public Cloud Edition에서는 ABAP Cloud 모델 하에서 동일하게 사용 가능합니다.
  • 개발 도구: ABAP Development Tools (ADT) 3.30 이상이 설치된 Eclipse. SE80/SE11로도 CDS 뷰를 열람할 수 있지만, 정의 확인과 미리보기(F8)에는 ADT가 훨씬 편리합니다.
  • 권한: 개발자 키(S_DEVELOP), CDS 뷰 조회를 위한 SD 조직 권한(V_VBAK_VKO 등), 최소한 하나의 견적 문서 데이터.
  • 테스트 데이터: VA21 트랜잭션으로 견적을 최소 3~5건 생성해 두면 조회 결과 확인이 용이합니다. 판매조직(VKORG)과 문서유형(AUART, 예: QT)을 미리 메모해 두세요.

버전이 낮은 시스템에서는 I_Quotation이 존재하지 않거나 필드 구성이 다를 수 있습니다. ADT의 Open ABAP Development Object (Ctrl+Shift+A) 검색에서 I_Quotation을 입력해 존재 여부부터 확인하시기 바랍니다.

I_Quotation CDS 뷰의 정체와 등장 배경

SAP는 S/4HANA 출시와 함께 Virtual Data Model(VDM)이라는 데이터 접근 표준을 도입했습니다. VDM은 물리 테이블 위에 세 계층의 CDS 뷰를 쌓아 올린 구조입니다.

VBAK는 창고에 쌓인 원자재, I_Quotation은 제품 라벨과 검수 스티커까지 붙여 출고 가능하게 만든 완제품 박스와 같습니다. 개발자는 원자재를 직접 꺼내 다듬을 필요 없이, 라벨을 보고 바로 사용하기만 하면 됩니다.

VDM 계층은 일반적으로 다음과 같이 구분됩니다.

  • Basic View (I_): 물리 테이블에 1:1로 근접하되, 필드명을 의미 있게 바꾸고 통화·수량·조직 연결을 정리한 계층. I_Quotation이 여기에 속합니다.
  • Composite View (C_): 여러 Basic View를 조합해 특정 업무 시나리오에 맞춘 계층.
  • Consumption View: Fiori/OData 노출을 위한 최상위 계층.

I_Quotation은 견적 문서(VBTYP = 'B')만 걸러낸 Basic 계층 뷰로, VBAK 위에 얹혀 있으면서도 통화 참조, 조직 필드 연결, 상태 필드 정규화 등의 어노테이션이 미리 부여되어 있습니다. 즉 원시 테이블을 그대로 노출하는 대신, 견적이라는 비즈니스 개념에 맞게 정제된 API를 제공합니다.

핵심 필드 구조와 VBAK 필드 매핑

실무에서 자주 쓰는 VBAK 필드가 I_Quotation에서 어떤 이름으로 바뀌었는지 정리해 두면 마이그레이션이 매우 수월해집니다.

VBAK 필드I_Quotation 필드의미
VBELNQuotation견적 문서 번호
AUARTSalesQuotationType견적 문서 유형
VKORGSalesOrganization판매 조직
VTWEGDistributionChannel유통 경로
SPARTOrganizationDivision부문
KUNNRSoldToParty판매처 고객
ERDATCreationDate생성일
NETWRTotalNetAmount순 금액 합계
WAERKTransactionCurrency거래 통화
GBSTKOverallSDProcessStatus전체 처리 상태

필드명이 훨씬 서술적이라 자동완성만으로도 의도 파악이 쉬워집니다.

기본 SELECT로 견적 헤더 조회하기

가장 기본적인 패턴부터 살펴봅니다. 특정 판매조직에서 오늘 생성된 견적을 조회하는 실전 예제입니다.

REPORT zdemo_quotation_basic.

DATA: lt_quotation TYPE TABLE OF i_quotation,
      ls_quotation TYPE i_quotation.

SELECT Quotation,
       SalesQuotationType,
       SoldToParty,
       CreationDate,
       TotalNetAmount,
       TransactionCurrency,
       OverallSDProcessStatus
  FROM I_Quotation
  WHERE SalesOrganization = '1010'
    AND CreationDate      = @sy-datum
  INTO TABLE @lt_quotation.

LOOP AT lt_quotation INTO ls_quotation.
  WRITE: / ls_quotation-Quotation,
           ls_quotation-SoldToParty,
           ls_quotation-TotalNetAmount CURRENCY ls_quotation-TransactionCurrency,
           ls_quotation-TransactionCurrency.
ENDLOOP.

주목할 점은 VBAK 쿼리에서 반드시 필요했던 VBTYP = 'B' 조건이 사라졌다는 것입니다. I_Quotation은 이미 뷰 정의 자체에서 견적으로 필터링되어 있기 때문입니다. 또한 WRITE 문에서 CURRENCY 지정 시 통화 필드를 그대로 사용할 수 있는 것은 뷰 내부에 이미 통화 참조 어노테이션이 걸려 있기 때문입니다.

아이템 연계 — I_QuotationItem과의 조인

실무 시나리오에서는 헤더만으로 부족하고 자재(Material)와 수량이 담긴 아이템까지 함께 조회해야 합니다. VBAK-VBAP 조인 패턴을 CDS로 옮겨봅니다.

TYPES: BEGIN OF ty_quot_detail,
         quotation_no    TYPE i_quotation-Quotation,
         sold_to         TYPE i_quotation-SoldToParty,
         item_no         TYPE i_quotationitem-SalesQuotationItem,
         material        TYPE i_quotationitem-Material,
         order_qty       TYPE i_quotationitem-RequestedQuantity,
         qty_unit        TYPE i_quotationitem-RequestedQuantityUnit,
         net_amount      TYPE i_quotationitem-NetAmount,
         currency        TYPE i_quotationitem-TransactionCurrency,
       END OF ty_quot_detail.

DATA: lt_detail TYPE TABLE OF ty_quot_detail.

TRY.
    SELECT h~Quotation           AS quotation_no,
           h~SoldToParty         AS sold_to,
           i~SalesQuotationItem  AS item_no,
           i~Material            AS material,
           i~RequestedQuantity   AS order_qty,
           i~RequestedQuantityUnit AS qty_unit,
           i~NetAmount           AS net_amount,
           i~TransactionCurrency AS currency
      FROM I_Quotation AS h
      INNER JOIN I_QuotationItem AS i
        ON h~Quotation = i~SalesQuotation
      WHERE h~SalesOrganization = @gv_vkorg
        AND h~CreationDate BETWEEN @gv_from AND @gv_to
        AND i~SalesQuotationItemCategory <> 'TATX'
      ORDER BY h~Quotation, i~SalesQuotationItem
      INTO TABLE @lt_detail.

    IF sy-subrc <> 0.
      MESSAGE |조회 기간에 견적이 없습니다.| TYPE 'I'.
      RETURN.
    ENDIF.

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

헤더 뷰(I_Quotation)의 키 필드명이 Quotation인 반면, 아이템 뷰(I_QuotationItem)에서 헤더를 가리키는 외래 키는 SalesQuotation이라는 점이 조인의 핵심입니다. 이는 CDS 뷰 어노테이션에서 연결(association) 관계에 의해 명명된 것이며, ADT에서 뷰 정의를 열어 확인할 수 있습니다.

권한 처리와 I_Quotation의 내장 체크

VBAK를 직접 조회하던 시절에는 AUTHORITY-CHECK OBJECT 'V_VBAK_VKO'를 명시적으로 호출해 판매 조직 권한을 검사하는 코드를 매번 작성해야 했습니다. 잊고 배포하면 감사에서 지적받는 단골 이슈였습니다.

I_QuotationAccess Control(DCL, Data Control Language)과 함께 사용하면 이 부담이 크게 줄어듭니다. 표준 뷰에는 대부분 @AccessControl.authorizationCheck: #CHECK 어노테이션이 걸려 있고, 이에 대응하는 DCL이 판매 조직·유통 경로 등을 자동으로 필터링합니다.

" 사용자가 판매조직 1010에만 권한이 있고
" 1020에는 없을 때, 아래 쿼리는 1010 결과만 반환합니다.
SELECT Quotation, SalesOrganization, SoldToParty
  FROM I_Quotation
  WHERE SalesOrganization IN ('1010', '1020')
  INTO TABLE @DATA(lt_result).

" 별도의 AUTHORITY-CHECK 호출이 없어도
" DCL이 SELECT 실행 시점에 WHERE 조건에 권한 필터를 자동으로 추가합니다.

다만 DCL이 모든 권한 객체를 자동 처리하는 것은 아니므로, 문서 유형(AUART)에 대한 별도 권한 등 특수 케이스는 여전히 명시적 체크가 필요할 수 있습니다. 뷰의 DCL 정의를 ADT에서 열어 어떤 권한이 자동 반영되는지 확인하는 습관을 들이시길 권장합니다.

ABAP 클래스에서 I_Quotation을 활용하는 프로덕션 패턴

실무 프로젝트에서는 SELECT 로직을 리포트에 늘어놓지 않고, 조회 책임을 갖는 클래스로 분리하는 것이 유지보수에 유리합니다. 다음은 견적 조회 서비스 클래스의 골격입니다.

CLASS zcl_quotation_reader DEFINITION
  PUBLIC FINAL CREATE PUBLIC.

  PUBLIC SECTION.
    TYPES: BEGIN OF ty_quot_summary,
             quotation    TYPE i_quotation-Quotation,
             sold_to      TYPE i_quotation-SoldToParty,
             total_amount TYPE i_quotation-TotalNetAmount,
             currency     TYPE i_quotation-TransactionCurrency,
             status       TYPE i_quotation-OverallSDProcessStatus,
           END OF ty_quot_summary,
           tt_quot_summary TYPE STANDARD TABLE OF ty_quot_summary WITH EMPTY KEY.

    METHODS get_open_quotations
      IMPORTING iv_sales_org      TYPE vkorg
                iv_valid_from     TYPE datum
      RETURNING VALUE(rt_result)  TYPE tt_quot_summary
      RAISING   cx_sy_open_sql_db.

ENDCLASS.

CLASS zcl_quotation_reader IMPLEMENTATION.

  METHOD get_open_quotations.

    SELECT Quotation,
           SoldToParty,
           TotalNetAmount   AS total_amount,
           TransactionCurrency AS currency,
           OverallSDProcessStatus AS status
      FROM I_Quotation
      WHERE SalesOrganization       = @iv_sales_org
        AND QuotationValidityStartDate >= @iv_valid_from
        AND OverallSDProcessStatus  = 'A'
      ORDER BY Quotation
      INTO CORRESPONDING FIELDS OF TABLE @rt_result.

  ENDMETHOD.

ENDCLASS.

이 구조의 장점은 다음과 같습니다.

  • 테스트 용이성: 조회 메서드가 명확한 입출력을 가지므로 ABAP Unit 테스트로 감싸기 쉽습니다. Test Double Framework(cl_osql_test_environment)를 사용하면 실제 DB 없이도 I_Quotation에 대한 목(mock) 데이터를 주입할 수 있습니다.
  • 재사용성: 여러 리포트/OData 서비스에서 동일 로직을 재활용할 수 있습니다.
  • 성능: 필요한 필드만 명시적으로 선언하여 SELECT * 대비 컬럼 스토어 HANA에서의 성능이 우수합니다.

흔한 실수와 트러블슈팅

Q1. SELECT * FROM I_Quotation을 실행했더니 VBAK보다 훨씬 느립니다. 왜죠?
CDS 뷰는 여러 어소시에이션과 계산 필드가 얹혀 있기 때문에, SELECT *는 불필요한 조인을 유발합니다. HANA 컬럼 스토어의 이점을 살리려면 반드시 필요한 필드만 명시하세요. 특히 통화 환산이나 상태 판정이 포함된 컴포지트 뷰에서는 SELECT * 대비 필드 지정이 10배 이상 빠른 경우가 흔합니다.

Q2. 권한이 있는데도 데이터가 안 나옵니다.
DCL이 자동 필터링을 하는 뷰라면, 사용자에게 판매 조직·유통 경로·부문 세 가지 모두에 대한 권한이 필요할 수 있습니다. SU53으로 최근 권한 체크 실패 로그를 확인하고, PFCG 롤에 누락된 조직 값을 추가하세요. 개발 시스템에서는 SACM 트랜잭션으로 DCL 시뮬레이션도 가능합니다.

Q3. 커스텀 Z 필드를 VBAK에 추가했었는데, I_Quotation에서는 안 보입니다.
Basic 뷰는 표준 필드 위주로 구성되므로, Z 필드는 Extension View로 별도 확장하거나, Key User Extensibility의 커스텀 CDS 뷰를 생성해 조인해야 합니다. VBAK를 직접 SELECT하는 것은 최후의 수단으로만 고려하세요.

Q4. OverallSDProcessStatus 값이 예상과 다릅니다.
CDS 뷰의 상태 필드는 원시 VBUK 상태와 다를 수 있습니다. 뷰 정의에서 어떤 소스 필드로부터 파생되는지 ADT에서 확인하고, 값 목록은 도메인 GBSTA를 참조하세요.

VBAK에서 I_Quotation으로의 마이그레이션 전략

레거시 리포트를 한 번에 전면 개편하는 것은 위험합니다. 다음 3단계 접근이 실무에서 안정적입니다.

  1. 1단계 — 인벤토리 작성: SE80 Cross-Reference 또는 코드 검색(Ctrl+H)을 통해 VBAK를 직접 SELECT하는 모든 프로그램을 목록화합니다. 영향 범위를 파악하는 것이 출발점입니다.
  2. 2단계 — 신규 개발부터 CDS 뷰 강제: 신규 개발과 기존 리포트의 큰 변경 건에 대해 I_Quotation 사용을 코드 리뷰 체크리스트에 포함시킵니다. 병렬 유지가 잠시 이어지더라도, 새 코드가 레거시 패턴을 상속하지 않도록 방지하는 것이 우선입니다.
  3. 3단계 — 성능·기능 검증 후 치환: 트래픽이 많은 리포트부터 CDS로 치환하되, 반드시 A/B 결과 비교(레코드 수, 합계 금액)를 수행합니다. 상태 필드 파생 로직이 다를 수 있으므로 무조건 신뢰하지 말고 샘플링 검증을 권장합니다.

이 전략을 6~12개월 로드맵에 담아 추진하면, S/4HANA 후속 업그레이드나 Public Cloud로의 확장에도 훨씬 유연하게 대응할 수 있습니다. VBAK는 여전히 물리 테이블로 남아 있지만, 애플리케이션 계층의 표준 진입점은 이제 I_Quotation이라는 사실을 팀 전체가 공유하는 것이 마이그레이션 성공의 첫걸음입니다.

댓글 0

아직 댓글이 없습니다.