ABAP

CSKS vs I_CostCenter — 원가 센터 조회 차이 #shorts #SAP #ABAP

▶ YouTube에서 보기

I_CostCenter가 필요한 이유 — CSKS 직접 SELECT의 문제점

Controlling 영역에서 원가 센터(Cost Center) 정보를 다뤄야 할 때, 많은 ABAP 개발자들이 본능적으로 CSKS 테이블을 직접 조회합니다. 그런데 이 접근 방식은 S/4HANA 환경에서 여러 문제를 만들어냅니다. CSKS는 시간 종속(time-dependent) 테이블이라 같은 원가 센터(KOSTL)에 대해 유효기간(DATBI/DATAB)별로 여러 레코드가 존재하며, 회사 코드·통화·언어·텍스트 정보는 CSKT, CEPC, T001 등에 분산돼 있습니다.

이 글에서 다룰 항목을 먼저 정리합니다.

  • CSKS 직접 조회의 한계와 I_CostCenter 도입 배경
  • I_CostCenter의 핵심 필드 및 시간 종속 데이터 처리 방식
  • Controlling Area, Company Code, Hierarchy 정보의 통합 조회
  • 실무 보고서(BudgetManager) 구현 예제
  • 성능 함정과 권한 처리 시 자주 발생하는 실수

알아두면 좋은 것

ABAP CDS View의 기본 문법(@AbapCatalog.sqlViewName, association _Assoc)과 Open SQL의 SELECT ... FROM cds_view 패턴, 그리고 Controlling 모듈의 원가 센터·관리회계 영역(Controlling Area, KOKRS) 개념을 어렴풋이라도 알고 있다면 충분합니다. 추가로 CSKS / CSKT / CEPC 테이블 구조를 한 번이라도 들여다본 경험이 있다면 본문 비교 부분이 훨씬 쉽게 읽힙니다.

환경 준비

이 글의 예제는 일반적으로 다음 환경을 가정합니다.

  • SAP S/4HANA 2022 또는 2023 (On-Premise) / S/4HANA Cloud Public Edition
  • ABAP Platform 7.57 이상, ADT (Eclipse용 ABAP Development Tools)
  • Controlling 모듈 활성화 및 최소 하나 이상의 Controlling Area(예: 1010, A000) 마스터 등록
  • I_CostCenter, I_ControllingArea, I_CostCenterHierarchyNode 등 Released CDS View 접근 권한

S/4HANA Cloud 환경에서는 일부 필드가 Release Contract에 따라 제한될 수 있으므로, ADT의 "Released Objects" 뷰에서 컬럼별 release 상태(C0/C1/C2)를 먼저 확인하는 것을 권장합니다.

CSKS 구조와 I_CostCenter 구조 비교

CSKS는 1990년대 R/3 시절부터 이어진 클래식 테이블입니다. 핵심 키는 MANDT, KOKRS(Controlling Area), KOSTL(Cost Center), DATBI(Valid To)입니다. 즉, "유효기간 끝나는 날짜"가 키의 일부라는 점이 함정입니다. 한 원가 센터의 현재 유효 레코드 하나를 얻으려면 다음과 같은 필터링이 필요합니다.

SELECT kokrs, kostl, datab, datbi, verak, kosar
  FROM csks
  WHERE kokrs = @lv_kokrs
    AND kostl = @lv_kostl
    AND datab <= @sy-datum
    AND datbi >= @sy-datum
  INTO TABLE @DATA(lt_csks).

여기에 텍스트가 필요하면 CSKT를, 회사 코드의 통화가 필요하면 T001을, 사용자 책임자 이메일이 필요하면 USR21/ADR6를 조인해야 합니다. CDS View I_CostCenter는 이 모든 조합을 association으로 묶어 일관된 인터페이스로 제공하는 Released 마스터 뷰입니다. 비유하자면 CSKS가 원자재 창고라면, I_CostCenter는 자주 쓰는 부품을 모아놓은 작업대인 셈입니다.

구조적으로 I_CostCenter의 핵심은 다음과 같습니다.

  • 키: ControllingArea + CostCenter + ValidityEndDate (CSKS의 DATBI에 대응)
  • 유효기간 필드: ValidityStartDate, ValidityEndDate
  • 분류 필드: CostCenterCategory, CostCenterCurrency, CompanyCode, BusinessArea
  • 책임 정보: CostCtrResponsiblePersonName, CostCtrResponsibleUser
  • Association: _ControllingArea, _CompanyCode, _Text, _CostCenterCategory

필드명이 의미 중심으로 풀어져 있어 가독성이 좋고, association을 따라가면 조인 없이 텍스트와 마스터를 가져올 수 있다는 점이 가장 큰 이점입니다.

I_CostCenter 핵심 필드 해부

