ABAP

Analytical Query Variable 설정, 50%가 Value Help 빠뜨립니다 #shorts #SAP #ABAP

개요 및 학습할 내용

ABAP Analytical Query는 SAP S/4HANA의 분석 모델(Analytical Cube + Query) 위에서 사용자가 직접 조회 조건을 입력할 수 있도록 Variable을 제공합니다. Variable은 단순한 파라미터를 넘어, Value Help 연결, 기본값 자동 파생, 단일/범위/다중 선택 모드 지원 등 다양한 UX를 제공합니다. 이 글에서는 Analytical Query CDS View(@Analytics.query: true)에서 Variable을 정의하고 Value Help CDS View와 연결하는 패턴을 단계별로 다룹니다.

  • Analytical Query Variable의 종류와 동작 방식 이해
  • @Consumption.filter.selectionType의 #SINGLE / #RANGE / #MULTIPLE 차이
  • @Consumption.derivation으로 기본값(예: 로그인 사용자, 회계연도) 자동 파생
  • Value Help CDS View 연결 패턴 (@ObjectModel.foreignKey.association, @Consumption.valueHelpDefinition)
  • Mandatory / Optional Variable이 Fiori 화면과 OData 쿼리에 미치는 영향

사전 준비

아래 항목을 미리 익히고 시작하면 흐름을 따라가기가 수월합니다.

  • ABAP CDS View(DEFINE VIEW ENTITY) 기본 문법과 association 정의
  • Analytical Model 3계층 구조: Cube View → Query View → Consumption
  • Fiori Elements Analytical List Page(ALP) 또는 Multi-Dimensional Report의 필터 영역 동작
  • SAP Gateway 또는 RAP Service Binding으로 OData V4 노출 경험

환경 및 버전 정보

아래 환경에서 동작을 검증하는 것을 권장합니다. 버전이 낮을수록 annotation 일부가 지원되지 않을 수 있어, 사용 전 SAP Note와 릴리스 노트를 확인하는 것이 일반적입니다.

  • SAP S/4HANA 2022 FPS02 이상 또는 S/4HANA Cloud Public Edition 2308 이상
  • ABAP Platform 2023 / ABAP Cloud (Steampunk) 환경에서도 동일한 annotation 사용 가능
  • 개발 도구: ADT(ABAP Development Tools) for Eclipse 최신 패치
  • 미리보기: ADT의 Data Preview, Fiori Elements Preview, 또는 Analytical Query Browser(RSRT 후속 도구)
  • 권장 명명 규칙: Cube는 ZC_, Query는 ZQ_, Value Help는 ZI_..._VH
주의: @Analytics.query: true가 붙은 view에서만 Variable의 모든 annotation이 정상 해석됩니다. 일반 CDS view에서 동일 annotation을 사용하면 무시되거나 활성화 단계에서 경고가 발생할 수 있습니다.

핵심 개념 — Variable, Selection Type, Value Help

Analytical Query Variable은 "리포트 실행 시 사용자가 입력하는 필터값"입니다. 일반 CDS의 parameter(WITH PARAMETERS)와는 다르게, Query View의 element에 @Consumption.filter annotation을 붙여 정의합니다. 즉 "별도 입력칸"이 아니라 "실제 컬럼에 대한 필터 동작 자체를 사용자 입력으로 만든다"는 개념입니다.

