ABAP

뷰 수정 없이 CDS 필드 추가 3가지 #shorts #SAP #ABAP

▶ YouTube에서 보기

개요 및 이 글에서 다루는 것

표준 SAP에서 제공하는 CDS 뷰(예: I_SalesDocument, I_PurchaseOrder)에 자사 비즈니스 요건에 맞는 필드 — 예컨대 "긴급 배송 플래그", "지역별 우선순위 코드", "사내 분류 등급" 등을 추가해야 하는 상황은 SAP S/4HANA 도입 프로젝트의 단골 메뉴입니다. 이때 가장 쉬워 보이는 방법은 표준 뷰의 소스 코드를 직접 수정하는 것이지만, 이는 향후 SAP 노트/업그레이드 시 충돌(Modification Adjustment)을 일으키며 클린 코어(Clean Core) 원칙에도 정면으로 위배됩니다.

이 글은 CDS View Extension — 즉 원본 뷰의 소스 코드를 단 한 줄도 건드리지 않고 extend view 키워드로 필드와 어소시에이션을 안전하게 덧붙이는 기법을 다룹니다. 다음 항목을 깊이 있게 살펴봅니다.

  • CDS View Extension의 메커니즘과 ABAP Dictionary 레벨에서의 동작 원리
  • @AbapCatalog.viewEnhancementCategory 어노테이션의 역할과 카테고리 종류
  • 실전 예제 3단계: 단순 필드 추가 → 어소시에이션·계산 필드 → 프로덕션 가드
  • 익스텐션 충돌·활성화 오류·성능 함정에 대한 트러블슈팅
  • BAdI 기반 필드 확장(Append Structure, CDS Append) 대비 장단점 비교

사전에 알고 있으면 좋은 것

이 글은 중급 ABAP 개발자를 대상으로 합니다. DEFINE VIEW 또는 DEFINE VIEW ENTITY 문법, ADT(ABAP Development Tools for Eclipse) 사용 경험, DDIC 테이블/뷰 활성화 절차, 그리고 어소시에이션(association to)의 기본 개념을 알고 있다는 전제로 진행합니다. RAP(RESTful ABAP Programming Model)에서 BDEF Extension까지 다루지는 않지만, CDS Extension은 RAP 확장의 출발점이므로 RAP 입문자에게도 유용합니다.

환경 및 준비물

