ABAP

아직도 MKPF JOIN? CDS로 자재 이동 3가지 #shorts #SAP #ABAP

I_GoodsMovement 뷰란 무엇인가

SAP S/4HANA에서 자재 이동(Goods Movement)은 입고·출고·전송·재고 조정 등 물류 흐름의 근간을 이루는 트랜잭션입니다. 전통적으로 MM 모듈 개발자는 MKPF(자재 문서 헤더)와 MSEG(자재 문서 아이템)를 직접 JOIN하여 데이터를 조회해 왔지만, S/4HANA 도입 이후 SAP은 가상 데이터 모델(VDM, Virtual Data Model) 기반의 CDS 뷰를 권장하고 있습니다. I_GoodsMovement는 그 핵심 인터페이스 뷰 중 하나로, MKPF/MSEG의 원시 데이터를 의미 있는 비즈니스 엔티티 형태로 노출합니다.

이 뷰는 SAP Note 및 ABAP 개발 가이드에서 "Interface View"로 분류되며, 접두사 I_가 의미하듯 다른 Composite/Consumption 뷰의 기반이 되는 재사용 가능한 계층입니다. RAP(ABAP RESTful Application Programming Model), CAP, OData Fiori 앱, Analytic 시나리오 등에서 자재 이동 정보가 필요할 때 일관된 단일 진입점 역할을 합니다.

이 글의 핵심 포인트는 다음과 같습니다.

  • MKPF/MSEG의 물리 구조와 I_GoodsMovement의 의미 매핑 이해
  • CDS 기반 자재 이동 데이터 조회 패턴 습득
  • 이동 유형(Movement Type) 필터링 및 집계 처리
  • BAPI 트랜잭션과 CDS 조회를 연계하는 실무 패턴
  • 대용량 환경에서의 성능 함정과 회피 전략

알고 있어야 할 배경 지식

이 글을 효과적으로 따라가려면 ABAP 7.50 이상 OpenSQL 문법, CDS View 기본 문법(define view, association), MM 모듈의 자재 문서 개념(자재 문서 번호 MBLNR, 회계연도 MJAHR, 아이템 ZEILE), 그리고 이동 유형(예: 101 입고, 261 출고, 311 전송)에 대한 기본 이해가 필요합니다. S/4HANA 1909 이상 환경을 기준으로 설명합니다.

실습 환경 및 준비물

실전 예제는 다음 환경에서 검증되었습니다.

  • SAP S/4HANA 2022 (FPS01) 또는 2023 — Cloud Private Edition / On-Premise 모두 호환
  • ABAP Development Tools (ADT) for Eclipse 2024-03 이상
  • 패키지 권한: MM-IM 영역의 CDS 뷰 조회 권한, S_TABU_DIS 권한 객체에 MKPF/MSEG 테이블 권한
  • 샘플 데이터: MIGO 또는 MB1A 트랜잭션으로 생성된 자재 문서 다수 (이동 유형 101, 201, 261, 311 혼합)
  • 분석 클라이언트: Eclipse Data Preview, Fiori "View Browser" 앱(F2170), 또는 SAP HANA Studio

S/4HANA Cloud Public Edition에서는 직접 MSEG/MKPF 테이블 접근이 제한되므로, 반드시 Released API 등급의 CDS 뷰(예: I_MaterialDocumentItem, I_MaterialDocumentHeader)를 활용해야 합니다. 본 글에서는 On-Premise/Private Edition 관점에서 I_GoodsMovement 계열 뷰를 다룹니다.

MKPF와 MSEG — 자재 이동 문서의 물리적 구조

자재 이동 문서는 회계 전표와 유사한 헤더-아이템 구조를 가집니다. MKPF는 헤더 정보(문서 번호, 전기일, 사용자, 참조 문서 등)를 담고, MSEG는 실제 이동 라인(자재, 수량, 플랜트, 저장위치, 이동 유형 등)을 담습니다. 두 테이블은 MANDT(클라이언트), MBLNR(자재 문서 번호), MJAHR(회계연도)로 1:N 관계를 이룹니다.

비유하자면 MKPF는 "택배 송장의 표지"이고, MSEG는 "박스 안 품목 목록"입니다. 표지 한 장에 여러 박스 라인이 매달려 있는 구조입니다.

테이블주요 칼럼의미
MKPFMBLNR, MJAHR, BUDAT, BLDAT, USNAM, XBLNR문서번호, 회계연도, 전기일, 문서일, 사용자, 외부참조번호
MSEGZEILE, BWART, MATNR, WERKS, LGORT, MENGE, MEINS아이템번호, 이동유형, 자재, 플랜트, 저장위치, 수량, 단위

