ABAP

MARC 직접 조회하면 큰일 #shorts #SAP #ABAP

개요와 이 글에서 얻을 수 있는 것

SAP S/4HANA에서 자재 마스터는 클라이언트 레벨의 일반 데이터(MARA)와 플랜트별 데이터(MARC)로 분리되어 관리됩니다. 이 글은 MARC 테이블을 기반으로 한 CDS View I_MaterialPlant를 활용해 플랜트별 자재 정보와 MRP(Material Requirements Planning) 관련 필드를 조회·분석하는 방법을 다룹니다. 실전 ABAP CDS 코드, 성능 최적화 패턴, 데이터 불일치 트러블슈팅까지 단계별로 안내합니다.

  • MARC 테이블의 역할과 클라이언트/플랜트 데이터 분리 구조 이해
  • I_MaterialPlant CDS View 필드 매핑과 Association 활용
  • MRP Type, Lot Size, Reorder Point 등 핵심 필드 해석
  • 플랜트+자재 복합키를 활용한 성능 최적화 ABAP 코드 작성
  • 실무 환경에서 자주 발생하는 데이터 불일치 원인과 점검 패턴

이 글을 읽기 전 알아두면 좋은 것

ABAP SELECT 문 기본 문법, CDS View 정의 구조(@AbapCatalog, define view 등), Association 사용법, 그리고 SAP MM(Materials Management) 모듈의 자재 마스터 화면(MM03) 구성에 대한 기초 지식이 있으면 빠르게 따라올 수 있습니다. ECC 시절 MARA/MARC를 SE16N으로 조회해 본 경험도 도움이 됩니다.

실습 환경과 준비물

이 글의 예제는 다음 환경을 기준으로 작성되었습니다.

  • SAP S/4HANA 2022 이상 (On-Premise 또는 Private Cloud Edition). Public Cloud에서는 Released API 가용성을 사전에 확인해야 합니다.
  • ABAP Development Tools for Eclipse (ADT) 3.40 이상
  • 권한: S_DEVELOP (개발 객체), S_TABU_DIS (MARC 조회 권한 그룹 MA)
  • 샘플 데이터: 최소 하나의 플랜트(예: 1010)와 MRP Type이 설정된 자재 5건 이상
  • 테스트 클라이언트: 운영 데이터 직접 조회는 권장하지 않으며, QAS 또는 Sandbox에서 진행

일반적으로 I_MaterialPlant는 Basic View 계층(I_*)에 속하며 Released 상태(C1 Contract)로 제공되는 경우가 많습니다. 실제 시스템에서는 ADT에서 View를 열어 @API.state 어노테이션과 Release Contract를 먼저 확인하는 것이 좋습니다.

핵심 개념 정리

SAP 자재 마스터는 데이터 적용 범위(Organizational Level)에 따라 여러 테이블로 분리됩니다. 흔히 사용하는 비유는 "회사 인사기록"입니다. 사번·이름·생년월일 같은 공통 정보는 본사에서 관리하고(MARA = 클라이언트), 어느 지점에서 어떤 업무를 하는지는 지점별로 따로 보관됩니다(MARC = 플랜트). 같은 자재 코드라도 플랜트 1010에서는 사내 생산품이고 플랜트 2010에서는 외주 구매품일 수 있는 이유가 여기에 있습니다.

MARC 테이블은 다음과 같은 영역의 데이터를 담습니다.

  • MRP 영역: DISMM(MRP Type), DISPO(MRP Controller), DISLS(Lot Size), MINBE(Reorder Point), EISBE(Safety Stock)
  • 구매/조달 영역: EKGRP(Purchasing Group), BESKZ(Procurement Type), SOBSL(Special Procurement Type)
  • 생산 영역: FEVOR(Production Scheduler), FHORI(Floats), STRGR(Strategy Group)
  • 창고/재고 영역: LGPRO(Issue Storage Location), LGFSB(External Procurement Storage Location)

I_MaterialPlant는 이 MARC 데이터를 의미 있는 영문 필드명으로 노출한 CDS Basic View입니다. 예를 들어 MARC-MATNR은 Material, MARC-WERKS는 Plant, MARC-DISMM은 MRPType으로 매핑됩니다. 또한 Association을 통해 _Material(상위 자재 일반 데이터), _Plant(플랜트 마스터), _MRPController(MRP 담당자) 같은 연관 엔티티에 즉시 접근할 수 있어, 과거 JOIN을 직접 작성하던 코드를 훨씬 간결하게 다시 쓸 수 있습니다.

