SAP CDS View 만들기 — 처음 하는 사람을 위한 8단계 실습

BTP 트라이얼 환경 구축과 /DMO/ 데이터 실습은
이전 글에 따로 정리해 두었습니다.

S/4HANA 프로젝트에 들어가면 가장 먼저 마주치는 단어가 CDS View입니다. 그런데 막상 만들려고 하면 SE11에는 메뉴가 없고, Eclipse를 켜라는 말만 돌아옵니다. 이 글은 CDS View를 한 번도 만들어 본 적 없는 개발자가 첫 뷰를 활성화하고 데이터를 눈으로 확인할 때까지의 과정을, 빠지는 단계 없이 순서대로 정리한 것입니다.

목차

  1. CDS View는 왜 만들어졌나
  2. CDS View의 실체 — 소스 하나가 만드는 것들
  3. 시작 전 준비물 체크리스트
  4. STEP 1~8 실습
  5. Association — CDS의 진짜 무기
  6. 뷰를 층으로 쌓는 방법 (VDM)
  7. 자주 만나는 오류와 해결
  8. 다음에 공부할 것

1. CDS View는 왜 만들어졌나

기존 ABAP 개발 방식을 떠올려 봅시다. 테이블에서 데이터를 통째로 내부 테이블에 담고, LOOP를 돌면서 READ TABLE로 짝을 맞추고, 금액을 더합니다. 익숙한 패턴이지만 여기에는 구조적인 낭비가 있습니다. 데이터베이스는 조회만 하고 계산은 전부 애플리케이션 서버가 떠맡는 구조이기 때문입니다.

30만 건을 올려서 12줄을 만들 거라면, 그 계산을 데이터가 있는 쪽에서 하는 편이 낫습니다. 이 발상의 전환을 코드 푸시다운(Code Pushdown)이라고 부르고, 이를 ABAP 안에서 구현하는 수단이 CDS View입니다.

데이터를 코드로 가져올 것인가, 코드를 데이터로 보낼 것인가 BEFORE 기존 방식 · Data to Code ABAP 애플리케이션 서버 SELECT * FROM vbak INTO TABLE lt_head. LOOP AT lt_head … READ TABLE lt_item … lv_sum = lv_sum + ls_item-netwr. 전송량: 30만 건 필요한 건 결과 12줄뿐 데이터베이스 단순 조회만 수행 · 계산은 안 함 네트워크 부하 ↑ 응답 속도 ↓ DB 자원 유휴 AFTER CDS 방식 · Code to Data ABAP 애플리케이션 서버 SELECT * FROM zi_order_summary INTO TABLE @DATA(lt_result). 루프도, 집계 로직도 없음 전송량: 12 건 결과만 올라온다 데이터베이스 — CDS View JOIN · WHERE · SUM · CASE 를 여기서 수행 네트워크 부하 ↓ 응답 속도 ↑ DB 성능 활용
그림 1. 데이터를 코드로 가져올 것인가, 코드를 데이터로 보낼 것인가

정리하면 CDS View를 쓰는 이유는 세 가지입니다.

  • 성능 — JOIN과 집계를 HANA가 처리하므로 전송량이 줄어듭니다.
  • 재사용 — 한 번 정의한 뷰를 ABAP, OData, 분석, BW가 모두 씁니다.
  • 표준 — Fiori와 RAP는 CDS를 전제로 설계되어 있어, CDS 없이는 진입 자체가 어렵습니다.

2. CDS View의 실체 — 소스 하나가 만드는 것들

초보자가 가장 많이 헷갈리는 지점이 여기입니다. CDS View는 DDL 소스 파일이고, 이것을 활성화하면 ABAP 리포지토리에 런타임 오브젝트가 생기며, 동시에 데이터베이스에도 실제 뷰가 만들어집니다. 즉 우리가 편집하는 텍스트 파일과 DB에 존재하는 객체는 다른 것입니다.

