ABAP

프로젝트-WBS 조인, 외부키 써도 될까? #shorts #SAP #ABAP

▶ YouTube에서 보기

이 글에서 다룰 것

SAP S/4HANA의 Project System(PS) 모듈은 오랫동안 PROJ 테이블을 프로젝트 헤더의 근간으로 사용해 왔습니다. 최근 S/4HANA에서는 이 테이블을 CDS View인 I_ProjectDefinition으로 감싸 재사용 가능한 데이터 모델로 노출합니다. 이 글에서는 I_ProjectDefinition의 구조, PROJ와의 관계, WBS 요소(I_WBSElement)와의 계층 연결, 그리고 실무 보고서·OData API에서의 활용 패턴을 단계별로 살펴봅니다.

  • PROJ 테이블과 I_ProjectDefinition의 매핑 관계 이해
  • CDS Association을 통해 프로젝트-WBS 계층 탐색
  • ABAP SQL/CDS Consumption View에서 실전 필터링 기법 익히기
  • OData Service 노출과 성능 고려사항 파악

사전에 알아둘 내용

이 예제를 이해하려면 ABAP CDS View의 기본 문법(define view, association, annotation)과 PS 모듈의 프로젝트 마스터 개념(프로젝트 정의, WBS 요소, 네트워크)을 알고 있어야 합니다. 또한 ABAP SQL의 SELECT ... FROM cds_view 구문과 Path Expression(_Association[...]) 사용 경험이 있으면 좋습니다.

환경 및 준비물

본 예제는 SAP S/4HANA 2022 이상(Cloud Private Edition 또는 On-Premise)을 기준으로 합니다. Cloud Public Edition에서는 릴리스된 CDS View만 접근 가능하므로 @AccessControl.authorizationCheck 및 릴리스 상태를 반드시 확인해야 합니다.

  • SAP S/4HANA 2022 FPS02 이상 (2023 권장)
  • ABAP Development Tools (ADT) for Eclipse 2024-03 이상
  • PS 모듈이 활성화된 클라이언트 및 최소 하나 이상의 프로젝트(PROJ) 데이터
  • 권한 오브젝트: C_PROJ_KOK(프로젝트 코딩 마스크), C_PROJ_VNR(책임자)
  • 테스트 트랜잭션: CJ20N(프로젝트 빌더), SE16/SE16N(테이블 브라우저), DBTG(뷰 미리보기)
Cloud Public Edition에서 CDS를 소비할 때는 릴리스 상태(C1, C0)와 안정성 계약을 확인해야 하며, 필드 목록이 릴리스된 것만 사용 가능합니다.

핵심 개념

I_ProjectDefinition은 PS 모듈의 프로젝트 헤더를 표현하는 Basic Interface CDS View입니다. 근간이 되는 데이터베이스 테이블은 PROJ이며, 여기에 조직 단위(회사 코드·플랜트·컨트롤링 영역) 및 상태(JEST) 정보를 조인해 통합된 모습을 제공합니다. 개념적으로는 "PROJ 테이블 위에 얹은 의미론적 계층"이라 볼 수 있으며, 필드명은 기술명(PSPID, PSPNR) 대신 의미명(Project, ProjectInternalID)으로 재명명되어 있습니다.

프로젝트는 계층 구조를 가집니다. 헤더인 프로젝트 정의(Project Definition)가 최상위이며, 그 아래에 WBS Element가 트리 형태로 매달립니다. 데이터베이스 관점에서 PROJ.PSPNR은 WBS 테이블인 PRPSPSPHI(프로젝트 헤더 내부번호)와 연결됩니다. CDS 세계에서는 이 관계가 _WBSElement Association으로 표현됩니다.

-- 개념적 관계도
PROJ (프로젝트 헤더)
 |── PSPNR (내부키) ──> PRPS.PSPHI ──── I_WBSElement
 |── PSPID (외부키: 프로젝트 코드)
 |── VERNR (책임자)
 |── PLFAZ / PLSEZ (계획 시작/종료일)
 └── STSMA (상태 프로파일) ──> JEST/TJ02

이 뷰의 강점은 두 가지입니다. 첫째, Association을 통해 조인 없이 관련 엔티티(회사코드, 통제영역, WBS, 담당자)에 접근할 수 있습니다. 둘째, Extension View 또는 CDS View Entity로 확장할 수 있어 커스텀 필드를 추가하기 쉽습니다. 아키텍처 관점에서 I_ 접두사는 재사용 인터페이스 뷰임을 뜻하며, 최종 소비 앱은 C_ 뷰를 만들어 사용하는 것이 권장 패턴입니다.

실전 예제 1단계: 기본 조회

