이 글에서 다루는 범위와 도달 목표
실무에서 자재 수량을 다루다 보면 EA(개), BOX(박스), PAL(파레트), KG, G처럼 서로 다른 단위가 한 테이블에 섞여 있습니다. 이때 개발자는 종종 ABAP 측에서 LOOP를 돌리며 환산 비율을 곱하는 코드를 작성합니다. 이 글은 그 패턴을 ABAP SQL의 내장 함수 UNIT_CONVERSION으로 대체하는 방법을 다룹니다. 다음을 마치면 직접 변환 로직을 손에서 떼고 DB 푸시다운으로 처리할 수 있습니다.
- UNIT_CONVERSION의 시그니처와 파라미터 의미 이해
- T006 단위 마스터와 ISO 코드 체계 파악
- SELECT/SUM 안에서 단위 변환을 동시에 처리하는 패턴
- 변환 불가 단위에 대한 에러 모드(
error_handling) 제어 - 전통적인 직접 계산 코드와 성능·가독성 비교
이 글을 읽기 전에 알아두면 좋은 것
ABAP Open SQL의 기본 문법(SELECT ... FROM ... INTO TABLE), 7.55 이상의 새 ABAP SQL 표현식(CAST, COALESCE, 내장 함수 호출 방식), 그리고 SAP의 단위 관리 테이블인 T006, T006A의 존재를 알고 있다면 충분합니다. CDS View 작성 경험이 있다면 같은 함수가 CDS에서도 동일하게 동작한다는 점도 참고하세요.
버전과 사용 환경
UNIT_CONVERSION은 ABAP 7.53 이상에서 ABAP SQL 내장 함수로 도입되었고, ABAP 7.55부터는 표현식(expression) 컨텍스트에서 더 자유롭게 활용됩니다. 본 글의 예제는 다음 환경을 가정합니다.
- SAP S/4HANA 2022 이상 또는 ABAP Platform 2023
- ABAP 언어 버전: Standard ABAP / RAP-ready
- DB: SAP HANA (AnyDB에서도 동작하지만 푸시다운 동작은 HANA에서 가장 유리)
- 개발 도구: ADT(ABAP Development Tools) for Eclipse
- 권한:
S_TABU_DIS로T006조회 가능, 테스트 데이터용 자재 마스터 접근 권한
실습 전에 SE16으로 T006에 본인이 사용할 단위(예: KG, G, TO, L, ML)가 등록되어 있는지, 그리고 T006의 DIMID(차원 ID)가 같은지 확인하세요. 차원이 다르면(예: 질량 vs 부피) 변환이 불가능합니다.
1. 직접 단위 변환 계산이 만드는 부채
가장 흔한 패턴은 다음과 비슷합니다. 입고 트랜잭션 테이블 ZTGR_RECV에서 박스 단위와 개 단위가 섞인 수량을 합산해야 한다고 가정합니다.
" 안티 패턴: ABAP 레이어에서 환산 비율 하드코딩
DATA(lv_total_ea) = 0.
LOOP AT lt_recv ASSIGNING FIELD-SYMBOL(<fs>).
CASE <fs>-msehi.
WHEN 'EA'.
lv_total_ea = lv_total_ea + <fs>-menge.
WHEN 'BOX'.
lv_total_ea = lv_total_ea + <fs>-menge * 12. " 박스당 12개 가정
WHEN 'PAL'.
lv_total_ea = lv_total_ea + <fs>-menge * 480. " 박스당 12, 파레트당 40박스
ENDCASE.
ENDLOOP.
이 코드의 문제는 명확합니다. 환산 비율이 코드에 박혀 있어 마스터 데이터가 바뀌어도 반영되지 않습니다. T006의 ZAEHL(분자) / NENNR(분모) / DECAN(10의 지수)이 이미 갖고 있는 표준 환산식을 무시하는 셈이죠. 게다가 LOOP는 모든 데이터를 애플리케이션 서버로 끌어오기 때문에 데이터가 수십만 건이 되면 네트워크와 메모리에서 모두 손해를 봅니다. 이 글의 핵심 메시지는 단순합니다. "마스터에 이미 있는 정보를 다시 코드로 옮기지 말고, DB에서 끝내라"입니다.
2. SAP 단위 체계와 T006 — 변환의 근거가 되는 마스터
UNIT_CONVERSION이 어떤 비율로 변환하는지는 결국 T006의 값에 달려 있습니다. 핵심 컬럼은 다음과 같습니다.
MSEHI: 내부 단위 키(예:KG,G,L)DIMID: 차원 ID. 같은 차원끼리만 변환 가능(MASS,VOLUME,LENGTH등)ZAEHL/NENNR: 분자/분모(기본 단위 대비 환산 비)EXP10(또는DECAN): 10의 지수 보정ISOCODE: ISO 표준 단위 코드(KGM,GRM,LTR)
비유하자면 T006은 환율 테이블과 같은 역할을 합니다. 통화 변환에서 TCURR가 환율을 보관하듯, 단위 변환에서는 T006이 "기본 단위(SI 단위)에 대한 환산 비"를 들고 있습니다. UNIT_CONVERSION은 출발 단위를 기본 단위로 정규화한 뒤, 다시 도착 단위로 환산하는 2단 합성을 자동으로 수행합니다.
3. UNIT_CONVERSION 함수의 시그니처
기본 형태는 다음과 같습니다.
UNIT_CONVERSION(
quantity = <수량 컬럼 또는 리터럴>,
source_unit = <원본 단위>,
target_unit = <목표 단위>,
client = @sy-mandt, " 옵션, 기본은 현재 클라이언트
error_handling = <오류 처리 모드>,
on_error = <@oref> " 7.55+ 옵션
)
error_handling은 변환이 불가능할 때(차원이 다르거나 단위가 마스터에 없을 때) 어떻게 할지 결정합니다. 값은 일반적으로 'FAIL_ON_ERROR', 'SET_TO_NULL', 'KEEP_UNCONVERTED' 중 선택합니다. 운영성 잡을 짤 때는 SET_TO_NULL로 잡고 후속 단계에서 NULL 행을 따로 처리하는 패턴을 권장합니다.
4. 실전 예제 — 1단계: 입고 수량을 KG으로 일괄 환산
창고팀이 "오늘 입고된 모든 자재를 KG 기준으로 보여달라"고 요청했다고 가정합니다. 원본 테이블 ZTGR_RECV에는 MENGE(수량)와 MSEHI(단위, 예: G, KG, TO)가 들어 있습니다.
SELECT
matnr,
menge AS original_qty,
msehi AS original_uom,
UNIT_CONVERSION(
quantity = menge,
source_unit = msehi,
target_unit = CAST( 'KG' AS msehi ),
error_handling = 'SET_TO_NULL'
) AS qty_in_kg
FROM ztgr_recv
WHERE recv_date = @sy-datum
INTO TABLE @DATA(lt_recv_kg).
여기서 두 가지에 주목하세요. 첫째, target_unit에는 단위 키를 msehi 타입으로 캐스팅해야 합니다. 리터럴을 그냥 넣으면 타입 불일치 오류가 납니다. 둘째, SET_TO_NULL 덕분에 차원이 안 맞는 행(예: 부피 단위가 섞여 있을 때)은 결과가 NULL로 떨어지고, 다른 행은 정상 계산됩니다.
5. 실전 예제 — 2단계: SUM과 결합한 자재별 KG 집계
이번에는 자재별로 합계를 내야 합니다. 직접 계산이라면 LOOP 후 INTERNAL TABLE을 COLLECT해야 하지만, ABAP SQL에서는 UNIT_CONVERSION을 SUM 안에 그대로 넣을 수 있습니다. 동시에 로깅을 위해 처리 시점과 사용자도 함께 가져옵니다.
DATA: lv_target_uom TYPE msehi VALUE 'KG'.
TRY.
SELECT
matnr,
SUM(
UNIT_CONVERSION(
quantity = menge,
source_unit = msehi,
target_unit = @lv_target_uom,
error_handling = 'FAIL_ON_ERROR'
)
) AS total_kg,
COUNT( * ) AS line_count
FROM ztgr_recv
WHERE recv_date BETWEEN @lv_date_from AND @lv_date_to
AND plant = @lv_plant
GROUP BY matnr
ORDER BY total_kg DESCENDING
INTO TABLE @DATA(lt_summary).
" 로깅: 정상 처리 건수만 출력
cl_demo_output=>write_data( |집계 자재 수: { lines( lt_summary ) }| ).
CATCH cx_sy_open_sql_db INTO DATA(lx_db).
" 변환 실패 시 — 어떤 단위가 문제인지 메시지에 노출
MESSAGE |단위 변환 실패: { lx_db->get_text( ) }| TYPE 'E'.
ENDTRY.
FAIL_ON_ERROR 모드는 변환 불가 단위가 하나라도 있으면 SQL이 예외를 발생시킵니다. 데이터 품질을 보장해야 하는 정산/회계성 처리에 어울리는 옵션입니다. 반대로 운영 대시보드처럼 "보이는 만큼만 보여라" 류는 SET_TO_NULL 또는 KEEP_UNCONVERTED가 더 안전합니다.
6. 실전 예제 — 3단계: 다중 목표 단위와 조건부 변환
생산팀은 자재별로 표시 단위가 다르길 원합니다. 액체류는 L로, 분체류는 KG으로 보고 싶다고 합니다. 이런 요구사항은 CASE 식과 결합하면 한 번에 풀립니다. 동시에 트랜잭션 안전성을 위해 변환 결과를 검증하고, 성능을 위해 인덱스 컬럼만 WHERE에 둡니다.
SELECT
p~matnr,
p~werks AS plant,
p~prod_qty AS source_qty,
p~msehi AS source_uom,
" 분류별 목표 단위 결정 + 변환을 한 표현식에 결합
UNIT_CONVERSION(
quantity = p~prod_qty,
source_unit = p~msehi,
target_unit = CASE
WHEN m~mtart = 'FERT' AND m~matkl = 'LIQUID'
THEN CAST( 'L' AS msehi )
WHEN m~mtart = 'FERT' AND m~matkl = 'POWDER'
THEN CAST( 'KG' AS msehi )
ELSE p~msehi
END,
error_handling = 'KEEP_UNCONVERTED'
) AS display_qty,
CASE
WHEN m~matkl = 'LIQUID' THEN 'L'
WHEN m~matkl = 'POWDER' THEN 'KG'
ELSE p~msehi
END AS display_uom,
" 변환이 실제로 일어났는지 표시(감사 로그용)
CASE
WHEN p~msehi = CASE
WHEN m~matkl = 'LIQUID' THEN 'L'
WHEN m~matkl = 'POWDER' THEN 'KG'
ELSE p~msehi
END
THEN 'N'
ELSE 'Y'
END AS was_converted
FROM ztgr_prod AS p
INNER JOIN mara AS m ON m~matnr = p~matnr
WHERE p~prod_date = @sy-datum
AND p~werks IN @rt_werks
INTO TABLE @DATA(lt_display).
이 패턴의 강점은 보안과 테스트성입니다. 변환 비율을 코드에 박지 않으므로, 마스터 데이터가 변경되면 별도 빌드 없이 다음 실행부터 즉시 반영됩니다. 단위 매핑(LIQUID -> L, POWDER -> KG)도 향후 별도 커스터마이징 테이블로 외부화하면 비즈니스 룰을 IT 작업 없이 변경할 수 있습니다. 단위 테스트는 ABAP Unit + CDS Test Double Framework로 ztgr_prod와 mara를 더블링하면 결정적(deterministic)으로 작성할 수 있습니다.
7. 자주 부딪히는 함정과 해결 — FAQ
실무에서 UNIT_CONVERSION을 처음 도입할 때 자주 막히는 지점을 정리합니다.
- Q1. 변환했는데 결과가
NULL만 나옵니다. 대부분error_handling = 'SET_TO_NULL'인 상태에서 출발 단위와 목표 단위의DIMID가 다른 경우입니다.SE16으로T006에서 두 단위의DIMID를 비교해 보세요. 같은 차원이 아니면 어떤 함수로도 변환할 수 없습니다(질량 ↔ 부피는 밀도가 필요한데 SAP 표준 단위 변환은 이를 알지 못함). - Q2. 단위 키를 그냥 문자열로 넣으면 컴파일 오류가 납니다.
target_unit은msehi타입을 요구합니다.CAST( 'KG' AS msehi )로 명시적으로 캐스팅하거나,@lv_target_uom같은 호스트 변수로 넘기세요. - Q3. 같은 SELECT를 HANA에서 돌릴 때와 AnyDB에서 돌릴 때 결과 자릿수가 다릅니다. UNIT_CONVERSION의 결과는 일반적으로
QUAN타입의 정밀도 규칙을 따르지만, DB별로 내부 반올림 차이가 미세하게 발생할 수 있습니다. 회계/정산 로직이라면 결과를 명시적으로CAST( ... AS DEC( 23, 6 ) )로 받고, 호출 측에서 라운딩 정책을 고정하는 것이 안전합니다. - Q4.
FAIL_ON_ERROR로 잡았는데 어떤 행이 문제인지 알 수 없습니다. 예외만 받지 말고, 사전 단계에서 변환 대상 단위 목록을SELECT DISTINCT msehi로 추출한 뒤T006과 조인해서DIMID가 맞지 않는 항목을 미리 걸러내는 검증 쿼리를 한 번 더 두는 패턴을 권장합니다.
8. 다음으로 살펴볼 만한 주제
UNIT_CONVERSION에 익숙해졌다면, 같은 계열의 CURRENCY_CONVERSION(통화 변환)도 동일한 사고방식으로 접근할 수 있습니다. CDS View 안에서는 어노테이션 @Semantics.unitOfMeasure와 함께 쓰면 Fiori Elements UI에서 단위까지 자동으로 표시됩니다. RAP 비즈니스 객체에서는 가상 필드(virtual element)로 변환된 단위를 노출하는 패턴이 자주 쓰입니다. 더 깊이는 ABAP SQL의 푸시다운 능력을 파악하기 위해 ST05 SQL Trace로 실제 HANA에서 어떤 식으로 실행되는지 확인해 보는 것을 권장합니다.
더 읽을거리 — 공식 문서와 보충 자료
- ABAP Keyword Documentation — UNIT_CONVERSION (help.sap.com)
- ABAP SQL — Built-in Functions for Unit and Currency Conversion (help.sap.com)
- SAP S/4HANA — Units of Measure 마스터 데이터(help.sap.com)
- SAP Community — ABAP CDS 태그(블로그 모음)
- SAP Community — ABAP Topic 페이지
- SAP for Me — Notes 검색(키워드: UNIT_CONVERSION, T006)
요약: 단위 변환은 마스터 데이터(
T006)가 가진 정보입니다. 그걸 코드로 옮기지 말고, ABAP SQL의UNIT_CONVERSION으로 DB에서 끝내세요. SUM/CASE/CDS와 결합하면 직접 계산 대비 코드량은 줄고, 마스터 데이터 변경에 대한 회복력은 올라갑니다.
댓글 0
아직 댓글이 없습니다.