ABAP

CDS 세션 변수로 사용자 자동 필터 30초 완성 #shorts #SAP #ABAP

▶ YouTube에서 보기

CDS에서 세션 변수가 필요한 이유

ABAP CDS 뷰를 개발하다 보면 "현재 로그인한 사용자가 담당하는 데이터만 보여줘야 한다", "오늘 날짜 기준 유효한 계약만 조회해야 한다" 같은 요구사항을 자주 만납니다. 전통적인 ABAP 리포트에서는 sy-uname, sy-datum 같은 시스템 필드를 사용하면 되지만, CDS 뷰는 데이터베이스 레이어에서 실행되기 때문에 ABAP 런타임 시스템 필드에 직접 접근할 수 없습니다.

이때 등장하는 것이 CDS Session Variables입니다. 이 글에서 다음을 확인합니다.

  • CDS $session 네임스페이스의 구조와 지원 변수
  • $session.system_date, $session.user 등 주요 변수의 사용법
  • SELECT 절과 WHERE 조건에서 세션 변수 활용
  • DCL(Access Control)에서 세션 변수를 이용한 행 단위 권한 제어
  • 실무 시나리오: 담당자별 수주 필터링과 유효 기간 검증
  • 세션 변수 사용 시 성능·캐시·테스트 관점의 유의사항

사전에 알아두면 좋은 내용

이 글은 ABAP CDS View(또는 CDS View Entity) 정의 문법을 어느 정도 다뤄본 개발자를 대상으로 합니다. DEFINE VIEW ... AS SELECT FROM 문법, 어노테이션 지정 방식, DCL(Data Control Language) 기본 개념, 그리고 ABAP Development Tools(ADT / Eclipse) 사용 경험이 있으면 예제를 그대로 재현할 수 있습니다. 추가로 sy-uname, sy-datum이 ABAP 런타임에서 어떤 값을 가지는지 알고 있다면 세션 변수와의 매핑 관계를 이해하기 수월합니다.

환경 및 준비물

본문 예제는 다음 환경을 기준으로 검증했습니다. 릴리스마다 지원되는 세션 변수 목록이 확장되어 왔기 때문에 사용 중인 시스템 버전을 먼저 확인하는 것이 좋습니다.

  • SAP S/4HANA 2022 이상 (온프레미스) 또는 SAP BTP ABAP Environment (Steampunk) 2308 이상
  • AS ABAP 7.55 이상 권장 (CDS View Entity 활용 시 7.56+)
  • ABAP Development Tools for Eclipse 3.36 이상
  • 테스트용 데이터 테이블(예: ZSALES_ORDER, ZCUSTOMER) 및 개발 권한(S_DEVELOP)
  • DCL 예제 실행을 위한 CDS Role 편집 권한

온프레미스 시스템에서는 DDLS(CDS View)와 DCLS(Access Control) 오브젝트로 예제를 저장합니다. Steampunk 환경에서는 released API로 지정된 CDS만 배포 대상에 포함되므로, 실습용은 로컬 패키지에 두는 것을 권장합니다.

$session 네임스페이스 이해

CDS 세션 변수는 SQL 문이 실행되는 시점에 ABAP 런타임이 데이터베이스 세션에 주입하는 값입니다. 개념적으로 표현하면 다음 순서로 동작합니다.

  1. ABAP 프로그램이 CDS 뷰를 SELECT로 호출
  2. ABAP SQL 엔진이 $session.xxx 참조를 실제 값으로 바인딩
  3. HANA(또는 DB) 세션 파라미터(SET 'CDS_CLIENT' 등)를 통해 값이 전달
  4. 뷰 내부의 CASE/WHERE 조건이 그 값을 사용해 필터링

비유하자면 세션 변수는 "요청 헤더"에 가깝습니다. 데이터베이스 쿼리 본문은 정적이지만, 호출자가 누구인지·언제 호출했는지에 따라 실행 결과가 달라져야 하므로 CDS는 그 정보를 세션 변수 형태로 표준화해 제공합니다.

주요 변수는 다음과 같습니다. 정확한 지원 여부는 릴리스 노트를 확인해야 하며, 일부는 CDS View Entity에서만 사용 가능합니다.

세션 변수대응 ABAP 필드설명
$session.usersy-uname현재 로그인 사용자 ID
$session.clientsy-mandt클라이언트 번호
$session.system_datesy-datum애플리케이션 서버 날짜
$session.system_languagesy-langu로그온 언어
$session.user_timezone사용자 타임존USR02에 저장된 타임존

