WBS 요소란 무엇인가 - 프로젝트 계층 구조의 핵심
SAP Project System(PS) 모듈에서 WBS(Work Breakdown Structure) 요소는 프로젝트를 관리 가능한 단위로 세분화하는 계층적 구성 요소입니다. 하나의 프로젝트는 최상위 프로젝트 정의(Project Definition) 아래에 여러 단계의 WBS 요소가 트리 구조로 배치되며, 각 요소는 예산·비용·일정·책임자 정보를 독립적으로 보유합니다.
실무적으로 WBS 요소는 "비용을 집계하는 계정 지시 오브젝트(Account Assignment Object)" 역할을 합니다. 예를 들어 대형 플랜트 건설 프로젝트라면 최상위 WBS는 "플랜트 A 건설"이고, 그 아래에 "기초 공사", "구조물 설치", "전기 배선"과 같은 하위 WBS가 존재하며, 실제 자재 구매나 인건비는 최하위 WBS에 계상됩니다.
이 글에서 다룰 I_WBSElement는 이러한 WBS 요소의 마스터 데이터를 표준 형태로 노출하는 Virtual Data Model(VDM) 인터페이스 뷰이며, S/4HANA에서 프로젝트 데이터를 조회하는 진입점 역할을 합니다.
PRPS 테이블의 역할과 한계
클래식 ABAP 환경에서 WBS 요소 마스터는 PRPS 테이블에 저장되어 왔습니다. 컬럼 이름은 대부분 독일어 약어(POSID, PSPNR, PBUKR 등) 기반이라 초심자에게 진입 장벽이 높고, 텍스트는 별도 테이블 PROJ·PRTX로 분리되어 있어 조인 부담이 큽니다.
또한 PRPS만으로는 "이 WBS의 총 실적 비용" 같은 집계 정보를 얻을 수 없고, 별도 COSP·COSS·BPGE 테이블을 결합해야 합니다. 필드 검증 로직도 SAP GUI 트랜잭션(CJ20N 등)에 하드코딩된 경우가 많아, 커스텀 리포트에서 이를 정확히 재현하기 어렵다는 문제가 있습니다. S/4HANA에서는 이러한 복잡성을 CDS View 계층으로 추상화하여 안정적인 조회 API를 제공하는 방향으로 진화했습니다.
선행 지식 및 준비 환경
이 글의 예제는 SAP S/4HANA 2022 이상 온프레미스 또는 SAP S/4HANA Cloud Public Edition 2308 이상에서 검증되었습니다. 실습에 필요한 사전 지식은 다음과 같습니다.
- CDS View DDL 문법(
define view,association,@Annotation)에 대한 기본 이해 - ABAP Development Tools(ADT, Eclipse 기반) 사용 경험
- Open SQL 및 CDS Path 표현식 기본 문법
- SAP PS 모듈의 프로젝트/WBS 개념 인지
권한 측면에서는 S_DEVELOP(오브젝트 유형 DDLS 조회)과 프로젝트 마스터 조회 권한(C_PROJ_VNR)이 있어야 합니다. 개발 시스템에서는 표준 클라이언트(000이 아닌 개발 클라이언트, 예: 100)에서 실습을 진행하는 것이 일반적으로 권장됩니다.
I_WBSElement CDS View 내부 구조 분석
I_WBSElement는 SAP가 제공하는 Interface 계층 VDM 뷰로, 이름 앞의 I_ 접두어가 그 위치를 나타냅니다. 아래는 뷰의 논리적 구조를 간략히 재구성한 형태입니다(실제 표준 소스와는 다를 수 있으며 이해를 돕기 위한 개요입니다).
@AbapCatalog.sqlViewName: 'IWBSELEMENT'
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'WBS Element - Interface View'
@VDM.viewType: #BASIC
define view I_WBSElement
as select from prps as WbsElementRoot
association [0..1] to I_Project as _Project
on $projection.Project = _Project.Project
association [0..*] to I_WBSElementT as _Text
on $projection.WBSElementInternalID = _Text.WBSElementInternalID
{
key WbsElementRoot.pspnr as WBSElementInternalID,
WbsElementRoot.posid as WBSElement,
WbsElementRoot.pspkz as WBSElementCategoryCode,
WbsElementRoot.pbukr as CompanyCode,
WbsElementRoot.pwposi as ControllingArea,
WbsElementRoot.psphi as ProjectInternalID,
WbsElementRoot.stufe as WBSElementLevelValue,
WbsElementRoot.up as ParentWBSElementInternalID,
_Project.Project as Project,
_Project,
_Text
}
주목할 필드는 다음과 같습니다. WBSElementInternalID(PSPNR)는 시스템 내부 숫자 키로 조인 성능에 유리하며, WBSElement(POSID)는 사용자가 보는 문자 코드입니다. WBSElementLevelValue(STUFE)는 계층 깊이를 나타내는 정수로, 최상위 요소가 1이며 하위로 갈수록 증가합니다. ParentWBSElementInternalID(UP)는 상위 WBS 요소의 내부 ID로, 자기 참조 계층을 형성합니다.
WBS 계층 탐색 실전 예제 — ProjectCostAnalysis 시나리오
가상의 요구사항을 설정해 보겠습니다. 컨트롤링 부서에서 "특정 대형 프로젝트의 전체 WBS 계층과 각 요소의 계층 깊이를 트리 순서로 조회하는 리포트"를 요청했다고 가정합니다. 이를 위해 Z_R_ProjectCostAnalysis라는 커스텀 CDS View를 작성합니다.
@AbapCatalog.sqlViewName: 'ZRPRJCOSTANLYS'
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Project Cost Analysis - WBS Hierarchy'
@VDM.viewType: #CONSUMPTION
define view Z_R_ProjectCostAnalysis
with parameters
P_ProjectDefinition : abap.char(24)
as select from I_WBSElement as WbsNode
association [0..1] to I_WBSElement as _ParentNode
on $projection.ParentWBSElementInternalID = _ParentNode.WBSElementInternalID
{
key WbsNode.WBSElementInternalID as WbsInternalId,
WbsNode.WBSElement as WbsCode,
WbsNode.WBSElementLevelValue as HierarchyDepth,
WbsNode.ParentWBSElementInternalID as ParentWbsId,
_ParentNode.WBSElement as ParentWbsCode,
WbsNode.CompanyCode as CompanyCode,
WbsNode.ControllingArea as ControllingArea,
cast( repeat( ' ', WbsNode.WBSElementLevelValue * 2 )
as abap.char(40) ) as IndentPrefix
}
where WbsNode.Project = :P_ProjectDefinition
파라미터 P_ProjectDefinition은 프로젝트 정의 코드를 받으며, IndentPrefix는 계층 깊이만큼 공백을 붙여 SALV 리포트에서 트리 형태로 보이게 하는 시각화 트릭입니다. Consumption 뷰로 지정했기 때문에 OData 서비스로 노출해 SAP Fiori Elements 앱에서 직접 소비할 수 있습니다.
2단계: 비용 집계와 예외 처리 강화
실무에서는 계층뿐 아니라 각 WBS 요소의 실적 비용, 예산 소진율까지 함께 봐야 합니다. 아래 예제는 I_WBSElement에 실적 비용 집계 뷰를 결합합니다.
@AbapCatalog.sqlViewName: 'ZRPRJCOSTAGG'
@EndUserText.label: 'WBS with Actual Cost Aggregation'
define view Z_R_WBSCostAggregation
with parameters
P_ControllingArea : abap.char(4),
P_FiscalYear : abap.numc(4)
as select from I_WBSElement as Wbs
association [0..*] to I_WBSElementActualCost as _ActualCost
on $projection.WbsInternalId = _ActualCost.WBSElementInternalID
and _ActualCost.FiscalYear = $parameters.P_FiscalYear
{
key Wbs.WBSElementInternalID as WbsInternalId,
Wbs.WBSElement as WbsCode,
Wbs.WBSElementLevelValue as HierarchyDepth,
Wbs.ControllingArea as ControllingArea,
@Semantics.amount.currencyCode: 'Currency'
cast( coalesce(
sum( _ActualCost.ActualAmountInCoCodeCrcy ), 0 )
as abap.curr(15,2) ) as TotalActualCost,
Wbs.CompanyCodeCurrency as Currency,
_ActualCost
}
where Wbs.ControllingArea = :P_ControllingArea
group by
Wbs.WBSElementInternalID,
Wbs.WBSElement,
Wbs.WBSElementLevelValue,
Wbs.ControllingArea,
Wbs.CompanyCodeCurrency
coalesce로 비용이 없는 WBS도 0으로 처리하여 좌측 조인 시 NULL이 리포트에 표시되지 않도록 합니다. @Semantics.amount.currencyCode 어노테이션은 SAP Fiori에서 금액과 통화를 함께 렌더링하기 위한 필수 메타데이터입니다.
ABAP 소비 로직에서는 예외 처리를 다음과 같이 설계합니다.
TRY.
SELECT WbsCode, HierarchyDepth, TotalActualCost, Currency
FROM Z_R_WBSCostAggregation( P_ControllingArea = '1000',
P_FiscalYear = '2026' )
WHERE HierarchyDepth <= 3
ORDER BY WbsCode
INTO TABLE @DATA(lt_cost).
IF lt_cost IS INITIAL.
MESSAGE |No WBS data for CoArea 1000 / FY 2026| TYPE 'I'.
RETURN.
ENDIF.
CATCH cx_sy_open_sql_db INTO DATA(lx_sql).
" 권한/파라미터 오류는 별도 로그 테이블에 기록
zcl_prj_logger=>log_error(
iv_context = 'WBS_COST_QUERY'
iv_message = lx_sql->get_text( ) ).
RAISE SHORTDUMP TYPE cx_sy_open_sql_db EXPORTING previous = lx_sql.
ENDTRY.
3단계: Association과 조인으로 확장하기 (프로덕션 패턴)
대규모 프로젝트에서는 응답성이 관건입니다. 아래는 재사용성과 성능을 함께 잡는 프로덕션급 패턴입니다.
@AbapCatalog.sqlViewName: 'ZCPRJDASH'
@AccessControl.authorizationCheck: #CHECK
@Analytics.dataCategory: #FACT
@EndUserText.label: 'Project Cost Dashboard'
@VDM.viewType: #CONSUMPTION
define view Z_C_ProjectDashboard
as select from Z_R_WBSCostAggregation( P_ControllingArea: '1000',
P_FiscalYear : '2026' ) as Cost
association [0..1] to I_Project as _Project
on $projection.ProjectInternalId = _Project.ProjectInternalID
association [0..1] to I_ResponsibleCostCenter as _CostCenter
on $projection.ResponsibleCostCenter = _CostCenter.CostCenter
{
key Cost.WbsInternalId,
Cost.WbsCode,
Cost.HierarchyDepth,
@DefaultAggregation: #SUM
Cost.TotalActualCost,
Cost.Currency,
_Project.ProjectDescription,
_CostCenter.CostCenterName,
_Project,
_CostCenter
}
성능 관점에서 유의할 점은 다음과 같습니다. 첫째, 최상위 소비 뷰에는 반드시 @AccessControl.authorizationCheck: #CHECK를 지정하여 DCL(Data Control Language)을 통한 권한 필터링이 자동 적용되게 합니다. 둘째, HANA 옵티마이저가 Fact/Dimension 관계를 이해하도록 @Analytics.dataCategory 어노테이션을 부여합니다. 셋째, 계층 탐색이 반복될 경우 hierarchy( ... ) 표현식으로 재귀 CDS를 활용하는 것이 성능상 유리합니다.
실무에서 자주 하는 실수 3가지
실수 1: PSPNR과 POSID를 혼동 — PSPNR(내부 ID)은 시스템 내부용 숫자, POSID(외부 코드)는 사용자용 문자열입니다. 조인 키는 반드시 내부 ID(WBSElementInternalID)를 사용해야 하며, 외부 코드로 조인하면 성능이 급격히 저하됩니다.
실수 2: 텍스트 뷰(I_WBSElementText) 언어 필터 누락 — 텍스트 어소시에이션 사용 시 Language 조건을 잊으면 다국어 텍스트가 중복 반환되어 결과 행이 폭증합니다. @ObjectModel.text.association 어노테이션 또는 명시적 where language = $session.system_language를 사용하는 것이 일반적으로 안전합니다.
실수 3: 파라미터 없이 대량 조회 — I_WBSElement는 전사 프로젝트 데이터를 담고 있어 필터 없이 조회하면 수백만 행이 반환될 수 있습니다. 반드시 Project, ControllingArea, CompanyCode 중 하나 이상으로 사전 필터링해야 하며, 애플리케이션 서버 메모리 소진(MEMORY_NO_MORE_PAGING) 덤프를 유발할 수 있습니다.
자주 묻는 질문
- Q1. I_WBSElement와 C_WBSElement의 차이는? —
I_는 Interface(기술 계층),C_는 Consumption(비즈니스 소비 계층)입니다. 커스텀 뷰에서 재사용할 때는I_를 사용하고, OData 서비스로 노출할 때는C_계층을 참조하는 것이 VDM 규약상 일반적으로 권장됩니다. - Q2. 계층 재귀 조회는 어떻게 하나? — S/4HANA 2020 이상에서는 CDS
hierarchy()함수로 재귀 계층을 직접 정의할 수 있으며, 별도 재귀 CTE 없이 최상위/자식/깊이를 얻을 수 있습니다. - Q3. 프로젝트 정의 텍스트는 어디서 얻나? — 프로젝트 텍스트는
I_Project뷰의ProjectDescription필드로 노출됩니다. WBS 요소의 자체 텍스트는_Text어소시에이션을 통해I_WBSElementText에서 조회합니다.
프로젝트 데이터 분석에 활용하는 방법
이 뷰를 익힌 뒤 이어서 살펴볼 만한 주제는 I_Project(프로젝트 정의), I_ProjectNetwork(네트워크·액티비티), I_WBSElementActualCost(실적 비용), I_WBSElementBudget(예산)입니다. 이들을 조합하면 예산 대비 실적 대시보드, 프로젝트 진척률 리포트, 원가 초과 조기 경보 시스템을 구축할 수 있습니다.
또한 SAP Analytics Cloud와 연동할 때는 Consumption 뷰에 @Analytics.query: true를 부여하여 Analytical Query로 등록하고, RSRT/Query Browser로 성능 튜닝을 진행하는 흐름이 실무에서 자주 쓰입니다. RAP(RESTful ABAP Programming) 모델에서는 이 뷰들을 Behavior Definition의 Read-only projection으로 감싸 Fiori 앱의 카드/리스트 컴포넌트에 바로 바인딩하는 패턴도 널리 사용됩니다.
도움이 되는 링크
- SAP Help Portal - S/4HANA On-Premise Documentation
- SAP Help Portal - S/4HANA Cloud CDS Views
- SAP Help Portal - ABAP CDS View Development Guide
- SAP Community - ABAP CDS and VDM Best Practices
- SAP Blogs - Project System and WBS Element Insights
- SAP API Business Hub - I_WBSElement OData Service Catalog
댓글 0
아직 댓글이 없습니다.