한 개의 DDL 소스가 만들어 내는 것들 개발 계층 Eclipse + ADT — DDL 소스 define view entity ZI_Flight_Basic as select from sflight { … } SE11/SE80 아님. Eclipse에서만 작성 가능 활성화 Ctrl + F3 리포지토리 계층 ABAP Dictionary 런타임 오브젝트 CDS View Entity ABAP에서 직접 SELECT 하는 이름 DDIC SQL View (구방식만) @AbapCatalog.sqlViewName 로 생성 DB에 뷰 생성 데이터베이스 계층 SAP HANA — Database View 기본 테이블 — SFLIGHT · VBAK · VBAP · KNA1 · BSEG … CDS View는 데이터를 복제하지 않는다. 조회 시점에 계산된다. 소비 채널 ABAP Open SQL SELECT … FROM zi_flight_basic RAP → OData → Fiori Service Definition / Binding Analytics · KPI @Analytics.dataCategory BW / SAC / 외부 리포팅 Extraction 애노테이션 하나의 뷰를 정의하면 네 갈래로 재사용된다.
그림 2. 한 개의 DDL 소스가 만들어 내는 것들

중요 — CDS View는 데이터를 복제하지 않습니다. 조회하는 순간 원본 테이블을 읽어 계산합니다. 그래서 원본이 바뀌면 뷰의 결과도 즉시 바뀝니다. 별도의 적재나 갱신 작업이 필요 없습니다.

3. 시작 전 준비물 체크리스트

항목내용확인 방법
시스템ABAP 7.4 SP05 이상. define view entity를 쓰려면 7.55 이상(S/4HANA 2020~)SAP GUI 상단 메뉴 → 시스템 → 상태
EclipseEclipse IDE for Java Developers (최신 릴리스)
ADTABAP Development Tools 플러그인Help → Install New Software
개발 권한S_DEVELOP 권한, 개발자 키(구버전)Basis 담당자에게 요청
패키지Z로 시작하는 개발 패키지와 전송 요청SE80 또는 ADT에서 생성

ADT 설치 URL은 SAP 공식 업데이트 사이트를 사용합니다. Eclipse 버전마다 URL이 다르니, SAP Development Tools 사이트에서 자신의 Eclipse 릴리스에 맞는 주소를 확인하세요.

4. STEP 1~8 실습

전체 흐름은 다음과 같습니다. 1~3단계는 처음 한 번만 거치고, 실무에서 반복되는 구간은 4 → 5 → 6입니다.

처음부터 끝까지, 여덟 단계 1 환경 준비 Eclipse + ADT 설치 ABAP 프로젝트로 시스템 연결 2 DDL 소스 생성 패키지 우클릭 → New Data Definition 3 템플릿 선택 Define View Entity 신규 개발의 기본값 4 소스 작성 필드 선택과 별칭 Ctrl+Space 활용 5 활성화 Ctrl + F3 여기서 DB 뷰가 만들어진다 6 데이터 확인 F8 — Data Preview 건수와 값을 눈으로 검증 7 기능 확장 Association · 집계 파라미터 · CASE 8 소비 ABAP SELECT OData · Fiori 1~3단계는 처음 한 번만. 실무에서 반복되는 구간은 4 → 5 → 6 이다.
그림 3. 처음부터 끝까지, 여덟 단계

STEP 1. ABAP 프로젝트로 시스템 연결

Eclipse를 열고 File → New → ABAP Project를 선택합니다. SAP GUI에 등록된 시스템 목록이 뜨면 접속할 시스템을 고르고, 클라이언트·사용자·비밀번호를 입력합니다. 연결에 성공하면 Project Explorer에 시스템 노드가 생깁니다.

화면 구성이 낯설다면 Window → Perspective → Open Perspective → ABAP으로 ABAP 퍼스펙티브를 켜 두세요. Project Explorer, 에디터, Problems 뷰가 함께 배치됩니다.

STEP 2. 패키지에 DDL 소스 만들기

Project Explorer에서 작업할 Z 패키지를 찾아 우클릭 → New → Other ABAP Repository Object를 선택합니다. 검색창에 data definition을 입력하면 Core Data Services → Data Definition이 나옵니다.