비유하자면 일반 CDS parameter는 음식 주문 시 미리 적어둔 메모지(쿼리 실행 전 반드시 값이 정해져야 함)이고, Analytical Variable은 식당 테이블 위의 양념통(있어도 되고 없어도 되며, 사용자가 그때그때 조정)에 가깝습니다.

  • selectionType
    • #SINGLE: 단일값만 허용 (예: 단일 회계연도)
    • #RANGE: BETWEEN/GT/LT 등 범위 연산자 허용 (예: 게시일 from~to)
    • #MULTIPLE: 여러 값 동시 선택 (예: 회사코드 여러 개)
  • mandatory: true이면 사용자가 값을 입력해야만 실행되며, Fiori 필터 바에 별표(*)가 표시됩니다.
  • defaultValue / derivation: 초기값을 정적으로 지정하거나, @Consumption.derivation으로 시스템 컨텍스트(현재 사용자, 오늘 날짜, 현재 회계연도 등)에서 파생합니다.
  • Value Help: 입력 칸 옆 돋보기 버튼에서 호출되는 검색 도움말. @Consumption.valueHelpDefinition 또는 association 기반 @ObjectModel.foreignKey.association으로 연결합니다.

구조를 그림으로 풀면 다음과 같습니다.

[ Basic View ZI_Sales ]
        |
        v
[ Cube View  ZC_SalesCube ]   @Analytics.dataCategory: #CUBE
        |
        v
[ Query View ZQ_SalesQuery ]  @Analytics.query: true
        |  +-- element CompanyCode  -> Variable (MULTIPLE, mandatory)
        |  +-- element FiscalYear   -> Variable (SINGLE, derivation)
        |  +-- element PostingDate  -> Variable (RANGE, optional)
        |
        v
[ Service Binding / Fiori ALP ]

실전 코드 3단계

1단계 — 기본 Variable 정의

가장 단순한 형태입니다. Cube View 위에 Query View를 올리고, 두 개의 element에 Variable annotation을 부여합니다. CompanyCode는 다중 선택 필수, FiscalYear는 단일 선택 옵션입니다.

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Query - Basic Variables'
@Analytics.query: true
@OData.publish: false
define view entity ZQ_SALES_QUERY_BASIC
  as select from ZC_SALES_CUBE
{
  @Consumption.filter:
    { selectionType: #MULTIPLE,
      multipleSelections: true,
      mandatory: true }
  key CompanyCode,

  @Consumption.filter:
    { selectionType: #SINGLE,
      mandatory: false,
      defaultValue: '2026' }
  key FiscalYear,

  @Consumption.filter:
    { selectionType: #RANGE,
      mandatory: false }
  PostingDate,

  @DefaultAggregation: #SUM
  NetAmount,

  Currency
}

활성화 후 ADT의 Data Preview에서 실행하면, 필터 바에 세 개의 Variable 입력 칸이 나타나고 CompanyCode에는 mandatory 표시가 붙습니다.

2단계 — Value Help 연결 + 기본값 파생

실무에서는 "사용자가 코드값을 외우게 하지 말 것"이 원칙입니다. Value Help CDS View를 별도로 만들고 association으로 연결합니다. 동시에 @Consumption.derivation으로 로그인 사용자의 기본 회사코드와 현재 회계연도를 자동 채워줍니다.

@EndUserText.label: 'Company Code Value Help'
@ObjectModel.resultSet.sizeCategory: #XS
@Search.searchable: true
define view entity ZI_COMPANYCODE_VH
  as select from I_CompanyCode
{
  @ObjectModel.text.element: ['CompanyCodeName']
  @Search.defaultSearchElement: true
  key CompanyCode,

  @Semantics.text: true
  CompanyCodeName,
  Country,
  ChartOfAccounts
}