예제 환경은 다음을 가정합니다.

  • SAP S/4HANA 2022 또는 2023 (On-Premise) / SAP BTP ABAP Environment (Steampunk) — CDS View Extension은 NetWeaver 7.40 SP05부터 지원되지만, viewEnhancementCategory 어노테이션과 안전한 확장 메타데이터는 7.50 SP02 이상에서 안정적으로 권장됩니다.
  • ABAP Development Tools (ADT) 3.30 이상 — Eclipse 2023-09 이상과 함께 사용하는 것이 일반적입니다. SAP GUI의 SE11/SE80에서는 CDS DDL 편집이 제한됩니다.
  • 패키지 네임스페이스 — 고객 네임스페이스(Z*, Y*) 또는 등록된 사내 네임스페이스(/COMP/*)를 권장합니다. 예제에서는 ZSD_EXT 패키지를 사용합니다.
  • 권한 — 개발 권한 S_DEVELOP, DDIC 객체 생성 권한, 그리고 표준 뷰가 속한 소프트웨어 컴포넌트에 대한 확장(Enhancement) 권한이 필요합니다.

S/4HANA Public Cloud(SaaS)에서는 표준 SAP 객체의 직접 확장이 차단되며, Released CDS View에 한해 Key User Extension 또는 Developer Extensibility(ABAP Cloud)로 제한됩니다. 이 글의 문법은 On-Premise와 Steampunk 환경 기준입니다.

핵심 개념: extend view의 동작 원리

CDS View Extension을 이해하는 가장 빠른 비유는 "건물 옥상 증축"입니다. 원본 뷰는 이미 임차인(소비자 코드)이 사용 중인 본관 건물이고, Extension은 그 위에 별도 도면(Extension Include)으로 올리는 증축 층입니다. 본관의 골조(뷰 SELECT 구문)는 그대로 두고, 활성화 시점에 ABAP Dictionary가 두 도면을 합쳐 최종 런타임 뷰를 생성합니다.

기술적으로 보면, extend view Z_BaseView with ZX_BaseView_Ext 형태로 선언된 Extension은 활성화 시 원본 뷰의 SELECT 리스트 끝에 새 필드를 덧붙이는 방식으로 머지(merge)됩니다. 머지된 결과는 HANA 데이터베이스 레벨에서 단일 Calculation View 또는 Column View로 컴파일되므로, 런타임에 추가 조인 비용이 발생하지 않습니다. 즉 Extension은 "물리적으로 합쳐진" 단일 뷰가 되며, 소비자 입장에서는 원본 뷰를 SELECT했을 뿐인데 확장 필드가 자동으로 따라옵니다.

여기서 핵심이 되는 것이 @AbapCatalog.viewEnhancementCategory 어노테이션입니다. 이 어노테이션은 원본 뷰 정의에 선언되며, 어떤 종류의 확장을 허용할지를 명시적으로 선언합니다. 일반적으로 사용되는 값은 다음과 같습니다.

  • #NONE — 확장을 일체 허용하지 않음. extend view 시도 시 활성화 오류.
  • #PROJECTION_LIST — SELECT 리스트(필드 목록)에만 필드 추가 허용. GROUP BY/UNION이 없는 단순 뷰에 권장.
  • #GROUP_BY — GROUP BY 절을 포함한 집계 뷰에서 필드와 GROUP BY 키를 함께 확장할 때 사용.
  • #UNION — UNION 뷰에서 각 브랜치에 필드를 추가할 때 사용.

이 카테고리는 배열로 지정할 수 있어 [#PROJECTION_LIST, #GROUP_BY]처럼 복수 허용도 가능합니다. 어노테이션이 누락되어 있으면 기본값은 #NONE으로 간주되며, 표준 뷰가 이를 명시하지 않았다면 안타깝게도 해당 뷰는 Extension 대상이 될 수 없고, 대신 자체 Composite View를 만들어야 합니다.

실전 예제 3단계

1단계 — 기본: 단순 필드 추가

먼저 시나리오를 설정합니다. 자사는 표준 영업문서 뷰 I_SalesDocumentBasic을 기반으로 자체 영업문서 뷰 Z_I_SalesDocument를 운영하고 있다고 가정합니다. 이 뷰는 다음과 같이 정의되어 있습니다(원본은 수정 대상 아님).

@AbapCatalog.sqlViewName: 'ZISALESDOC'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@AbapCatalog.viewEnhancementCategory: [#PROJECTION_LIST]
@EndUserText.label: '자사 영업문서 기본 뷰'
define view Z_I_SalesDocument
  as select from vbak
{
  key vbeln  as SalesOrderId,
      erdat  as CreationDate,
      ernam  as CreatedBy,
      kunnr  as SoldToParty,
      netwr  as NetAmount,
      waerk  as Currency
}

이제 추가 요건이 발생했습니다. 물류 부서가 "우선순위 코드(ZZ_PRIORITY_CODE)"와 "긴급 배송 플래그(ZZ_URGENT_FLAG)" 두 필드를 영업문서 조회 화면에 노출하고 싶다고 합니다. 두 필드는 이미 VBAK Append Structure로 추가되어 DB 레벨에는 존재하지만, CDS 뷰에는 노출되지 않은 상태입니다. 원본 뷰를 수정하지 않고 Extension으로 처리합니다.

@EndUserText.label: '영업문서 뷰 확장 - 물류 필드'
extend view Z_I_SalesDocument with Z_I_SalesDocument_LogiExt
{
  vbak.zz_priority_code as PriorityCode,
  vbak.zz_urgent_flag   as UrgentDeliveryFlag
}

활성화 후 임의의 ABAP 리포트에서 SELECT * FROM z_i_salesdocument를 수행하면 결과 구조에 PriorityCodeUrgentDeliveryFlag가 자동으로 포함됩니다. 원본 뷰 정의는 한 글자도 바뀌지 않았습니다.

2단계 — 실무: 어소시에이션·계산 필드·로깅

다음으로 텍스트 부서가 "우선순위 코드의 한글 설명을 함께 보고 싶다"고 요청합니다. 별도 텍스트 테이블 ZTPRIO_TXT가 있고 키는 (언어, 우선순위코드)입니다. 또한 NetAmount에 부가세 10%를 더한 GrossAmount도 계산 필드로 넣어달라고 합니다.

@EndUserText.label: '영업문서 뷰 확장 - 텍스트 어소시에이션 및 계산'
extend view Z_I_SalesDocument with Z_I_SalesDocument_TxtExt
  association [0..1] to Z_I_PriorityText as _PriorityText
    on  $projection.PriorityCode = _PriorityText.PriorityCode
    and _PriorityText.Language   = $session.system_language
{
  // 계산 필드
  cast( netwr * '1.10' as abap.curr(15,2) ) as GrossAmount,

  // 어소시에이션 노출 (소비자가 _PriorityText 경로로 접근 가능)
  _PriorityText
}

주의할 점은 Extension 안에서 정의한 어소시에이션은 원본 뷰의 어소시에이션과 동일한 네임스페이스에 들어간다는 것입니다. 즉 원본에 이미 _PriorityText가 있다면 활성화 오류가 발생하므로, Extension 내 어소시에이션은 사내 prefix(_Zz, _Ext_ 등)를 붙이는 명명 규칙을 권장합니다.

운영 환경에서 Extension의 변경 이력은 추적이 어려운 편이라, CTS(Transport)에 Extension 객체를 명시적으로 포함하고, Application Log(SLG1) 또는 Custom Audit Table에 활성화 시점·작성자를 별도로 기록하는 운영 정책을 일반적으로 권장합니다.

3단계 — 프로덕션: 보안·성능·테스트 가드

프로덕션 적용 전에는 다음 세 가지를 점검해야 합니다.

// (1) 보안: 확장 필드에 PFCG 권한 체크가 필요한 경우 DCL로 격리
@MappingRole: true
define role Z_I_SalesDocument_Ext_Role {
  grant select on Z_I_SalesDocument
    where (PriorityCode) = aspect pfcg_auth(Z_PRIO, ACTVT = '03');
}

// (2) 성능: 확장 필드가 다른 테이블 조인을 유발한다면 INDEX 검토
//     - PriorityCode 텍스트 어소시에이션은 lazy join이므로 SELECT 시 미참조면 비용 0
//     - 단, ORDER BY/WHERE에 사용 시 강제 조인 발생

// (3) 테스트: ABAP Unit으로 회귀 검증
CLASS ltc_salesdoc_ext DEFINITION FOR TESTING
  RISK LEVEL HARMLESS DURATION SHORT.
  PRIVATE SECTION.
    METHODS extension_fields_exposed FOR TESTING.
ENDCLASS.

CLASS ltc_salesdoc_ext IMPLEMENTATION.
  METHOD extension_fields_exposed.
    SELECT SINGLE SalesOrderId, PriorityCode, GrossAmount
      FROM z_i_salesdocument
      INTO @DATA(ls_row)
      UP TO 1 ROWS.

    cl_abap_unit_assert=>assert_bound(
      act = ls_row-SalesOrderId
      msg = '확장 뷰가 데이터를 반환해야 함' ).
  ENDMETHOD.
ENDCLASS.

프로덕션 이관 시에는 Extension과 원본 뷰가 동일 트랜스포트 레이어에 있는지, 그리고 Append Structure(DB 컬럼 추가) → CDS Extension(뷰 노출) 순서로 import 되도록 트랜스포트 의존성을 정렬해야 합니다. 역순으로 import 되면 활성화 실패가 연쇄적으로 발생합니다.

흔한 실수와 트러블슈팅

Q1. "Enhancement of view is not allowed" 오류가 납니다. 원본 뷰에 @AbapCatalog.viewEnhancementCategory 어노테이션이 없거나 #NONE으로 설정되어 있을 때 발생합니다. 표준 SAP 뷰의 경우 이 어노테이션이 누락되어 있다면 해당 뷰는 Extension 대상이 아니므로, 표준 뷰를 Base로 하는 자체 Wrapper 뷰(Z_C_Wrapper)를 만들어 그곳에 필드를 추가하는 우회 전략을 사용해야 합니다. 자체 뷰라면 원본 뷰 어노테이션을 [#PROJECTION_LIST]로 수정 후 재활성화하면 됩니다.

Q2. Extension 필드를 추가했는데 OData 서비스나 Fiori 화면에 안 나타납니다. CDS Extension은 데이터베이스 뷰 레벨에서는 즉시 노출되지만, OData 메타데이터 캐시(/IWFND/CACHE_CLEANUP)나 RAP의 BDEF Projection은 별도로 갱신해야 합니다. RAP 시나리오에서는 Projection View와 BDEF에 대해서도 각각 Extension을 추가해야 Fiori까지 전파됩니다.

Q3. 같은 원본 뷰에 여러 개의 Extension을 만들 수 있나요? 가능합니다. Extension 이름만 다르면 N개까지 허용되며, 활성화 시 모두 머지됩니다. 다만 필드명 또는 어소시에이션 이름이 충돌하면 활성화 실패하므로, 팀별/모듈별 prefix 규칙(예: _LogiExt, _FinExt)을 정해두는 것이 운영 안전성에 좋습니다.

Q4. Append Structure로 컬럼을 추가했는데 CDS에서 자동 노출되지 않나요? 자동 노출되지 않습니다. DB 컬럼이 있어도 CDS는 명시적으로 SELECT 리스트에 적힌 필드만 노출하므로, Extension으로 별도 등록해야 합니다. 이는 의도된 설계이며, "DB 스키마와 의미 모델(Semantic Model)의 분리" 원칙을 따르는 것입니다.

이어서 살펴볼 주제

CDS View Extension은 클린 코어 확장의 첫걸음일 뿐입니다. 다음 단계로 RAP의 Behavior Definition Extension(BDEF Extension)을 학습하면 확장 필드에 대한 Validation/Determination 로직까지 원본 수정 없이 추가할 수 있습니다. 또한 Metadata Extension(MDE)은 UI 어노테이션(label, position, fieldGroup)을 별도 파일로 분리해 Fiori 화면을 동적으로 재구성하게 해줍니다. 마지막으로 SAP BTP ABAP Environment에서는 Released CDS View만 Extension 대상이 되므로, API 안정성 등급(C0/C1/C2)을 확인하는 습관도 함께 들이는 것이 좋습니다.

읽어볼 자료

댓글 0

아직 댓글이 없습니다.