다음 화면에서 두 가지를 입력합니다.

  • Name — 예: ZI_FLIGHT_BASIC (30자 이내, Z 또는 Y로 시작)
  • Description — 뷰의 용도를 한 줄로

전송 요청을 선택하면 템플릿 선택 화면으로 넘어갑니다.

STEP 3. 템플릿 선택 — View 인가 View Entity 인가

템플릿 목록에서 Define View Entity와 Define View가 함께 보입니다. 둘의 차이를 모르고 지나가면 나중에 반드시 발목을 잡히므로 여기서 짚고 갑니다.

구분Define View (구방식)Define View Entity (신방식)
키워드define viewdefine view entity
DDIC SQL View별도로 생성됨 (16자 제한)생성되지 않음
필수 애노테이션@AbapCatalog.sqlViewName 등 다수거의 없음
클라이언트 처리수동 고려 필요기본적으로 자동 처리
ABAP Cloud사용 불가사용 가능
권장기존 코드 유지보수용신규 개발은 이쪽

이 글에서는 신규 개발 기준인 Define View Entity로 진행합니다. 다만 현장에서는 구방식 뷰를 읽을 일이 훨씬 많으므로, 아래 두 형태를 모두 보여 드립니다.

구방식 — Define View

@AbapCatalog.sqlViewName: 'ZVFLIGHTBAS'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: '항공편 기본 뷰'
define view ZI_FLIGHT_BASIC
  as select from sflight
{
  key carrid   as AirlineCode,
  key connid   as ConnectionNumber,
  key fldate   as FlightDate,
      price    as Price,
      currency as CurrencyCode
}

구방식의 함정 — sqlViewName은 16자 이내여야 하고, 시스템 전체에서 유일해야 합니다. 뷰 이름과 다른 별도의 이름이므로 명명 규칙을 미리 정해 두지 않으면 금방 뒤엉킵니다.

신방식 — Define View Entity

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: '항공편 기본 뷰'
define view entity ZI_Flight_Basic
  as select from sflight
{
  key carrid   as AirlineCode,
  key connid   as ConnectionNumber,
  key fldate   as FlightDate,
      price    as Price,
      currency as CurrencyCode
}

보시다시피 신방식이 훨씬 간결합니다. 관리할 이름이 하나로 줄어드는 것만으로도 실수가 크게 줄어듭니다.

STEP 4. 소스 작성 — 각 줄의 역할

코드를 처음 보면 어디가 문법이고 어디가 이름인지 구분이 안 됩니다. 아래 그림에서 각 줄이 맡은 역할을 확인하세요.

코드 한 줄 한 줄이 무슨 일을 하는가 @EndUserText.label: ‘항공편 기본 뷰’ @AccessControl.authorizationCheck: #CHECK define view entity ZI_Flight_Basic as select from sflight association [0..1] to ZI_Carrier as _Carrier on $projection.AirlineCode = _Carrier.AirlineCode { key carrid as AirlineCode, key connid as ConnectionNumber, fldate as FlightDate, price as Price, _Carrier.CarrierName, _Carrier } where fldate >= ‘20240101’ — 세미콜론은 붙이지 않는다 1 애노테이션 뷰의 성질을 선언하는 메타데이터. @ 로 시작 2 뷰 이름 ABAP에서 SELECT 할 이름. 파일명과 동일해야 함 3 데이터 원천 테이블 또는 다른 CDS 뷰를 지정 4 어소시에이션 다른 뷰와의 관계 선언. 아직 조인은 안 일어남 5 키 필드와 별칭 key 로 식별자 지정, as 로 읽기 쉬운 이름 부여 6 경로 표현식 여기서 처음 실제 조인이 발생한다 7 어소시에이션 노출 상위 뷰가 이어서 타고 갈 수 있게 공개 8 필터 조건 중괄호 뒤에 온다. 순서를 바꾸면 문법 오류 필수 순서 — 애노테이션 → define → as select from → association → { 필드 } → where → group by
그림 4. 코드 한 줄 한 줄이 무슨 일을 하는가

