News

CDS DCL 모르면 데이터 다 새나간다? #shorts #SAP #ABAP

▶ YouTube에서 보기

1. 개요 및 이 글의 목표

ABAP CDS(Core Data Services)는 데이터 모델링 계층을 SAP HANA 데이터베이스에 가깝게 끌어올린 핵심 기술입니다. 그런데 데이터 모델이 강력해질수록 "누가 어떤 행(row)을 볼 수 있는가"라는 질문이 더 중요해집니다. ABAP CDS DCL(Data Control Language)은 바로 이 질문에 선언적으로 답하기 위해 도입된 언어입니다.

본 글에서는 다음을 단계적으로 다룹니다.

  • 전통적 AUTHORITY-CHECK 방식과 CDS DCL의 차이
  • DEFINE ROLE, GRANT SELECT ON, ASPECT PFCG_AUTH의 사용법
  • PFCG 역할과 CDS 뷰의 권한 필드 매핑
  • 개발/테스트 환경에서의 PRIVILEGED 액세스
  • 다중 조건, 상속 뷰에서의 권한 전파 패턴

대상 독자는 ABAP 7.5x 이상 기반의 S/4HANA 또는 ABAP Platform 환경에서 CDS 뷰를 작성한 경험이 있는 개발자입니다.

2. 핵심 개념 — CDS DCL이란 무엇인가

전통적인 ABAP에서 권한 검사는 일반적으로 명령형(imperative) 방식이었습니다. 개발자가 ABAP 로직 안에서 AUTHORITY-CHECK OBJECT 'S_CARRID' ID 'CARRID' FIELD ls_data-carrid ID 'ACTVT' FIELD '03'처럼 직접 호출하고, sy-subrc 값을 확인해 분기하는 구조였습니다. 이 방식은 다음과 같은 한계를 가집니다.

  • 읽기 권한 체크 위치가 SELECT 문 이전/이후로 분산됨
  • OData/Fiori처럼 데이터를 자동으로 노출하는 채널에서 누락되기 쉬움
  • 같은 뷰를 여러 곳에서 재사용할 때 중복 체크 발생

CDS DCL은 권한을 데이터 모델 자체에 부착하는 선언적 접근입니다. 비유하자면 전통 방식이 "건물 입구마다 경비원을 세우는 것"이라면, DCL은 "각 방의 문 자체에 자물쇠를 다는 것"에 가깝습니다. CDS 엔티티에 DCL 역할(role)을 연결하면 OData, AMDP, Open SQL, RAP 어느 경로로 접근하든 동일한 필터가 자동 적용됩니다.

일반적으로 ABAP CDS 뷰를 ABAP CDS Session Variable과 함께 쓰면, 클라이언트/사용자/언어/시각 정보가 권한 조건에 자연스럽게 들어갑니다.

핵심 구성요소는 다음과 같습니다.

  • DCL Source(DDLX-like): @MappingRole: true 어노테이션과 함께 DEFINE ROLE 블록 작성
  • GRANT SELECT ON: 어느 CDS 엔티티에 어떤 WHERE 조건으로 SELECT를 허용할지 명시
  • ASPECT PFCG_AUTH: PFCG 권한 오브젝트의 필드 값을 동적으로 가져와 WHERE 조건으로 변환

3. 1단계: DEFINE ROLE 구문으로 CDS 뷰에 권한 조건 연결

가장 단순한 형태부터 시작합니다. 예를 들어 Z_I_FlightBooking이라는 CDS 뷰가 있고, 이 뷰에 항공사 코드(CarrierId)별로 접근을 제한하고 싶다고 가정합니다.

3-1. 기본 CDS 뷰 정의

@AbapCatalog.sqlViewName: 'ZIFLIGHTBOOK'
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Flight Booking View'
define view Z_I_FlightBooking
  as select from sbook as Booking
{
  key Booking.carrid    as CarrierId,
  key Booking.connid    as ConnectionId,
  key Booking.fldate    as FlightDate,
  key Booking.bookid    as BookingId,
      Booking.customid  as CustomerId,
      Booking.luggweight as LuggageWeight,
      Booking.loccuram  as LocalAmount,
      Booking.loccurkey as LocalCurrency
}

여기서 핵심은 @AccessControl.authorizationCheck: #CHECK 어노테이션입니다. 이 값이 #CHECK일 때 ABAP 런타임은 해당 엔티티에 연결된 DCL 역할을 적용하려고 시도합니다. #NOT_REQUIRED#NOT_ALLOWED로 설정하면 DCL이 비활성화됩니다.