S/4HANA의 단순화(Simplification)로 MSEG의 일부 칼럼은 ME(Migration Engine)에서 재계산되거나 인덱스가 재편되었고, MATDOC라는 단일 통합 테이블이 도입되어 내부적으로는 MATDOC가 정답 데이터로 사용됩니다. MKPF/MSEG는 호환성을 위해 CDS View로 재구성되었으며, I_GoodsMovement는 이 통합된 모델 위에 의미 계층을 입힌 결과물입니다.

I_GoodsMovement CDS 뷰 내부 구조 분석

S/4HANA의 자재 문서 도메인에서 권장되는 인터페이스 뷰는 I_MaterialDocumentHeader(헤더)와 I_MaterialDocumentItem(아이템)이며, 둘을 결합한 분석 친화적 뷰가 I_GoodsMovement 계열입니다. 일반적인 정의는 다음과 같은 구조를 가집니다.

@AbapCatalog.sqlViewName: 'ZIGOODSMVT'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Goods Movement Interface View'
@VDM.viewType: #COMPOSITE

define view Z_I_GoodsMovement
  as select from I_MaterialDocumentItem as Item
  association [1..1] to I_MaterialDocumentHeader as _Header
    on  Item.MaterialDocument     = _Header.MaterialDocument
    and Item.MaterialDocumentYear = _Header.MaterialDocumentYear
  association [0..1] to I_Material as _Material
    on Item.Material = _Material.Material
  association [0..1] to I_Plant as _Plant
    on Item.Plant = _Plant.Plant
{
  key Item.MaterialDocument,
  key Item.MaterialDocumentYear,
  key Item.MaterialDocumentItem,
      Item.GoodsMovementType,
      Item.Material,
      Item.Plant,
      Item.StorageLocation,
      Item.QuantityInEntryUnit,
      Item.EntryUnit,
      _Header.PostingDate,
      _Header.DocumentDate,
      _Header.CreatedByUser,
      _Header.ReferenceDocument,
      _Header,
      _Material,
      _Plant
}

핵심은 세 가지입니다. 첫째, association을 사용해 헤더·자재 마스터·플랜트 마스터에 lazy join 형태로 연결합니다. 둘째, @VDM.viewType으로 뷰의 역할(BASIC/COMPOSITE/CONSUMPTION)을 선언합니다. 셋째, 칼럼명이 BWART → GoodsMovementType, MATNR → Material처럼 의미 기반 영문명으로 통일됩니다.

기본 SELECT 실전 예제 — 입고/출고 필터링

가장 빈번한 시나리오는 특정 기간의 입고(이동 유형 101) 또는 출고(이동 유형 261, 201) 라인을 조회하는 것입니다. 전통적인 MKPF/MSEG JOIN 대신 CDS 뷰 한 줄로 단순화됩니다.

REPORT z_gm_basic_select.

DATA: lt_movements TYPE TABLE OF z_i_goodsmovement,
      lv_date_from TYPE budat VALUE '20260601',
      lv_date_to   TYPE budat VALUE '20260629'.

SELECT MaterialDocument,
       MaterialDocumentItem,
       GoodsMovementType,
       Material,
       Plant,
       QuantityInEntryUnit,
       EntryUnit,
       PostingDate
  FROM z_i_goodsmovement
  WHERE PostingDate      BETWEEN @lv_date_from AND @lv_date_to
    AND GoodsMovementType IN ('101','201','261','311')
  INTO TABLE @lt_movements.

LOOP AT lt_movements ASSIGNING FIELD-SYMBOL(<fs_mvt>).
  WRITE: / <fs_mvt>-MaterialDocument,
           <fs_mvt>-GoodsMovementType,
           <fs_mvt>-Material,
           <fs_mvt>-QuantityInEntryUnit,
           <fs_mvt>-EntryUnit.
ENDLOOP.

여기서 주목할 점은 @ 호스트 변수 표기와, 칼럼명에 의미 있는 영문 식별자가 사용된다는 점입니다. 별도의 JOIN 없이도 헤더 칼럼(PostingDate)에 바로 접근할 수 있는 이유는 I_GoodsMovement 정의 안에서 이미 헤더가 평탄화되어 있기 때문입니다.

이동 유형(Movement Type)별 데이터 분류