작성할 때 반드시 지켜야 할 규칙 네 가지입니다.

  1. 순서를 지킵니다. 애노테이션 → define → as select from → association → { 필드 } → where → group by. 이 순서를 벗어나면 문법 오류입니다.
  2. 마지막에 세미콜론을 붙이지 않습니다. ABAP 습관 때문에 가장 자주 나오는 실수입니다.
  3. 필드 구분자는 쉼표입니다. 마지막 필드 뒤에는 쉼표를 붙이지 않습니다.
  4. 별칭은 대문자로 시작하는 카멜 표기를 씁니다. AirlineCode처럼요. SAP 표준 뷰와 일관성이 맞아야 나중에 조합하기 쉽습니다.

단축키 — 필드명을 외울 필요는 없습니다. 중괄호 안에서 Ctrl + Space를 누르면 원천 테이블의 필드가 전부 나옵니다. Ctrl+Shift+F는 코드 정렬입니다.

STEP 5. 활성화

Ctrl + F3을 누르면 활성화됩니다. 이 순간 데이터베이스에 실제 뷰가 생성됩니다. Problems 뷰에 빨간 표시가 없으면 성공입니다.

노란색 경고는 대부분 무시해도 동작하지만, 애노테이션 누락 경고는 나중에 OData나 분석에서 문제가 되므로 초기에 정리해 두는 편이 낫습니다.

STEP 6. 데이터 확인 — Data Preview

에디터에서 F8을 누르면 Data Preview가 열립니다. 실제 데이터가 표 형태로 나오고, 상단에서 필터를 걸거나 건수를 확인할 수 있습니다. 여기서 예상한 건수가 나오는지 반드시 확인하세요. 조인을 잘못 걸면 건수가 몇 배로 부풀어 있습니다.

SQL 실행 계획을 보고 싶다면 Data Preview 화면에서 SQL 콘솔로 전환할 수 있습니다. 성능 문제를 추적할 때 유용합니다.

STEP 7. 기능 확장

필터 — WHERE

define view entity ZI_Flight_2024
  as select from sflight
{
  key carrid as AirlineCode,
  key connid as ConnectionNumber,
  key fldate as FlightDate,
      price  as Price
}
where fldate >= '20240101'

조건 분기 — CASE

      case
        when seatsocc >= seatsmax then 'FULL'
        when seatsocc >= seatsmax * 0.8 then 'HIGH'
        else 'LOW'
      end as OccupancyLevel,

집계 — SUM과 GROUP BY

@EndUserText.label: '항공사별 매출 합계'
define view entity ZC_Flight_Revenue
  as select from ZI_Flight_Basic
{
  key AirlineCode,
  key CurrencyCode,
      @Semantics.amount.currencyCode: 'CurrencyCode'
      sum(Price) as TotalRevenue
}
group by AirlineCode, CurrencyCode

금액·수량 필드 규칙 — 금액 필드에는 @Semantics.amount.currencyCode, 수량 필드에는 @Semantics.quantity.unitOfMeasure를 붙이고, 참조하는 통화·단위 필드도 반드시 SELECT 목록에 포함해야 합니다. 빠뜨리면 활성화 오류가 납니다.

파라미터 받기

define view entity ZI_Flight_ByDate
  with parameters
    p_from_date : abap.dats,
    p_to_date   : abap.dats
  as select from sflight
{
  key carrid as AirlineCode,
  key fldate as FlightDate,
      price  as Price
}
where fldate between :p_from_date and :p_to_date

ABAP에서 호출할 때는 다음과 같이 씁니다.

SELECT * FROM zi_flight_bydate( p_from_date = '20240101',
                                p_to_date   = '20241231' )
  INTO TABLE @DATA(lt_flights).

STEP 8. 만든 뷰를 소비하기

ABAP에서 직접 조회

SELECT AirlineCode, ConnectionNumber, Price
  FROM zi_flight_basic
  WHERE AirlineCode = 'LH'
  INTO TABLE @DATA(lt_result).

주의할 점은 별칭으로 조회한다는 것입니다. 원본 테이블 필드명 carrid가 아니라 뷰에서 정의한 AirlineCode를 씁니다.