3-2. 최소 DCL Role 작성

@EndUserText.label: 'Role for Flight Booking'
@MappingRole: true
define role Z_I_FlightBooking {
  grant select on Z_I_FlightBooking
    where CarrierId = 'LH';
}

위 역할은 모든 사용자가 CarrierId = 'LH'인 데이터만 볼 수 있도록 제한합니다. 단순하지만 권한 조건이 ABAP 로직 외부, 즉 데이터 모델 옆에 선언되었다는 점이 중요합니다.

4. 2단계: GRANT SELECT ON WHERE — 권한 오브젝트 기반 필터 조건 정의

실무에서는 모든 사용자가 'LH'만 볼 수 있도록 고정하는 일은 거의 없습니다. 사용자마다 다른 항공사 코드에 접근해야 하므로 PFCG 권한 오브젝트와 연동된 동적 필터가 필요합니다.

4-1. PFCG 권한 오브젝트 연동 패턴

@EndUserText.label: 'Role for Flight Booking with PFCG'
@MappingRole: true
define role Z_I_FlightBooking {
  grant select on Z_I_FlightBooking
    where
      ( CarrierId ) =
        aspect pfcg_auth ( S_CARRID, CARRID, ACTVT = '03' );
}

읽는 방법은 다음과 같습니다.

  • S_CARRID: PFCG 권한 오브젝트 이름
  • CARRID: 해당 오브젝트의 필드 중 CDS 컬럼 CarrierId와 매핑할 필드
  • ACTVT = '03': ACTVT(활동) 필드 값이 '03'(읽기)인 권한만 사용

런타임에 사용자가 SELECT를 호출하면, ABAP 런타임은 해당 사용자가 보유한 S_CARRID 권한 인스턴스를 모아 CarrierId IN ( ... ) 형태의 동적 WHERE 절을 자동 생성합니다.

4-2. 여러 필드를 한 번에 매핑

define role Z_I_FlightBooking {
  grant select on Z_I_FlightBooking
    where
      ( CarrierId, ConnectionId ) =
        aspect pfcg_auth ( S_CARRID, CARRID, CONNID, ACTVT = '03' );
}

튜플 형태로 여러 컬럼을 한 권한 오브젝트의 여러 필드에 동시에 묶을 수 있습니다. 이 경우 권한 인스턴스 단위로 (CarrierId, ConnectionId) 조합이 그대로 적용됩니다.

4-3. AND/OR 조합

define role Z_I_FlightBooking {
  grant select on Z_I_FlightBooking
    where
      ( CarrierId ) =
        aspect pfcg_auth ( S_CARRID, CARRID, ACTVT = '03' )
      and
      ( FlightDate ) =
        aspect pfcg_auth ( Z_FLT_DATE, FLDATE, ACTVT = '03' );
}

두 권한 오브젝트를 AND로 묶으면 두 조건을 동시에 만족하는 행만 노출됩니다. OR도 동일한 문법으로 가능하나, OR 사용 시 권한이 의도보다 넓어지지 않도록 주의가 필요합니다.

5. 3단계: ASPECT PFCG_AUTH로 PFCG 역할과 CDS 권한 연결

DCL과 PFCG를 실제로 연결하려면 SU21에서 권한 오브젝트가 활성 상태여야 하고, PFCG 역할에 해당 오브젝트가 포함되어 있어야 합니다. 흐름을 정리하면 다음과 같습니다.

  1. SU21에서 권한 오브젝트(예: S_CARRID) 정의 — 필드 CARRID, ACTVT 포함
  2. PFCG에서 역할 생성 후 권한 오브젝트 값 할당 (예: CARRID = LH, AA / ACTVT = 03)
  3. 해당 역할을 사용자(SU01)에게 배정
  4. CDS 뷰에 @AccessControl.authorizationCheck: #CHECK
  5. DCL 소스에서 aspect pfcg_auth( S_CARRID, ... ) 작성
  6. DCL을 활성화하면 ABAP 런타임이 자동 필터 적용

5-1. RAP Behavior Definition과의 연계

@EndUserText.label: 'RAP-managed Booking'
@MappingRole: true
define role Z_I_BookingRap {
  grant select on Z_I_Booking
    where
      ( CarrierId ) = aspect pfcg_auth ( S_CARRID, CARRID, ACTVT = '03' );

  grant update on Z_I_Booking
    where
      ( CarrierId ) = aspect pfcg_auth ( S_CARRID, CARRID, ACTVT = '02' );
}