CostCenter는 10자리 문자열(예: 1000K001)로 CSKS-KOSTL과 동일합니다. CostCenterCategory는 한 글자 코드(예: F 생산, H 보조, V 영업)로, I_CostCenterCategory와 association으로 연결돼 텍스트 조회가 가능합니다. ValidityStartDate/ValidityEndDate는 시간 종속 키이므로, 같은 CostCenter에 대해 여러 행이 반환될 수 있다는 점을 항상 의식해야 합니다. CostCtrResponsibleUser는 SY-UNAME 형식의 사용자 ID로, USR21·ADR6 association을 통해 이메일까지 추적 가능합니다.

기본 조회 예제 — 유효 원가 센터 목록 추출

가장 자주 만나는 요구사항은 "오늘 기준으로 유효한 원가 센터를 회사 코드별로 뽑아달라"입니다. CSKS 직접 조회 대비 코드가 얼마나 단순해지는지 비교해 봅시다.

REPORT zr_cc_active_list.

DATA: lv_today TYPE d.
lv_today = sy-datum.

SELECT ControllingArea,
       CostCenter,
       CompanyCode,
       CostCenterCategory,
       CostCtrResponsiblePersonName,
       ValidityStartDate,
       ValidityEndDate
  FROM I_CostCenter
  WHERE CompanyCode = '1010'
    AND ValidityStartDate <= @lv_today
    AND ValidityEndDate   >= @lv_today
  ORDER BY CostCenter
  INTO TABLE @DATA(lt_active_cc).

LOOP AT lt_active_cc INTO DATA(ls_cc).
  WRITE: / ls_cc-CostCenter,
           ls_cc-CostCenterCategory,
           ls_cc-CostCtrResponsiblePersonName.
ENDLOOP.

CSKS를 다뤘다면 DATAB/DATBI 비교 방향을 헷갈리기 쉬운데, I_CostCenter는 필드명이 자연어에 가까워 실수가 줄어듭니다. 다만 ValidityEndDate에는 보통 99991231이 들어있다는 점, 그리고 클라이언트 필터는 CDS 런타임이 자동 처리한다는 점을 기억하세요.

Controlling Area·CompanyCode를 조인 없이 함께 읽기

실무 보고서에서는 원가 센터의 통화나 회사 코드 텍스트가 함께 필요한 경우가 대부분입니다. CDS의 path expression을 활용하면 JOIN 작성 없이 association을 따라 필드를 가져올 수 있습니다.

SELECT cc~ControllingArea,
       cc~CostCenter,
       cc~CompanyCode,
       cc~_ControllingArea-ControllingAreaName AS ca_name,
       cc~_CompanyCode-CompanyCodeName        AS cc_name,
       cc~_CompanyCode-Currency               AS local_crcy,
       cc~_Text-CostCenterName                AS cc_text
  FROM I_CostCenter AS cc
  WHERE cc~CompanyCode = '1010'
    AND cc~ValidityEndDate >= @sy-datum
    AND cc~ValidityStartDate <= @sy-datum
    AND cc~_Text-Language = @sy-langu
  INTO TABLE @DATA(lt_cc_enriched).

여기서 _Text는 텍스트 association이며, 언어 키를 명시적으로 걸어주는 것이 권장됩니다. 명시하지 않으면 모든 언어 행이 카티전 곱처럼 반환돼 결과가 부풀려질 수 있습니다.

계층 구조 접근 — CostCenterHierarchy 연결

경영 보고에서는 단일 원가 센터보다 그룹(Standard Hierarchy) 단위 집계가 중요합니다. CSKS만으로는 SETLEAF/SETNODE 테이블을 재귀 탐색해야 했지만, S/4HANA에서는 I_CostCenterHierarchyNode, I_CostCenterStandardHierarchy가 평탄화된 형태로 제공됩니다. 다음은 특정 노드 아래의 모든 리프 원가 센터를 추출하는 예제입니다.

SELECT node~HierarchyNode,
       node~ParentNode,
       node~CostCenter,
       cc~CostCenterCategory,
       cc~CostCtrResponsibleUser
  FROM I_CostCenterHierarchyNode AS node
  INNER JOIN I_CostCenter AS cc
    ON  cc~ControllingArea = node~ControllingArea
    AND cc~CostCenter      = node~CostCenter
    AND cc~ValidityEndDate >= @sy-datum
    AND cc~ValidityStartDate <= @sy-datum
  WHERE node~ControllingArea = 'A000'
    AND node~HierarchyName   = 'H1'
    AND node~CostCenter     <> ''
  INTO TABLE @DATA(lt_hier_cc).

실제 환경에 따라 사용 가능한 계층 뷰가 다를 수 있으므로 ADT의 Released Objects 검색으로 시스템에 풀린 뷰를 먼저 확인하세요.