비유: MARC를 "지점별 자재 카드"로 보고, I_MaterialPlant는 그 카드를 깨끗하게 정리해 색인을 붙인 디지털 카탈로그라고 생각하면 이해가 쉽습니다.

1단계 — MARC 핵심 필드 기본 조회

먼저 단일 플랜트의 자재 목록과 MRP 기본 정보를 조회하는 예제입니다. 시나리오는 "플랜트 1010에서 MRP Type이 'PD'(MRP)인 자재만 추출"입니다.

REPORT zr_mrp_basic_lookup.

DATA: lt_plant_materials TYPE TABLE OF i_materialplant.

SELECT material,
       plant,
       mrptype,
       mrpcontroller,
       lotsizingprocedure,
       reorderquantity,
       safetystockquantity
  FROM i_materialplant
  WHERE plant   = '1010'
    AND mrptype = 'PD'
  INTO TABLE @lt_plant_materials
  UP TO 50 ROWS.

LOOP AT lt_plant_materials INTO DATA(ls_row).
  WRITE: / ls_row-material,
           ls_row-plant,
           ls_row-mrptype,
           ls_row-mrpcontroller,
           ls_row-reorderquantity.
ENDLOOP.

이 코드의 핵심은 plantmrptype으로 필터를 거는 부분입니다. MARC의 기본키는 MANDT + MATNR + WERKS이므로, WHERE 절에 Plant를 명시하면 인덱스가 효과적으로 동작합니다.

2단계 — 재주문점 분석과 예외 로깅

실무에서는 MRP 설정이 잘못된 자재를 가려내는 일이 빈번합니다. 예를 들어 "MRP Type이 VB(Reorder Point Planning)인데 Reorder Point가 0인 자재"는 결국 자동 발주가 일어나지 않습니다. 이런 이상 데이터를 식별하고 Application Log에 기록하는 예제입니다.

CLASS zcl_mrp_audit DEFINITION PUBLIC FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    METHODS run IMPORTING iv_plant TYPE werks_d.
  PRIVATE SECTION.
    METHODS log_issue IMPORTING iv_material TYPE matnr
                                iv_reason   TYPE string.
ENDCLASS.

CLASS zcl_mrp_audit IMPLEMENTATION.
  METHOD run.
    SELECT material, plant, mrptype, reorderquantity,
           safetystockquantity, lotsizingprocedure
      FROM i_materialplant
      WHERE plant   = @iv_plant
        AND mrptype IN ( 'VB', 'V1', 'V2' )
      INTO TABLE @DATA(lt_data).

    LOOP AT lt_data INTO DATA(ls_data).
      IF ls_data-reorderquantity IS INITIAL.
        log_issue( iv_material = ls_data-material
                   iv_reason   = |Reorder Point missing for MRP Type { ls_data-mrptype }| ).
      ELSEIF ls_data-safetystockquantity > ls_data-reorderquantity.
        log_issue( iv_material = ls_data-material
                   iv_reason   = |Safety stock exceeds reorder point| ).
      ENDIF.
    ENDLOOP.
  ENDMETHOD.

  METHOD log_issue.
    cl_demo_output=>write( |Material { iv_material }: { iv_reason }| ).
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.
  NEW zcl_mrp_audit( )->run( iv_plant = '1010' ).

이 단계에서 중요한 점은 (1) MRP Type을 IN 절로 그룹화해 한 번에 처리하고, (2) 비즈니스 규칙 위반을 로깅 가능한 구조로 분리한 것입니다. 운영 환경에서는 cl_demo_output 대신 BAL(Business Application Log)을 사용해 SLG1에서 추적할 수 있도록 구현합니다.

3단계 — Association 기반 프로덕션 조회 패턴

다음은 OData 서비스나 Fiori 앱의 백엔드로 쓰일 수준의 Consumption View 예제입니다. I_MaterialPlant의 Association을 활용해 자재 텍스트, 기본 단위, 플랜트명까지 한 번에 조립합니다.