RAP(ABAP RESTful Application Programming Model) 시나리오에서는 grant select 외에도 grant update, grant insert, grant delete, grant execute를 함께 정의해 OData PATCH/POST/DELETE 호출에도 동일한 권한 필터가 적용되도록 할 수 있습니다.

5-2. 상수 + 권한 오브젝트 혼합

define role Z_I_FlightBooking {
  grant select on Z_I_FlightBooking
    where
      ( CarrierId ) = aspect pfcg_auth ( S_CARRID, CARRID, ACTVT = '03' )
      and Client = $session.client
      and FlightDate >= $session.system_date;
}

$session 변수를 함께 사용하면 클라이언트 분리, 미래 일정만 노출 같은 추가 정책도 같은 DCL 안에서 표현됩니다.

6. DCL 비활성화와 테스트 — PRIVILEGED ACCESS, Mock 권한 설정

개발 단계에서 권한이 적용되어 결과가 비어 보이면 디버깅이 어렵습니다. 일반적으로 다음 두 가지 접근을 활용합니다.

6-1. PRIVILEGED Access

SELECT * FROM z_i_flightbooking
  WITH PRIVILEGED ACCESS
  INTO TABLE @DATA(lt_all_data).

WITH PRIVILEGED ACCESS를 붙이면 DCL 필터를 일시적으로 우회하고 전체 데이터를 조회합니다. 일반적으로 백엔드 서비스, 데이터 적재 잡, 슈퍼유저 보고서 등 의도적으로 모든 행이 필요한 경우에만 권장됩니다. 권한 우회 흔적이 코드에 그대로 남기 때문에 코드 리뷰 시 추적이 쉽다는 장점도 있습니다.

6-2. ABAP Unit + Mock 권한

CLASS ltc_dcl DEFINITION FOR TESTING
  RISK LEVEL HARMLESS DURATION SHORT.
  PRIVATE SECTION.
    METHODS test_lh_only FOR TESTING.
ENDCLASS.

CLASS ltc_dcl IMPLEMENTATION.
  METHOD test_lh_only.
    cl_cds_test_environment=>create(
      i_for_entity = 'Z_I_FLIGHTBOOKING' ).

    cl_abap_authority_check=>set_check_mode(
      i_check_mode = cl_abap_authority_check=>c_check_mode-simulated ).

    " 테스트 환경에서 의도된 권한만 부여
    " ... 권한 시뮬레이션 코드 ...

    SELECT carrierid FROM z_i_flightbooking
      INTO TABLE @DATA(lt).

    cl_abap_unit_assert=>assert_true(
      act = xsdbool( line_exists( lt[ carrierid = 'LH' ] ) ) ).
  ENDMETHOD.
ENDCLASS.

CDS Test Double Framework와 권한 시뮬레이션을 조합하면, 실제 PFCG 역할을 만들지 않고도 DCL 동작을 단위 테스트로 검증할 수 있습니다.

7. 자주 겪는 함정 — 권한 누락 시 빈 결과, WHERE 절 조건 충돌, 전송 순서

7-1. 빈 결과(Empty Result)

가장 흔한 증상입니다. 사용자가 SELECT를 했는데 데이터가 없다며 문의가 옵니다. 원인은 대개 셋 중 하나입니다.

  • 사용자에게 해당 PFCG 권한이 아예 없음
  • ACTVT 필드 값이 '03'이 아님 (예: '02'만 있음)
  • DCL의 ACTVT 조건이 PFCG 값과 일치하지 않음

일반적으로 SU53으로 마지막 권한 실패를 확인하지만, CDS DCL의 동적 WHERE는 SU53에 항상 나타나지 않습니다. 대신 STAUTHTRACE 또는 ST05로 실제로 만들어진 WHERE 절을 확인하는 편이 좋습니다.

7-2. WHERE 절 조건 충돌

DCL에서 CarrierId = 'LH'로 고정해 두고, 애플리케이션 SQL에서 WHERE carrid = 'AA'를 추가하면 결과는 항상 비게 됩니다. 두 조건이 AND로 결합되기 때문입니다. DCL은 "최대로 허용되는 집합"을 정의하고, SQL WHERE는 그 부분 집합에 다시 필터를 거는 구조라는 점을 기억해야 합니다.

7-3. 전송(Transport) 순서

