KNA1 직접 접근이 왜 문제인가
SAP ERP 시대에 ABAP 개발자에게 KNA1은 고객 마스터의 대명사였습니다. 고객 코드, 이름, 국가, 도시, 계정 그룹 등 기본 속성이 이 테이블 하나에 담겨 있었기에 SELECT 문 대부분이 SELECT * FROM kna1 WHERE kunnr = ... 형태로 시작되었죠. 그러나 S/4HANA로 전환되면서 이 접근 방식은 세 가지 근본적인 문제를 만들어 냅니다.
첫째, 데이터 모델 확장에 취약합니다. 고객 마스터는 KNA1 단독이 아니라 KNB1(회사 코드 뷰), KNVV(영업 조직 뷰), KNVP(파트너 기능), ADRC(주소) 등 여러 테이블에 흩어져 있습니다. KNA1만 읽으면 항상 JOIN 로직을 직접 짜야 하고, 이는 개발자마다 다르게 구현되어 유지보수 지옥으로 이어집니다.
둘째, Business Partner(BP) 통합 이후 KNA1은 논리적으로 파생 테이블입니다. S/4HANA에서 고객은 BUT000(BP) 중심으로 관리되고, KNA1은 CVI(Customer-Vendor Integration) 동기화로 유지됩니다. 즉 KNA1을 직접 UPDATE 하는 로직은 이미 위험합니다.
셋째, Fiori/RAP/Analytics와 단절됩니다. SAP가 공식 발표한 Virtual Data Model(VDM)은 CDS 뷰 기반이고, 표준 앱은 모두 I_Customer 같은 릴리스된 뷰를 소비합니다. KNA1 직접 SELECT는 이 흐름에서 벗어나 재사용성이 떨어집니다.
I_Customer CDS 뷰란 무엇인가
SAP S/4HANA의 Virtual Data Model은 세 층위로 나뉩니다 — Basic View(I_*), Composite View(I_* 상위), Consumption View(C_*). I_Customer는 그중 Basic Interface View에 속하는 릴리스된 뷰로, 고객 마스터를 조회하는 표준 진입점 역할을 합니다.
비유하자면 KNA1이 "원자재 창고 선반"이라면, I_Customer는 "잘 포장되어 라벨이 붙은 완제품 매대"입니다. KNA1, KNB1, KNVV, KNVP, ADRC, BUT000의 필요한 컬럼들이 이미 조합되어 있고, 릴리스 계약(C1 — Public API)에 따라 필드명이 안정적으로 유지됩니다. 예를 들어 KNA1-KUNNR은 Customer로, KNA1-NAME1은 CustomerName으로 노출됩니다.
또한 @AccessControl.authorizationCheck: #CHECK 어노테이션 덕분에 뷰를 조회하는 것만으로 DCL(Data Control Language) 권한 필터가 자동 적용됩니다. KNA1을 직접 읽을 때 개발자가 AUTHORITY-CHECK를 잊어버리는 흔한 실수를 구조적으로 방지하는 셈입니다.
I_Customer의 핵심 필드 구조
실무에서 자주 쓰이는 필드는 다음과 같습니다. 필드명은 CamelCase로 통일되어 있으며, 의미가 명확합니다.
| CDS 필드 | 원본(참고) | 설명 |
|---|---|---|
Customer | KNA1-KUNNR | 고객 코드 (Key) |
CustomerName | KNA1-NAME1 | 고객명 |
Country | KNA1-LAND1 | 국가 코드 |
CityName | KNA1-ORT01 | 도시 |
PostalCode | KNA1-PSTLZ | 우편번호 |
CustomerAccountGroup | KNA1-KTOKD | 계정 그룹 |
Industry | KNA1-BRSCH | 산업 코드 |
CreationDate | KNA1-ERDAT | 생성 일자 |
IsMarkedForDeletion | KNA1-LOEVM | 삭제 플래그 |
영업 조직·판매 지역 관련 정보는 I_CustomerSalesArea라는 하위 뷰에 있으며, 여기서 SalesOrganization, DistributionChannel, Division, SalesDistrict(판매 지역) 등을 얻을 수 있습니다.
ABAP 코드에서 I_Customer 사용하기
가장 단순한 형태는 KNA1 SELECT를 그대로 뷰 이름만 바꾸는 것입니다. 다만 필드명이 다르다는 점만 유의하면 됩니다.
" S/4HANA 2022 이상, ABAP for Cloud Development 호환
DATA lt_customers TYPE TABLE OF i_customer.
SELECT customer,
customername,
country,
cityname,
customeraccountgroup
FROM i_customer
WHERE country = 'KR'
AND ismarkedfordeletion = ' '
INTO TABLE @lt_customers
UP TO 100 ROWS.
LOOP AT lt_customers INTO DATA(ls_cust).
WRITE: / ls_cust-customer, ls_cust-customername, ls_cust-cityname.
ENDLOOP.
동일한 결과를 KNA1로 얻으려면 KTOKD 필터, LOEVM 필터, 그리고 앞으로 추가될 BP 관련 조건까지 개발자가 매번 챙겨야 합니다. I_Customer는 이러한 표준 필터를 뷰 내부에 캡슐화하고 있어 코드 라인이 짧고 의도가 명확해집니다.
ABAP CDS Query로 SalesRegion 필터링 실전 예제
실무 시나리오를 하나 세워보겠습니다 — "한국 내 특정 판매 지역(SalesDistrict)에 속한 활성 고객 목록을 뽑아, 계정 그룹별로 집계한다." 이때 I_Customer와 I_CustomerSalesArea를 조합해 자체 CDS 뷰를 만들면 재사용성이 높습니다.
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'Active Customers by Sales District (KR)'
define view entity ZC_CustomerBySalesDistrict
as select from I_Customer as cust
inner join I_CustomerSalesArea as sa
on cust.Customer = sa.Customer
{
key cust.Customer as CustomerId,
cust.CustomerName as CustomerName,
cust.Country as Country,
cust.CityName as CityName,
cust.CustomerAccountGroup as AccountGroup,
sa.SalesOrganization as SalesOrg,
sa.DistributionChannel as DistrChannel,
sa.Division as Division,
sa.SalesDistrict as SalesDistrict
}
where cust.Country = 'KR'
and cust.IsMarkedForDeletion = ' '
and sa.CustomerDeletionFlag = ' '
이 뷰를 ABAP에서 소비하는 코드는 다음과 같습니다. 로깅과 예외 처리도 함께 넣어 실무에 가깝게 구성합니다.
CLASS zcl_customer_report DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
METHODS aggregate_by_district
IMPORTING iv_sales_district TYPE bzirk
RETURNING VALUE(rt_result) TYPE ztt_district_summary
RAISING cx_sy_open_sql_db.
ENDCLASS.
CLASS zcl_customer_report IMPLEMENTATION.
METHOD aggregate_by_district.
TRY.
SELECT accountgroup,
COUNT(*) AS customer_count
FROM zc_customerbysalesdistrict
WHERE salesdistrict = @iv_sales_district
GROUP BY accountgroup
INTO TABLE @rt_result.
IF rt_result IS INITIAL.
MESSAGE |No customers found for district { iv_sales_district }|
TYPE 'I'.
ENDIF.
CATCH cx_sy_open_sql_db INTO DATA(lx_sql).
MESSAGE lx_sql->get_text( ) TYPE 'E'.
RAISE EXCEPTION lx_sql.
ENDTRY.
ENDMETHOD.
ENDCLASS.
이 구조의 장점은 명확합니다 — 뷰(ZC_CustomerBySalesDistrict)는 데이터 모델을, 클래스는 비즈니스 로직을 담당합니다. 향후 요구사항이 "판매 조직도 함께 그룹핑"으로 바뀌면 클래스만 수정하면 되고, 뷰 자체는 다른 리포트에서도 재사용됩니다.
OPEN SQL과 I_Customer 결합 패턴
Open SQL에서 I_Customer를 다른 트랜잭션 데이터와 조인해 쓰는 패턴도 자주 등장합니다. 예를 들어 판매 오더 헤더(I_SalesOrder)와 조인해 "국가별 오더 건수"를 뽑는다면 다음과 같습니다.
SELECT c~country,
COUNT( DISTINCT so~salesorder ) AS order_count
FROM i_salesorder AS so
INNER JOIN i_customer AS c
ON so~soldtoparty = c~customer
WHERE so~creationdate >= @lv_from_date
GROUP BY c~country
ORDER BY order_count DESCENDING
INTO TABLE @DATA(lt_country_stats).
여기서 눈여겨볼 점은 SoldToParty — 즉 판매처 파트너 — 가 곧 I_Customer.Customer와 매핑된다는 사실입니다. KNA1을 직접 조인했다면 어느 파트너 기능이 SoldTo인지 KNVP를 뒤져야 했을 겁니다. 표준 VDM은 이런 시맨틱 매핑을 이미 반영하고 있어 개발자가 도메인 지식 부담을 덜 수 있습니다.
성능 최적화 — WHERE 절 푸시다운 활용
CDS 뷰는 결국 HANA DB에서 실행되는 SQL로 컴파일됩니다. 성능을 극대화하려면 다음 원칙을 지키는 것이 일반적으로 권장됩니다.
- WHERE 절은 최대한 좁게, 최대한 일찍 —
Country,CustomerAccountGroup처럼 인덱스나 파티션 키가 될 가능성이 높은 필드로 먼저 필터링해야 HANA가 푸시다운을 잘 수행합니다. - SELECT * 지양 — I_Customer는 수십 개 필드를 노출합니다. 필요한 컬럼만 나열하면 컬럼 스토어 특성상 IO가 극적으로 줄어듭니다.
- LOOP 안에서 SELECT 금지 — FOR ALL ENTRIES 또는 뷰 내부 JOIN으로 한 번에 가져오세요.
- PACKAGE SIZE / UP TO n ROWS — 대량 데이터를 화면에 뿌리는 리포트라면 초기 페이지만 로드하고 이후 페이지네이션 하는 편이 응답 시간에 유리합니다.
또 하나의 팁 — CDS 뷰에는 @Analytics.dataCategory나 @ObjectModel.usageType 같은 어노테이션이 붙어 있어 뷰의 용도가 문서화되어 있습니다. Analytics 용으로 태그된 뷰를 트랜잭션성 목적으로 남용하지 않는 편이 안전합니다.
S/4HANA 전환 시 KNA1 → I_Customer 마이그레이션 체크리스트
레거시 커스텀 프로그램을 S/4HANA(예: 2022, 2023 릴리스)에서 정비할 때 참고할 만한 실전 체크리스트입니다.
- KNA1 SELECT 사용처 스캔 — Code Inspector 또는 ATC(ABAP Test Cockpit)의 "S4HANA Readiness" 체크로 KNA1 직접 접근 지점을 목록화합니다.
- 필드 매핑 표 작성 — KUNNR→Customer, NAME1→CustomerName 식으로 팀 공용 매핑 시트를 만들어 두면 리팩터링 속도가 빨라집니다.
- 추가 데이터 소스 판별 — KNB1/KNVV/ADRC까지 JOIN 하는 로직이라면 I_Customer 단독으로는 부족할 수 있습니다.
I_CustomerCompany,I_CustomerSalesArea,I_BusinessPartner를 함께 검토하세요. - 수정 로직은 BAPI/RAP로 이관 — I_Customer는 조회용이 원칙입니다. 고객 마스터를 갱신해야 한다면
BAPI_CUSTOMER_*또는 BP 관련 RAP BO를 사용하는 편이 권장됩니다. - 권한 재검토 — DCL이 자동 적용되므로 기존 AUTHORITY-CHECK 로직과 중복되지 않는지, 반대로 누락된 권한 필터가 뷰에서 커버되는지 확인합니다.
- 단위 테스트 추가 — ABAP Unit + CDS Test Double Framework(
cl_cds_test_environment)로 뷰 소비 로직을 격리 테스트해두면 뷰 필드 변경 시 회귀 이슈를 빨리 잡을 수 있습니다.
KNA1 시대의 관성을 버리고 I_Customer 중심의 데이터 접근으로 옮겨가면 코드는 짧아지고, 표준 앱과의 호환성은 넓어지며, 성능·권한·유지보수 모든 축에서 이득을 얻을 수 있습니다. 신규 개발이라면 처음부터 릴리스된 CDS 뷰를 기준선으로 삼는 편이 장기적으로 안전한 선택입니다.
댓글 0
아직 댓글이 없습니다.