개요 및 이 글에서 다루는 것
SAP S/4HANA 기반 시스템에서 국가 코드는 단순한 두 글자 식별자가 아니라, 세금 처리·통화·주소 포맷·언어·EU 회원국 여부 등 수많은 비즈니스 로직의 분기점입니다. 전통적으로 ABAP 개발자는 T005 테이블을 직접 SELECT 했지만, RAP·Fiori Elements·CDS 기반 분석 환경에서는 I_Country 표준 뷰를 통한 접근이 더 자연스럽고 안전합니다. 이 글에서는 I_Country의 정체, T005와의 관계, 다국어 텍스트(I_CountryText) 연결 메커니즘, 그리고 실무 배송지 검증 시나리오까지 살펴봅니다.
핵심 개념: 코드 뒤에 숨겨진 마스터 데이터의 계층 구조
SAP에서 "국가"는 표면적으로 보면 DE, KR, US 같은 2자리 ISO 코드 문자열에 불과합니다. 하지만 실제로는 다음과 같은 다층 구조로 저장되어 있습니다.
T005(마스터 속성) ↔ T005T(언어별 명칭) ↔ T005S/T005U(지역·세금) ↔ I_Country(View)
T005는 국가 키(LAND1)와 함께 ISO 코드, 통화 단위, 주소 레이아웃 키, EU 코드, 인텔리스탯 코드 등 약 30여 개의 컨피그성 컬럼을 보유합니다. T005T는 LAND1과 SPRAS(언어 키)를 복합 키로 갖는 텍스트 테이블이며, 한국어로 로그온한 사용자에게는 "독일", 영어 사용자에게는 "Germany"가 보이도록 자동 분기되는 메커니즘의 원천입니다.
I_Country는 이 두 테이블을 Basic View 계층에서 추상화한 결과물입니다. 비유하자면 T005가 "도서관 서가의 책 한 권"이고, T005T가 "각 언어로 번역된 표지"라면, I_Country는 "사서가 정리해 둔 도서 카드"입니다. VDM 계층에서 I_Country는 Basic Interface View이고, 텍스트 전용 뷰인 I_CountryText가 별도로 존재하여 _Text association으로 연결됩니다. 이 패턴은 I_Currency, I_UnitOfMeasure 등 다른 마스터 뷰에서도 동일하게 반복되는 SAP의 일관된 설계 철학입니다.
실전 단계 1: 국가 코드 단순 조회 — T005 vs I_Country
가장 기본적인 형태로, 활성화된 국가 목록을 조회해 보겠습니다. T005를 직접 읽는 레거시 방식과 I_Country를 사용하는 방식을 나란히 비교합니다.
" 레거시 방식: T005 직접 SELECT
SELECT land1, intca, currency, xegld
FROM t005
INTO TABLE @DATA(lt_countries_raw)
WHERE xegld = 'X'. " EU 회원국만
LOOP AT lt_countries_raw INTO DATA(ls_row).
WRITE: / ls_row-land1, ls_row-intca, ls_row-currency.
ENDLOOP.
위 코드는 동작은 하지만 두 가지 약점이 있습니다. 첫째, 국가명을 얻으려면 T005T를 별도 조인해야 합니다. 둘째, 컬럼명이 독일어 약어 기반(LAND1, INTCA)이라 가독성이 떨어집니다. I_Country를 쓰면 다음과 같이 단순해집니다.
SELECT Country,
CountryISOCode,
Currency,
IsEUMember
FROM I_Country
INTO TABLE @DATA(lt_country_vdm)
WHERE IsEUMember = 'X'.
컬럼명이 영문 표준 필드명으로 통일되어 코드 가독성이 높아집니다. 또한 S/4HANA 클라우드 환경에서는 T005 직접 접근이 차단되는 경우가 많아, 마이그레이션 관점에서도 I_Country 사용이 유리합니다.
실전 단계 2: 다국어 텍스트 연결과 배송지 국가 검증
실무에서 흔히 마주치는 시나리오를 다뤄 봅시다. 판매 오더 등록 시 입력된 배송지 국가 코드가 유효한지 확인하고, 사용자의 로그온 언어로 국가 명칭을 화면에 표시하는 로직입니다.
CLASS lcl_shipto_validator DEFINITION.
PUBLIC SECTION.
TYPES: BEGIN OF ty_result,
country_code TYPE land1,
country_name TYPE text50,
is_eu TYPE abap_bool,
is_valid TYPE abap_bool,
message TYPE string,
END OF ty_result.
METHODS validate_shipto
IMPORTING iv_country_code TYPE land1
RETURNING VALUE(rs_result) TYPE ty_result.
ENDCLASS.
CLASS lcl_shipto_validator IMPLEMENTATION.
METHOD validate_shipto.
rs_result-country_code = iv_country_code.
SELECT SINGLE
c~Country,
c~IsEUMember,
t~CountryName
FROM I_Country AS c
LEFT OUTER JOIN I_CountryText AS t
ON t~Country = c~Country
AND t~Language = @sy-langu
INTO @DATA(ls_country)
WHERE c~Country = @iv_country_code.
IF sy-subrc = 0.
rs_result-is_valid = abap_true.
rs_result-country_name = ls_country-CountryName.
rs_result-is_eu = ls_country-IsEUMember.
rs_result-message = |Shipto country { iv_country_code } accepted|.
ELSE.
rs_result-is_valid = abap_false.
rs_result-message = |Invalid country code: { iv_country_code }|.
ENDIF.
ENDMETHOD.
ENDCLASS.
LEFT OUTER JOIN을 사용한 이유는, 매우 드물게 텍스트가 누락된 신규 국가 코드도 검증은 통과시키기 위함입니다. 비즈니스 요구에 따라 INNER JOIN으로 강제할 수도 있습니다. 또한 sy-langu를 조인 조건으로 넘겨야 사용자 세션 언어에 맞는 국가명이 반환됩니다.
실전 단계 3: 커스텀 CDS 뷰로 국가 데이터 재사용 구조화
프로덕션 단계에서는 위 로직을 반복 호출하기보다, 재사용 가능한 Composite View를 정의해 두는 편이 깔끔합니다. 다음은 배송지 분석용 커스텀 뷰 예시입니다.
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Shipto Country Enriched View'
define view entity ZC_ShiptoCountryEnriched
as select from I_Country as c
association [0..1] to I_CountryText as _Text
on _Text.Country = c.Country
and _Text.Language = $session.system_language
{
key c.Country as ShiptoCountryCode,
c.CountryISOCode as IsoAlpha2,
c.CountryThreeLetterISOCode as IsoAlpha3,
c.Currency as DefaultCurrency,
c.IsEUMember as EuMembershipFlag,
_Text.CountryName as ShiptoCountryName,
case c.IsEUMember
when 'X' then 'INTRA_EU'
else 'EXTRA_EU'
end as TradeRegion,
_Text
}
이 뷰는 세션 언어($session.system_language)를 활용해 사용자가 한국어 로그온이면 한국어 국가명을, 영어 로그온이면 영어 국가명을 자동 반환합니다. TradeRegion 같은 파생 컬럼은 BI 보고서에서 EU/비EU 거래 분석을 단순화합니다.
" ABAP Unit 테스트 예시
CLASS ltcl_country_test DEFINITION FINAL FOR TESTING
DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
METHODS:
valid_kr FOR TESTING,
invalid_xx FOR TESTING.
ENDCLASS.
CLASS ltcl_country_test IMPLEMENTATION.
METHOD valid_kr.
DATA(lo_validator) = NEW lcl_shipto_validator( ).
DATA(ls_res) = lo_validator->validate_shipto( 'KR' ).
cl_abap_unit_assert=>assert_true( ls_res-is_valid ).
ENDMETHOD.
METHOD invalid_xx.
DATA(lo_validator) = NEW lcl_shipto_validator( ).
DATA(ls_res) = lo_validator->validate_shipto( 'XX' ).
cl_abap_unit_assert=>assert_false( ls_res-is_valid ).
ENDMETHOD.
ENDCLASS.
성능 관점에서 I_Country는 마스터 데이터 뷰이므로 결과 셋이 작지만(전 세계 250여 개), 트랜잭션 테이블과 조인할 때는 association으로 잡아 lazy resolution하는 편이 메모리 사용 측면에서 유리합니다. SELECT 절에서 실제로 참조하지 않는 association은 HANA가 평가 단계에서 제거합니다.
Value Help 어노테이션 — Fiori Elements 연동
커스텀 CDS의 국가 코드 필드에 다음 어노테이션을 부여하면 Fiori Elements 화면에서 자동으로 F4 도움말이 표시됩니다.
define view entity ZC_SalesOrder
as select from zsales_order
{
key so_id as SalesOrderId,
@Consumption.valueHelpDefinition: [{
entity: {
name: 'I_Country',
element: 'Country'
}
}]
shipto_country as ShiptoCountry,
net_amount as NetAmount
}
이 어노테이션 하나로 사용자가 국가 코드 입력 필드를 클릭하면 I_Country에서 가져온 국가 목록이 팝업으로 표시됩니다. 별도 ValueHelp ABAP 클래스를 작성할 필요가 없어집니다.
자주 마주치는 실수와 해결 팁
Q1. I_Country를 SELECT 했는데 데이터가 비어 있습니다.
클라이언트 의존 뷰이므로 현재 작업 클라이언트에 T005 데이터가 SAP 표준 머지로 주입되어 있는지 확인하세요. SE16N에서 T005를 직접 열어 빈 상태인지 검증하면 됩니다.
Q2. CountryName이 항상 영어로 나옵니다.
I_CountryText 조인 시 Language = sy-langu 조건을 빠뜨렸을 가능성이 큽니다. 또한 T005T에 해당 언어 텍스트가 적재되지 않았을 수 있습니다. SMLT(언어 도구)로 번역 적재 상태를 확인하세요.
Q3. T005 직접 SELECT는 금지인가요?
공식적으로 "금지"는 아니지만, S/4HANA의 Clean Core 원칙상 마스터 데이터 접근은 Released CDS View를 통하는 것이 권장됩니다. 특히 클라우드 ABAP 환경에서는 T005 직접 접근이 차단되는 경우가 많아 마이그레이션 리스크가 있습니다.
Q4. IsEUMember 필드가 없는데요.
시스템 릴리스에 따라 필드명이 다를 수 있습니다. ADT에서 I_Country를 열어 실제 필드 목록을 Data Preview로 확인하거나, SE11에서 I_COUNTRY를 검색해 CDS 뷰 엔티티 구조를 직접 확인하세요.
마무리
국가 마스터를 이해했다면, 자연스러운 다음 길은 지역 마스터 I_Region(T005S 기반), 통화 I_Currency, 언어 I_Language로 이어집니다. 또한 RAP 비즈니스 객체에서 Foreign Key Association을 통해 국가 필드를 잡는 패턴, BAdI ADDR_CHECK_COUNTRY를 활용한 주소 검증 확장, 그리고 Fiori Elements에서 @ObjectModel.foreignKey.association을 어떻게 사용하는지 함께 익히면 마스터 데이터 모델링의 큰 그림이 잡힙니다.
댓글 0
아직 댓글이 없습니다.