DCL Source는 자신이 보호하는 CDS 엔티티에 의존합니다. 일반적으로 다음 순서를 지키지 않으면 활성화에 실패합니다.

  1. 대상 CDS DDL Source 활성화
  2. 관련 권한 오브젝트(SU21) 활성화
  3. DCL Source 활성화 및 전송

FAQ

  • Q. DCL이 적용되었는지 어떻게 빠르게 확인하나요?
    A. ADT(ABAP Development Tools)에서 CDS 뷰를 열고 Data Preview 시 우상단에서 권한 적용 여부와 적용된 역할 목록을 확인할 수 있습니다.
  • Q. DCL 없이 CDS 뷰만 만들면 누구나 모든 행을 볼 수 있나요?
    A. @AccessControl.authorizationCheck를 명시하지 않으면 일반적으로 경고가 발생하며, #NOT_REQUIRED로 설정되어 행 단위 필터링이 적용되지 않을 수 있습니다.
  • Q. DCL은 Open SQL의 모든 경로에 적용되나요?
    A. 일반적으로 ABAP SQL의 일반 SELECT에 적용되며, WITH PRIVILEGED ACCESS 또는 @AccessControl.authorizationCheck: #NOT_ALLOWED인 뷰에서는 적용되지 않습니다.

8. 응용 패턴 — 다중 조건 권한, 상속 뷰에서의 DCL 전파

8-1. 다중 조건 권한 (Inherited / Combined)

@MappingRole: true
define role Z_I_BookingDetail {
  grant select on Z_I_BookingDetail
    where
      ( CarrierId ) inheriting conditions from entity Z_I_FlightBooking;
}

inheriting conditions from entity 구문을 사용하면 기반 뷰의 DCL을 자식 뷰가 그대로 상속받습니다. 같은 권한 정책을 여러 곳에 중복 작성하는 비용을 크게 줄여줍니다.

8-2. 조건 분리(Branching) 패턴

define role Z_I_FlightBooking {
  grant select on Z_I_FlightBooking
    where
      ( CarrierId ) = aspect pfcg_auth ( S_CARRID, CARRID, ACTVT = '03' );

  grant select on Z_I_FlightBooking
    where
      CarrierId = 'LH'
      and FlightDate >= $session.system_date;
}

같은 엔티티에 대해 grant select를 여러 번 작성하면 결과적으로 OR 결합이 됩니다. 즉, 어느 하나라도 만족하면 데이터가 노출되는 식입니다. "S_CARRID 권한이 없는 일반 사용자에게도 LH 미래 일정만은 보여주고 싶다" 같은 시나리오에 유용합니다.

8-3. CDS Hierarchy와 DCL

CDS 계층(Interface View → Composite View → Consumption View)에서는 일반적으로 가장 아래의 Interface View에 DCL을 두는 것이 권장됩니다. 한 곳에서 행 보안을 정의하면 상위 뷰들이 자동으로 같은 필터를 상속받기 때문입니다. 단, 중간 뷰가 권한 컬럼을 잘라내면 DCL이 적용될 수 없으므로 권한 키 컬럼은 끝까지 보존해야 합니다.

8-4. 성능 관점

DCL은 결국 동적으로 WHERE 절을 더하는 메커니즘입니다. 일반적으로 권한 키 컬럼에 인덱스가 잘 잡혀 있으면 성능 영향이 미미하지만, 권한 인스턴스 수가 수천 개를 넘는 경우 IN 리스트가 커져 HANA 옵티마이저에 부담을 줄 수 있습니다. 이 경우 권한 키를 좁히거나, 역할을 통합해 인스턴스 수를 줄이는 설계가 권장됩니다.

8-5. 다음 단계로 추천하는 주제

  • ABAP RAP에서 Behavior 단위 권한 제어 (authorization master, authorization dependent)
  • CDS Access Control과 OData V4 노출 시 Fiori Launchpad 카탈로그 권한 분리
  • S/4HANA Embedded Analytics CDS 뷰에 대한 권한 모델 적용
  • CDS Test Double Framework + Authority Mock 자동화 회귀 테스트 구성

DCL은 단순한 문법이지만, 데이터 모델 전체의 보안 자세(security posture)를 바꿉니다. ABAP 로직에 흩어져 있던 권한 체크를 한곳으로 모으고, OData/RAP/Embedded Analytics 같은 다양한 채널에 동일한 규칙을 자동 적용한다는 점에서, 일반적으로 신규 S/4HANA 개발에서는 DCL을 우선 설계하는 편이 권장됩니다.

댓글 0

아직 댓글이 없습니다.