개요 — 검색창이 아예 사라진 List Report
RAP(ABAP RESTful Application Programming Model)으로 List Report를 처음 만들면 대부분 한 번은 겪는 문제가 있습니다. 화면은 잘 뜨는데 상단 검색창이 회색으로 비활성화되어 있거나, 검색어를 넣어도 결과가 0건으로 나오는 현상입니다. 원인은 거의 항상 하나, CDS View에 @Search 어노테이션을 선언하지 않았기 때문입니다. 이 글에서는 구매오더(Purchase Order) 시나리오로 검색이 실패하는 원리부터 3단계 실전 예제까지 다룹니다.
- 검색창이 비활성화되는 메타데이터 수준의 원인 이해
@Search.searchable/@Search.defaultSearchElement선언 방법@Search.ranking,@Search.fuzzinessThreshold로 검색 품질 조정- 어소시에이션 필드 검색과 프로덕션 체크리스트
미리 갖춰두면 좋은 배경
ABAP CDS View Entity 작성 경험, RAP의 기본 구조(Base View → Projection View → Service Definition → Service Binding), Fiori Elements List Report가 OData 메타데이터를 읽어 UI를 그린다는 개념 정도를 알고 있으면 충분합니다. OData V4의 $search 쿼리 옵션을 접해봤다면 더 빠르게 이해할 수 있습니다.
개발 환경과 버전 확인
이 글의 예제는 다음 환경을 기준으로 작성했습니다.
- SAP BTP ABAP Environment 또는 SAP S/4HANA 2022 이상 (on-premise, ABAP for Cloud Development 언어 버전)
- ABAP Development Tools(ADT) for Eclipse — CDS 에디터와 서비스 바인딩 Preview 사용
- OData V4 UI 서비스 바인딩 (
OData V4 - UI) - 데이터베이스는 SAP HANA —
fuzzinessThreshold는 HANA의 퍼지 검색 엔진을 사용하므로 일반적으로 HANA 기반에서만 의미가 있습니다
S/4HANA 1909 이하의 구버전에서는 일부 어노테이션 전파 동작이 다를 수 있으므로 시스템 릴리스를 먼저 확인하는 것이 권장됩니다.
@Search 어노테이션이 동작하는 원리
Fiori Elements List Report의 검색창은 개발자가 그리는 것이 아니라, 프레임워크가 OData 서비스의 $metadata를 읽고 스스로 판단해서 그립니다. 이때 참조하는 것이 Capabilities.SearchRestrictions 어노테이션입니다. CDS View에 @Search.searchable: true가 없으면 RAP 런타임은 메타데이터에 "이 엔티티는 검색 불가(Searchable=false)"라고 기록하고, Fiori Elements는 그 값을 읽어 검색창을 숨기거나 비활성화합니다. 즉 UI 버그가 아니라 백엔드가 "검색 못 한다"고 선언한 결과입니다.
비유하자면 도서관과 색인 카드의 관계입니다. 책(데이터)이 아무리 많아도 색인 카드(defaultSearchElement)를 만들어두지 않으면 사서(OData 런타임)는 어떤 책장을 뒤져야 할지 모릅니다. 세 가지 어노테이션의 역할을 정리하면 다음과 같습니다.
| 어노테이션 | 선언 위치 | 역할 |
|---|---|---|
@Search.searchable | 뷰(엔티티) 레벨 | 자유 텍스트 검색 자체를 켜는 스위치 |
@Search.defaultSearchElement | 필드 레벨 | 검색 대상 필드 지정 (색인 카드) |
@Search.ranking / @Search.fuzzinessThreshold | 필드 레벨 | 결과 정렬 가중치 / 오타 허용도 |
동작 흐름은 이렇습니다. 사용자가 검색어를 입력하면 Fiori Elements가 $search=검색어 쿼리를 전송하고, RAP 런타임은 defaultSearchElement: true로 표시된 필드들을 대상으로 검색 조건을 생성합니다. HANA에서는 이 조건이 퍼지 검색으로 변환될 수 있으며, fuzzinessThreshold(0~1, 일반적으로 0.7~0.9 권장)가 낮을수록 오타를 관대하게 허용합니다. ranking은 #HIGH / #MEDIUM(기본값) / #LOW 세 값으로 어느 필드에서 매칭된 결과를 상위에 노출할지 가중치를 줍니다. 중요한 점은 searchable: true를 선언했다면 최소 한 개 이상의 defaultSearchElement가 반드시 필요하다는 것입니다. 스위치만 켜고 색인 카드를 안 만들면 검색창은 보여도 결과가 비게 됩니다.
실전 예제 — 3단계로 완성하는 검색 필터
1단계: 기본 검색 활성화
구매오더 프로젝션 뷰에 검색 스위치를 켜고, 오더번호와 공급업체명을 검색 대상으로 지정합니다.
@EndUserText.label: '구매오더 Projection View'
@AccessControl.authorizationCheck: #CHECK
@Metadata.allowExtensions: true
@Search.searchable: true
define root view entity ZC_SLX_PurchaseOrder
provider contract transactional_query
as projection on ZR_SLX_PurchaseOrder
{
key PurchaseOrderUuid,
@Search.defaultSearchElement: true
PurchaseOrderNo,
@Search.defaultSearchElement: true
SupplierName,
OrderDate,
TotalAmount,
CurrencyCode,
OverallStatus
}
활성화 후 서비스 바인딩의 Preview로 확인하면 검색창이 활성화되고, "ACME"를 입력하면 PurchaseOrderNo와 SupplierName 두 필드를 대상으로 검색이 수행됩니다. 브라우저 개발자 도구에서 ?$search=ACME 형태의 요청이 나가는 것을 확인할 수 있습니다.
2단계: 실무 시나리오 — 랭킹과 오타 허용, 그리고 검증
실무에서는 "오더번호 정확 매칭이 업체명 부분 매칭보다 위에 나와야 한다", "거래처명 오타(Simens → Siemens)도 잡아야 한다" 같은 요구가 나옵니다. ranking과 fuzzinessThreshold를 조합합니다.
@Search: { defaultSearchElement: true,
ranking: #HIGH }
PurchaseOrderNo,
@Search: { defaultSearchElement: true,
ranking: #MEDIUM,
fuzzinessThreshold: 0.8 }
SupplierName,
@Search: { defaultSearchElement: true,
ranking: #LOW,
fuzzinessThreshold: 0.9 }
PurchaserNote,
배포 후 검색이 기대대로 동작하지 않으면 두 곳을 순서대로 점검합니다. 첫째, $metadata에서 SearchRestrictions가 Searchable=true로 나오는지 확인합니다. false라면 어노테이션이 프로젝션 뷰가 아닌 다른 레이어에 있거나 활성화가 누락된 것입니다. 둘째, ADT의 트러블슈팅 도구나 게이트웨이 오류 로그에서 $search 요청이 어떤 SQL로 변환되는지 확인합니다. 메타데이터 변경이 UI에 반영되지 않을 때는 서비스 바인딩을 다시 게시(unpublish/publish)하고 브라우저 캐시를 비우는 것이 일반적인 해결책입니다.
3단계: 프로덕션 — 어소시에이션·성능·보안
공급업체 마스터의 도시명처럼 어소시에이션 너머의 필드도 검색하려면, 해당 필드를 프로젝션에 노출한 뒤 어노테이션을 붙입니다. 노출하지 않은 어소시에이션 필드는 검색 대상이 될 수 없습니다.
@Search: { defaultSearchElement: true,
ranking: #LOW,
fuzzinessThreshold: 0.85 }
_Supplier.CityName as SupplierCity,
/* 어소시에이션 자체 노출 */
_Supplier : redirected to ZC_SLX_Supplier
프로덕션 체크리스트는 다음과 같습니다.
- 성능: 검색 필드 수는 3~5개 이내로 제한하는 것이 일반적으로 권장됩니다. 필드가 늘수록 HANA 퍼지 검색 비용이 커지고, 계산 필드(virtual element, case 식 결과)는 검색 대상으로 삼지 않는 편이 안전합니다.
- fuzziness 하한: 0.7 미만은 무관한 결과가 급증하므로 0.8 전후에서 시작해 사용자 피드백으로 조정합니다.
- 테스트: Preview 외에 OData 직접 호출로 회귀 테스트를 만들어 둡니다. 예:
GET .../PurchaseOrder?$search=Siemens&$count=true응답 건수를 검증하는 자동 테스트. - 보안: 검색 결과도 DCL(Access Control) 필터를 그대로 통과합니다. 즉 권한 없는 데이터가 검색으로 새지는 않지만, 반대로 DCL 조건이 과도하면 "검색이 안 된다"는 오해를 낳으므로 권한 이슈와 검색 이슈를 분리해 진단해야 합니다.
- Custom Entity 주의: 쿼리를 직접 구현하는 custom entity에서는
io_request->get_search_expression( )으로 검색어를 수동 처리해야 하며, 어노테이션만으로는 동작하지 않습니다.
자주 겪는 실수와 FAQ
Q1. 검색창 자체가 안 보이거나 회색입니다. 뷰 레벨의 @Search.searchable: true 누락이 원인입니다. 특히 Base View에만 선언하고 프로젝션 뷰에 빠뜨리는 경우가 많은데, UI 서비스가 노출하는 것은 프로젝션 뷰이므로 프로젝션 레이어에 선언해야 합니다.
Q2. 검색창은 보이는데 결과가 항상 0건입니다. searchable: true만 켜고 defaultSearchElement를 한 필드도 지정하지 않은 상태입니다. ADT가 경고를 표시하지만 활성화 자체는 되기 때문에 놓치기 쉽습니다. 최소 한 개의 문자형 필드에 defaultSearchElement: true를 붙이세요.
Q3. 어노테이션을 추가했는데 UI에 반영이 안 됩니다. 메타데이터 캐시 문제인 경우가 대부분입니다. 서비스 바인딩 재게시와 브라우저 하드 리프레시를 먼저 시도하고, on-premise OData V2 환경이라면 게이트웨이 캐시 정리 절차를 함께 수행합니다.
Q4. 금액·날짜 필드가 검색되지 않습니다. 자유 텍스트 검색은 일반적으로 문자 기반 필드를 대상으로 합니다. 숫자·날짜는 검색창이 아니라 필터 바(@UI.selectionField)로 제공하는 것이 올바른 설계입니다. 검색과 필터는 서로 다른 메커니즘이라는 점을 기억하세요.
여기서 더 나아가기
검색이 완성되면 자연스럽게 이어지는 주제는 세 가지입니다. 첫째, @UI.selectionField와 @Consumption.valueHelpDefinition으로 필터 바와 값 도움말을 구성해 검색과 필터를 함께 제공하는 패턴. 둘째, @Search.defaultSearchElement를 Value Help 뷰에 적용해 F4 다이얼로그 내부 검색을 개선하는 방법. 셋째, @ObjectModel.filter.enabled·@UI.presentationVariant를 활용한 초기 정렬/필터 상태 설계입니다. 이 조합까지 갖추면 List Report의 조회 경험이 완성됩니다.
댓글 0
아직 댓글이 없습니다.