개요 및 이 글에서 다루는 것
ABAP CDS 뷰에서 검색 기능을 선언적으로 노출하려면 @Search 어노테이션 패밀리를 활용해야 합니다. 단순히 OData 필터로 동등 비교를 거는 것과 달리, @Search는 Fiori Elements의 글로벌 검색창, SADL(서비스 어댑테이션 정의 언어) 런타임, 그리고 HANA 데이터베이스 레벨의 텍스트 검색을 한 줄짜리 선언으로 묶어줍니다. 이 글에서는 다음 항목을 단계적으로 다룹니다.
- 뷰 헤더 레벨
@Search.searchable의 의미와 기본값 동작 - 요소 레벨
@Search.defaultSearchElement로 검색 대상 필드 노출 @Search.ranking(HIGH / MEDIUM / LOW) 가중치가 결과 정렬에 미치는 효과@Search.fuzzinessThreshold0.0~1.0 값과 오타 허용 검색의 내부 동작- 구매 오더 뷰(Z_I_PurchaseOrder) 예제로 공급업체명·자재설명 검색 활성화
- SADL/OData 파이프라인이 어떻게
$search쿼리를 HANA fuzzy로 변환하는가 @Consumption.filter와의 의미적 차이
읽기 전 권장 배경
이 글은 중급 난이도로, CDS 뷰 정의(DEFINE VIEW, DEFINE VIEW ENTITY) 구문을 어느 정도 다뤄본 개발자를 가정합니다. ABAP 7.55 이상(SAP S/4HANA 2020 onPremise) 환경에서 검증된 내용이며, 일부 어노테이션은 HANA 컬럼 스토어 + 풀텍스트 인덱스가 전제될 때만 최대 성능을 발휘합니다. SADL 프레임워크와 RAP(ABAP RESTful Application Programming Model), Fiori Elements List Report의 동작 방식을 알면 도움이 됩니다. ADT(ABAP Development Tools) 환경에서 어노테이션 자동완성 기능을 사용할 수 있도록 최신 Eclipse 플러그인을 갖춰두는 것이 일반적으로 권장됩니다.
환경 및 준비물
예제 코드는 다음 환경에서 검증되었습니다.
- SAP NetWeaver AS ABAP 7.55 이상 (S/4HANA 2021 FPS02 기준)
- SAP HANA 2.0 SPS06 이상 — fuzzy search 기능은 HANA 텍스트 엔진 의존
- ABAP Development Tools (ADT) 3.30 이상 — CDS 어노테이션 코드 인사이트 권장
- Gateway / OData V2 또는 V4 서비스 바인딩 (SADL 기반)
- 테이블 권장 사양: HANA 컬럼 스토어, 검색 대상 컬럼에
FULLTEXT INDEX가 있으면 일반적으로 더 빠름
fuzzy 검색을 사용하려면 검색 대상 컬럼의 자료형이 CHAR, STRING, SSTRING 등 문자열 계열이어야 합니다. 숫자형/날짜형은 @Search.defaultSearchElement 선언이 가능하지만 fuzzy 매칭에서는 일반적으로 정확 매칭으로 처리됩니다. 권한 측면에서는 ST05/SAT로 SQL 트레이스를 확인할 수 있어야 검색 변환 과정을 디버깅하기 수월합니다.
핵심 개념과 동작 원리
관계형 데이터베이스에서 "검색"은 보통 WHERE column LIKE '%foo%'로 시작합니다. 그러나 이런 와일드카드 접근은 인덱스를 거의 활용하지 못하고, 사용자가 "Hambrug"라고 잘못 입력해도 "Hamburg"를 찾아주지 못합니다. @Search 어노테이션은 이런 한계를 우회하기 위해 HANA의 CONTAINS() 술어와 FUZZY() 옵션으로 쿼리를 자동 변환합니다.
이 흐름을 비유하자면, @Search는 도서관 사서에게 "이 책장(뷰)은 검색 가능합니다(searchable=true), 그 중 책 제목과 저자 이름표(defaultSearchElement)를 우선 살펴주세요, 제목이 더 중요하니 가중치(ranking)는 HIGH로, 그리고 손님이 한두 글자 틀려도(fuzzinessThreshold=0.8) 비슷한 책을 추천해주세요"라고 지시문을 붙여둔 것과 같습니다.
구성 요소는 크게 네 층으로 나뉩니다.
- 뷰 레벨 스위치:
@Search.searchable: true— 이 뷰가 글로벌 검색($search) 처리 대상임을 SADL 런타임에 알립니다. 누락 시 요소 어노테이션은 사실상 무시됩니다. - 필드 노출:
@Search.defaultSearchElement: true— Fiori 검색창에 입력된 키워드가 매칭될 후보 컬럼을 지정합니다. 여러 필드에 동시에 줄 수 있고, OR 조건으로 결합됩니다. - 가중치:
@Search.ranking: #HIGH | #MEDIUM | #LOW— 동일 키워드가 두 필드 모두에 매칭될 때 HIGH 가중치 필드에서 일치한 행이 결과 상단에 노출되는 경향이 있습니다. - 퍼지니스 임계값:
@Search.fuzzinessThreshold: 0.85— 1.0은 완전 일치, 0.7 근처는 상당히 관대한 유사 매칭, 0.5 이하는 잡음이 많아 일반적으로 권장되지 않습니다. HANA 내부에서 편집 거리 기반 유사도 점수를 산출합니다.
SADL은 OData 요청 ?$search=ACME를 받으면, 어노테이션 메타데이터를 읽고 다음과 같은 SQL을 생성합니다(개념적 표현).
SELECT ... FROM z_i_purchaseorder
WHERE CONTAINS( (supplier_name, item_text),
'ACME',
FUZZY(0.85, 'similarCalculationMode=search') )
ORDER BY SCORE() DESC;
@Consumption.filter와 혼동하기 쉬운데, 이는 사용자가 명시적으로 필터 UI에 노출할 항목을 정의하는 어노테이션입니다. @Search는 "한 줄짜리 글로벌 검색바"용이고, @Consumption.filter는 "필드별 필터 박스"용으로 사용 목적이 분리됩니다.
실전 예제 1단계 — 최소 구성
구매 오더 헤더 정보를 노출하는 인터페이스 뷰 Z_I_PurchaseOrder를 만들고, 공급업체명과 자재 설명을 검색 가능하게 한다고 가정합니다. 가장 기본적인 형태는 다음과 같습니다.
@AbapCatalog.sqlViewName: 'ZIPOSRCH'
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Purchase Order Search View'
@Search.searchable: true
define view Z_I_PurchaseOrder
as select from ekko as hdr
inner join ekpo as itm on itm.ebeln = hdr.ebeln
inner join lfa1 as sup on sup.lifnr = hdr.lifnr
{
key hdr.ebeln as PurchaseOrderId,
@Search.defaultSearchElement: true
@Search.ranking: #HIGH
@Search.fuzzinessThreshold: 0.85
sup.name1 as SupplierName,
@Search.defaultSearchElement: true
@Search.ranking: #MEDIUM
@Search.fuzzinessThreshold: 0.80
itm.txz01 as ItemText,
hdr.bukrs as CompanyCode,
hdr.bsart as DocumentType,
hdr.aedat as CreatedOn
}
이 상태에서 OData 서비스를 노출하고 GET /Z_I_PurchaseOrder?$search=ACME를 호출하면, SADL은 SupplierName과 ItemText 두 컬럼을 OR로 결합한 fuzzy 쿼리를 실행합니다. SupplierName이 HIGH로 지정되어 있으므로 동률 매칭 시 공급업체명에서 일치한 행이 상위에 올라오는 경향이 있습니다.
실전 예제 2단계 — 다중 필드와 로깅
실무에서는 검색 대상이 더 다양해지고, 잘못된 쿼리가 들어왔을 때 추적이 필요합니다. 자재번호, 플랜트, 자재그룹까지 검색 범위를 확장하고, 자료유형별 가중치를 차등화하겠습니다.
@AbapCatalog.sqlViewName: 'ZIPOSRCH2'
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'PO Search - Extended'
@Search.searchable: true
@VDM.viewType: #COMPOSITE
define view Z_I_PurchaseOrder_Ext
as select from ekko as hdr
inner join ekpo as itm on itm.ebeln = hdr.ebeln
inner join lfa1 as sup on sup.lifnr = hdr.lifnr
left outer join makt as mat
on mat.matnr = itm.matnr
and mat.spras = $session.system_language
{
key hdr.ebeln as PurchaseOrderId,
key itm.ebelp as ItemNumber,
@Search.defaultSearchElement: true
@Search.ranking: #HIGH
@Search.fuzzinessThreshold: 0.85
sup.name1 as SupplierName,
@Search.defaultSearchElement: true
@Search.ranking: #HIGH
@Search.fuzzinessThreshold: 0.90
itm.matnr as MaterialNumber,
@Search.defaultSearchElement: true
@Search.ranking: #MEDIUM
@Search.fuzzinessThreshold: 0.80
mat.maktx as MaterialDescription,
@Search.defaultSearchElement: true
@Search.ranking: #LOW
@Search.fuzzinessThreshold: 0.75
itm.werks as Plant,
@Search.defaultSearchElement: true
@Search.ranking: #LOW
@Search.fuzzinessThreshold: 0.75
itm.matkl as MaterialGroup,
hdr.bukrs as CompanyCode,
cast( itm.netwr as abap.curr( 15, 2 ) ) as NetAmount,
itm.waers as Currency,
hdr.aedat as CreatedOn
}
검색이 의도와 다르게 동작할 때 가장 먼저 확인할 것은 SADL이 만든 실제 SQL입니다. 트랜잭션 ST05로 SQL 트레이스를 켠 뒤 Fiori 앱에서 검색을 실행하고, 트레이스에서 CONTAINS 절을 찾아 어노테이션이 의도대로 변환되었는지 검증하는 흐름이 일반적입니다. 추가로 클래스 CL_SADL_GW_QUERY 계열에 외부 중단점을 설정하면 검색 키워드 파싱 과정을 들여다볼 수 있습니다.
실전 예제 3단계 — 프로덕션 고려사항
운영 환경에서는 단순한 어노테이션 부착을 넘어 성능, 보안, 다국어를 함께 다뤄야 합니다. 다음은 RAP 기반 서비스 정의와 결합한 권장 패턴입니다.
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'PO Search - Production Ready'
@Search.searchable: true
@ObjectModel.usageType: {
serviceQuality: #D,
sizeCategory: #L,
dataClass: #TRANSACTIONAL
}
define root view entity Z_C_PurchaseOrderSearch
as projection on Z_I_PurchaseOrder_Ext
{
key PurchaseOrderId,
key ItemNumber,
@Search.defaultSearchElement: true
@Search.ranking: #HIGH
@Search.fuzzinessThreshold: 0.85
@UI.lineItem: [{ position: 10, label: 'Supplier' }]
SupplierName,
@Search.defaultSearchElement: true
@Search.ranking: #HIGH
@Search.fuzzinessThreshold: 0.90
MaterialNumber,
@Search.defaultSearchElement: true
@Search.ranking: #MEDIUM
@Search.fuzzinessThreshold: 0.80
MaterialDescription,
CompanyCode,
NetAmount,
Currency,
CreatedOn
}
그리고 서비스 정의(Service Definition)와 바인딩(Service Binding)에서 OData V4로 노출하면 Fiori Elements List Report가 자동으로 검색창을 활성화합니다. 프로덕션에서 점검해야 할 항목은 다음과 같습니다.
- fuzzinessThreshold 튜닝: 0.85가 일반적인 시작값이지만, 도메인별로 0.80~0.90 범위에서 A/B 비교해 결정하는 흐름이 권장됩니다. 너무 낮으면 무관한 결과가 많아 사용자 불만이 생기고, 너무 높으면 fuzzy 효과가 사라집니다.
- 인덱스 관리: HANA에서
CREATE FULLTEXT INDEX를 검색 대상 컬럼에 생성하면 대용량 테이블에서도 응답 시간이 일반적으로 안정화됩니다. - 권한 일관성:
@AccessControl.authorizationCheck: #CHECK와 DCL(Data Control Language)을 함께 적용해, 검색 결과가 사용자가 볼 수 없는 행을 노출하지 않도록 합니다. - 다국어 자재명:
makt조인에$session.system_language를 적용해 로그온 언어로만 검색이 작동하도록 제한합니다. - 테스트: 클래스
CL_RAP_QUERY_FACTORY또는 ABAP Unit으로 검색 시나리오를 회귀 테스트화하면 어노테이션 누락을 조기에 잡을 수 있습니다.
흔한 실수와 트러블슈팅
Q1. @Search.defaultSearchElement를 붙였는데 검색 결과가 비어 있습니다.
A. 가장 흔한 원인은 뷰 헤더에 @Search.searchable: true가 빠진 경우입니다. 필드 어노테이션만으로는 SADL이 글로벌 검색 처리를 시작하지 않습니다. 또한 OData V2 서비스에서 $search 대신 $filter로 호출하면 fuzzy 변환이 작동하지 않으니, 클라이언트 측 호출 형태도 확인합니다.
Q2. fuzzinessThreshold를 0.5로 낮췄더니 결과가 너무 많아졌습니다.
A. 임계값은 1.0에 가까울수록 엄격합니다. 0.5는 절반 정도만 일치해도 매칭되므로 노이즈가 폭증합니다. 0.80~0.90 사이가 일반적으로 권장되며, 수치형 키(예: 자재번호)는 0.90 이상이 안전한 편입니다. 임계값 조정 후에는 반드시 대표 키워드 10~20개로 회귀 검증을 수행합니다.
Q3. @Search.ranking: #HIGH를 모든 필드에 줬는데 정렬이 기대대로 되지 않습니다.
A. 모든 필드가 HIGH이면 가중치 차등 효과가 사라집니다. ranking은 상대적 차이로 의미가 생기므로, 핵심 식별자(공급업체명, 자재번호)에만 HIGH를 부여하고 보조 속성에는 MEDIUM/LOW를 분배하는 패턴이 권장됩니다. 또한 OData 응답이 기본 정렬 키(예: PurchaseOrderId)로 다시 정렬되어 있다면 클라이언트 측 정렬을 제거하거나 $orderby를 명시하지 않도록 합니다.
Q4. HANA가 아닌 다른 DB에서도 fuzzy가 동작합니까?
A. fuzzinessThreshold는 HANA의 텍스트 엔진에 의존합니다. AnyDB 환경에서는 일반적으로 LIKE 기반의 대체 처리가 적용되어 오타 허용이 제한됩니다. S/4HANA 외 환경 배포 시에는 사용자 기대치를 사전에 조정해야 합니다.
다음으로 확장해볼 주제
이 글의 내용을 바탕으로 더 깊이 파고들 수 있는 영역은 다음과 같습니다. 먼저 Value Help 통합 — @Consumption.valueHelpDefinition과 @Search를 조합하면 F4 도움말에서도 fuzzy 검색을 제공할 수 있습니다. 둘째, RAP 비즈니스 객체와 검색 액션 — 커스텀 액션에서 검색 결과를 받아 후속 처리를 트리거하는 패턴은 트랜잭션 앱에서 유용합니다. 셋째, HANA Text Analysis와의 결합으로 단순 fuzzy를 넘어 형태소 분석, 동의어 사전을 활용한 검색을 구현할 수 있습니다. 마지막으로 Enterprise Search(ESH)와의 비교 분석을 통해 어떤 경우에 CDS @Search로 충분하고 어떤 경우에 ESH가 필요한지 판단 기준을 세워두는 것이 권장됩니다.
참고 자료
댓글 0
아직 댓글이 없습니다.