가장 단순하게 특정 회사코드와 담당자에 속한 프로젝트 목록을 뽑는 예제입니다. I_ProjectDefinition을 직접 SELECT하는 방식으로, PROJ의 raw 필드가 아닌 의미명을 사용합니다.

REPORT z_ps_project_list.

DATA: lt_projects TYPE STANDARD TABLE OF i_projectdefinition.

SELECT Project,
       ProjectDescription,
       ResponsiblePersonName,
       CompanyCode,
       PlannedStartDate,
       PlannedEndDate,
       ProjectProfile
  FROM I_ProjectDefinition
  WHERE CompanyCode  = '1010'
    AND Project      LIKE 'INV-%'
  ORDER BY PlannedStartDate DESCENDING
  INTO CORRESPONDING FIELDS OF TABLE @lt_projects
  UP TO 100 ROWS.

LOOP AT lt_projects ASSIGNING FIELD-SYMBOL(<fs_proj>).
  WRITE: / <fs_proj>-project,
           <fs_proj>-projectdescription,
           <fs_proj>-plannedstartdate,
           <fs_proj>-plannedenddate.
ENDLOOP.

여기서 눈여겨볼 점은 PSPID 대신 Project, PLFAZ 대신 PlannedStartDate가 쓰였다는 것입니다. 재명명 덕분에 도메인 지식이 없어도 코드가 자체 설명적(self-documenting)이 됩니다.

실전 예제 2단계: WBS 계층 탐색 및 로깅

실무에서는 프로젝트 헤더뿐 아니라 그 아래 WBS 요소를 함께 조회하는 경우가 많습니다. Association Path Expression을 활용하면 여러 조인을 명시적으로 쓸 필요 없이 간결하게 표현할 수 있습니다.

CLASS zcl_ps_wbs_reader DEFINITION PUBLIC FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    TYPES: BEGIN OF ty_project_wbs,
             project              TYPE i_projectdefinition-project,
             project_description  TYPE i_projectdefinition-projectdescription,
             wbs_element          TYPE i_wbselement-wbselement,
             wbs_description      TYPE i_wbselement-wbselementdescription,
             wbs_level            TYPE i_wbselement-wbselementlevelvalue,
             is_billing_element   TYPE i_wbselement-isbillingelement,
           END OF ty_project_wbs.

    METHODS read_project_tree
      IMPORTING iv_project     TYPE i_projectdefinition-project
      RETURNING VALUE(rt_tree) TYPE STANDARD TABLE OF ty_project_wbs
      RAISING   cx_sy_open_sql_db.
ENDCLASS.

CLASS zcl_ps_wbs_reader IMPLEMENTATION.
  METHOD read_project_tree.
    TRY.
        SELECT proj~Project,
               proj~ProjectDescription,
               wbs~WBSElement,
               wbs~WBSElementDescription,
               wbs~WBSElementLevelValue,
               wbs~IsBillingElement
          FROM I_ProjectDefinition AS proj
          INNER JOIN I_WBSElement AS wbs
            ON  wbs~ProjectInternalID = proj~ProjectInternalID
          WHERE proj~Project = @iv_project
          ORDER BY wbs~WBSElementLevelValue,
                   wbs~WBSElement
          INTO CORRESPONDING FIELDS OF TABLE @rt_tree.

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

이 코드에서 ProjectInternalID는 PROJ.PSPNR(8자리 숫자 내부키)에 해당하며, PRPS.PSPHI와 매칭됩니다. 외부 사용자에게 보이는 Project(PSPID)가 아닌 내부키를 조인 조건으로 써야 성능이 나옵니다. 내부키에는 인덱스가 걸려 있기 때문입니다.

실전 예제 3단계: 프로덕션급 Consumption View + OData

실제 앱에서는 앞의 로직을 반복 SELECT로 처리하지 않고, 별도의 Consumption CDS View를 만들어 OData/RAP로 노출하는 것이 일반적입니다. 아래는 프로젝트-WBS 통합 뷰와 그 소비 예입니다.

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Project with WBS - Consumption'
@Metadata.allowExtensions: true
@Search.searchable: true
@OData.publish: true
define view entity ZC_ProjectWithWBS
  as select from I_ProjectDefinition as Project

  association [0..*] to I_WBSElement as _WBS
    on $projection.ProjectInternalID = _WBS.ProjectInternalID

  association [0..1] to I_CompanyCode as _CompanyCode
    on $projection.CompanyCode = _CompanyCode.CompanyCode
{
  key Project.Project                        as ProjectID,
      Project.ProjectInternalID,
      @Search.defaultSearchElement: true
      Project.ProjectDescription,
      Project.ResponsiblePersonName,
      Project.CompanyCode,
      Project.PlannedStartDate,
      Project.PlannedEndDate,
      Project.ProjectProfile,
      Project.OverallStatus,

      cast( ( select count(*)
                from I_WBSElement as w
               where w.ProjectInternalID = Project.ProjectInternalID )
            as abap.int4 )                    as WBSCount,

      _WBS,
      _CompanyCode
}
where Project.ProjectDeletionIndicator = ''

