SAP 의 분류(Classification) 체계는 자재(MARA) · 배치(MCH1) · 자산(AUSP 일반 객체) 같은 마스터/트랜잭션 객체에 “표준 필드로는 담을 수 없는 부가 속성” 을 클래스와 특성(Characteristic)으로 붙여 관리하는 SAP 표준 메커니즘입니다. 자재 한 건이 어떤 클래스에 묶여 있는지, 그 클래스에는 어떤 특성이 있고, 특성마다 어떤 값이 들어가 있는지 — 이 세 가지를 코드로 다루는 일이 분류 ABAP 의 거의 전부입니다.
문제는 분류 관련 표준 함수가 이름이 비슷비슷하고 파라미터 구조가 까다로워서 처음 보면 어떤 걸 어떤 순서로 호출해야 하는지 헷갈린다는 점입니다. CLAP_DDB_GET_CLASSIFICATION, CLAF_CLASSIFICATION_OF_OBJECTS, BAPI_OBJCL_CONCATENATEKEY, BAPI_OBJCL_CHANGE — 이 네 함수가 한 세트입니다.
이 글에서는 “자재의 클래스 찾기 → 그 클래스의 특성 찾기 → 배치의 KEY 만들기 → 배치 특성값 변경” 4단계 흐름과, 각 단계에서 어떤 표준 FM 을 어떤 파라미터로 호출하는지를 Classic Syntax 와 New Syntax(7.40+) 두 가지 패턴으로 함께 정리합니다.
핵심 — 분류 ABAP 함수 4종
| 함수 | 용도 | 언제 사용 |
|---|---|---|
CLAP_DDB_GET_CLASSIFICATION |
객체가 속한 클래스 목록 조회 | “이 자재가 어떤 클래스에 묶여 있나?” |
CLAF_CLASSIFICATION_OF_OBJECTS |
클래스의 특성 + 값 조회 | “이 클래스에는 어떤 특성이 있고, 객체별로 어떤 값이 들어가 있나?” |
BAPI_OBJCL_CONCATENATEKEY |
복합 KEY 결합 | 배치처럼 KEY 가 여러 필드(MATNR + CHARG)인 객체의 KEY 를 한 줄로 만들 때 |
BAPI_OBJCL_CHANGE |
특성값 변경/생성 | 자재/배치의 특성 값을 직접 채우거나 수정할 때 |
조회는 CLAP_* · CLAF_* 함수, 변경은 BAPI_OBJCL_* 라는 점만 기억하면 분류 작업의 95% 는 커버됩니다. KEY 가 여러 필드(자재+배치) 인 경우엔 BAPI_OBJCL_CONCATENATEKEY 로 먼저 한 줄 KEY 를 만들어야 합니다.
Classification Type 코드
분류 함수의 CLASSTYPE 파라미터는 SAP 표준 코드 — 어떤 객체의 클래스인지 결정합니다.
| Classtype | 대상 | OBJECTTABLE |
|---|---|---|
001 |
자재 (Material Class) | MARA |
023 |
배치 (Batch Class) — 가장 빈도 높음 | MCH1 (또는 MARA 와 함께 사용) |
300 |
Variant Configuration | MARA |
032 |
고객/거래처 | KNA1 · LFA1 |
배치 특성을 다루는 경우가 실무에서 가장 많아 023 을 자주 만나게 됩니다. 그래서 아래 예시도 배치 기준입니다.
1단계 — 자재가 속한 클래스 찾기
자재가 속한 클래스를 알아내려면 CLAP_DDB_GET_CLASSIFICATION 을 호출합니다. 입력은 자재번호와 OBTAB = 'MARA', 출력은 ALLOCATIONS 테이블입니다. 여기서 원하는 KLART(Classtype) 에 해당하는 행의 CLASS 컬럼이 “그 자재가 속한 클래스 이름” 입니다.
DATA: lt_allocations TYPE TABLE OF api_kssk WITH HEADER LINE.
CALL FUNCTION 'CLAP_DDB_GET_CLASSIFICATION'
EXPORTING
object = CONV kssk-objek( is_data-matnr ) " 자재번호
obtab = 'MARA' " MARA 객체
TABLES
allocations = lt_allocations.
" 배치 분류(KLART='023') 행의 클래스명 추출
DATA(lv_class) = lt_allocations[ klart = '023' ]-class.
자재 한 건이 여러 클래스에 묶여 있을 수 있으므로 (예: 자재 분류 + 배치 분류 동시 적용) 반드시 KLART 로 필터링해야 원하는 클래스를 정확히 잡습니다.
2단계 — 클래스의 특성과 객체값 조회
클래스명을 얻었으면 CLAF_CLASSIFICATION_OF_OBJECTS 로 그 클래스가 가진 특성과, 특정 객체의 특성값을 한꺼번에 조회할 수 있습니다.
DATA: lt_sclass TYPE TABLE OF sclass WITH HEADER LINE,
lt_clobjdat TYPE TABLE OF clobjdat WITH HEADER LINE.
CALL FUNCTION 'CLAF_CLASSIFICATION_OF_OBJECTS'
EXPORTING
class = lv_class " 1단계에서 얻은 클래스
classtype = '023' " 배치 분류
language = sy-langu
object = CONV ausp-objek( is_data-matnr )
objecttable = 'MARA'
key_date = sy-datum
classtext = 'X' " 클래스명 텍스트 포함
features = 'X' " 특성 포함
initial_charact = 'X' " 값 없는 특성도 포함
change_service_clf = 'X' " 변경마스터 고려
TABLES
t_class = lt_sclass " 클래스 정보
t_objectdata = lt_clobjdat. " 객체별 특성값
" 첫 번째 특성의 이름 추출
DATA(lv_atnam) = lt_clobjdat[ posnr = '1' ]-atnam.
핵심 파라미터:
FEATURES = 'X'와INITIAL_CHARACT = 'X'를 함께 설정 — 특성 정의 자체와 값이 비어있는 특성까지 모두 받아오기.KEY_DATE = sy-datum— 변경마스터(Change Number) 가 걸려 있는 경우 시점 기준 값을 가져옴.T_OBJECTDATA의POSNR은 특성의 표시 순서.ATNAM이 특성 이름,AUSP1등이 값.
3단계 — 배치 KEY 만들기 (MATNR + CHARG)
자재 클래스는 KEY 가 자재번호 한 개라 그대로 쓰면 되지만, 배치 분류(023) 는 MATNR + CHARG 두 필드가 KEY 라서 분류 BAPI 에 그대로 못 넘깁니다. BAPI_OBJCL_CONCATENATEKEY 로 두 필드를 한 줄짜리 KEY 로 합쳐야 합니다.
DATA: lt_keytab TYPE STANDARD TABLE OF bapi1003_object_keys
WITH HEADER LINE,
gt_ret TYPE STANDARD TABLE OF bapiret2 WITH HEADER LINE,
l_object TYPE bapi1003_key-object.
" 자재
lt_keytab-key_field = 'MATNR'.
lt_keytab-value_int = 'TEST-MAT-001'.
APPEND lt_keytab.
" 배치
lt_keytab-key_field = 'CHARG'.
lt_keytab-value_int = 'BATCH-001'.
APPEND lt_keytab.
CALL FUNCTION 'BAPI_OBJCL_CONCATENATEKEY'
EXPORTING
objecttable = 'MCH1'
IMPORTING
objectkey_conc = l_object " MATNR + CHARG 가 결합된 단일 KEY
TABLES
objectkeytable = lt_keytab
return = gt_ret.
결과로 받은 l_object 가 다음 단계 BAPI_OBJCL_CHANGE 의 OBJECTKEY 에 들어갑니다. 자재 클래스(001) 의 경우엔 이 단계를 건너뛰고 자재번호를 그대로 OBJECTKEY 로 써도 됩니다.
4단계 — 특성값 변경/생성
이제 실제 특성 값을 채워서 BAPI_OBJCL_CHANGE 를 호출합니다. 특성은 타입별로 3개의 테이블 — 문자형(Char) · 숫자형(Num) · 통화형(Curr) — 으로 나눠 전달합니다.
DATA: lt_value_tab TYPE STANDARD TABLE OF bapi1003_alloc_values_char
WITH HEADER LINE,
lt_value_tab2 TYPE STANDARD TABLE OF bapi1003_alloc_values_num
WITH HEADER LINE,
lt_value_tab3 TYPE STANDARD TABLE OF bapi1003_alloc_values_curr
WITH HEADER LINE.
" 문자형 특성 값 1건
lt_value_tab-charact = 'XX_FEATURE_KEY'. " 특성 이름
lt_value_tab-value_neutral = 'BATCH-001'. " 특성에 들어갈 값
APPEND lt_value_tab.
CALL FUNCTION 'BAPI_OBJCL_CHANGE'
EXPORTING
objectkey = l_object " 3단계 결과 (MATNR+CHARG)
objecttable = 'MCH1'
classnum = 'ZXX_BATCH_CL01' " 배치 특성을 담은 클래스명
classtype = '023' " 배치 분류
TABLES
allocvaluescharnew = lt_value_tab " 문자형 — 가장 자주 쓰임
allocvaluesnumnew = lt_value_tab2 " 숫자형 (없으면 빈 테이블)
allocvaluescurrnew = lt_value_tab3 " 통화형 (없으면 빈 테이블)
return = gt_ret.
" 분류 변경은 commit 필요
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
EXPORTING
wait = abap_true.
핵심 포인트 3가지:
- 변경 안 할 타입의 테이블도 선언은 해야 함. 빈 채로라도 TABLES 파라미터에 모두 넘겨야 BAPI 가 정상 동작.
VALUE_NEUTRAL은 SAP 내부 표현(언어 중립). 사용자에게 보이는 텍스트는VALUE_CHAR컬럼에 따로 들어가기도 함 — 보통 두 값을 같이 채움.BAPI_TRANSACTION_COMMIT를 호출하지 않으면 DB 에 반영되지 않음.wait = abap_true옵션은 후속 SELECT 에서 변경 결과를 곧바로 보고 싶을 때 사용.
특성 임시테이블 활용 — User-Exit 보완 패턴
특성을 “생성/변경하는 표준 흐름” 이 따로 없거나, 입고 시점에 EXIT 이 적당한 타이밍에 도는데 거기서 특성을 채워야 하는 경우엔 임시 테이블 + EXIT FM 조합을 자주 씁니다.
DATA: lt_char_buf TYPE TABLE OF ztxx_char_buf, " 특성 임시 버퍼 테이블
ls_char_buf TYPE ztxx_char_buf.
" CLAF_CLASSIFICATION_OF_OBJECTS 로 atnam 까지 얻어둔 상태
ls_char_buf-atnam = lv_atnam. " 특성 이름
ls_char_buf-atwrt = is_data-charg. " 채울 값
ls_char_buf-matnr = is_data-matnr.
ls_char_buf-charg = is_data-charg.
ls_char_buf-run_date = sy-datum.
APPEND ls_char_buf TO lt_char_buf.
" 임시 테이블 일괄 INSERT
IF lt_char_buf IS NOT INITIAL.
INSERT ztxx_char_buf FROM TABLE lt_char_buf.
IF sy-subrc = 0.
COMMIT WORK AND WAIT.
ELSE.
ROLLBACK WORK.
ENDIF.
ENDIF.
EXIT FM 안에서는 이 임시 테이블을 SELECT 해서 매칭되는 행을 찾아 실제 배치 특성으로 적재합니다. BAPI_OBJCL_CHANGE 를 EXIT 안에서 직접 호출하는 것보다, “데이터 준비”(메인 프로그램) 와 “특성 적재”(EXIT) 책임을 분리하기 위한 패턴입니다.
New Syntax (7.40+) 전체 흐름 한눈에
같은 4단계를 7.40 이후 신규 구문(VALUE #( ) · FILTER · 인라인 DATA( )) 으로 한 번에 보면:
" 1) 자재 클래스 찾기
DATA(lt_allocations) = VALUE kssk_t( ).
CALL FUNCTION 'CLAP_DDB_GET_CLASSIFICATION'
EXPORTING
object = CONV kssk-objek( is_data-matnr )
obtab = 'MARA'
TABLES
allocations = lt_allocations.
DATA(lv_class) = VALUE #(
FILTER #( lt_allocations USING KEY default
WHERE klart = '023' ) )[ 1 ]-class.
" 2) 클래스의 특성 조회
DATA(lt_sclass) = VALUE table_of_clas( ).
DATA(lt_clobjdat) = VALUE table_of_objdat( ).
CALL FUNCTION 'CLAF_CLASSIFICATION_OF_OBJECTS'
EXPORTING
class = lv_class
classtype = '023'
language = sy-langu
object = CONV ausp-objek( is_data-matnr )
objecttable = 'MARA'
key_date = sy-datum
classtext = 'X'
features = 'X'
initial_charact = 'X'
change_service_clf = 'X'
TABLES
t_class = lt_sclass
t_objectdata = lt_clobjdat.
DATA(lv_atnam) = VALUE #(
FILTER #( lt_clobjdat WHERE posnr = '1' ) )[ 1 ]-atnam.
" 3) 배치 KEY 결합 (MATNR + CHARG)
DATA(lt_keytab) = VALUE STANDARD TABLE OF bapi1003_object_keys
WITH EMPTY KEY (
( key_field = 'MATNR' value_int = 'TEST-MAT-001' )
( key_field = 'CHARG' value_int = 'BATCH-001' )
).
DATA: lv_object TYPE bapi1003_key-object,
gt_ret TYPE STANDARD TABLE OF bapiret2 WITH EMPTY KEY.
CALL FUNCTION 'BAPI_OBJCL_CONCATENATEKEY'
EXPORTING
objecttable = 'MCH1'
IMPORTING
objectkey_conc = lv_object
TABLES
objectkeytable = lt_keytab
return = gt_ret.
" 4) 특성값 변경
DATA(lt_value_tab) = VALUE STANDARD TABLE OF bapi1003_alloc_values_char
WITH EMPTY KEY (
( charact = 'XX_FEATURE_KEY' value_neutral = 'BATCH-001' )
).
DATA(lt_value_tab2) = VALUE STANDARD TABLE OF bapi1003_alloc_values_num
WITH EMPTY KEY.
DATA(lt_value_tab3) = VALUE STANDARD TABLE OF bapi1003_alloc_values_curr
WITH EMPTY KEY.
CALL FUNCTION 'BAPI_OBJCL_CHANGE'
EXPORTING
objectkey = lv_object
objecttable = 'MCH1'
classnum = 'ZXX_BATCH_CL01'
classtype = '023'
TABLES
allocvaluescharnew = lt_value_tab
allocvaluesnumnew = lt_value_tab2
allocvaluescurrnew = lt_value_tab3
return = gt_ret.
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
EXPORTING
wait = abap_true.
New Syntax 의 이점은 두 가지 — (a) 작업 변수 선언이 별도 줄로 빠지지 않고 인라인으로 들어가 코드가 짧아짐, (b) FILTER #( ... WHERE ... ) 로 LOOP/READ TABLE 없이 원하는 행을 바로 추출. 단, FILTER 결과가 비어 있으면 CX_SY_ITAB_LINE_NOT_FOUND 가 터지므로, 데이터가 없을 가능성이 있으면 옛 방식대로 READ TABLE ... WITH KEY + sy-subrc 체크가 안전합니다.
자주 빠뜨리는 함정
KLART 필터 누락
CLAP_DDB_GET_CLASSIFICATION 의 결과 테이블 ALLOCATIONS 에는 같은 자재가 여러 클래스 타입(001 · 023) 에 동시 등록된 경우 여러 줄이 옵니다. [ klart = '023' ] 같은 필터를 안 쓰면 첫 행이 잘못 잡혀서 자재 클래스 이름이 배치 클래스 자리에 들어가는 사고가 납니다.
BAPI_OBJCL_CHANGE 후 COMMIT 누락
분류 변경은 BAPI 단독으로는 DB 반영이 안 됩니다. 반드시 BAPI_TRANSACTION_COMMIT 호출. 그리고 호출 직후 SELECT 로 결과를 확인할 거라면 wait = abap_true 옵션도 꼭 같이.
MCH1 OBJECTKEY 직접 조합
“그냥 MATNR과 CHARG 를 문자열 연결로 합치면 되겠지” 라고 직접 만든 키는 SAP 내부 표현과 길이/패딩이 다를 수 있어 BAPI 가 객체를 못 찾습니다. 반드시 BAPI_OBJCL_CONCATENATEKEY 로 만들 것.
변경 안 할 타입 테이블 누락
BAPI_OBJCL_CHANGE 의 TABLES 파라미터에서 ALLOCVALUESNUMNEW · ALLOCVALUESCURRNEW 같이 “이번엔 안 쓰는” 테이블을 아예 빼면 활성화 오류 또는 런타임 에러. 빈 테이블이라도 선언하고 그대로 넘겨야 함.
VALUE_NEUTRAL vs VALUE_CHAR 혼동
문자형 특성에서 VALUE_NEUTRAL 은 SAP 내부 비교용, VALUE_CHAR 은 화면 표시용. 두 값을 같이 채우면 안전하지만, 둘이 다른 값으로 들어가면 화면과 DB 가 어긋나는 함정. 보통은 동일 값으로 통일.
요약
| 단계 | 호출 함수 | 결과 |
|---|---|---|
| 1 | CLAP_DDB_GET_CLASSIFICATION |
자재가 속한 클래스명 추출 (반드시 KLART 필터) |
| 2 | CLAF_CLASSIFICATION_OF_OBJECTS |
클래스의 특성(ATNAM) + 객체의 현재 값 조회 |
| 3 | BAPI_OBJCL_CONCATENATEKEY |
MATNR + CHARG → 단일 OBJECTKEY (배치 분류일 때만 필요) |
| 4 | BAPI_OBJCL_CHANGE + BAPI_TRANSACTION_COMMIT |
특성값 변경/생성 → DB 반영 |
| 선택 | 임시 테이블 + EXIT FM | 표준 흐름이 없을 때 데이터 준비와 적재 분리 (입고 EXIT 등) |
분류 ABAP 의 본질은 “객체 → 클래스 → 특성 → 값” 4계층을 함수 4개로 거슬러 올라갔다가 거꾸로 내려오며 값을 채우는 작업입니다. KLART 코드(023 = 배치) 와 OBJECTTABLE(MCH1) 의 짝, BAPI_TRANSACTION_COMMIT 누락 방지, 3타입 테이블(Char/Num/Curr) 모두 선언 — 이 셋만 챙기면 처음 다루는 분류 시나리오도 위 4단계 패턴으로 거의 그대로 풀립니다.
Disclaimer — 이 포스트는 실무 정리 노트를 바탕으로 AI 보조로 정리되었습니다.
분류(Classification) 관련 함수들의 세부 파라미터와 동작은 NetWeaver 버전(ECC 6.0 / S/4HANA on-premise · Cloud)에 따라 일부 변경이 있을 수 있으니, 실제 적용 시에는 해당 시스템의 클래스/특성 마스터 정의 상태와 함께 확인하시기 바랍니다.