ABAP

I_Currency 없이 통화 검증 — 실수 3가지 #shorts #SAP #ABAP

▶ YouTube에서 보기

개요 및 이 글에서 다루는 범위

SAP S/4HANA 환경에서 모든 금액 필드는 반드시 통화 코드(Currency Key)와 짝을 이뤄야 합니다. 이때 사용되는 표준 마스터 데이터 뷰가 I_Currency입니다. 이 글은 TCURC 테이블 기반의 I_Currency CDS 뷰가 어떻게 설계되어 있는지, 그리고 판매 주문이나 송장 처리 같은 실무 시나리오에서 어떻게 활용되는지를 단계별로 설명합니다.

  • I_Currency 뷰의 필드 구조와 TCURC 매핑 이해
  • I_CurrencyText 등 텍스트 뷰와의 연결 구조 파악
  • 판매 주문 처리 시 통화 코드 검증 패턴 작성
  • 환율(TCURR), 환율 유형(TCURV)과의 연계 구조 이해
  • 버퍼링 및 성능 최적화 전략 적용

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

ABAP CDS(Core Data Services) 뷰의 기본 문법, @ObjectModel.dataCategory와 같은 어노테이션의 의미, 그리고 SAP 마스터 데이터의 일반적인 구조(텍스트 분리, 클라이언트 종속성)에 대한 기초 이해가 있으면 도움이 됩니다. SE16/SE11에서 TCURC, TCURR, TCURT 테이블을 한 번이라도 조회해 본 경험이 있으면 더욱 좋습니다.

환경 및 시스템 요건

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

  • SAP S/4HANA 2022 또는 2023 (On-Premise / Private Cloud)
  • ABAP Development Tools (ADT) for Eclipse 2023-06 이상
  • ABAP Platform 7.58 이상 (CDS View Entity 지원)
  • HANA DB 2.0 SPS 06 이상 권장

SAP BTP ABAP Environment(Steampunk)에서는 일부 Public 릴리스 상태가 다를 수 있으니, API_HUB와 ADT의 Release Contract 정보를 확인하는 것이 일반적으로 권장됩니다. I_Currency는 Foundation Layer에 속하는 기본 뷰로, 대부분의 S/4HANA 시스템에서 별도 설정 없이 사용할 수 있습니다.

핵심 개념: TCURC와 I_Currency의 관계

SAP의 통화 관련 마스터 데이터는 네 개의 핵심 테이블로 구성되어 있습니다. 이를 이해하는 것이 I_Currency를 제대로 활용하는 출발점입니다.

테이블역할대응 CDS 뷰
TCURC통화 코드 마스터 (ISO 코드 포함)I_Currency
TCURT통화 코드 언어별 텍스트I_CurrencyText
TCURR환율 정보 (날짜별)I_ExchangeRate
TCURV환율 유형 (M, B, G 등)I_ExchangeRateType
TCURX통화별 소수점 자리수I_CurrencyDecimal

I_Currency는 TCURC 테이블을 1차 소스로 삼는 마스터 데이터 뷰입니다. 비유하자면 TCURC는 "은행 금고"이고 I_Currency는 "은행 창구"입니다. 금고에 직접 접근하는 대신, 표준화된 창구(CDS 뷰)를 통해 데이터를 가져오면 어노테이션, 권한, 라벨, 연관 관계가 일관되게 적용됩니다.

주요 필드 구조는 다음과 같습니다.

  • Currency (WAERS): 내부 통화 코드 (예: USD, KRW, EUR)
  • ISOCurrency (ISOCD): ISO 4217 표준 코드
  • CurrencyISOCode: ISO 코드 별칭
  • Decimals (DECIMALS): 소수점 자리수
  • AlternativeCurrencyKey: 대체 통화 키
  • PrimaryCurrencyForCountry: 국가별 주통화 여부

주의할 점은 I_Currency 자체에는 텍스트(통화 이름)가 포함되지 않는다는 것입니다. "미국 달러", "대한민국 원" 같은 설명 텍스트는 I_CurrencyText 뷰에서 Language 키와 함께 별도로 관리됩니다. 이는 SAP가 다국어 환경을 일관되게 처리하기 위해 텍스트를 분리한 설계 철학입니다.

1단계: 기본 조회 예제

가장 단순한 형태로, 시스템에 등록된 모든 통화 코드를 조회하면서 한국어 텍스트를 함께 가져오는 CDS 뷰를 만들어 봅니다.

@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Currency with Korean Text'
define view entity ZC_CurrencyWithText
  as select from I_Currency as Curr
  association [0..1] to I_CurrencyText as _Text
    on  _Text.Currency = Curr.Currency
    and _Text.Language = 'K'
{
  key Curr.Currency,
      Curr.ISOCurrency,
      Curr.Decimals,
      _Text.CurrencyName,
      _Text.CurrencyShortName,
      _Text._Language
}

