I_Stock이란 무엇인가 — S/4HANA 재고 관리의 통합 뷰
I_Stock은 SAP S/4HANA의 Virtual Data Model(VDM) 계층에 속하는 Basic Interface CDS View로, 자재(Material)·플랜트(Plant)·저장위치(Storage Location)·배치(Batch)·특별재고(Special Stock) 단위로 산재되어 있던 재고 정보를 단일 인터페이스로 노출하는 역할을 합니다. 과거 ECC 시대에 MARD, MCHB, MSKA, MSLB, MKOL 등 다수의 재고 테이블을 조합해야 얻을 수 있었던 결과를 하나의 CDS 모델로 추상화한 것이 특징입니다.
이 글은 다음 항목을 중심으로 진행됩니다.
- I_Stock의 도입 배경과 VDM 계층에서의 위치
- 핵심 필드 구조와 재고 유형(Stock Type) 매핑 규칙
- ABAP Open SQL로 통합 재고를 조회·집계하는 실전 예제
- Association을 활용한 플랜트·자재 마스터 결합 패턴
- 운영 환경에서의 성능 최적화 및 권한·릴리즈 상태 고려사항
읽기 전에 갖추면 좋은 배경
이 글은 ABAP CDS의 기본 문법(@AbapCatalog 어노테이션, define view 구문)을 한 번이라도 다뤄본 개발자를 대상으로 합니다. 또한 SAP MM(자재관리) 모듈의 재고 개념 — 가용재고(Unrestricted), 품질검사재고(Quality Inspection), 제한재고(Blocked) — 을 알고 있으면 이해가 수월합니다. ADT(ABAP Development Tools in Eclipse)에서 View 정의를 열람할 수 있는 환경이 권장됩니다.
실습 환경과 버전 요구사항
I_Stock View는 S/4HANA 도입 이후 점진적으로 확장되어 왔으며, 릴리즈에 따라 노출되는 필드와 연관 View 구성이 달라질 수 있습니다. 이 글에서 다루는 예제는 일반적으로 다음 환경에서 동작합니다.
| 항목 | 권장 버전 / 도구 |
| SAP S/4HANA | 2020 이상 (On-Premise) 또는 최신 Cloud 릴리즈 |
| ABAP Platform | 7.55 이상 (CDS Amendment, Table Function 지원) |
| 개발 도구 | ADT (Eclipse Photon 이상) |
| HANA DB | SPS 04 이상 권장 |
| 권한 오브젝트 | S_RS_AUTH, M_MATE_WRK, S_DEVELOP |
릴리즈 노트와 API Hub에서 I_Stock의 C1/Released 상태를 먼저 확인한 뒤 사용하는 것이 안전합니다. Cloud 환경에서는 Released 상태가 아닌 객체에 대한 접근이 제한되므로 개발 전 반드시 체크해야 합니다.
핵심 개념 — 흩어진 재고 테이블을 하나의 창으로 묶다
전통적인 재고 조회 방식은 마치 여러 개의 창고 장부를 각각 열어 합산하는 작업과 비슷했습니다. 자재의 일반 재고는 MARD, 배치 단위 재고는 MCHB, 판매오더 재고(Sales Order Stock)는 MSKA, 프로젝트 재고는 MSPR, 고객 컨사인먼트 재고는 MSKU, 공급업체 컨사인먼트는 MKOL에 분산 저장됩니다. 각 테이블마다 키 구조와 수량 필드명이 미묘하게 달라 UNION ALL을 직접 작성하면 코드가 길고 유지보수가 어렵습니다.
I_Stock은 이 모든 테이블을 내부적으로 결합·표준화하여 하나의 행 구조로 노출합니다. 핵심 개념은 다음과 같습니다.
- VDM 계층 분류:
I_*접두사는 Basic Interface View로, 비즈니스 객체의 정규화된 표현을 제공합니다. 상위에는C_*(Consumption) View가 위치합니다. - Stock Type: 가용·품질검사·제한 등 재고 상태가 하나의 필드(
InventoryStockType)로 통합됩니다. - Special Stock Indicator: 판매오더·프로젝트·컨사인먼트 등 특별재고 구분이
InventorySpecialStockType으로 표시됩니다. - Association:
_Material,_Plant,_StorageLocation같은 경로로 마스터 데이터를 lazy하게 연결합니다. - 측정 단위 표준화: 기본 단위(Base Unit of Measure)로 수량을 노출해 단위 변환 부담을 줄입니다.
구조적으로는 다음과 같은 흐름으로 이해할 수 있습니다.
[MARD, MCHB, MSKA, MSPR, MKOL, MSKU, ...]
│
▼ (UNION + 표준화)
I_StockBase (내부)
│
▼ (Released Interface)
I_Stock
│
▼ (Annotation / UI 노출)
C_StockBalanceCube 등 소비 계층
실전 코드 1단계 — 단일 플랜트의 기본 재고 조회
가장 단순한 형태로, 특정 플랜트의 모든 자재 재고를 조회하는 예제입니다. 시나리오는 "서울 물류센터(플랜트 1010)의 현재 가용재고 합계를 자재별로 본다"입니다.
REPORT zr_inv_basic_lookup.
DATA: lt_inv_rows TYPE TABLE OF i_stock,
lv_plant TYPE werks_d VALUE '1010'.
SELECT material,
plant,
storagelocation,
inventorystocktype,
matlwrhsstkqtyinmatlbaseunit AS warehouse_qty,
matlbaseunit
FROM i_stock
WHERE plant = @lv_plant
AND inventorystocktype = '01' "01 = Unrestricted-Use
INTO TABLE @DATA(lt_unrestricted).
LOOP AT lt_unrestricted INTO DATA(ls_row).
WRITE: / ls_row-material,
ls_row-storagelocation,
ls_row-warehouse_qty,
ls_row-matlbaseunit.
ENDLOOP.
여기서 InventoryStockType = '01'은 가용재고를 의미합니다. '02'는 품질검사재고, '03'은 제한재고에 대응하는 것이 일반적입니다. 코드 한 줄로 과거 MARD-LABST, MCHB-CLABS를 따로 조회하던 작업이 통합됩니다.
실전 코드 2단계 — 다중 재고 유형 통합 집계 및 예외 처리
실무에서는 "특정 자재 그룹에 대해 가용·품질검사·제한재고를 한 번에 조회하고, 0이 아닌 라인만 보고하라"는 요구가 자주 나옵니다. 또한 트랜잭션 실패 시 로깅이 필요합니다.
CLASS zcl_inv_consolidator DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES: BEGIN OF ty_inv_summary,
material TYPE matnr,
plant TYPE werks_d,
stock_type TYPE c LENGTH 2,
total_qty TYPE p LENGTH 13 DECIMALS 3,
base_uom TYPE meins,
END OF ty_inv_summary,
tt_inv_summary TYPE TABLE OF ty_inv_summary.
METHODS get_consolidated_stock
IMPORTING iv_plant_from TYPE werks_d
iv_plant_to TYPE werks_d
it_mat_group TYPE RANGE OF matkl
RETURNING VALUE(rt_result) TYPE tt_inv_summary
RAISING cx_sy_open_sql_db.
ENDCLASS.
CLASS zcl_inv_consolidator IMPLEMENTATION.
METHOD get_consolidated_stock.
TRY.
SELECT stk~material,
stk~plant,
stk~inventorystocktype AS stock_type,
SUM( stk~matlwrhsstkqtyinmatlbaseunit ) AS total_qty,
stk~matlbaseunit AS base_uom
FROM i_stock AS stk
INNER JOIN i_product AS prod
ON stk~material = prod~product
WHERE stk~plant BETWEEN @iv_plant_from AND @iv_plant_to
AND stk~inventorystocktype IN ( '01', '02', '03' )
AND prod~productgroup IN @it_mat_group
GROUP BY stk~material, stk~plant,
stk~inventorystocktype, stk~matlbaseunit
HAVING SUM( stk~matlwrhsstkqtyinmatlbaseunit ) <> 0
INTO TABLE @rt_result.
CATCH cx_sy_open_sql_db INTO DATA(lx_db).
MESSAGE lx_db->get_text( ) TYPE 'E'.
RAISE EXCEPTION lx_db.
ENDTRY.
ENDMETHOD.
ENDCLASS.
핵심 포인트는 다음과 같습니다.
SUM+GROUP BY로 자재·플랜트·재고유형 단위 집계HAVING으로 0 수량 라인 제거 → 결과 데이터셋 축소i_product와의 INNER JOIN으로 자재 그룹 필터링- SQL 예외를
cx_sy_open_sql_db로 명시적 캐치하여 상위로 재전파
실전 코드 3단계 — Association·페이징·권한 분리 적용
프로덕션 환경에서는 단순 SELECT를 넘어 Association을 활용한 lazy join, 페이징(Offset/Top), Access Control(DCL)을 함께 고려해야 합니다. 다음은 위 두 단계를 발전시킨 Custom CDS View 정의 예시입니다.
@AbapCatalog.viewEnhancementCategory: [#NONE]
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Consolidated Plant Stock View'
@Metadata.ignorePropagatedAnnotations: true
define view entity ZI_PLANT_STOCK_SNAPSHOT
as select from I_Stock as Stk
association [0..1] to I_Plant as _Plant
on $projection.Plant = _Plant.Plant
association [0..1] to I_Product as _Material
on $projection.Material = _Material.Product
{
key Stk.Material,
key Stk.Plant,
key Stk.StorageLocation,
key Stk.InventoryStockType,
Stk.InventorySpecialStockType,
@Semantics.quantity.unitOfMeasure: 'MatlBaseUnit'
Stk.MatlWrhsStkQtyInMatlBaseUnit as WarehouseQty,
Stk.MatlBaseUnit,
_Material.ProductGroup as MaterialGroup,
_Plant.PlantName as PlantName,
_Material,
_Plant
}
where Stk.InventoryStockType in ('01','02','03')
이 View를 ABAP에서 호출할 때는 페이징과 권한 분리를 함께 적용합니다.
DATA(lv_page_size) = 500.
DATA(lv_page_offset) = 0.
DO.
SELECT material, plant, storagelocation,
inventorystocktype, warehouseqty, matlbaseunit,
plantname, materialgroup
FROM zi_plant_stock_snapshot
WHERE plant = @CONV werks_d( '1010' )
ORDER BY material, storagelocation
INTO TABLE @DATA(lt_page)
OFFSET @lv_page_offset
UP TO @lv_page_size ROWS.
IF lt_page IS INITIAL.
EXIT.
ENDIF.
" 비즈니스 로직 처리 (예: ALV 출력, BAdI 호출 등)
PERFORM process_inv_chunk USING lt_page.
lv_page_offset = lv_page_offset + lv_page_size.
ENDDO.
프로덕션 적용 시 점검할 항목은 다음과 같습니다.
@AccessControl.authorizationCheck: #CHECK— DCL 통한 행 단위 권한 적용 권장- OFFSET/UP TO 페이징 — 대용량 자재 마스터에서 메모리 폭주 방지
- HANA Plan Visualizer로 실행 계획 확인 후 인덱스/Annotation 튜닝
- Released API 여부 확인 — Cloud 환경 호환성 보장
자주 마주치는 함정과 해결 팁
Q1. I_Stock에 분명히 자재가 있는데 수량이 0으로 나옵니다.
재고 유형(InventoryStockType)을 지정하지 않고 합산하면 가용·품질검사·제한이 각각 다른 행으로 들어와 의도와 다르게 계산될 수 있습니다. WHERE 조건과 GROUP BY에 명시적으로 재고 유형을 포함하는 것이 안전합니다.
Q2. MARD 직접 SELECT보다 I_Stock이 느린 경우가 있습니다.
I_Stock은 내부적으로 여러 테이블을 UNION하므로, 단일 테이블 조회 대비 오버헤드가 발생할 수 있습니다. 특정 재고 유형만 필요하다면 WHERE 조건에 InventoryStockType, InventorySpecialStockType을 가능한 한 빨리 지정해 풀스캔을 막아야 합니다. 또한 자재·플랜트 키를 함께 거는 것이 일반적으로 권장됩니다.
Q3. 컨사인먼트 재고가 보이지 않습니다.
공급업체 컨사인먼트는 InventorySpecialStockType = 'K'로 분류되며, 기본 SELECT에서 이 조건을 누락하면 결과에 포함되지 않습니다. 시나리오에 따라 특별재고 indicator 범위를 명확히 지정해야 합니다.
이 외에도 배치 관리 자재의 경우 동일 자재가 배치별로 여러 행으로 나뉘므로, 보고서 설계 시 배치 합산 여부를 사전에 결정해야 합니다. 단위 변환이 필요한 경우 UNIT_CONVERSION SQL 함수 또는 MD_CONVERT_MATERIAL_UNIT 모듈 호출을 조합하는 패턴이 자주 쓰입니다.
이후 살펴보면 좋은 주제
I_Stock의 동작을 이해했다면, 다음 주제로 확장하면서 재고 도메인 모델 전반을 더 깊게 다룰 수 있습니다.
I_MaterialStock,I_MaterialStockTimeSeries등 파생 View와의 차이- C_StockBalanceCube와 Fiori "Stock Single Material" 앱 연동
- Released ODATA Service
API_MATERIAL_STOCK_SRV로의 외부 노출 - CDS Table Function으로 사용자 정의 재고 계산 로직 삽입
- BTP 환경에서 ABAP RAP 기반 Custom Inventory Application 개발
참고 링크 모음
댓글 0
아직 댓글이 없습니다.