@AccessControl.authorizationCheck: #CHECK
@Metadata.allowExtensions: true
@EndUserText.label: 'Plant Material with MRP Insight'
define view entity ZC_PlantMaterialMRP
  as select from I_MaterialPlant as MatPlant
  association [0..1] to I_Material      as _Mat   on _Mat.Material  = MatPlant.Material
  association [0..1] to I_Plant         as _Plnt  on _Plnt.Plant    = MatPlant.Plant
{
  key MatPlant.Material,
  key MatPlant.Plant,
      _Plnt.PlantName,
      MatPlant.MRPType,
      MatPlant.MRPController,
      MatPlant.LotSizingProcedure,
      MatPlant.ReorderQuantity,
      MatPlant.SafetyStockQuantity,
      MatPlant.MinimumLotSizeQuantity,
      MatPlant.MaximumLotSizeQuantity,
      _Mat.BaseUnit,
      _Mat.MaterialGroup,
      case when MatPlant.SafetyStockQuantity = 0
            and MatPlant.MRPType <> 'ND'
           then 'X' else '' end as IsRiskyConfig,
      _Mat,
      _Plnt
}
where MatPlant.MRPType <> 'ND'
METHOD get_risky_materials.
  DATA(lv_offset) = iv_page * 100.

  SELECT material, plant, plantname, mrptype, mrpcontroller,
         reorderquantity, safetystockquantity, isriskyconfig
    FROM zc_plantmaterialmrp
    WHERE plant         IN @it_plant_range
      AND isriskyconfig = 'X'
    ORDER BY plant, material
    INTO TABLE @rt_result
    OFFSET @lv_offset
    UP TO 100 ROWS.
ENDMETHOD.

프로덕션 관점에서 중요한 세 가지는 (1) @AccessControl.authorizationCheck: #CHECK로 DCL 기반 권한을 강제, (2) OFFSET/UP TO로 페이징 처리, (3) Association 노출로 UI에서 추가 호출 없이 데이터 확장이 가능하도록 한 점입니다.

현장에서 자주 마주치는 함정과 대처

Q1. I_MaterialPlant를 조회했는데 일부 자재가 누락됩니다.

가장 흔한 원인은 해당 자재가 그 플랜트에 확장(Extend)되지 않은 경우입니다. MM01/MMSC에서 플랜트 뷰가 생성되지 않으면 MARC에 레코드 자체가 없습니다. 또한 삭제 플래그(MARC-LVORM, CDS의 DeletionIndicator)가 설정된 자재는 기본적으로 노출되지만 비즈니스 로직에서 제외해야 합니다. 조회 시 WHERE DeletionIndicator = ''를 명시적으로 추가하는 것을 권장합니다.

Q2. MARC를 직접 SELECT하던 기존 코드를 I_MaterialPlant로 교체해도 되나요?

대부분의 경우 가능하지만 두 가지를 확인해야 합니다. 첫째, MARC에는 있지만 CDS View가 노출하지 않는 필드(예: 고객 추가 필드)가 있을 수 있습니다. 이 경우 Extension View(I_MaterialPlant_E 패턴) 또는 자체 CDS Extend를 만들어야 합니다. 둘째, MATNR 변환 — MARC의 MATNR은 내부 포맷(40자)이며 CDS는 Material로 그대로 노출하지만, 외부 입력은 CONVERSION_EXIT_MATN1_INPUT을 거쳐야 안전합니다.

Q3. MRP Type 변경 이력은 어디서 보나요?

I_MaterialPlant는 현재 상태만 보여줍니다. 이력은 CDPOS/CDHDR 테이블에 저장되며, I_ChangeDocumentItem 같은 CDS를 통해 조회하거나 MM04 트랜잭션으로 확인합니다. 변경 추적이 비활성화된 경우 SCDO에서 객체 클래스 MATERIAL의 설정을 점검해야 합니다.

Q4. 대량 플랜트를 조회하면 성능이 떨어집니다.

WHERE 절에 plant 없이 material만 주면 클라이언트 전체를 스캔할 수 있습니다. 가능하면 Plant Range를 먼저 좁히고, 필요시 Hint나 SQL Trace(ST05)로 실행 계획을 확인합니다. 또한 SELECT 시 필요한 컬럼만 지정해 HANA 컬럼 스토어의 장점을 살리는 것이 일반적으로 더 빠릅니다.

이어서 살펴볼 만한 주제

  • I_ProductPlantBasic, I_ProductPlantProcurement — S/4HANA의 Product 모델 기반 뷰로 마이그레이션
  • I_MRPElement, I_MaterialStock — MRP 실행 결과와 현재고 분석
  • RAP(ABAP RESTful Application Programming Model)로 자재 마스터 관리 앱 구축
  • Embedded Analytics를 통한 플랜트별 MRP KPI 대시보드 구성
  • Behavior Definition에 Validation을 추가해 MRP 설정 오류 사전 차단

더 깊이 파고들 때 도움이 되는 자료

  • SAP S/4HANA On-Premise 도큐멘테이션 허브
  • SAP S/4HANA Cloud — CDS Released Views 카탈로그
  • ABAP Platform — ABAP CDS Development Guide
  • SAP API Business Hub — Material/Plant 관련 OData 서비스
  • SAP Community Blogs — Material Master & MRP

댓글 0

아직 댓글이 없습니다.