실전 예제 — BudgetManager 보고서 구현

위 조각들을 모아 "원가 센터별 예산 책임자 + 이메일 + 카테고리 텍스트"를 출력하는 BudgetManager 보고서를 만들어봅니다. 예외 처리, 로깅, 옵션 파라미터까지 포함한 1차 프로덕션 골격입니다.

CLASS zcl_budget_manager_report DEFINITION
  PUBLIC FINAL CREATE PUBLIC.

  PUBLIC SECTION.
    TYPES:
      BEGIN OF ty_budget_owner,
        controlling_area TYPE c LENGTH 4,
        cost_center      TYPE c LENGTH 10,
        category_text    TYPE string,
        owner_user       TYPE syuname,
        owner_name       TYPE string,
        owner_email      TYPE string,
        valid_to         TYPE d,
      END OF ty_budget_owner,
      tt_budget_owner TYPE STANDARD TABLE OF ty_budget_owner WITH EMPTY KEY.

    METHODS:
      get_owners
        IMPORTING iv_company_code TYPE bukrs
                  iv_key_date     TYPE d DEFAULT sy-datum
        RETURNING VALUE(rt_data)  TYPE tt_budget_owner
        RAISING   cx_sy_open_sql_db.
ENDCLASS.

CLASS zcl_budget_manager_report IMPLEMENTATION.
  METHOD get_owners.
    TRY.
        SELECT cc~ControllingArea       AS controlling_area,
               cc~CostCenter             AS cost_center,
               cc~CostCtrResponsibleUser AS owner_user,
               cc~CostCtrResponsiblePersonName AS owner_name,
               addr~SmtpAddress          AS owner_email,
               cc~ValidityEndDate        AS valid_to
          FROM I_CostCenter AS cc
          LEFT OUTER JOIN I_BusinessUserBasic AS usr
            ON usr~UserID = cc~CostCtrResponsibleUser
          LEFT OUTER JOIN I_AddressEmailAddress AS addr
            ON addr~AddressID = usr~AddressID
           AND addr~IsDefaultEmailAddress = 'X'
          WHERE cc~CompanyCode       = @iv_company_code
            AND cc~ValidityStartDate <= @iv_key_date
            AND cc~ValidityEndDate   >= @iv_key_date
          ORDER BY cc~CostCenter
          INTO TABLE @rt_data.

      CATCH cx_sy_open_sql_db INTO DATA(lx_sql).
        RAISE EXCEPTION lx_sql.
    ENDTRY.
  ENDMETHOD.
ENDCLASS.

이 클래스는 세 가지 프로덕션 포인트를 담고 있습니다. 첫째, key date를 파라미터화해 과거 시점 분석도 가능합니다. 둘째, LEFT OUTER JOIN으로 책임자 미등록 원가 센터도 누락하지 않습니다. 셋째, IsDefaultEmailAddress = X 조건으로 이메일 중복을 차단합니다.

자주 하는 실수와 성능 주의사항

Q1. 같은 CostCenter가 두 번 나옵니다. 시간 종속 키 때문입니다. ValidityStartDate ≤ key_date ≤ ValidityEndDate 조건을 양방향으로 모두 걸어야 단일 시점의 한 행을 보장할 수 있습니다. 키 날짜 없이 그냥 SELECT하면 과거 이력까지 모두 잡힙니다.

Q2. 텍스트가 안 나오거나 행이 부풀려져요. _Text association을 path로 끌어 쓸 때 언어 조건(_Text-Language = @sy-langu)이 빠진 경우입니다. 다국어 텍스트 테이블은 언어 필터 없이는 카티전 곱처럼 작동합니다.

Q3. CSKS는 즉시 보이는데 I_CostCenter에서는 안 보입니다. 권한이 원인일 가능성이 높습니다. CDS View에는 DCL(Data Control Language)로 권한이 걸려 있어 K_CCA, K_CSKS_SET 같은 권한 객체와 연동됩니다. 백그라운드 잡에서는 WITH PRIVILEGED ACCESS 사용을 검토하되, 비즈니스 적합성을 반드시 확인하세요.

성능 측면에서는 (1) ValidityEndDate를 셀렉션 첫 줄에 두지 말고 ControllingArea/CompanyCode 같은 선택성 높은 필드를 우선 거는 것, (2) 루프 안에서 I_CostCenter를 반복 SELECT하지 말고 FOR ALL ENTRIES 또는 INNER JOIN으로 한 번에 가져오는 것, (3) 대량 추출 시 association path 대신 미리 INNER JOIN으로 풀어쓰면 HANA 옵티마이저가 더 유리한 플랜을 잡는 경우가 많다는 점을 권장합니다.

댓글 0

아직 댓글이 없습니다.