이동 유형은 자재 흐름의 의미를 결정하는 코드입니다. 101은 구매발주 기준 입고, 102는 그 취소, 261은 생산오더 기준 출고, 311은 저장위치 간 전송 등입니다. 운영 보고서에서는 보통 이를 "입고 그룹"과 "출고 그룹"으로 묶어 표현합니다.

SELECT Plant,
       CASE
         WHEN GoodsMovementType IN ('101','103','105','501') THEN 'INBOUND'
         WHEN GoodsMovementType IN ('201','261','551','601') THEN 'OUTBOUND'
         WHEN GoodsMovementType IN ('311','313','315')       THEN 'TRANSFER'
         ELSE 'OTHER'
       END                                       AS MovementCategory,
       SUM( QuantityInEntryUnit )                AS TotalQty,
       COUNT( DISTINCT MaterialDocument )        AS DocumentCount
  FROM z_i_goodsmovement
  WHERE PostingDate BETWEEN @lv_date_from AND @lv_date_to
  GROUP BY Plant,
           CASE
             WHEN GoodsMovementType IN ('101','103','105','501') THEN 'INBOUND'
             WHEN GoodsMovementType IN ('201','261','551','601') THEN 'OUTBOUND'
             WHEN GoodsMovementType IN ('311','313','315')       THEN 'TRANSFER'
             ELSE 'OTHER'
           END
  INTO TABLE @DATA(lt_summary).

실무에서는 위 CASE 식을 CDS 뷰 안에 캡슐화해 MovementCategory 칼럼으로 제공하는 것이 권장됩니다. 여러 보고서가 동일한 분류 규칙을 재사용할 수 있고, 비즈니스 변경 시 한 곳만 수정하면 됩니다.

플랜트·창고 기반 집계 쿼리 예제

운영 환경에서 자주 요청되는 보고서는 "이번 달 플랜트별·저장위치별 이동 수량 합계"입니다. CDS의 association을 활용하면 자재 마스터의 자재 그룹(MATKL)이나 플랜트 마스터의 회사코드까지 함께 조회할 수 있습니다.

SELECT gm~Plant,
       gm~StorageLocation,
       gm~\_Material-MaterialGroup AS MaterialGroup,
       gm~GoodsMovementType,
       SUM( gm~QuantityInEntryUnit ) AS Qty
  FROM z_i_goodsmovement AS gm
  WHERE gm~PostingDate BETWEEN @lv_date_from AND @lv_date_to
    AND gm~Plant       IN @lr_plant
  GROUP BY gm~Plant,
           gm~StorageLocation,
           gm~\_Material-MaterialGroup,
           gm~GoodsMovementType
  ORDER BY gm~Plant, gm~StorageLocation
  INTO TABLE @DATA(lt_agg).

gm~\_Material-MaterialGroup은 association을 통한 path expression입니다. 내부적으로는 LEFT OUTER JOIN으로 변환되며, HANA 옵티마이저가 효율적으로 처리합니다. 다만 path expression은 조인 비용을 유발하므로 꼭 필요한 칼럼만 선택하는 것이 일반적으로 권장됩니다.

BAPI_GOODSMVT_CREATE와의 연계 패턴

CDS 뷰가 "읽기" 측을 담당한다면, 자재 이동의 "쓰기"는 여전히 BAPI_GOODSMVT_CREATE가 표준입니다. 통합 시나리오로는 다음 패턴이 자주 사용됩니다. 외부 시스템에서 입고 요청이 들어오면 BAPI로 자재 문서를 생성한 뒤, 즉시 I_GoodsMovement를 다시 조회하여 결과를 검증·통보하는 흐름입니다.

DATA: ls_header   TYPE bapi2017_gm_head_01,
      ls_code     TYPE bapi2017_gm_code,
      lt_items    TYPE TABLE OF bapi2017_gm_item_create,
      ls_item     TYPE bapi2017_gm_item_create,
      lv_mat_doc  TYPE bapi2017_gm_head_ret-mat_doc,
      lv_doc_year TYPE bapi2017_gm_head_ret-doc_year,
      lt_return   TYPE TABLE OF bapiret2.

ls_header-pstng_date = sy-datum.
ls_header-doc_date   = sy-datum.
ls_code-gm_code      = '01'.

ls_item-material  = 'FG-12345'.
ls_item-plant     = '1010'.
ls_item-stge_loc  = '101A'.
ls_item-move_type = '101'.
ls_item-entry_qnt = '50'.
ls_item-entry_uom = 'PC'.
ls_item-po_number = '4500001234'.
ls_item-po_item   = '00010'.
APPEND ls_item TO lt_items.