OData 서비스로 노출

구방식에서는 @OData.publish: true 한 줄과 SEGW/트랜잭션 /IWFND/MAINT_SERVICE 등록으로 끝났습니다. 신규 개발이라면 Service Definition과 Service Binding을 만드는 RAP 방식이 표준입니다.

@EndUserText.label: '항공편 서비스'
define service ZUI_FLIGHT {
  expose ZC_Flight_Revenue as FlightRevenue;
}

이후 Service Binding을 생성하고 활성화하면 URL이 발급되며, Preview 버튼으로 Fiori Elements 화면을 바로 확인할 수 있습니다.

5. Association — CDS의 진짜 무기

여기까지는 사실 DB View로도 할 수 있는 일입니다. CDS가 확실히 다른 지점은 Association입니다.

JOIN은 항상 실행된다. Association은 부를 때만 실행된다. JOIN 관계를 즉시 실행한다 as select from sflight inner join scarr on sflight.carrid = scarr.carrid SFLIGHT 항공편 30만 건 SCARR 항공사 마스터 결과 · 항공사 이름을 안 쓰는 조회에서도 조인이 붙는다 · 카디널리티가 틀리면 행 수가 늘어난다 · 상위 뷰가 관계를 다시 정의해야 한다 고정된 결합. 필요 없어도 비용을 낸다. ASSOCIATION 관계를 선언만 해 둔다 association [0..1] to ZI_Carrier as _Carrier on $projection.AirlineCode = _Carrier.AirlineCode SFLIGHT 항공편 30만 건 잠들어 있음 ZI_Carrier _Carrier 로 참조 결과 · _Carrier.CarrierName 을 쓴 순간에만 조인 · 안 쓰면 실행 계획에 아예 안 나온다 · 상위 뷰가 \_Carrier 로 이어서 탈 수 있다 느슨한 결합. 쓸 때만 값을 치른다.
그림 5. JOIN은 항상 실행된다. Association은 부를 때만 실행된다.
@EndUserText.label: '항공편 + 항공사명'
define view entity ZI_Flight_WithCarrier
  as select from sflight
  association [0..1] to ZI_Carrier as _Carrier
    on $projection.AirlineCode = _Carrier.AirlineCode
{
  key carrid as AirlineCode,
  key connid as ConnectionNumber,
      price  as Price,

      _Carrier.CarrierName,   // 여기서 실제 조인 발생
      _Carrier                // 상위 뷰가 쓸 수 있도록 노출
}

핵심은 세 가지입니다.

  • $projection은 지금 이 뷰의 SELECT 목록을 가리킵니다. 원본 필드명이 아니라 별칭을 씁니다.
  • 카디널리티 [0..1]은 대상 쪽 건수입니다. 잘못 쓰면 결과 행 수가 어긋나므로 신중하게 지정합니다.
  • 이름 앞에 붙이는 언더스코어는 SAP 관례입니다. 필드와 구분하기 위해서입니다.

노출된 Association은 상위 뷰나 ABAP에서 경로로 이어 탈 수 있습니다.

SELECT AirlineCode, \_Carrier-CarrierName AS CarrierName
  FROM zi_flight_withcarrier
  INTO TABLE @DATA(lt_data).

6. 뷰를 층으로 쌓는 방법 (VDM)

실무에서 뷰 하나에 모든 것을 넣으면 두 번째 요구사항이 들어오는 순간 무너집니다. SAP는 목적에 따라 뷰를 계층으로 나누는 가상 데이터 모델(VDM) 방식을 권장합니다.