이제 Query View에서 Value Help과 derivation을 묶어줍니다.

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Query - With Value Help'
@Analytics.query: true
define view entity ZQ_SALES_QUERY_VH
  as select from ZC_SALES_CUBE
  association [0..*] to ZI_COMPANYCODE_VH as _CCodeVH
    on $projection.CompanyCode = _CCodeVH.CompanyCode
{
  @Consumption.filter:
    { selectionType: #MULTIPLE,
      multipleSelections: true,
      mandatory: true }
  @Consumption.valueHelpDefinition:
    [{ entity: { name: 'ZI_COMPANYCODE_VH',
                 element: 'CompanyCode' } }]
  @ObjectModel.foreignKey.association: '_CCodeVH'
  key CompanyCode,

  @Consumption.filter:
    { selectionType: #SINGLE,
      mandatory: true }
  @Consumption.derivation:
    { lookupEntity:    'ZI_USER_DEFAULTS',
      binding: [{ targetElement: 'UserName',
                  type: #SYSTEM_FIELD,
                  value: #USER }],
      resultElement: 'DefaultFiscalYear' }
  key FiscalYear,

  @Consumption.filter:
    { selectionType: #RANGE,
      mandatory: false }
  PostingDate,

  @DefaultAggregation: #SUM
  NetAmount,

  Currency,

  _CCodeVH
}

핵심 포인트는 다음과 같습니다.

  • foreignKey.association: Fiori 필터 칸에 description(회사명)을 함께 표시할 때 사용합니다.
  • valueHelpDefinition: F4 검색창에 표시될 컬럼/검색 가능 필드를 지정합니다. @Search.defaultSearchElement로 fuzzy 검색을 활성화하는 것이 일반적입니다.
  • derivation: 사용자별 기본값 테이블(ZI_USER_DEFAULTS)에서 현재 사용자(SY-UNAME)로 lookup하여 DefaultFiscalYear를 가져옵니다.

3단계 — 프로덕션 패턴 (성능/권한/검증)

운영 환경에서는 Value Help가 매번 수십만 건을 조회하지 않도록 사이즈 카테고리, 권한 체크, 그리고 사용자 입력 검증을 함께 설계합니다.

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Query - Production'
@Analytics.query: true
@VDM.viewType: #CONSUMPTION
@Metadata.allowExtensions: true
define view entity ZQ_SALES_QUERY_PROD
  with parameters
    @Consumption.hidden: true
    P_DisplayCurrency : vdm_v_display_currency
  as select from ZC_SALES_CUBE
  association [0..*] to ZI_COMPANYCODE_VH    as _CCodeVH
    on $projection.CompanyCode = _CCodeVH.CompanyCode
  association [0..*] to ZI_PROFITCENTER_VH   as _PCVH
    on $projection.ProfitCenter = _PCVH.ProfitCenter
{
  @Consumption.filter:
    { selectionType: #MULTIPLE,
      multipleSelections: true,
      mandatory: true }
  @Consumption.valueHelpDefinition:
    [{ entity: { name: 'ZI_COMPANYCODE_VH', element: 'CompanyCode' },
       additionalBinding: [{ localElement: 'ControllingArea',
                             element: 'ControllingArea',
                             usage: #FILTER_AND_RESULT }] }]
  @ObjectModel.foreignKey.association: '_CCodeVH'
  key CompanyCode,

  @Consumption.filter:
    { selectionType: #SINGLE, mandatory: true }
  @Consumption.derivation:
    { lookupEntity: 'ZI_USER_DEFAULTS',
      binding: [{ targetElement: 'UserName',
                  type: #SYSTEM_FIELD, value: #USER }],
      resultElement: 'DefaultFiscalYear' }
  key FiscalYear,

  @Consumption.filter:
    { selectionType: #RANGE, mandatory: true,
      defaultLowValue: 'FIRST_DAY_OF_YEAR',
      defaultHighValue: 'TODAY' }
  PostingDate,

  @Consumption.filter:
    { selectionType: #MULTIPLE, multipleSelections: true }
  @Consumption.valueHelpDefinition:
    [{ entity: { name: 'ZI_PROFITCENTER_VH', element: 'ProfitCenter' } }]
  @ObjectModel.foreignKey.association: '_PCVH'
  ProfitCenter,

  @DefaultAggregation: #SUM
  @Semantics.amount.currencyCode: 'Currency'
  NetAmount,

  @Semantics.currencyCode: true
  Currency,

  _CCodeVH,
  _PCVH
}

운영 환경 체크리스트입니다.

  • 권한: @AccessControl.authorizationCheck: #CHECK와 DCL(Data Control Language)로 회사코드/조직 단위 분리를 적용합니다.
  • Value Help 성능: VH view에 @ObjectModel.resultSet.sizeCategory를 적절히(보통 #XS/#S) 지정하고, 필요 시 검색 인덱스(@Search.fuzzinessThreshold)를 조정합니다.
  • Range 기본값: 회계 리포트는 "연초~오늘"이 자주 쓰이므로 defaultLowValue/defaultHighValue로 사전 설정하는 것이 사용성 향상에 좋습니다.
  • additionalBinding: Value Help 결과를 다른 필터(예: ControllingArea)와 동기화해 잘못된 조합을 사전 차단합니다.
  • 유닛 테스트: ABAP Unit으로 query view 호출 시 mandatory 누락, derivation 결과 정합성을 검증합니다.

흔한 실수와 트러블슈팅

Variable과 Value Help 구성에서 자주 마주치는 이슈를 FAQ 형식으로 정리합니다.

  • Q1. 필터 바에 Variable이 보이지 않습니다.
    대부분 @Analytics.query: true가 누락되었거나, Cube/Dimension view에 annotation을 잘못 붙인 경우입니다. Variable annotation은 Query View 레벨에서만 효력을 갖는다는 점을 확인하세요. 또한 Service Binding 후 메타데이터 캐시를 재생성하지 않으면 Fiori 측에서 옛 정의를 사용할 수 있습니다.
  • Q2. Value Help을 열면 결과가 비어 있습니다.
    VH view의 @AccessControl.authorizationCheck#CHECK인데 사용자에게 권한이 없거나, additionalBindinglocalElement 명이 query view 컬럼과 다른 경우가 흔합니다. ADT의 Element Info로 association path를 다시 검증하세요.
  • Q3. derivation 기본값이 채워지지 않습니다.
    lookupEntity로 지정한 view가 @ObjectModel.usageType.dataClass: #MASTER 등으로 적절히 분류되어야 derivation 엔진이 이를 찾을 수 있습니다. binding의 targetElement가 lookup view의 key와 정확히 일치하는지, 그리고 system field 타입(#USER, #SYSTEM_DATE)이 적절한지 확인하세요.
  • Q4. Mandatory인데 사용자가 빈값으로 실행 가능해 보입니다.
    OData V4 클라이언트에서 $apply 쿼리를 직접 호출하면 Fiori UI의 mandatory 검증을 우회할 수 있습니다. 서버 사이드 보호를 위해 query view에 default value를 함께 지정하거나, CDS behavior에서 사전 검증 로직을 추가하는 것이 일반적입니다.
  • Q5. RANGE Variable에 BETWEEN을 입력하면 성능이 급격히 떨어집니다.
    Cube view 단계에서 사용된 컬럼이 HANA 컬럼스토어 인덱스 친화적인지, 그리고 Calculation View로 변환된 후 partition pruning이 작동하는지 plan viz로 확인합니다.

다음으로 살펴볼 주제

Variable과 Value Help이 익숙해졌다면 아래 주제로 확장해보길 권장합니다.

  • Hierarchy Variable: ProfitCenter/CostCenter 계층 노드 선택용 Variable
  • Currency Conversion Variable: P_DisplayCurrency@Semantics.amount.currencyCode 결합
  • Time Dependent Master Data: KeyDate Variable로 시점 기준 마스터 조회
  • RAP Behavior + Analytical Query: Draft 데이터에 대한 분석 필터 패턴
  • Custom Analytical Query in Embedded Analytics: KPI 타일, Story 연결

참고 자료

핵심 한 줄

Analytical Query Variable은 @Consumption.filter로 선택 모드를, @Consumption.valueHelpDefinition으로 검색 UX를, @Consumption.derivation으로 기본값을 결정하는 세 축의 조합으로 설계하는 것이 핵심입니다.

댓글 0

아직 댓글이 없습니다.