QMEL 테이블과 PM 알림 데이터의 출발점
SAP Plant Maintenance(PM) 모듈에서 알림(Notification)은 설비 이상, 결함, 작업 요청을 추적하는 핵심 객체입니다. 알림 헤더는 전통적으로 QMEL 테이블에 저장되며, 각 알림에 연결된 결함 항목(Item)은 QMFE에 기록됩니다. S/4HANA 환경에서는 이 테이블 구조 위에 가상 데이터 모델(VDM)이 덮여 있고, I_NotificationItem은 결함 항목 레벨에 접근하기 위한 기본(Basic) 인터페이스 뷰입니다.
이 글에서 다루는 범위는 다음과 같습니다.
- QMEL/QMFE 테이블과 I_NotificationItem CDS 뷰의 매핑 관계 이해
- 알림 결함 항목의 핵심 필드 식별과 코드 카탈로그 해석
- OPEN SQL 및 ABAP CDS 컨슈머 뷰에서 활용하는 패턴
- 설비 단위 반복 결함 분석 리포트 구현
- 대량 알림 데이터를 다룰 때 자주 발생하는 성능 이슈와 대응 방법
대상 독자는 PM 모듈의 표준 트랜잭션(IW21/IW22/IW28)을 어느 정도 알고 있으며, S/4HANA에서 데이터 추출 또는 Fiori 앱 확장 작업을 맡고 있는 개발자입니다.
알아두면 도움이 되는 배경 지식
이 글은 ABAP CDS 뷰 작성 경험과 OPEN SQL 기본 문법을 전제로 합니다. SELECT ... FROM 절, JOIN, ASSOCIATION의 차이를 알고 있어야 하며, PM 알림의 비즈니스 흐름(생성 → 처리 → 완료)을 한 번이라도 따라가 본 경험이 있으면 이해가 빠릅니다. QM(품질) 모듈의 결함 코드 그룹(Code Group)/Code 구조도 동일한 카탈로그 메커니즘을 공유하므로 함께 알아두면 좋습니다.
환경, 릴리스, 그리고 준비 도구
I_NotificationItem은 S/4HANA 1709 이후 릴리스에서 표준 VDM의 일부로 제공되는 것으로 일반적으로 알려져 있습니다. 이 글의 코드 예제는 다음 환경에서 검증하는 것을 권장합니다.
- S/4HANA 2022 또는 2023 (On-Premise) — RAP 기반 컨슈머 뷰 사용 시 동일하게 적용 가능
- ABAP Development Tools (ADT) for Eclipse 3.34 이상
- 분석용 DB 클라이언트: ADT의 Data Preview 또는 SE16N
- 권한:
S_TABU_DIS의 QM 인증 그룹, 그리고 CDS 뷰 실행을 위한S_DATASET/S_RFC(사용 시나리오에 따라)
릴리스에 따라 필드 일부(예: MalfunctionStartDate, CauseText)가 추가되거나 키 컬럼 명명이 달라질 수 있으므로, 운영에 적용하기 전 본인 시스템의 I_NotificationItem 정의를 ADT에서 직접 확인하는 것을 권장합니다.
VDM 계층에서 I_NotificationItem이 차지하는 위치
SAP의 가상 데이터 모델은 마치 도서관의 분류 체계와 유사합니다. 원천 테이블(QMEL, QMFE 등)이 서가의 책이라면, Basic 뷰는 "이 책을 사람이 읽기 쉽게 색인한 카드"이고, Composite 뷰는 그 카드들을 주제별로 묶은 폴더이며, Consumption 뷰는 특정 사용자(Fiori 앱)에게 맞춰 재포장한 결과물입니다.
이 계층에서 I_NotificationItem은 다음과 같은 좌표를 갖습니다.
- 네이밍:
I_접두사 — Interface(Basic) 뷰 - 원천: 주로
QMFE(알림 항목) + 부분적으로QMEL(헤더 필드 일부) 및 코드 카탈로그 - 상위 컨슈머:
C_MaintenanceNotification계열, 분석용I_NotificationItemAnalytics등에서 재사용 - 관련 형제 뷰:
I_Notification(헤더),I_NotificationTask(조치 항목),I_NotificationActivity(활동)
즉, 알림 한 건은 헤더 1개 + 결함 항목 N개 + 작업/조치 항목 N개의 트리 구조이며, I_NotificationItem은 그 트리의 "결함 가지"를 담당합니다. 알림 번호(MaintenanceNotification)와 항목 번호(NotificationItem) 두 키로 행을 식별합니다.
비유하자면, 한 대의 펌프(설비)에 발행된 작업 지시(알림) 하나에 "베어링 마모", "이상 소음", "오일 누유" 세 가지 증상이 적혀 있다면, 각 증상이 하나의 NotificationItem 행으로 떨어집니다.
결함 항목을 구성하는 주요 필드
모델링 작업을 시작하기 전에 자주 다루게 되는 컬럼을 정리합니다. 필드명은 VDM 의미 기반 명칭이며, 괄호 안은 일반적으로 매핑되는 원천 컬럼입니다.
MaintenanceNotification(QMEL-QMNUM) — 알림 번호, 헤더 키NotificationItem(QMFE-FENUM) — 항목 일련번호NotificationCauseCodeGroup/NotificationCauseCode— 원인 코드 카탈로그NotificationItemCodeGroup/NotificationItemCode— 결함 자체 코드(증상)NotificationItemText— 결함 단문 설명NotificationItemIsCompleted— 완료 플래그MalfunctionEffect— 고장 영향 코드AssemblyOfNotification— 결함이 발생한 어셈블리
코드 그룹/코드는 카탈로그 유형(예: 'B' 결함, 'C' 원인)에 묶여 의미가 결정되므로, 실제 화면 표시 텍스트는 I_NotificationCodeText 또는 텍스트 어소시에이션을 통해 가져와야 합니다.
1단계 — 기본 조회 패턴 만들기
가장 단순한 시나리오부터 시작합니다. 특정 기능 위치(Functional Location)에 속한 알림의 결함 항목 목록을 가져오려면, 헤더 뷰와 항목 뷰를 결합합니다.
REPORT zpm_notif_item_basic.
DATA: lt_items TYPE STANDARD TABLE OF i_notificationitem,
lv_funcloc TYPE /scwm/de_lgnum VALUE 'PLANT-A-PUMP-101'.
SELECT itm~maintenancenotification,
itm~notificationitem,
itm~notificationitemcodegroup,
itm~notificationitemcode,
itm~notificationitemtext,
hdr~functionallocation,
hdr~notificationcreationdate
FROM i_notificationitem AS itm
INNER JOIN i_notification AS hdr
ON hdr~maintenancenotification = itm~maintenancenotification
WHERE hdr~functionallocation = @lv_funcloc
AND itm~notificationitemiscompleted = @abap_false
INTO TABLE @DATA(lt_open_defects)
UP TO 200 ROWS.
cl_demo_output=>display( lt_open_defects ).
이 코드는 미완료 결함만 추출합니다. UP TO 200 ROWS로 초기 탐색 시 결과 폭주를 막는 것이 일반적으로 안전한 습관입니다.
2단계 — 코드 텍스트 결합과 예외 처리
실무에서는 결함 코드만 보고 의미를 알 수 없으므로 텍스트 결합이 필수입니다. 또한 권한 부족이나 빈 결과에 대한 방어 코드를 함께 작성합니다.
CLASS zcl_pm_defect_reader DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES: BEGIN OF ty_defect_row,
notification TYPE i_notification-maintenancenotification,
item TYPE i_notificationitem-notificationitem,
code_group TYPE i_notificationitem-notificationitemcodegroup,
code TYPE i_notificationitem-notificationitemcode,
code_text TYPE string,
functional_loc TYPE i_notification-functionallocation,
created_on TYPE i_notification-notificationcreationdate,
END OF ty_defect_row.
TYPES tt_defects TYPE STANDARD TABLE OF ty_defect_row WITH EMPTY KEY.
METHODS read_defects
IMPORTING iv_funcloc TYPE i_notification-functionallocation
iv_date_from TYPE d
RETURNING VALUE(rt_out) TYPE tt_defects
RAISING cx_sy_open_sql_db.
ENDCLASS.
CLASS zcl_pm_defect_reader IMPLEMENTATION.
METHOD read_defects.
SELECT itm~maintenancenotification AS notification,
itm~notificationitem AS item,
itm~notificationitemcodegroup AS code_group,
itm~notificationitemcode AS code,
txt~codedescription AS code_text,
hdr~functionallocation AS functional_loc,
hdr~notificationcreationdate AS created_on
FROM i_notificationitem AS itm
INNER JOIN i_notification AS hdr
ON hdr~maintenancenotification = itm~maintenancenotification
LEFT OUTER JOIN i_notificationcodetext AS txt
ON txt~codecataloguetype = 'B'
AND txt~codegroup = itm~notificationitemcodegroup
AND txt~code = itm~notificationitemcode
AND txt~language = @sy-langu
WHERE hdr~functionallocation = @iv_funcloc
AND hdr~notificationcreationdate >= @iv_date_from
INTO CORRESPONDING FIELDS OF TABLE @rt_out.
IF sy-subrc <> 0.
MESSAGE i208(00) WITH 'No defect items for selection.' INTO DATA(lv_msg).
ENDIF.
ENDMETHOD.
ENDCLASS.
코드 카탈로그 유형(codecataloguetype) 값은 결함은 'B', 원인은 'C', 작업은 'A' 등으로 구분되며, 잘못된 카탈로그 유형으로 조인하면 텍스트가 영구히 NULL이 되므로 주의해야 합니다.
3단계 — 프로덕션 수준의 CDS 컨슈머 뷰
리포트나 Fiori Elements 앱을 만들 때는 ABAP에서 매번 SELECT하는 대신 컨슈머 뷰로 캡슐화하는 편이 유지보수에 유리합니다. 다음은 설비별 미완료 결함 카운트를 노출하는 컨슈머 뷰 예제입니다.
@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Open Defect Items by Equipment'
@Metadata.ignorePropagatedAnnotations: true
define view entity ZC_OpenDefectByEquipment
as select from I_NotificationItem as Item
inner join I_Notification as Header
on Header.MaintenanceNotification = Item.MaintenanceNotification
left outer to one association [0..1] to I_NotificationCodeText as _CodeText
on _CodeText.CodeCatalogueType = 'B'
and _CodeText.CodeGroup = Item.NotificationItemCodeGroup
and _CodeText.Code = Item.NotificationItemCode
and _CodeText.Language = $session.system_language
{
key Header.TechnicalObject as Equipment,
key Item.MaintenanceNotification as Notification,
key Item.NotificationItem as ItemNumber,
Item.NotificationItemCodeGroup as DefectCodeGroup,
Item.NotificationItemCode as DefectCode,
_CodeText.CodeDescription as DefectText,
Header.NotificationCreationDate as CreatedOn,
Header.MaintPriority as Priority,
cast( 1 as abap.int4 ) as ItemCount,
_CodeText
}
where Item.NotificationItemIsCompleted = ' '
and Header.TechnicalObject is not initial
이 뷰는 분석 도구에서 Equipment로 집계하면 그대로 "설비별 미완료 결함 수"가 됩니다. 운영 배포 전 다음 세 가지를 점검하는 것을 권장합니다.
- 접근 제어(DCL):
I_Notification이 권한 체크를 갖고 있더라도 컨슈머 뷰에@AccessControl.authorizationCheck를 명시 - 대량 조회 대비 인덱스 —
TechnicalObject가 자주 필터로 쓰인다면 시스템에서 사용 통계 확인 - 단위 테스트:
CL_CDS_TEST_ENVIRONMENT로 더블 환경을 만들어 코드 텍스트 누락/완료 플래그 경계값 검증
설비별 반복 결함 분석 리포트
"같은 설비에서 같은 결함이 90일 안에 N번 이상 발생한 경우"를 추출하는 시나리오는 PM 분석에서 매우 자주 등장합니다. 이를 SQL로 작성하면 다음과 같습니다.
SELECT Equipment,
DefectCodeGroup,
DefectCode,
COUNT(*) AS RecurrenceCount,
MIN(CreatedOn) AS FirstSeen,
MAX(CreatedOn) AS LastSeen
FROM zc_opendefectbyequipment
WHERE CreatedOn >= '20260401'
GROUP BY Equipment, DefectCodeGroup, DefectCode
HAVING COUNT(*) >= 3
ORDER BY RecurrenceCount DESCENDING
이 결과를 Fiori Elements List Report로 노출하면, 보전 엔지니어가 "반복 고장이 잦은 설비 + 결함 코드" 조합을 한눈에 식별할 수 있습니다.
주변 CDS 뷰와 결합 전략
I_NotificationItem 단독으로는 의미가 제한적이며, 다음 뷰들과 조합할 때 활용도가 크게 올라갑니다.
| 뷰 | 역할 | 전형적 조인 키 |
|---|---|---|
| I_Notification | 알림 헤더, 우선순위, 설비 | MaintenanceNotification |
| I_NotificationCause | 원인 항목(결함별 원인 N개) | Notification + Item |
| I_NotificationTask | 조치 작업 항목 | Notification + Item |
| I_NotificationActivity | 실행된 활동 기록 | Notification + Item |
| I_MaintenanceOrder | 알림에서 생성된 작업 오더 | MaintenanceNotification |
조인 전략에서 주의할 점은 카디널리티입니다. 한 결함 항목에 원인이 여러 개 붙을 수 있고, 그 원인마다 작업이 또 N개 붙는 구조이므로, 무심코 다중 조인을 하면 행이 곱셈으로 늘어납니다. 집계 전용 뷰에서는 가급적 SUM/COUNT(DISTINCT)를 명시적으로 사용하고, 분석 대시보드에서는 별도의 어소시에이션 기반 컴포지트 뷰를 만들어 각 가지를 따로 조회하는 것이 일반적으로 안전합니다.
운영 환경에서 챙겨야 할 성능 포인트
실무 적용 시 자주 부딪히는 함정과 권장 대응을 정리합니다.
- SELECT * 회피 —
I_NotificationItem은 30개 이상 필드를 노출합니다. 열 기반 저장소(HANA) 특성상 필요한 컬럼만 선택해야 컬럼 스토어 캐시 효율이 유지됩니다. - 날짜 범위 필터 필수 — 운영 시스템에서 알림은 누적 수백만 건 단위로 쌓이므로,
NotificationCreationDate또는MalfunctionStartDate범위를 반드시 지정하는 것을 권장합니다. - 코드 텍스트 조인 비용 —
I_NotificationCodeText는 다국어/카탈로그 조합으로 행이 많습니다. 가능한 한Language = $session.system_language조건을 어소시에이션 내부에 두어 미리 가지치기합니다. - 완료 플래그의 빈 문자열 —
NotificationItemIsCompleted는 SPACE/'X' 양자값입니다. ABAP에서는abap_false로 비교 가능하지만, CDS에서는= ' '로 명시해야 의도가 분명합니다.
자주 마주치는 문제와 해결 힌트
Q1. 코드 텍스트가 일부 행에서 비어 보입니다.
A. 카탈로그 유형 매핑 누락이 가장 흔합니다. 결함 항목은 'B', 원인은 'C', 작업은 'A'를 사용합니다. 또한 텍스트 테이블의 Plant 필드가 코드 그룹에 영향을 주는 경우가 있어, Plant 의존 카탈로그를 쓴다면 헤더 Plant도 조인 조건에 넣어야 합니다.
Q2. 같은 알림에서 동일 결함 코드가 여러 번 보입니다.
A. 알림 항목은 사용자가 의도적으로 중복 등록할 수 있는 구조입니다. 분석 시에는 COUNT(DISTINCT MaintenanceNotification) 또는 알림+항목 단위로 사전 집계 후 상위 차원으로 올리는 것이 일반적으로 안전합니다.
Q3. ADT Data Preview에서는 데이터가 보이는데 ABAP SELECT 결과는 비어 있습니다.
A. @AccessControl.authorizationCheck: #CHECK가 적용된 뷰이므로, 실행 사용자의 PM 권한(특히 I_QMEL/S_TCODE 'IW28' 관련)이 다를 가능성이 큽니다. 백그라운드 잡 사용자의 권한 프로파일을 별도로 점검하는 것을 권장합니다.
댓글 0
아직 댓글이 없습니다.