system_time은 표준 세션 변수로는 노출되지 않는 경우가 있어, 시간이 필요하면 UTCLONG_CURRENT() 같은 SQL 함수를 조합하는 방식이 일반적으로 권장됩니다.

1단계: 세션 변수를 SELECT 절에 노출하는 기본 예제

가장 단순한 형태로, 세션 변수를 뷰의 컬럼으로 그대로 노출해 값이 어떻게 채워지는지 확인해 봅니다. 수주 헤더 테이블 ZSO_HEADER가 있다고 가정합니다.

@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #NOT_REQUIRED
@Metadata.allowExtensions: true
@EndUserText.label: 'Session variable sandbox'
define view entity ZC_SO_SESSION_PROBE
  as select from zso_header as so
{
  key so.so_id                     as SalesOrderId,
      so.customer_id               as CustomerId,
      so.responsible_user          as ResponsibleUser,
      so.valid_from                as ValidFrom,
      so.valid_to                  as ValidTo,

      // 세션 변수를 컬럼으로 노출
      cast( $session.user          as abap.char( 12 ) ) as CurrentUser,
      cast( $session.system_date   as abap.dats )       as ServerDate,
      cast( $session.client        as abap.clnt )       as CurrentClient
}

이 뷰를 ADBC 또는 Data Preview(F8)로 실행하면 모든 행에서 CurrentUser가 동일한 값(현재 로그인 ID)으로 채워지는 것을 확인할 수 있습니다. 여기서 중요한 포인트는 세션 변수는 뷰에 저장되는 것이 아니라 호출 시점에 계산된다는 사실입니다. 다른 사용자로 로그인해 같은 뷰를 조회하면 값이 달라집니다.

2단계: WHERE 조건과 유효기간 필터링 (실무 시나리오)

실무에서 세션 변수가 가장 자주 쓰이는 곳은 "오늘 기준 유효한 데이터만""내가 담당하는 데이터만"이라는 두 가지 조건입니다. 아래 예제는 담당자별로 오늘 유효한 수주만 노출합니다. 아울러 상태 컬럼을 CASE로 가공해, 조회 화면에서 별도 로직 없이 배지처럼 표시할 수 있게 만듭니다.

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'My active sales orders (today)'
define view entity ZC_MY_ACTIVE_SO
  as select from zso_header as so
  association [0..1] to ZI_Customer as _Customer
    on $projection.CustomerId = _Customer.CustomerId
{
  key so.so_id                          as SalesOrderId,
      so.customer_id                    as CustomerId,
      so.responsible_user               as ResponsibleUser,
      so.valid_from                     as ValidFrom,
      so.valid_to                       as ValidTo,
      so.net_amount                     as NetAmount,
      so.currency_code                  as CurrencyCode,

      case
        when so.valid_to  < $session.system_date then 'EXPIRED'
        when so.valid_from > $session.system_date then 'FUTURE'
        else 'ACTIVE'
      end                               as LifecycleStatus,

      _Customer
}
where so.responsible_user = $session.user
  and so.valid_from      <= $session.system_date
  and so.valid_to        >= $session.system_date

이렇게 정의하면 소비자 프로그램에서 별도로 WHERE responsible_user = sy-uname을 붙일 필요가 없습니다. 뷰 자체가 "나의 오늘 유효 수주"라는 의미를 갖습니다. 다만 반드시 함께 고려해야 하는 것이 로깅과 예외 상황입니다. 소비자 측 ABAP에서는 다음처럼 결과 없음(sy-subrc = 4) 케이스를 명확히 로깅해 두는 것을 권장합니다.

SELECT * FROM zc_my_active_so
  INTO TABLE @DATA(lt_my_so).

IF lt_my_so IS INITIAL.
  MESSAGE |사용자 { sy-uname } 담당 유효 수주가 없습니다({ sy-datum }).|
    TYPE 'S'.
  " 애플리케이션 로그 BAL_LOG_CREATE 사용을 권장
ENDIF.

3단계: DCL과 결합한 프로덕션 수준 접근 제어

WHERE 조건에 사용자 필터를 넣는 방식은 편리하지만, "관리자는 모든 데이터, 담당자는 본인 데이터만"과 같이 역할별로 다른 규칙이 필요할 때는 한계가 있습니다. 이때는 뷰의 WHERE 조건에서 사용자 필터를 제거하고, DCL(Access Control)에서 세션 변수를 활용해 행 단위 권한을 부여합니다.