위 예제는 한국어('K') 텍스트만 조인합니다. 이렇게 작성하면 다음과 같은 ABAP 코드에서 손쉽게 사용할 수 있습니다.

SELECT Currency, ISOCurrency, Decimals, CurrencyName
  FROM ZC_CurrencyWithText
  WHERE Currency IN ( 'USD', 'KRW', 'EUR', 'JPY' )
  INTO TABLE @DATA(lt_currency).

LOOP AT lt_currency INTO DATA(ls_currency).
  WRITE: / ls_currency-Currency,
           ls_currency-ISOCurrency,
           ls_currency-Decimals,
           ls_currency-CurrencyName.
ENDLOOP.

2단계: 판매 주문에서 통화 코드 검증하기

실무에서 가장 흔히 마주치는 시나리오는 외부 시스템에서 들어온 판매 주문 데이터의 통화 코드가 유효한지 검증하는 작업입니다. 예를 들어 EDI나 외부 e-Commerce 플랫폼에서 받은 주문에 잘못된 통화 코드가 섞여 들어올 수 있습니다.

CLASS zcl_sales_order_validator DEFINITION
  PUBLIC FINAL CREATE PUBLIC.

  PUBLIC SECTION.
    TYPES: BEGIN OF ty_validation_result,
             order_id    TYPE c LENGTH 10,
             currency    TYPE waers,
             is_valid    TYPE abap_bool,
             error_msg   TYPE string,
           END OF ty_validation_result.

    METHODS validate_currencies
      IMPORTING it_orders        TYPE STANDARD TABLE
      RETURNING VALUE(rt_result) TYPE STANDARD TABLE OF ty_validation_result.

ENDCLASS.

CLASS zcl_sales_order_validator IMPLEMENTATION.

  METHOD validate_currencies.
    DATA: lt_currency_codes TYPE STANDARD TABLE OF waers.

    LOOP AT it_orders ASSIGNING FIELD-SYMBOL(<fs_ord>).
      ASSIGN COMPONENT 'CURRENCY' OF STRUCTURE <fs_ord> TO FIELD-SYMBOL(<fv_cur>).
      IF sy-subrc = 0.
        APPEND <fv_cur> TO lt_currency_codes.
      ENDIF.
    ENDLOOP.
    SORT lt_currency_codes.
    DELETE ADJACENT DUPLICATES FROM lt_currency_codes.

    SELECT Currency
      FROM I_Currency
      FOR ALL ENTRIES IN @lt_currency_codes
      WHERE Currency = @lt_currency_codes-table_line
      INTO TABLE @DATA(lt_valid).

    LOOP AT it_orders ASSIGNING <fs_ord>.
      ASSIGN COMPONENT 'CURRENCY' OF STRUCTURE <fs_ord> TO <fv_cur>.
      DATA(ls_res) = VALUE ty_validation_result( currency = <fv_cur> ).

      IF line_exists( lt_valid[ Currency = <fv_cur> ] ).
        ls_res-is_valid = abap_true.
      ELSE.
        ls_res-is_valid  = abap_false.
        ls_res-error_msg = |Invalid currency code: { <fv_cur> }|.
      ENDIF.

      APPEND ls_res TO rt_result.
    ENDLOOP.
  ENDMETHOD.

ENDCLASS.

핵심 포인트는 주문 라인마다 SELECT를 던지지 않고 중복 제거된 통화 코드 리스트로 한 번만 조회하는 것입니다. TCURC 자체는 작은 테이블이지만, 트랜잭션 처리량이 많을 때는 이런 패턴이 일반적으로 권장됩니다.

3단계: 환율 변환과 OData 노출까지 포함한 프로덕션 패턴

실제 운영 환경에서는 통화 코드 검증에 그치지 않고, 주문 통화를 회사 통화로 환산하는 로직까지 함께 구현해야 합니다. 다음 CDS 뷰는 I_Currency를 마스터로 두고 I_ExchangeRate를 연관으로 가져오는 프로덕션 수준의 예제입니다.

