1. BNKA를 아직도 직접 조회하고 있나요? — 개요와 목표
S/4HANA 이전 시절부터 ABAP를 다뤄온 개발자라면 은행 관련 데이터를 조회할 때 습관적으로 SELECT ... FROM bnka를 작성해 왔을 것입니다. 하지만 SAP S/4HANA 환경, 특히 클린 코어(Clean Core)를 지향하는 프로젝트에서는 물리 테이블 직접 접근 대신 I_HouseBank 같은 표준 CDS 인터페이스 뷰 사용이 일반적으로 권장됩니다. 이 글에서는 BNKA 직접 조회의 한계와 I_HouseBank로 전환하는 구체적인 방법을 단계별로 다룹니다.
- BNKA 직접 조회가 S/4HANA에서 문제가 되는 이유를 설명할 수 있다
- I_HouseBank의 키 필드와 어소시에이션 구조를 파악한다
- 회사코드 기반 하우스 뱅크/계좌 조회 로직을 CDS 뷰 기반으로 작성한다
- 마이그레이션 시 필드 매핑과 권한 체크 차이를 점검한다
2. 미리 알아두면 좋은 배경
ABAP Open SQL의 기본 문법(SELECT, INTO TABLE, WHERE)을 사용할 수 있고, 재무회계(FI)에서 하우스 뱅크(House Bank)가 "우리 회사가 거래하는 은행 계좌 관리 단위"라는 점을 알고 있으면 충분합니다. CDS 뷰를 직접 만들어 본 경험은 없어도 따라올 수 있는 beginner 수준으로 구성했습니다.
3. 환경 · 버전 · 준비물
이 예제는 다음 환경을 기준으로 작성했습니다.
- SAP S/4HANA 2022 이상 (On-Premise) 또는 S/4HANA Cloud Public Edition — I_HouseBank는 두 에디션 모두에서 릴리스된 인터페이스 뷰로 제공됩니다
- ABAP 언어 버전 7.5x 이상 (인라인 선언, 새 Open SQL 문법 사용)
- Eclipse 기반 ADT(ABAP Development Tools) — CDS 뷰 정의 확인 및 Data Preview에 필요
- 테스트용 하우스 뱅크 마스터 데이터: 회사코드(예: 1710), 하우스 뱅크 ID(예: KRHB1) 및 계좌 ID가 구성된 시스템
참고로 S/4HANA Cloud Public Edition에서는 BNKA 같은 물리 테이블에 대한 직접 SELECT 자체가 릴리스되지 않은 오브젝트 접근으로 차단되므로, CDS 뷰 사용은 선택이 아니라 사실상 필수입니다.
4. I_HouseBank란 무엇인가 — 핵심 개념
I_HouseBank는 SAP가 제공하는 VDM(Virtual Data Model) 인터페이스 뷰로, 하우스 뱅크 마스터(전통적으로 T012 테이블에 저장되던 데이터)를 의미 있는 영문 필드명으로 노출합니다. 여기서 흔한 오해 하나를 먼저 짚겠습니다. BNKA는 "은행 일반 마스터"(전 세계 은행 디렉터리)이고, 하우스 뱅크는 "그중 우리 회사가 계좌를 보유한 은행"입니다. 과거 코드에서는 T012에서 하우스 뱅크를 읽고, 은행 이름·SWIFT 코드를 얻기 위해 BNKA를 수동으로 JOIN하는 패턴이 흔했습니다.
비유하자면 BNKA는 전국 은행 전화번호부이고, 하우스 뱅크(T012)는 내 지갑 속 거래 은행 카드 목록입니다. I_HouseBank는 이 카드 목록을 꺼내면서, 전화번호부(은행 마스터)로 가는 어소시에이션(_Bank)을 미리 연결해 둔 구조입니다. 덕분에 개발자가 BNKA를 직접 언급할 필요가 없어집니다.
BNKA 직접 조회 방식의 대표적인 문제는 다음과 같습니다.
- 데이터 모델 변경 리스크 — S/4HANA에서는 여러 테이블이 CDS 기반으로 가상화되거나 단순화되었습니다. 물리 테이블에 의존한 코드는 업그레이드 시 재검증 대상이 됩니다.
- 수동 JOIN 부담 — 하우스 뱅크 + 은행 마스터 + 계좌(T012K) 조합을 매번 손으로 작성해야 하고, 실수 여지가 큽니다.
- 클라우드 호환성 부재 — 릴리스되지 않은 테이블 접근은 ABAP Cloud 개발 모델에서 문법 오류로 처리됩니다.
- 가독성 —
BANKS,BANKL,HBKID같은 축약 필드명보다BankCountry,Bank,HouseBank가 유지보수에 유리합니다.
BNKA/T012와 I_HouseBank의 필드 매핑을 정리하면 다음과 같습니다.
| 기존 필드 (T012/BNKA) | I_HouseBank / 연관 뷰 필드 | 의미 |
|---|---|---|
| BUKRS | CompanyCode (키) | 회사코드 |
| HBKID | HouseBank (키) | 하우스 뱅크 ID |
| BANKS | BankCountry | 은행 국가 키 |
| BANKL | Bank | 은행 키(내부 은행 번호) |
| BNKA-BANKA | _Bank.BankName | 은행명 (어소시에이션 경유) |
| BNKA-SWIFT | _Bank.SWIFTCode | SWIFT/BIC 코드 |
| T012K-HKTID | I_HouseBankAccount.HouseBankAccount | 계좌 ID |
즉, 은행명이나 SWIFT 코드가 필요할 때 BNKA를 조회하는 대신 \_Bank 어소시에이션 경로 표현식 한 줄이면 됩니다.
5. 실전 코드 3단계
1단계 — 기본 SELECT: 하우스 뱅크 전체 목록
가장 단순한 형태부터 시작합니다. CDS 뷰도 Open SQL에서 테이블처럼 조회할 수 있습니다.
REPORT zdemo_housebank_basic.
SELECT FROM I_HouseBank
FIELDS CompanyCode,
HouseBank,
BankCountry,
Bank
ORDER BY CompanyCode, HouseBank
INTO TABLE @DATA(lt_house_banks).
LOOP AT lt_house_banks INTO DATA(ls_bank).
WRITE: / ls_bank-CompanyCode,
ls_bank-HouseBank,
ls_bank-BankCountry,
ls_bank-Bank.
ENDLOOP.
BNKA 시절과 달리 필드명이 자체 설명적이므로 주석 없이도 의도가 드러납니다. 은행명까지 함께 보고 싶다면 어소시에이션 경로를 사용합니다.
SELECT FROM I_HouseBank
FIELDS CompanyCode,
HouseBank,
\_Bank-BankName AS BankName,
\_Bank-SWIFTCode AS SwiftCode
INTO TABLE @DATA(lt_bank_names).
2단계 — 실무 시나리오: 회사코드별 지급 은행 조회 + 에러 처리
지급 처리 프로그램에서 특정 회사코드의 하우스 뱅크와 계좌 정보를 읽는 시나리오입니다. 결과가 없을 때의 처리와 로깅을 포함합니다.
CLASS zcl_payment_bank_reader DEFINITION
PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES: BEGIN OF ty_payment_bank,
company_code TYPE bukrs,
house_bank TYPE hbkid,
bank_account TYPE hktid,
bank_name TYPE string,
currency TYPE waers,
END OF ty_payment_bank,
tt_payment_bank TYPE STANDARD TABLE OF ty_payment_bank
WITH EMPTY KEY.
METHODS get_banks_for_company
IMPORTING iv_company_code TYPE bukrs
RETURNING VALUE(rt_result) TYPE tt_payment_bank
RAISING cx_static_check.
ENDCLASS.
CLASS zcl_payment_bank_reader IMPLEMENTATION.
METHOD get_banks_for_company.
" 하우스 뱅크 + 계좌 + 은행명을 한 번의 SELECT로 조회
SELECT FROM I_HouseBankAccount AS acct
INNER JOIN I_HouseBank AS hbank
ON acct~CompanyCode = hbank~CompanyCode
AND acct~HouseBank = hbank~HouseBank
FIELDS acct~CompanyCode AS company_code,
acct~HouseBank AS house_bank,
acct~HouseBankAccount AS bank_account,
hbank~\_Bank-BankName AS bank_name,
acct~Currency AS currency
WHERE acct~CompanyCode = @iv_company_code
INTO CORRESPONDING FIELDS OF TABLE @rt_result.
IF rt_result IS INITIAL.
MESSAGE w101(zfi_pay) WITH iv_company_code
INTO DATA(lv_msg_dummy).
RAISE EXCEPTION NEW zcx_no_house_bank(
company_code = iv_company_code ).
ENDIF.
ENDMETHOD.
ENDCLASS.
포인트는 세 가지입니다. 첫째, T012·T012K·BNKA 세 테이블 수동 JOIN이 인터페이스 뷰 2개 + 어소시에이션으로 줄었습니다. 둘째, 빈 결과를 정상 흐름이 아닌 예외로 승격시켜 호출부가 반드시 처리하도록 강제했습니다. 셋째, 메시지 클래스 기반 로깅으로 운영 중 추적이 가능합니다.
3단계 — 프로덕션: 커스텀 CDS 뷰 + 테스트 + 권한
반복 사용되는 조회 로직은 자체 CDS 뷰 엔티티로 캡슐화하는 편이 성능(필드 프루닝, 코드 푸시다운)과 재사용성 면에서 유리합니다.
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: '지급처리용 하우스뱅크 정보'
define view entity ZI_PaymentBankInfo
as select from I_HouseBank
{
key CompanyCode,
key HouseBank,
BankCountry,
Bank,
_Bank.BankName as BankName,
_Bank.SWIFTCode as SWIFTCode,
/* 소비자가 필요 시 계좌로 내려갈 수 있도록 노출 */
_HouseBankAccount
}
권한 관련해서 자주 나오는 질문: "AUTHORITY-CHECK 없이 데이터가 그냥 조회되는가?" — I_HouseBank 계열 뷰에는 일반적으로 CDS 접근 제어(DCL)가 연결되어 있어, @AccessControl.authorizationCheck: #CHECK 설정에 따라 조회 시점에 권한 필터가 자동 적용됩니다. 즉 별도 AUTHORITY-CHECK 구문 없이도 권한 없는 회사코드 데이터는 결과에서 제외될 수 있습니다. 다만 시스템·릴리스별로 DCL 구성이 다를 수 있으므로, ADT에서 해당 뷰의 Access Control 오브젝트를 직접 확인하는 것이 안전합니다. 반대로 BNKA 직접 SELECT는 어떤 권한 필터도 거치지 않으므로, 마이그레이션 후 "결과 건수가 줄었다"면 버그가 아니라 권한 필터가 정상 동작하는 것일 수 있습니다.
단위 테스트는 CDS Test Double Framework로 DB 의존 없이 검증합니다.
CLASS ltc_payment_bank_info DEFINITION FINAL
FOR TESTING RISK LEVEL HARMLESS DURATION SHORT.
PRIVATE SECTION.
CLASS-DATA go_env TYPE REF TO if_cds_test_environment.
CLASS-METHODS class_setup.
CLASS-METHODS class_teardown.
METHODS returns_bank_name FOR TESTING.
ENDCLASS.
CLASS ltc_payment_bank_info IMPLEMENTATION.
METHOD class_setup.
go_env = cl_cds_test_environment=>create(
i_for_entity = 'ZI_PAYMENTBANKINFO' ).
ENDMETHOD.
METHOD returns_bank_name.
go_env->clear_doubles( ).
DATA(lt_hbank) = VALUE if_cds_test_data=>tt_row(
( CompanyCode = '1710' HouseBank = 'KRHB1'
BankCountry = 'KR' Bank = '0040001' ) ).
go_env->insert_test_data( i_data = lt_hbank ).
SELECT SINGLE BankName FROM zi_paymentbankinfo
WHERE CompanyCode = '1710'
INTO @DATA(lv_name).
cl_abap_unit_assert=>assert_subrc( exp = 0 ).
ENDMETHOD.
METHOD class_teardown.
go_env->destroy( ).
ENDMETHOD.
ENDCLASS.
성능 관점에서는 FOR ALL ENTRIES로 하우스 뱅크를 건건이 읽어오던 레거시 패턴을 위처럼 JOIN 한 번으로 통합하는 것이 HANA 코드 푸시다운 측면에서 유리하며, 필요한 필드만 명시적으로 나열해 SELECT *를 피하는 습관도 함께 권장됩니다.
6. 흔한 실수와 트러블슈팅 FAQ
Q1. BNKA를 SELECT하던 코드를 I_HouseBank로 바꿨더니 건수가 크게 줄었습니다.
정상일 가능성이 높습니다. BNKA는 시스템에 등록된 모든 은행의 디렉터리이고, I_HouseBank는 회사코드에 배정된 하우스 뱅크만 담습니다. 은행 마스터 전체가 필요하다면 I_HouseBank가 아니라 I_Bank(BankCountry, BankInternalID, BankName, SWIFTCode 등 노출)를 사용해야 합니다. 용도에 맞는 뷰 선택이 마이그레이션의 첫 단추입니다.
Q2. 어소시에이션 경로(\_Bank-BankName)에서 문법 오류가 납니다.
Open SQL에서 경로 표현식은 역슬래시(\)로 시작하고 구성요소 구분자는 하이픈(-)입니다. CDS DDL 안에서는 _Bank.BankName처럼 점(.)을 쓰는 것과 혼동하기 쉽습니다. 또한 NetWeaver 7.5 미만 커널에서는 경로 표현식 자체가 지원되지 않으므로 명시적 JOIN으로 대체해야 합니다.
Q3. 개발기에서는 조회되는데 운영기에서 특정 사용자만 빈 결과를 받습니다.
CDS 접근 제어(DCL)가 원인일 확률이 높습니다. 해당 사용자의 회사코드 관련 권한(예: FI 하우스 뱅크 권한 오브젝트)을 확인하고, 트랜잭션 STAUTHTRACE로 권한 추적을 수행해 보세요. DCL은 실패 시 에러가 아니라 조용히 행을 필터링한다는 점이 디버깅을 어렵게 만듭니다.
Q4. 계좌 번호(IBAN 등) 상세가 I_HouseBank에 보이지 않습니다.
계좌 수준 데이터는 별도 뷰인 I_HouseBankAccount에 있습니다. I_HouseBank는 은행 수준(회사코드+하우스 뱅크 ID)까지만 담당하므로, 계좌 ID·통화 등은 어소시에이션이나 JOIN으로 내려가야 합니다.
7. 여기서 더 나아가려면
하우스 뱅크 조회를 CDS로 전환했다면, 다음 주제로 확장해 보세요. 첫째, I_HouseBankAccount와 은행계좌관리(BAM, Bank Account Management) — S/4HANA에서 계좌 마스터가 FCLM 기반으로 재편된 구조를 이해하면 지급/수금 개발이 한층 수월해집니다. 둘째, CDS 접근 제어(DCL) 직접 작성 — 커스텀 뷰에 회사코드 권한을 거는 방법. 셋째, ABAP Cloud 개발 모델에서 릴리스된 API만으로 애플리케이션을 구성하는 클린 코어 전략. 넷째, RAP(RESTful ABAP Programming Model)에서 I_HouseBank를 값 도움(Value Help) 소스로 활용하는 패턴도 실무 활용도가 높습니다.
8. 더 살펴볼 자료
- SAP Help Portal — SAP S/4HANA On-Premise 제품 문서
- SAP Help Portal — ABAP CDS Data Model (VDM 인터페이스 뷰 개념)
- SAP Business Accelerator Hub — I_HouseBank / I_HouseBankAccount / I_Bank 뷰 명세 검색
- SAP Community — CDS 뷰 마이그레이션 및 VDM 활용 사례 토론
- ABAP Keyword Documentation — Open SQL 경로 표현식과 CDS 접근 문법
댓글 0
아직 댓글이 없습니다.