SAP PS 네트워크 활동이란 — 프로젝트 일정의 핵심 단위
SAP Project System(PS)에서 네트워크(Network)는 프로젝트 실행 계획의 뼈대 역할을 하며, 네트워크 활동(Network Activity)은 그 뼈대를 구성하는 최소 실행 단위입니다. WBS 요소(Work Breakdown Structure Element)가 프로젝트를 계층적으로 분해한 구조라면, 네트워크 활동은 그 안에서 실제로 수행되는 작업의 시간·자원·비용을 관리하는 도구입니다. 예를 들어 "발전소 건설" 프로젝트의 WBS 아래 "터빈 설치" 네트워크가 있다면, "베어링 정렬", "블레이드 조립", "시운전" 같은 개별 액티비티가 네트워크 활동으로 정의됩니다.
이 글에서는 SAP S/4HANA 환경에서 네트워크 활동 데이터를 조회하기 위한 표준 CDS View인 I_NetworkActivity를 중심으로, 원본 테이블 AFVC의 한계, CDS View가 제공하는 부가 가치, 실무 리포트에서 자주 마주치는 패턴을 다룹니다. 다음 항목을 익히는 것을 목표로 합니다.
- AFVC / AFVV / AFKO 테이블 간 관계와 조회 시 주의점
I_NetworkActivityCDS View의 필드 매핑과 기본 조인 구조- NetworkMilestoneReport 시나리오로 보는 실전 조회 코드 3단계
- 계획 일정(Basic/Scheduled)과 실적 일정(Actual) 비교 패턴
- 마일스톤·관계(Relationships) 데이터와 결합하는 방법
이 글을 이해하기 위한 배경 지식
ABAP CDS View 문법(define view, association, @Semantics)에 대한 기본 이해와 ABAP RESTful Application Programming(RAP) 또는 ABAP for HANA 환경에서의 개발 경험이 있으면 좋습니다. 또한 SAP PS 모듈의 프로젝트 정의, WBS, 네트워크 헤더/활동 개념을 어느 정도 알고 있다면 예제 이해가 수월합니다. 트랜잭션 CN21/CN22/CN23을 다뤄 본 경험이 있다면 이 글의 데이터 흐름이 훨씬 직관적으로 다가올 것입니다.
환경 및 준비물
이 글의 예제는 SAP S/4HANA 2022 이상(권장 S/4HANA 2023 FPS01) 환경을 기준으로 작성되었습니다. ABAP Development Tools(ADT) for Eclipse 3.36 이상을 설치하고, PS 모듈이 활성화된 시스템에 접근할 수 있어야 합니다. 표준 CDS View I_NetworkActivity는 S/4HANA on-premise 및 Private Cloud Edition에서 일반적으로 사용 가능하며, Public Cloud Edition의 경우 해당 뷰의 릴리스 상태(C1/C0)를 SAP API Business Hub 또는 View Browser(트랜잭션 /n/IWFND/CUSTOMIZING 대신 ADT의 View Browser)에서 확인하는 것을 권장합니다.
권한 측면에서는 최소한 PS 조회 권한 오브젝트 C_PROJ, C_NETW가 필요하며, ABAP SQL 실행을 위한 S_DEVELOP 권한이 있어야 합니다. 데이터 준비를 위해 CJ20N 또는 CN21로 테스트용 프로젝트/네트워크를 생성해 두면 조회 결과를 즉시 확인할 수 있습니다. SAP GUI 8.00 이상, HANA DB 2.0 SPS06 이상 환경에서 검증된 코드를 소개합니다.
AFVC 테이블의 구조와 한계
네트워크 활동 데이터의 물리 저장소는 세 개의 테이블로 나뉩니다. AFKO는 네트워크 헤더(Order Master)를 담고, AFVC는 활동의 마스터 속성(활동 번호, 텍스트, 컨트롤 키, WBS 배정 등)을 저장하며, AFVV는 활동의 수량·일정 관련 값(계획 시작/종료, 실적 시작/종료, 작업량 등)을 별도로 관리합니다. 이렇게 나뉘어 있는 이유는 생산관리(PP) 모듈의 오더 활동과 구조를 공유하기 때문입니다. 즉 AFVC는 PS 전용이 아니라 PP 오더 활동도 담고 있으며, 이를 구분하려면 AUFPL(Task List Number)과 AUFNR(Order Number)를 통해 조인 경로를 정확히 설정해야 합니다.
원시 테이블을 직접 사용할 때의 한계는 다음과 같습니다. 첫째, 활동 하나를 조회하려면 AFKO → AFVC → AFVV 세 개 조인이 필수인데, 각 조인 키(AUFPL, APLZL)가 내부 번호라서 직관성이 떨어집니다. 둘째, 계획 일정과 실적 일정이 AFVV에 함께 있어 컬럼명(FSAVD, SSAVD, FSEDD 등)이 코드화되어 있고 의미 파악이 어렵습니다. 셋째, WBS 요소와의 연결을 PROJN(내부 프로젝트 번호)으로 해야 하는데, 이는 외부 표현(PSPID)과 다르기 때문에 PRPS 테이블을 한 번 더 조인해야 합니다. I_NetworkActivity는 이러한 조인·매핑·의미론적 애노테이션을 미리 처리한 표준 뷰로, 개발자가 비즈니스 로직에 집중할 수 있게 해 줍니다.
I_NetworkActivity CDS View 내부 분석
SAP가 제공하는 I_NetworkActivity는 Basic Interface View 계층에 속하며, 네트워크 활동 마스터를 표준화된 필드명으로 노출합니다. 주요 필드는 OrderInternalID(AUFPL 대응), OrderInternalCounter(APLZL 대응), NetworkInternalID, Activity(VORNR), ActivityText(LTXA1), ControlKey(STEUS), WBSElementInternalID(PROJN), OrderCategory, ActivityIsDeleted 등입니다. 계획/실적 일정은 관례상 별도의 뷰(I_NetworkActivityDates 또는 I_NetworkActivityWithScheduleD 계열)로 분리되는 경우가 많으나, 릴리스에 따라 I_NetworkActivity 자체가 관련 associations를 노출하기도 합니다.
Association 관점에서 이 뷰는 _Network(헤더), _WBSElement, _ControlKey, _Milestone, _NetworkActivityRelationship 같은 노출된 경로를 통해 하위 데이터로 자연스럽게 이동할 수 있습니다. 비유하자면, AFVC가 "원자재 창고"라면 I_NetworkActivity는 "라벨링·분류·통로가 정리된 물류센터"로 볼 수 있습니다. 별도의 조인 경로를 외우지 않고도 _WBSElement.WBSElementExternalID와 같이 점 표기로 원하는 데이터에 접근합니다. 또한 @ObjectModel.usageType 애노테이션이 붙어 있어 리포팅 목적(#ANALYTICAL / #TRANSACTIONAL) 여부를 파악할 수 있고, ODATA 노출 시 @OData.publish 옵션과 함께 즉시 서비스로 발행할 수 있습니다.
실전 예제: NetworkMilestoneReport 조회
가상의 요구사항으로 "특정 프로젝트 정의에 속하는 모든 네트워크 활동의 일정과 마일스톤 여부를 하나의 리스트로 보고 싶다"는 시나리오를 가정하겠습니다. 이를 ZNetworkMilestoneReport로 명명하고 세 단계로 확장합니다.
1단계 — 기본 조회
먼저 특정 프로젝트에 속한 활동을 단순히 나열합니다. 표준 뷰만 사용하며 별도의 커스텀 뷰는 만들지 않습니다.
REPORT znetwork_activity_basic.
DATA: lt_activity TYPE TABLE OF i_networkactivity,
lv_project TYPE /1bcdwb/o_pspid VALUE 'P-DEMO-001'.
SELECT act~network,
act~activity,
act~activitytext,
act~controlkey,
act~wbselementinternalid
FROM i_networkactivity AS act
INNER JOIN i_wbselement AS wbs
ON act~wbselementinternalid = wbs~wbselementinternalid
WHERE wbs~projectinternalid IN
( SELECT projectinternalid
FROM i_projectdefinition
WHERE project = @lv_project )
AND act~activityisdeleted = @abap_false
INTO CORRESPONDING FIELDS OF TABLE @lt_activity.
cl_demo_output=>display( lt_activity ).
여기서 ActivityIsDeleted 필터를 반드시 걸어야 논리적으로 삭제된 활동을 제외할 수 있습니다. AFVC를 직접 조회할 때는 LOEKZ를 봤어야 하지만, 표준 뷰에서는 의미론적 이름으로 통일되어 있습니다.
2단계 — 실무 시나리오: 일정·에러 로깅 추가
이제 계획 일정과 마일스톤 정보를 합치고, 결과가 없을 때 로깅합니다. 실무에서는 예외 처리와 성능 로그가 없으면 야간 배치에서 원인 추적이 어렵기 때문입니다.
CLASS zcl_network_milestone_rpt DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES: BEGIN OF ty_result,
project TYPE i_projectdefinition-project,
network TYPE i_networkactivity-network,
activity TYPE i_networkactivity-activity,
activity_text TYPE i_networkactivity-activitytext,
plan_start TYPE d,
plan_finish TYPE d,
has_milestone TYPE abap_bool,
END OF ty_result.
METHODS run
IMPORTING iv_project TYPE i_projectdefinition-project
RETURNING VALUE(rt_out) TYPE STANDARD TABLE OF ty_result.
ENDCLASS.
CLASS zcl_network_milestone_rpt IMPLEMENTATION.
METHOD run.
DATA(lv_start_ts) = cl_abap_tstmp=>utclong_current( ).
TRY.
SELECT act~network,
act~activity,
act~activitytext,
dates~scheduledstartdate AS plan_start,
dates~scheduledenddate AS plan_finish,
CASE WHEN ms~milestone IS NOT NULL
THEN @abap_true ELSE @abap_false END AS has_milestone,
pd~project
FROM i_networkactivity AS act
INNER JOIN i_networkactivitydates AS dates
ON act~orderinternalid = dates~orderinternalid
AND act~orderinternalcounter = dates~orderinternalcounter
INNER JOIN i_wbselement AS wbs
ON act~wbselementinternalid = wbs~wbselementinternalid
INNER JOIN i_projectdefinition AS pd
ON wbs~projectinternalid = pd~projectinternalid
LEFT OUTER JOIN i_networkactivitymilestone AS ms
ON act~orderinternalid = ms~orderinternalid
AND act~orderinternalcounter = ms~orderinternalcounter
WHERE pd~project = @iv_project
AND act~activityisdeleted = @abap_false
INTO CORRESPONDING FIELDS OF TABLE @rt_out.
IF rt_out IS INITIAL.
MESSAGE |No activities for project { iv_project }| TYPE 'I'.
ENDIF.
CATCH cx_sy_open_sql_db INTO DATA(lx_db).
cl_bali_free_text_setter=>create(
severity = if_bali_constants=>c_severity_error
text = lx_db->get_text( ) ).
ENDTRY.
DATA(lv_dur) = cl_abap_tstmp=>subtract(
tstmp1 = cl_abap_tstmp=>utclong_current( )
tstmp2 = lv_start_ts ).
" lv_dur를 성능 로그 테이블에 기록
ENDMETHOD.
ENDCLASS.
이 단계에서 마일스톤 뷰가 없는 경우도 LEFT OUTER JOIN으로 처리해 활동 자체는 누락되지 않게 하는 것이 핵심입니다. 정확한 마일스톤 표준 뷰명은 시스템 버전에 따라 I_NetworkActivityMilestone 또는 I_ProjectMilestone이 될 수 있으니 View Browser에서 확인이 필요합니다.
3단계 — 프로덕션 고려: 성능·보안·테스트
대규모 프로젝트에서는 활동 수가 수십만 건에 이르기도 합니다. 이 경우 CDS View 위에 커스텀 뷰를 만들고 세션 변수와 파라미터를 활용해 조기 필터링을 수행하는 것이 권장됩니다.
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Network Milestone Report'
define view entity ZC_NetworkMilestoneReport
with parameters
p_project : /1bcdwb/o_pspid
as select from I_NetworkActivity as Act
inner join I_WBSElement as Wbs
on Act.WBSElementInternalID = Wbs.WBSElementInternalID
inner join I_ProjectDefinition as Pd
on Wbs.ProjectInternalID = Pd.ProjectInternalID
left outer to one join I_NetworkActivityDates as D
on Act.OrderInternalID = D.OrderInternalID
and Act.OrderInternalCounter = D.OrderInternalCounter
{
key Pd.Project as ProjectID,
key Act.Network,
key Act.Activity,
Act.ActivityText,
D.ScheduledStartDate as PlannedStart,
D.ScheduledEndDate as PlannedFinish,
D.ActualStartDate as ActualStart,
D.ActualEndDate as ActualFinish
}
where Pd.Project = $parameters.p_project
and Act.ActivityIsDeleted = ''
테스트는 ABAP Unit + CDS Test Double Framework(cl_cds_test_environment)로 작성해, 실제 프로젝트 데이터에 의존하지 않고도 뷰 로직을 검증할 수 있습니다. 보안 측면에서는 DCL(ZC_NetworkMilestoneReport에 대한 define role)을 만들어 C_PROJ_VNR 권한 오브젝트와 연결하는 방식이 일반적입니다.
실제 일정 vs 계획 일정 비교 패턴
PS 리포팅에서 가장 흔한 요구는 "기본 일정(Basic Dates), 예정 일정(Scheduled Dates), 실적 일정(Actual Dates)을 나란히 보여 달라"는 것입니다. AFVV 필드 관점에서 FSAVD/FSEDD(기본 시작/종료), SSAVD/SSEDD(예정 시작/종료), ISAV/IEEV(실적)로 구분되는데, I_NetworkActivityDates에서는 이를 BasicStartDate, ScheduledStartDate, ActualStartDate처럼 의미 있는 이름으로 노출합니다. 이 세 가지를 CASE 식으로 조합하면 상태 컬럼(OnTime, Delayed, NotStarted)을 파생시킬 수 있고, 리포트에 그대로 사용할 수 있습니다.
SELECT act~network, act~activity,
d~basicstartdate, d~scheduledstartdate, d~actualstartdate,
CASE
WHEN d~actualstartdate IS NULL
AND d~scheduledstartdate < @sy-datum THEN 'DELAYED'
WHEN d~actualstartdate IS NOT NULL
AND d~actualstartdate <= d~scheduledstartdate THEN 'ON_TIME'
WHEN d~actualstartdate IS NULL THEN 'NOT_STARTED'
ELSE 'LATE_START'
END AS status
FROM i_networkactivity AS act
INNER JOIN i_networkactivitydates AS d
ON act~orderinternalid = d~orderinternalid
AND act~orderinternalcounter = d~orderinternalcounter
INTO TABLE @DATA(lt_status).
마일스톤 및 관계 데이터 연결
네트워크 활동은 마일스톤(트랜잭션 CN11에서 관리) 및 관계(Relationship, PS 테이블 AFAB)와 밀접하게 연결됩니다. 마일스톤은 활동의 특정 시점을 표시해 사용/청구/기술 완료 이벤트를 트리거하며, 관계는 활동 간 선후 조건(FS/SS/FF/SF)을 정의합니다. I_NetworkActivity의 association _Milestone과 _Relationship(정확한 이름은 릴리스별 상이)을 통해 활동 단위로 마일스톤 리스트, 선행/후행 활동 리스트를 얻을 수 있습니다. 실무에서는 다음처럼 파생 컬럼을 만들어 대시보드에 노출합니다.
SELECT act~network, act~activity,
COUNT( DISTINCT ms~milestone ) AS milestone_count,
COUNT( DISTINCT rel~successor_actv ) AS successor_count
FROM i_networkactivity AS act
LEFT OUTER JOIN i_networkactivitymilestone AS ms
ON act~orderinternalid = ms~orderinternalid
AND act~orderinternalcounter = ms~orderinternalcounter
LEFT OUTER JOIN i_networkactivityrelationship AS rel
ON act~orderinternalid = rel~predecessor_actv
GROUP BY act~network, act~activity
INTO TABLE @DATA(lt_dashboard).
실무에서 자주 하는 실수 세 가지
실수 1: AFVC와 I_NetworkActivity를 혼용해서 조인 — AFVC는 PP 오더 활동을 포함하므로 AUTYP = 20(네트워크) 필터를 놓치면 잘못된 데이터가 붙습니다. I_NetworkActivity는 이미 이 필터가 걸려 있으므로, 두 소스를 섞을 때는 반드시 카테고리를 재확인해야 합니다.
실수 2: ActivityIsDeleted 미필터 — CN22에서 활동을 삭제하면 물리적으로 지워지지 않고 삭제 플래그만 세팅됩니다. 이를 놓치면 이미 종결된 프로젝트에서 유령 활동이 리포트에 노출됩니다.
실수 3: 프로젝트 외부 ID로 직접 조인 — Project(외부 ID)로 조인하면 성능이 떨어지고, 마스킹된 프로젝트 코드에서 예상치 못한 결과가 나올 수 있습니다. 항상 ProjectInternalID로 조인하고, 최종 출력 단계에서만 외부 ID를 노출하는 것이 안전합니다.
자주 묻는 질문
Q1. I_NetworkActivity가 시스템에 없어요. A. Public Cloud나 일부 산업 솔루션에서는 릴리스 상태가 다를 수 있습니다. ADT의 View Browser에서 "NetworkActivity"로 검색해 대체 뷰(I_NetworkActivityAPI 계열)를 확인하세요.
Q2. 일정 필드를 시간 단위로 보고 싶어요. A. 날짜와 시간이 분리 저장되는 경우가 많습니다. ScheduledStartDate와 ScheduledStartTime을 함께 조회하고, 필요 시 UTCLONG으로 조합하세요.
Q3. Fiori 앱에서 이 뷰를 바로 쓰고 싶습니다. A. Consumption View(C_...)를 새로 만들어 UI 애노테이션을 추가한 뒤 서비스 정의(RAP Service Definition)로 노출하는 방식이 권장됩니다.
확장 학습으로 이어지는 주제
이 글에서 다룬 내용을 발판으로, PS 리포팅을 심화하려면 다음 주제를 이어서 학습하는 것이 좋습니다. 첫째, I_ProjectDefinition과 I_WBSElement를 조합한 프로젝트 계층 뷰 설계, 둘째, RAP 기반의 Managed/Unmanaged 시나리오로 네트워크 활동 편집 서비스 구현, 셋째, SAC(SAP Analytics Cloud) Live 연결을 통한 실시간 일정 대시보드 구축, 넷째, CDS Table Function으로 CPM(Critical Path Method) 계산을 서버 사이드에서 처리하는 패턴입니다. 또한 원가 관점의 I_NetworkActivityCosts 계열 뷰와 결합하면 EVM(Earned Value Management) 리포트로 확장할 수 있습니다.
더 살펴보면 좋은 자료
댓글 0
아직 댓글이 없습니다.