@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Sales Order with Currency Conversion'
@VDM.viewType: #CONSUMPTION
@OData.publish: true
@ObjectModel.usageType: {
  serviceQuality: #D,
  sizeCategory: #L,
  dataClass: #TRANSACTIONAL
}
define view entity ZC_SalesOrderEnriched
  as select from zsalesorder as SO
  association [1..1] to I_Currency     as _Currency
    on _Currency.Currency = SO.doc_currency
  association [0..1] to I_CurrencyText as _CurrencyText
    on  _CurrencyText.Currency = SO.doc_currency
    and _CurrencyText.Language = $session.system_language
{
  key SO.order_id,
      SO.customer_id,
      SO.doc_currency,
      _Currency.ISOCurrency,
      _Currency.Decimals,
      _CurrencyText.CurrencyName as CurrencyDescription,

      @Semantics.amount.currencyCode: 'doc_currency'
      SO.net_amount,

      currency_conversion(
        amount             => SO.net_amount,
        source_currency    => SO.doc_currency,
        target_currency    => cast( 'USD' as abap.cuky ),
        exchange_rate_date => SO.order_date,
        exchange_rate_type => 'M',
        error_handling     => 'SET_TO_NULL'
      ) as NetAmountInUSD,

      _Currency,
      _CurrencyText
}

이 뷰의 핵심 설계 포인트는 다음과 같습니다.

  • currency_conversion CDS 함수로 DB 레벨에서 환산 수행 → ABAP 레벨 루프 제거
  • error_handling => 'SET_TO_NULL'로 환율이 없을 때 NULL 반환, 예외 발생 방지
  • $session.system_language로 사용자 로그온 언어에 맞춘 텍스트 자동 선택
  • @Semantics.amount.currencyCode로 금액 필드와 통화 필드 의미 연결
  • @OData.publish: true로 Fiori/외부 시스템에서 즉시 소비 가능

단위 테스트에서는 CL_OSQL_TEST_ENVIRONMENT를 사용해 I_Currency 데이터를 가짜로 주입하면 안정적인 검증이 가능합니다.

DATA(lt_curr_mock) = VALUE i_currency_t(
  ( Currency = 'KRW' ISOCurrency = 'KRW' Decimals = 0 )
  ( Currency = 'USD' ISOCurrency = 'USD' Decimals = 2 )
).
mo_osql_env->insert_test_data( lt_curr_mock ).

자주 마주치는 실수와 트러블슈팅

Q1. WAERS와 ISOCD 중 어떤 것을 외부 인터페이스 키로 써야 하나요?
SAP 내부에서는 WAERS(예: USD)를 사용하지만, ISO 표준에 맞춰야 하는 외부 시스템(REST API, ISO 20022 페이먼트)과의 통신에서는 ISOCurrency를 매핑하는 것이 일반적으로 권장됩니다. 일부 SAP 내부 코드(예: 러시아 루블의 RUB vs RUR)는 ISO 코드와 다를 수 있습니다.

Q2. I_Currency 조회가 느린데 버퍼링이 작동하지 않는 것 같습니다.
TCURC는 표준적으로 단일 레코드 버퍼링이 설정되어 있어 매우 빠릅니다. 그런데도 느리다면 (1) FOR ALL ENTRIES가 빈 테이블로 호출되어 전체 스캔이 일어났는지, (2) CDS 뷰에 추가된 조인이 buffer를 우회시키는지 확인해야 합니다. SELECT SINGLE 또는 WHERE Currency = ... 단일 조건이 가장 안전합니다.

Q3. CurrencyText에서 한국어 텍스트가 비어 있어요.
TCURT 테이블은 언어별로 데이터가 입력되어야 하는데, 일부 통화는 한국어(K) 텍스트가 마스터에 채워지지 않은 경우가 있습니다. 이때는 COALESCE 패턴으로 fallback을 둡니다.

coalesce( _TextKO.CurrencyName, _TextEN.CurrencyName ) as CurrencyName

Q4. ABAP Cloud(Steampunk)에서 TCURC를 직접 SELECT하면 syntax 오류가 납니다.
ABAP Cloud에서는 DDIC 테이블 직접 접근이 제한됩니다. 반드시 I_Currency와 같은 Released CDS 뷰를 통해서만 접근해야 합니다. ADT의 Release Contract 탭에서 "Use System-Internally"가 아닌 "Use in Cloud Development"인지 확인하세요.

I_Currency 연관 뷰 생태계 이해하기

I_Currency를 이해했다면 자연스럽게 I_ExchangeRate(TCURR), I_ExchangeRateType(TCURV) 뷰로 확장할 수 있습니다. 또한 RAP(RESTful ABAP Programming Model)에서 @Semantics.amount.currencyCode 어노테이션이 Behavior Definition의 validation과 어떻게 연동되는지 살펴보면 통화 처리의 전체 그림이 완성됩니다. Fiori Elements 앱에서는 이 어노테이션 덕분에 별도 코딩 없이 통화 포맷팅이 자동 적용된다는 점도 흥미로운 확장 주제입니다.

댓글 0

아직 댓글이 없습니다.