// 1) 데이터 뷰: 사용자 필터 없이 정의
@AccessControl.authorizationCheck: #CHECK
define view entity ZC_SO_ALL
  as select from zso_header as so
{
  key so.so_id            as SalesOrderId,
      so.customer_id      as CustomerId,
      so.responsible_user as ResponsibleUser,
      so.net_amount       as NetAmount,
      so.valid_from       as ValidFrom,
      so.valid_to         as ValidTo
}
// 2) DCL: 세션 변수 기반 접근 제어
@EndUserText.label: 'Row level access for sales orders'
@MappingRole: true
define role ZC_SO_ALL_ROLE {
  grant select on ZC_SO_ALL
    where ResponsibleUser  = aspect user
      and ValidFrom       <= $session.system_date
      and ValidTo         >= $session.system_date;
}

aspect user는 DCL에서 $session.user와 동일한 의미로 사용됩니다. 이렇게 하면 뷰 정의가 깔끔해지고, 관리자 역할용 DCL을 추가할 때도 데이터 뷰는 손대지 않습니다. 프로덕션에서는 다음 항목을 함께 점검하는 것이 좋습니다.

  • 단위 테스트: ABAP Unit + CDS Test Double Framework로 특정 사용자·특정 날짜를 주입해 필터가 의도대로 동작하는지 검증
  • 성능: DCL이 걸린 뷰는 결합된 SQL이 커지므로, 자주 조회되는 컬럼에 인덱스가 있는지 확인
  • 감사(Audit): 사용자 필터 로직은 CDS에 집약되므로 리뷰 시 DCL 파일을 필수 체크리스트에 포함

흔한 실수와 트러블슈팅

Q1. 세션 변수를 사용했더니 뷰가 활성화되지 않고 "Session variable is unknown" 오류가 발생합니다.
사용 중인 시스템 릴리스가 해당 변수를 지원하지 않거나, CDS View Entity(define view entity)에서만 지원되는 변수를 클래식 뷰(define view)에서 사용했을 가능성이 큽니다. 릴리스 노트에서 $session 지원 목록을 확인하고, 필요하면 View Entity로 마이그레이션을 검토합니다.

Q2. 뷰가 캐시되어 어제 날짜로 계속 조회되는 것 같습니다.
CDS View Entity 자체는 세션 변수를 매 호출 시점에 바인딩하므로 캐시 문제는 드뭅니다. 다만 상위 서비스(SADL, RAP BO)에서 결과를 재사용하는 경우가 있으므로, 관측된 값이 이상하다면 뷰를 직접 Data Preview로 실행해 $session.system_date 컬럼을 노출한 뒤 값을 비교해 봅니다.

Q3. WHERE 절에 세션 변수를 넣었더니 성능이 눈에 띄게 떨어졌습니다.
바인딩된 값이 상수처럼 취급되지 않아 옵티마이저가 인덱스 사용 계획을 다르게 잡는 경우가 있습니다. Plan Visualizer(PlanViz)로 실행 계획을 확인하고, 필터 컬럼(responsible_user, valid_from/to)에 세컨더리 인덱스를 추가하거나, DCL로 조건을 옮겨 최적화 힌트를 활용하는 것을 권장합니다.

기타 자주 놓치는 사항으로, 세션 변수는 ABAP SQL을 경유해 호출될 때만 정상적으로 채워집니다. 외부 시스템에서 ODBC/JDBC로 직접 CDS를 호출하면 값이 비어 있거나 기본값이 들어갈 수 있으므로, 통합 시나리오에서는 뷰 호출 경로를 반드시 검토해야 합니다.

심화 주제

세션 변수에 익숙해졌다면 다음 주제로 확장해 보는 것을 권장합니다. 첫째, Analytical CDS와 세션 변수를 조합해 BI 리포트에서 사용자별 뷰를 자동 필터링하는 패턴. 둘째, RAP(RESTful ABAP Programming Model)의 Behavior에서 cl_abap_context_info와 세션 변수를 함께 사용해 서비스 레이어와 DB 레이어의 사용자 정보를 일관되게 관리하는 방법. 셋째, CDS Test Double Framework로 세션 변수 값을 주입해 시간 의존 로직을 재현 가능한 테스트로 만드는 방법입니다. 각 주제는 세션 변수를 "단순 편의 기능"에서 "권한·감사 아키텍처의 기반"으로 격상시켜 줍니다.

더 읽어볼 만한 자료

  • SAP Help Portal - ABAP CDS Session Variables
  • SAP Help Portal - CDS View Entity Overview
  • SAP Help Portal - CDS Access Control (DCL)
  • SAP Developers - Create Your First CDS View
  • SAP Community Blogs - ABAP CDS

댓글 0

아직 댓글이 없습니다.