개요 및 이 글에서 다루는 범위
SAP RAP(ABAP RESTful Application Programming Model) 기반의 Fiori Elements 앱에서 사용자가 입력 필드를 클릭하면 나타나는 F4 도움말(Value Help)을 CDS 어노테이션만으로 구현하는 실전 예제입니다. PurchaseOrder(구매 주문) 엔티티의 VendorId(거래처 ID) 필드에 거래처 마스터 데이터를 검색·선택할 수 있는 F4 창을 띄우고, 선택한 값이 본 화면에 자동으로 매핑되도록 구성합니다.
- CDS View Entity에
@Consumption.valueHelpDefinition어노테이션을 선언할 수 있다 - Value Help 소스 뷰(VH View)를 별도 분리하여 재사용성을 확보할 수 있다
- Service Definition / Service Binding을 통해 VH 뷰를 OData 서비스에 노출할 수 있다
- additionalBinding, collectiveSearch, distinctValues 등 고급 옵션의 의미를 구분해 활용할 수 있다
- 클라이언트(Fiori Elements) → 서버 흐름에서 VH 호출이 어떻게 일어나는지 설명할 수 있다
이 글을 읽기 전에 익숙해 두면 좋은 것들
ABAP CDS View / View Entity 문법, RAP의 BO(Business Object) 구조(Behavior Definition, Behavior Implementation), Service Definition과 Service Binding의 차이, ADT(ABAP Development Tools in Eclipse)의 기본 사용법, Fiori Elements List Report / Object Page의 동작 방식이 익숙하다면 빠르게 이해할 수 있습니다. UI 어노테이션(@UI.lineItem, @UI.identification)을 한 번이라도 다뤄봤다면 더 좋습니다.
실습 환경 및 준비물
이 글의 예제는 다음 환경을 기준으로 설명합니다.
- SAP BTP ABAP Environment(Steampunk) 또는 S/4HANA 2022 이상 온프레미스
- ABAP Platform 2022 이상 — View Entity와 최신 어노테이션 지원
- ADT(Eclipse) 최신 버전 + ABAP Development Tools 플러그인
- Fiori Elements Preview(Service Binding의 Preview 기능)
- 샘플 테이블:
zpo_header(구매 주문 헤더),zvendor(거래처 마스터)
온프레미스 시스템에서는 일부 어노테이션(예: collectiveSearch)의 동작이 SAP_BASIS SP 레벨에 따라 차이가 날 수 있어, 최신 SP를 권장합니다. BTP ABAP Environment는 일반적으로 가장 최신 어노테이션 사양을 우선 반영합니다.
핵심 개념: Value Help는 어떻게 동작하는가
RAP에서 F4 도움말은 더 이상 GUI 시절처럼 SE11 검색 도움말(Search Help)을 직접 호출하지 않습니다. Fiori Elements 클라이언트가 메타데이터($metadata) 안에 선언된 Value Help 정보를 읽고, 사용자가 입력 필드 옆 돋보기 아이콘을 누르는 순간 별도의 OData EntitySet을 호출하여 결과를 조회하는 구조입니다. 즉, "값을 제공할 뷰"와 "값을 사용할 뷰"가 분리되며, 둘 사이의 매핑은 어노테이션으로 선언합니다.
비유: 소비자 뷰(Consumer View)는 식당의 주문서, VH 뷰는 메뉴판입니다. 주문서의 빈칸을 채우기 위해 메뉴판을 펼치고, 선택한 메뉴(코드)와 부가 정보(가격, 설명)를 주문서에 복사해 옵니다.
localElement는 주문서의 빈칸,element는 메뉴판의 컬럼 이름입니다.
전형적인 호출 흐름은 다음과 같습니다.
- 사용자가 List Report 또는 Object Page의 VendorId 필드 옆 돋보기 클릭
- UI5가
$metadata에서 해당 필드에 연결된ValueList항목을 확인 - VH EntitySet에 대해
$filter,$search,$top파라미터로 OData 호출 - 서버가 VH 뷰(CDS View Entity)를 통해 결과 반환
- 사용자가 선택하면
localElement매핑에 따라 값이 본 화면에 채워짐
여기서 자주 등장하는 옵션을 미리 정리해두면 좋습니다.
- additionalBinding: 메인 매핑(
element↔localElement) 외에 추가로 양방향 매핑을 정의. 보통 회사 코드(CompanyCode)나 통화(Currency) 같은 컨텍스트 필터에 사용 - useForValidation: VH 결과로 입력값의 유효성 검증까지 수행할지 결정
- distinctValues: VH 뷰가 중복 제거된 값만 반환해야 할 때 (예: 같은 거래처에서 여러 라인이 나오는 경우)
- collectiveSearch: 여러 개의 VH를 하나의 탭 UI로 묶어 사용자에게 선택지를 제공
1단계 — 가장 단순한 F4 도움말 붙이기
먼저 거래처 마스터를 그대로 노출하는 VH 뷰를 만듭니다. 이름은 Z_C_VendorVH로 짓고, 필요한 최소 컬럼(거래처 ID, 거래처 명, 도시)을 표현합니다.
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Vendor Value Help View'
@Search.searchable: true
define view entity Z_C_VendorVH
as select from zvendor
{
@Search.defaultSearchElement: true
@Search.fuzzinessThreshold: 0.8
key vendor_id as VendorId,
@Search.defaultSearchElement: true
vendor_name as VendorName,
city as City,
country as Country
}
이제 PurchaseOrder를 표현하는 Consumer View에서 VendorId 필드에 F4를 연결합니다.
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Purchase Order Consumption View'
define view entity Z_C_PurchaseOrder
as select from zpo_header
{
key po_number as PoNumber,
@Consumption.valueHelpDefinition: [{
entity: { name: 'Z_C_VendorVH', element: 'VendorId' }
}]
vendor_id as VendorId,
order_date as OrderDate,
total_amount as TotalAmount,
currency_code as CurrencyCode
}
핵심은 @Consumption.valueHelpDefinition 배열입니다. entity.name은 VH 뷰의 이름, entity.element는 VH 뷰에서 "이 필드와 매칭되는 컬럼"을 지정합니다. localElement를 따로 적지 않으면, 어노테이션이 붙어 있는 현재 필드(VendorId)가 자동으로 로컬 요소로 간주됩니다.
2단계 — 실무 시나리오: 회사 코드 필터와 추가 필드 매핑
실제 업무에서는 "회사 코드(CompanyCode)별로 거래처가 다르다"거나 "거래처 선택 시 도시(City) 정보도 자동으로 채워야 한다" 같은 요건이 흔합니다. 이때 additionalBinding을 사용합니다.
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Vendor VH with Company Filter'
@Search.searchable: true
define view entity Z_C_VendorVH2
as select from zvendor as v
inner join zvendor_cc as cc
on v.vendor_id = cc.vendor_id
{
key cc.company_code as CompanyCode,
key v.vendor_id as VendorId,
@Search.defaultSearchElement: true
v.vendor_name as VendorName,
v.city as City,
v.country as Country,
v.payment_terms as PaymentTerms
}
Consumer View에서는 회사 코드를 필터 컨텍스트로 전달하고, 선택 결과의 City를 PO 화면의 ShipToCity로 복사합니다.
@AccessControl.authorizationCheck: #CHECK
define view entity Z_C_PurchaseOrder2
as select from zpo_header as po
{
key po.po_number as PoNumber,
po.company_code as CompanyCode,
@Consumption.valueHelpDefinition: [{
entity: { name: 'Z_C_VendorVH2', element: 'VendorId' },
additionalBinding: [
{ localElement: 'CompanyCode', element: 'CompanyCode',
usage: #FILTER_AND_RESULT },
{ localElement: 'ShipToCity', element: 'City',
usage: #RESULT }
],
useForValidation: true
}]
po.vendor_id as VendorId,
po.ship_to_city as ShipToCity,
po.order_date as OrderDate,
po.total_amount as TotalAmount,
po.currency_code as CurrencyCode
}
usage 옵션의 의미는 다음과 같습니다.
#FILTER_AND_RESULT: 호출 시 필터로도 사용하고, 결과로도 받아 옴 (회사 코드처럼 컨텍스트가 같아야 하는 경우)#FILTER: 호출 전 필터로만 사용#RESULT: 사용자가 선택한 행에서 값을 가져와 본 화면에 채워 줌 (도시·결제조건 등 부가정보)
여기에 간단한 로깅 패턴도 함께 다뤄둡니다. VH는 보통 읽기 전용이지만, 결과 검증 단계에서 "허용되지 않은 거래처가 선택되었다"는 메시지를 띄우려면 RAP Behavior Implementation의 validation 메서드에서 처리하는 것이 표준입니다.
METHOD validate_vendor.
READ ENTITIES OF z_i_purchaseorder IN LOCAL MODE
ENTITY PurchaseOrder
FIELDS ( VendorId CompanyCode )
WITH CORRESPONDING #( keys )
RESULT DATA(lt_po).
LOOP AT lt_po INTO DATA(ls_po).
SELECT SINGLE @abap_true FROM z_c_vendorvh2
WHERE CompanyCode = @ls_po-CompanyCode
AND VendorId = @ls_po-VendorId
INTO @DATA(lv_ok).
IF lv_ok = abap_false.
APPEND VALUE #( %tky = ls_po-%tky
%state_area = 'VALIDATE_VENDOR' )
TO failed-purchaseorder.
APPEND VALUE #( %tky = ls_po-%tky
%msg = new_message(
id = 'ZPO'
number = '001'
severity = if_abap_behv_message=>severity-error
v1 = ls_po-VendorId ) )
TO reported-purchaseorder.
ENDIF.
ENDLOOP.
ENDMETHOD.
3단계 — 프로덕션 품질: Service Definition 노출과 고급 옵션
VH 뷰는 단순히 어노테이션만 단다고 동작하지 않습니다. Service Definition에서 명시적으로 노출되어야 클라이언트가 OData 호출을 할 수 있습니다.
@EndUserText.label: 'Purchase Order Service'
define service Z_UI_PURCHASEORDER {
expose Z_C_PurchaseOrder2 as PurchaseOrder;
expose Z_C_VendorVH2 as VendorVH;
}
이후 Service Binding(OData V4 UI 타입)을 액티베이트하면, Fiori Elements Preview에서 VendorId 필드에 자동으로 돋보기 아이콘이 표시됩니다. VH 뷰를 누락하면 메타데이터에 ValueList가 생성되지 않아 F4가 동작하지 않습니다.
대규모 시스템에서는 다음 옵션들을 함께 검토하는 것이 일반적입니다.
@Consumption.valueHelpDefinition: [
{
entity: { name: 'Z_C_VendorVH2', element: 'VendorId' },
additionalBinding: [
{ localElement: 'CompanyCode', element: 'CompanyCode',
usage: #FILTER_AND_RESULT }
],
distinctValues: true,
qualifier: 'Standard'
},
{
entity: { name: 'Z_C_VendorByCountryVH', element: 'VendorId' },
qualifier: 'ByCountry'
}
]
@Consumption.valueHelpDefinition.collectiveSearch.qualifier: 'VendorSearches'
위 예시는 한 필드에 두 개의 VH(표준 / 국가별)를 연결하고, collectiveSearch로 묶어 사용자가 탭 UI에서 검색 방식을 고를 수 있게 합니다. distinctValues: true는 동일 VendorId가 회사 코드별로 여러 번 등장하는 상황에서 중복을 제거해 보여줍니다.
성능 측면에서는 다음을 권장합니다.
- VH 뷰의 베이스 테이블에 검색 컬럼 인덱스(또는 HANA의 fuzzy index) 확보
@Search.fuzzinessThreshold는 0.7~0.9 범위에서 시작 — 너무 낮으면 부정확, 너무 높으면 결과 누락- VH 뷰에서
JOIN을 남발하지 말고, 필요 시 별도 분석용 뷰로 분리 - 권한이 필요한 경우 VH 뷰 자체에
@AccessControl.authorizationCheck: #CHECK와 DCL(Access Control)을 적용
테스트 측면에서는 ATC(ABAP Test Cockpit) 변형으로 RAP 관련 체크를 수행하고, Service Binding의 Preview에서 다음을 확인합니다.
- 돋보기 클릭 시 VH 다이얼로그가 뜨는지
- 검색어 입력 시 필터 컬럼이 fuzzy로 동작하는지
- 선택 시 additionalBinding 매핑이 본 화면에 정확히 반영되는지
- 잘못된 VendorId를 수동 입력했을 때 validation이 에러 메시지를 띄우는지
흔히 마주치는 문제와 해결 포인트
Q1. 돋보기 아이콘이 아예 안 보입니다. Service Definition에 VH 뷰가 expose되어 있는지 가장 먼저 확인합니다. 또한 Service Binding을 다시 액티베이트해야 메타데이터가 갱신됩니다. 캐시가 남아 있는 경우 브라우저 강력 새로고침(Ctrl+Shift+R)도 함께 시도합니다.
Q2. F4 창은 뜨는데 결과가 항상 비어 있습니다. additionalBinding의 #FILTER_AND_RESULT로 묶인 필드(예: CompanyCode)가 본 화면에서 비어 있으면, VH 호출 시 빈 값으로 필터링되어 결과가 0건이 됩니다. 본 화면에서 해당 필드를 먼저 채우거나, usage를 #RESULT로 바꾸는 것을 검토합니다.
Q3. 검색어를 입력해도 fuzzy 검색이 동작하지 않습니다. @Search.searchable: true가 VH 뷰 헤더에 선언되어야 하며, 검색 대상 컬럼마다 @Search.defaultSearchElement: true가 있어야 합니다. HANA DB가 아닌 경우 fuzzy 검색 자체가 지원되지 않을 수 있어 일반 LIKE 검색으로 폴백됩니다.
Q4. additionalBinding으로 도시(City)를 받아오는데 본 화면에 안 채워집니다. 본 화면 필드(ShipToCity)가 read-only이거나 Behavior Definition에서 변경 불가로 잠겨 있는지 확인합니다. RAP는 클라이언트가 보낸 패치(PATCH)를 BO 권한에 따라 거부할 수 있습니다.
Q5. List Report 필터 바에서도 F4가 동작하나요? 네, 동일하게 동작합니다. 다만 필터 바에서는 additionalBinding의 컨텍스트(예: CompanyCode)가 없으므로, usage: #FILTER_AND_RESULT 매핑이 있는 VH는 빈 결과를 낼 수 있습니다. 이 경우 필터 바 전용 VH를 별도 qualifier로 정의하는 패턴을 사용합니다.
이어서 학습하면 좋은 주제
F4 도움말에 익숙해졌다면, 다음 주제로 확장해 보길 권장합니다.
- Draft 활성화된 RAP BO에서의 VH 동작 차이 — Draft 인스턴스에 대한 검증 시점
@ObjectModel.text.element를 활용한 텍스트 결합 표시(예: "V001 — ACME Corp")- CDS Custom Entity로 외부 시스템(예: S/4HANA 백엔드)의 거래처를 VH로 끌어오는 패턴
- Hierarchy VH — 조직 구조처럼 트리 형태의 도움말
- UI5 Custom Value Help Fragment와 RAP 연계 — 표준 다이얼로그로 부족할 때
더 깊이 파볼 만한 자료들
댓글 0
아직 댓글이 없습니다.