뷰를 층으로 쌓아 올린다 — VDM 구조 한 뷰에 모든 것을 넣지 않는다. 재사용 단위와 화면 단위를 분리하는 것이 SAP 표준 방식이다. ZC_ Consumption View Fiori 화면 · OData 서비스 · 분석 쿼리 전용 @UI 애노테이션, 사용자 언어 텍스트, 필드 순서가 여기 붙는다 화면에 보이는 층 재사용을 기대하지 않음 자주 바뀌어도 안전 ZI_ Composite View 여러 Basic 뷰를 조합 · 집계(SUM)와 계산 필드 · 업무 로직 예: 수주 헤더 + 아이템 + 고객을 묶어 “수주 잔량”을 계산 업무 로직 층 여러 화면이 공유 ZI_ Basic / Interface View 테이블 하나에 거의 1:1 · 필드명을 의미 있는 이름으로 바꾸는 층 vbeln → SalesOrder, kunnr → Customer. 여기서 이름을 통일해 둔다 재사용 층 가장 안정적 함부로 바꾸지 않음 기본 테이블 — VBAK · VBAP · KNA1 · MARA · BSEG 건드리지 않는 원본
그림 6. 뷰를 층으로 쌓아 올린다 — VDM 구조
접두어계층역할
ZI_Interface / Basic테이블과 거의 1:1. 필드명을 의미 있게 바꾸는 층. 가장 안정적
ZI_Composite여러 Basic 뷰를 조합하고 집계·계산을 수행
ZC_ConsumptionFiori/OData 전용. @UI 애노테이션이 붙는 층
ZR_RAP Root트랜잭션(생성·수정) 처리를 하는 RAP 루트 뷰

규칙은 단순합니다. 화면이 바뀌면 ZC_만 고치고, ZI_는 건드리지 않는다. 이 원칙 하나만 지켜도 유지보수 비용이 확연히 달라집니다.

7. 자주 만나는 오류와 해결

증상원인해결
SQL view name이 이미 존재한다는 오류구방식에서 sqlViewName 중복16자 이내의 다른 이름으로 변경. 또는 View Entity로 전환
필드가 key로 지정되어야 한다는 오류키 필드가 SELECT 목록 앞쪽에 연속으로 있지 않음key 필드를 목록 맨 앞에 몰아서 배치
통화/단위 참조 오류@Semantics가 가리키는 필드가 SELECT에 없음통화 코드 또는 단위 필드를 SELECT 목록에 추가
Data Preview에서 권한 오류DDL 실행 권한 또는 데이터 권한 부족테스트 중이라면 @AccessControl.authorizationCheck: #NOT_REQUIRED로 원인 분리
결과 건수가 예상보다 많음Association 카디널리티 오지정 또는 JOIN 조건 누락카디널리티를 [0..1]로 조정하고 ON 조건 재확인
ABAP에서 뷰를 못 찾음구방식에서 SQL View 이름으로 조회함CDS 엔티티 이름으로 조회. 별칭 필드명 사용 여부도 확인
Ctrl+Space 자동완성이 안 됨파일이 활성화되지 않았거나 원천 지정 전as select from까지 먼저 작성한 뒤 시도

디버깅 요령 — 뷰가 복잡해서 원인을 못 찾겠다면 필드를 하나씩 지우면서 활성화해 보세요. 오류가 사라지는 지점이 범인입니다. Association을 여러 개 걸었다면 하나씩 주석 처리하는 방식이 가장 빠릅니다.

8. 다음에 공부할 것

첫 뷰를 만들고 데이터를 확인했다면 절반은 온 것입니다. 이어서 볼 주제는 다음 순서를 권합니다.

  1. DCL (Access Control) — 사용자 권한에 따라 조회 결과를 제한하는 방법
  2. Extend View — 표준 SAP 뷰를 수정하지 않고 필드를 추가하는 방법
  3. Table Function — CDS 문법으로 표현이 안 되는 로직을 AMDP로 처리
  4. RAP — 조회를 넘어 생성·수정·삭제까지 처리하는 애플리케이션 모델
  5. @UI 애노테이션 — 코드만으로 Fiori 화면 레이아웃을 구성

CDS View는 문법 자체가 어렵지는 않습니다. 어려운 것은 어느 계층에 무엇을 넣을 것인가라는 설계 판단입니다. 처음에는 Basic 뷰 하나, Consumption 뷰 하나로 시작해 보고, 요구사항이 늘어날 때마다 계층을 나누는 연습을 하는 편이 가장 빠릅니다.

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