CALL FUNCTION 'BAPI_GOODSMVT_CREATE'
  EXPORTING  goodsmvt_header  = ls_header
             goodsmvt_code    = ls_code
  IMPORTING  materialdocument = lv_mat_doc
             matdocumentyear  = lv_doc_year
  TABLES     goodsmvt_item    = lt_items
             return           = lt_return.

IF line_exists( lt_return[ type = 'E' ] ).
  CALL FUNCTION 'BAPI_TRANSACTION_ROLLBACK'.
ELSE.
  CALL FUNCTION 'BAPI_TRANSACTION_COMMIT' EXPORTING wait = 'X'.

  SELECT SINGLE MaterialDocument, GoodsMovementType,
                QuantityInEntryUnit, PostingDate
    FROM z_i_goodsmovement
    WHERE MaterialDocument     = @lv_mat_doc
      AND MaterialDocumentYear = @lv_doc_year
    INTO @DATA(ls_check).

  WRITE: / 'Created:', ls_check-MaterialDocument,
           'Qty:', ls_check-QuantityInEntryUnit.
ENDIF.

이 패턴은 RAP의 Unmanaged 구현이나 OData Function Import에서도 동일하게 적용됩니다. BAPI 실행 직후 즉시 SELECT하더라도 COMMIT WORK가 완료된 이후라면 CDS 뷰에서 정상적으로 조회됩니다.

흔한 실수와 트러블슈팅

Q1. SELECT가 매우 느린데 원인이 무엇인가요? 가장 흔한 원인은 PostingDate 범위 없이 전체 기간을 스캔하는 것입니다. MATDOC 테이블은 대용량이므로 반드시 BUDAT(PostingDate) 또는 MBLNR 범위를 함께 지정해야 합니다. path expression을 남발하면 불필요한 OUTER JOIN이 발생합니다. 필요한 칼럼만 명시적으로 SELECT하세요.

Q2. BAPI로 생성한 직후 CDS 조회 결과에 데이터가 안 보입니다. COMMIT WORK 누락이 99%의 원인입니다. BAPI_TRANSACTION_COMMITWAIT = 'X'를 반드시 전달해야 후속 SELECT에서 일관성 있는 결과가 나옵니다.

Q3. 취소 문서(예: 102)도 동일하게 합산되어 수량이 부풀려집니다. 이동 유형 101의 취소는 102이며, QuantityInEntryUnit가 양수로 저장되지만 비즈니스 의미상 차감이어야 합니다. DebitCreditCode(SHKZG) 필드를 활용해 부호를 조정한 가상 칼럼을 CDS 안에 정의하는 것이 권장됩니다.

Q4. Cloud 환경에서 Z 뷰가 활성화되지 않습니다. S/4HANA Cloud Public Edition은 Released API 등급의 객체만 사용할 수 있습니다. I_GoodsMovement가 Released 상태인지, 의존하는 모든 association 대상도 Released인지 ABAP Released Object 카탈로그에서 확인해야 합니다.

운영 시 주의사항과 성능 고려 사항

대량 조회 시 BUDAT, WERKS, BWART 칼럼 조합으로 필터를 거는 것이 일반적으로 효율적입니다. MATDOC 테이블은 이들 칼럼에 대한 보조 인덱스가 사전 구성되어 있어 HANA 인메모리 처리에서도 파티션 프루닝이 효과적으로 작동합니다. 자주 쓰이는 집계 패턴은 별도 Composite 뷰로 캡슐화하고 @Analytics.dataCategory: #CUBE 또는 #FACT 어노테이션을 부여하면 Analytic Query에서 재사용이 쉬워집니다.

또한 자재 이동 취소 트랜잭션이 발생하면 원 문서와 별도 문서 번호로 새 레코드가 생성되므로, 순수 이동량 집계 시 취소 이동 유형(102, 122, 202 등)을 반드시 차감 처리해야 합니다.

더 읽어볼 자료

  • SAP Help Portal — S/4HANA On-Premise Documentation: MM-IM Goods Movement
  • SAP Help — ABAP CDS Views 개요 및 VDM 설계 가이드
  • SAP API Business Hub — A_MaterialDocumentHeader OData 서비스
  • SAP Community — ABAP 및 CDS Goods Movement 토론 보드
  • SAP Blogs — Inventory Management Simplification in S/4HANA

댓글 0

아직 댓글이 없습니다.