이 뷰는 @OData.publish: true로 서비스 바인딩을 자동 생성하며, Fiori Elements List Report/Object Page의 데이터 소스로 사용할 수 있습니다. 삭제 표시된 프로젝트를 자동으로 걸러내고, 검색 대상 필드를 명시함으로써 사용자가 자연스러운 검색 경험을 얻습니다.

" 소비 예: 활성 프로젝트 중 종료 임박 30일 이내
SELECT ProjectID,
       ProjectDescription,
       PlannedEndDate,
       WBSCount
  FROM zc_projectwithwbs
  WHERE PlannedEndDate BETWEEN @sy-datum
                           AND @( sy-datum + 30 )
    AND OverallStatus <> 'TECO'
  ORDER BY PlannedEndDate
  INTO TABLE @DATA(lt_ending_soon).

" 성능: Association 지연 평가 - 필요한 필드만 지정
SELECT ProjectID,
       \_WBS-WBSElement,
       \_WBS-WBSElementDescription
  FROM zc_projectwithwbs
  WHERE ProjectID = 'INV-2026-001'
  INTO TABLE @DATA(lt_flat_wbs).

보안 측면에서 @AccessControl.authorizationCheck: #CHECK는 DCL(Data Control Language)을 통해 회사코드·컨트롤링영역별 접근 제한이 자동 적용되도록 만듭니다. 프로덕션 환경에서는 반드시 활성화하고 별도의 define role을 작성해야 합니다.

흔한 실수와 트러블슈팅

Q1. WBS Element를 필터링했는데 프로젝트가 중복으로 나옵니다.
A. INNER JOIN I_WBSElement는 프로젝트당 WBS 수만큼 행을 반환합니다. 헤더 정보만 필요하면 WHERE EXISTS (SELECT ... FROM I_WBSElement ...) 서브쿼리를 쓰거나, Association Path에 [1: ...] 카디널리티를 명시하세요.

Q2. ProjectInternalIDProject를 혼동합니다.
A. Project는 사용자가 보는 코드(예: INV-2026-001), ProjectInternalID는 시스템 내부 8자리 숫자(PSPNR)입니다. WBS·네트워크·정산규칙 등 하위 오브젝트 조인에는 반드시 ProjectInternalID를 사용해야 합니다. 외부키로 조인하면 인덱스가 타지 않아 성능이 심하게 저하될 수 있습니다.

Q3. Cloud Public Edition에서 필드가 안 보입니다.
A. 릴리스 상태(C1: Public / C0: Cloud-Development)만 노출됩니다. ADT > CDS View > API State에서 릴리스된 필드 목록을 확인하고, 미릴리스 필드가 필요하다면 SAP Note를 통해 릴리스를 요청하거나 On-Premise·Private Cloud를 검토해야 합니다.

Q4. 프로젝트 상태(OverallStatus) 값이 이상합니다.
A. PS 상태는 시스템 상태(I0002 등)와 사용자 상태가 결합된 문자열입니다. 단순 비교보다는 I_ProjectDefinitionStatusSTATUS_TEXT_EDIT류 함수를 통해 해석하는 편이 안전합니다.

이후 심화 탐색 주제

본 글에서 다룬 I_ProjectDefinition은 PS 데이터 모델의 시작점일 뿐입니다. 심화 학습으로는 (1) I_WBSElement·I_ProjectNetwork·I_ProjectActivity로 이어지는 전체 계층 탐색, (2) 실적 데이터인 I_ActualCostsProject와의 조인, (3) RAP 기반 커스텀 프로젝트 관리 앱 구축, (4) Analytical Query(@Analytics.query: true)로 프로젝트 KPI 대시보드 만들기 등이 있습니다. 또한 CDS DCL을 활용한 세밀한 권한 통제, Extensibility Framework를 통한 커스텀 필드 추가도 실무에서 자주 다뤄집니다.

더 읽어볼 만한 자료

  • SAP Help Portal — Project System (PS) in S/4HANA
  • SAP Help — ABAP CDS View Entities Reference
  • SAP API Business Hub — Project Definition OData Services
  • SAP Community — CDS Views Topic Page

댓글 0

아직 댓글이 없습니다.