본문 바로가기
시스템 & 트랜잭션

[SAP ABAP] CBO 변경이력 CDHDR·CDPOS 구현 — SCDO 객체 등록·FM 호출 (SE11·SCDO)

by Song.sh 2026. 5. 18.

SAP에서 자체 개발한 CBO 테이블을 운영하다 보면, 결재함이나 주문서 같은 화면에서 사용자가 "이 데이터를 누가 언제 어떻게 변경했는지" 확인하고 싶어 하는 경우가 자주 발생합니다.

 

표준 트랜잭션(MM01·VA02·ME22N 등) 은 변경이력을 자동으로 CDHDR·CDPOS 에 저장하지만, 이는 SAP가 표준 프로그램 안에 변경 기록 로직을 이미 박아두었기 때문입니다. Z 테이블과 Z 프로그램은 동일한 기능을 쓰려면 데이터 element 설정 · SCDO 객체 정의 · Function Module 호출 까지 직접 구현해야 합니다.

 

이 글은 CBO 테이블 변경이력을 표준 CDHDR·CDPOS 에 적재하고 사용자 화면에서 조회할 수 있게 만드는 5단계 구현 절차와, 자주 혼동되는 Log Data Changes 옵션과의 차이를 정리한 메모입니다.


핵심 — DBTABLOG 와 CDHDR / CDPOS 는 서로 다른 메커니즘입니다

두 방식이 이름도 비슷하고 결과(변경이력 확인) 도 비슷해서 자주 혼동되지만, 트리거·저장 위치·조회 방법·구현 난이도가 모두 다릅니다.

항목 DBTABLOG CDHDR / CDPOS
트리거 SE13 의 Log Data Changes 체크 + 시스템 파라미터 rec/client 활성화 Data Element 의 Change Document 체크 + SCDO 객체 정의 + 프로그램에서 FM 호출
저장 테이블 DBTABLOG CDHDR(헤더) · CDPOS(필드별 전/후)
조회 방법 SCU3 트랜잭션 (관리자 권한) 표준 TX 의 Environment → Changes 메뉴 · RSSCD100 프로그램
묶음 단위 테이블 row 단위 업무 객체(예: 결재문서 1건) 단위로 묶임
구현 난이도 체크박스 한 번 — 코딩 불필요 3단계 구현 — ABAP 코딩 필요
일반 사용자 노출 불가 (관리자용 감사 로그) 가능 (업무 화면에서 직접 조회)

Log Data Changes 체크는 CDHDR·CDPOS 에 자동 적재해 주지 않으며, 반대로 SCDO 객체 정의가 DBTABLOG 에 기록을 남기지도 않습니다. 한쪽을 켰다고 다른 한쪽이 같이 작동하는 게 아닙니다. 둘 다 원하면 두 가지를 모두 별도로 설정해야 하며, 실무에서 중요한 CBO 테이블은 감사용(DBTABLOG) + 사용자 노출용(CDHDR) 둘 다 적용하기도 합니다.

이 글은 사용자 화면 노출용인 CDHDR·CDPOS 방식의 구현 절차를 다룹니다.


1단계 — Data Element 에 Change Document 체크 (SE11)

추적할 필드의 데이터 element 에 변경 추적 자격을 먼저 부여합니다. 이 단계 없이 SCDO 객체에 테이블을 등록해도 해당 필드는 CDPOS 에 기록되지 않습니다.

SE11 → Data type → 데이터 element 이름 입력 (예: ZAPPSTA) → Change
  → "Further Characteristics" 탭
  → ☑ Change document        ← 체크
  → 저장 + 활성화 (Ctrl + F3)

 

예시) CBO 테이블 ZTXX0123(결재문서) 기준으로, 결재 흐름에서 변경되는 핵심 필드인 APPSTA(승인 상태) · APPROVER1~APPROVER3(승인자) 등의 데이터 element 를 우선 체크합니다. 사용자가 결재함 화면에서 변경 흐름을 직접 확인해야 하는 필드만 골라 체크하면 됩니다.


2단계 — SCDO 에서 Change Document Object 생성

SCDO 객체는 변경이력을 묶어서 보여주는 업무 단위 정의입니다. "이 결재문서 한 건의 변경이력" 처럼 한 화면에 묶여 보이려면 SCDO 객체가 그 묶음의 식별자가 됩니다.

SCDO → "Change document objects" 입력란에 이름 (예: ZTXX0123) → Create
  → Object description: "결재문서 변경이력"
  → Table 등록: ZTXX0123
      ☑ Insertion documentation     (INSERT 시 기록)
      ☑ Deletion documentation      (DELETE 시 기록)
      ☑ Single field documentation  (필드별 변경 기록)
  → 저장 → Generate update pgm. → 패키지·트랜스포트 입력
  → 자동 생성됨: ZTXX0123_WRITE_DOCUMENT (Function Module)

 

세 가지 documentation 옵션의 의미는 다음과 같습니다.

옵션 의미
Insertion documentation INSERT 시 CDPOS 에 신규 row 기록
Deletion documentation DELETE 시 CDPOS 에 삭제 row 기록
Single field documentation UPDATE 시 변경된 필드별로 전 / 후 값 기록

세 가지 모두 켜두는 것이 표준입니다. 옵션을 끄면 해당 유형의 변경은 CDPOS 에 기록되지 않습니다.


3단계 — 자동 생성된 Function Module 확인 (SE37)

SCDO 활성화 시 Write Function Module 한 개가 자동 생성됩니다. 호출 인자를 미리 확인해 두면 다음 단계의 프로그램 작성이 편해집니다.

SE37 → Function module: ZTXX0123_WRITE_DOCUMENT → Display

 

인자 의미
OBJECTID 변경이력을 묶을 업무 키 (예: 결재문서 번호). CDHDR.OBJECTID 에 그대로 저장됨
TCODE 변경 시점의 트랜잭션 코드 — 보통 sy-tcode 그대로 전달
UTIME · UDATE 변경 시각 — sy-uzeit · sy-datum
USERNAME 변경자 — sy-uname
N_<테이블명> 변경 후 (NEW) 데이터 — 구조 또는 인터널 테이블
O_<테이블명> 변경 전 (OLD) 데이터
UPD_<테이블명> 작업 구분 — U(Update) · I(Insert) · D(Delete)

FM 은 내부에서 OLD / NEW 를 자동으로 비교해 차이 나는 필드만 CDPOS 에 기록합니다. 호출자가 "어떤 필드가 바뀌었는지" 일일이 계산할 필요는 없습니다.


4단계 — 프로그램에서 FM 호출

호출 순서는 OPEN → WRITE → CLOSE 세 단계입니다. 표준 트랜잭션이 변경이력을 자동으로 남기는 것처럼 보이는 이유는, SAP 가 표준 프로그램 안에 이 세 호출을 이미 박아두었기 때문입니다. Z 프로그램에서는 동일 흐름을 직접 작성합니다.

DATA: ls_new TYPE ztxx0123,
      ls_old TYPE ztxx0123.

* 1) 변경 전 데이터 읽기 (OLD)
SELECT SINGLE * FROM ztxx0123
  INTO ls_old
  WHERE docid = lv_docid.

* 2) 변경 후 데이터 구성 (NEW)
ls_new = ls_old.
ls_new-appsta    = 'A'.        " 1차 승인 처리
ls_new-approver1 = sy-uname.
ls_new-apdat1    = sy-datum.
ls_new-aptim1    = sy-uzeit.

* 3) DB UPDATE
UPDATE ztxx0123 FROM ls_new.

* 4) Change Document 기록
CALL FUNCTION 'CHANGEDOCUMENT_OPEN'
  EXPORTING
    objectclass = 'ZTXX0123'
    objectid    = lv_docid.

CALL FUNCTION 'ZTXX0123_WRITE_DOCUMENT'
  EXPORTING
    objectid     = lv_docid
    tcode        = sy-tcode
    utime        = sy-uzeit
    udate        = sy-datum
    username     = sy-uname
    n_ztxx0123   = ls_new
    o_ztxx0123   = ls_old
    upd_ztxx0123 = 'U'.

CALL FUNCTION 'CHANGEDOCUMENT_CLOSE'.

COMMIT WORK.

핵심 포인트:

  • OBJECTID 는 사용자 화면에서 "이 문서의 변경이력" 으로 묶어 볼 단위 키입니다. 결재문서면 결재번호, 주문서면 주문번호처럼 업무 의미 있는 단일 키로 지정합니다.
  • OPEN · WRITE · CLOSE 세 호출은 항상 묶음으로 다룹니다. CLOSE 누락 시 같은 세션의 다음 변경이 같은 묶음에 잘못 합쳐질 수 있습니다.
  • COMMIT WORK 직전까지 기록은 임시 큐에 쌓이며, COMMIT 시점에 CDHDR · CDPOS 로 일괄 적재됩니다. 데이터 UPDATE 와 같은 트랜잭션 안에서 처리해야 정합성이 보장됩니다.

5단계 — 변경이력 조회 (RSSCD100 또는 자체 화면)

조회 방식은 두 가지입니다. 단발 조회·디버깅이 목적이면 표준 프로그램을 그대로 실행하고, 사용자 업무 화면에 노출하려면 자체 화면에서 표준 FM 을 호출합니다.

방식 A — 표준 프로그램 RSSCD100

SE38 → RSSCD100 → Execute
  Object Class : ZTXX0123
  Object Value : (결재문서번호)
  Date / Time  : (기간 지정)
  → Execute

실행하면 "누가 · 언제 · 어떤 필드를 어떤 값에서 어떤 값으로 바꿨는지" 가 한 줄씩 출력됩니다. 일반 사용자에게도 권한 부여 시 조회 가능합니다.

 

방식 B — 자체 화면에서 변경이력 보기 버튼 구현

자체 결재함이나 주문서 조회 화면에 "변경이력 보기" 버튼을 만들 때 사용합니다. 표준 FM 두 개를 조합합니다.

DATA: lt_cdhdr TYPE STANDARD TABLE OF cdhdr,
      lt_cdpos TYPE STANDARD TABLE OF cdpos.

CALL FUNCTION 'CHANGEDOCUMENT_READ_HEADERS'
  EXPORTING
    objectclass = 'ZTXX0123'
    objectid    = lv_docid
  TABLES
    i_cdhdr     = lt_cdhdr.

LOOP AT lt_cdhdr INTO DATA(ls_cdhdr).
  CALL FUNCTION 'CHANGEDOCUMENT_READ_POSITIONS'
    EXPORTING
      changenumber = ls_cdhdr-changenr
    TABLES
      editpos      = lt_cdpos.
  " 결과를 ALV · 테이블 컨트롤 등에 표시
ENDLOOP.

조회 결과에서 핵심 컬럼은 CDPOS-CHNGIND(변경 유형 표시) 입니다.

CHNGIND 의미
U Update — 필드 값 변경 (전 / 후 값 모두 기록됨)
I Insert — 신규 row 추가
D Delete — row 삭제 (삭제 직전 값이 기록됨)

흔히 빠뜨리는 함정

Data Element 만 체크하고 SCDO 등록 안 함

데이터 element 의 Change document 체크는 추적 자격을 부여하는 것일 뿐, 그 자체로 CDPOS 에 기록되지 않습니다. SCDO 객체에 테이블 · 필드 등록까지 마쳐야 합니다.

SCDO 등록만 하고 FM 호출 안 함

SCDO 활성화 후 FM (ZTXX0123_WRITE_DOCUMENT) 이 자동 생성되지만, 프로그램이 그것을 호출하지 않으면 CDHDR 는 한 줄도 쌓이지 않습니다. SAP 가 자동으로 가로채는 메커니즘이 아닙니다.

Log Data Changes 옵션과 혼동

Log Data Changes 체크는 DBTABLOG 에 기록되는 별개 메커니즘으로, SCU3 트랜잭션으로 조회합니다. CDHDR · CDPOS 와 저장 위치 · 조회 방법 · 구현 절차가 모두 다릅니다. 한쪽을 켰다고 다른 한쪽이 같이 작동하지 않으므로, 두 가지 모두 원하면 둘 다 별도로 설정해야 합니다.

필드 추가 후 SCDO 재생성 안 함

CBO 테이블에 필드를 추가한 후 SCDO 에서 Generate update pgm. 을 다시 실행하지 않으면 새 필드는 추적되지 않습니다. 테이블 구조 변경 시 SCDO 재생성을 워크플로우에 포함시켜야 합니다.

CHANGEDOCUMENT_CLOSE 누락

OPEN 만 부르고 CLOSE 안 부르면 같은 세션의 다음 변경 기록이 잘못된 객체에 묶일 수 있습니다. 함수 내부에서 예외 발생 시에도 CLOSE 가 호출되도록 흐름을 구성합니다.

WRITE_DOCUMENT 중간에 COMMIT WORK 끼우기

OPEN ~ CLOSE 사이에 별도 COMMIT WORK 를 끼우면 데이터 변경은 반영됐는데 이력만 빠지는 사태가 발생할 수 있습니다. 호출 측 프로그램이 마지막에 한 번만 COMMIT WORK 를 호출해야 합니다.

OBJECTID 키 누락 또는 임의 값

OBJECTID 를 빈 값으로 호출하면 CDHDR 에 빈 키로 기록되어, 사용자 화면에서 "어떤 문서의 변경인지" 식별이 불가능합니다. 업무 의미 있는 단일 키를 반드시 채워서 전달합니다.


전체 코드 — 복사용 통합본

결재 1차 승인 처리 시점에 변경이력까지 함께 기록하는 표준 흐름입니다. SE38 에 그대로 붙여넣어 실행 가능한 형태로 구성했습니다.

REPORT z_cdhdr_demo.

* ★ 사전 작업
* 1) SE11 → 추적할 필드의 Data Element 에 Change document 체크
* 2) SCDO → ZTXX0123 객체 생성 → 테이블 등록 → Generate update pgm.
* 3) 위 두 단계 완료 후 본 프로그램 실행

PARAMETERS: p_docid TYPE ztxx0123-docid OBLIGATORY.

DATA: ls_new TYPE ztxx0123,
      ls_old TYPE ztxx0123.

* 1) 변경 전 데이터 읽기 (OLD)
SELECT SINGLE * FROM ztxx0123
  INTO ls_old
  WHERE docid = p_docid.

IF sy-subrc <> 0.
  WRITE: / '해당 문서가 존재하지 않습니다:', p_docid.
  RETURN.
ENDIF.

* 2) 변경 후 데이터 (NEW) — 1차 승인 처리
ls_new = ls_old.
ls_new-appsta    = 'A'.
ls_new-approver1 = sy-uname.
ls_new-apdat1    = sy-datum.
ls_new-aptim1    = sy-uzeit.

* 3) DB UPDATE
UPDATE ztxx0123 FROM ls_new.

IF sy-subrc <> 0.
  WRITE: / 'UPDATE 실패'.
  ROLLBACK WORK.
  RETURN.
ENDIF.

* 4) Change Document 기록 — OPEN → WRITE → CLOSE 묶음
CALL FUNCTION 'CHANGEDOCUMENT_OPEN'
  EXPORTING
    objectclass = 'ZTXX0123'
    objectid    = p_docid.

CALL FUNCTION 'ZTXX0123_WRITE_DOCUMENT'
  EXPORTING
    objectid     = p_docid
    tcode        = sy-tcode
    utime        = sy-uzeit
    udate        = sy-datum
    username     = sy-uname
    n_ztxx0123   = ls_new
    o_ztxx0123   = ls_old
    upd_ztxx0123 = 'U'.

CALL FUNCTION 'CHANGEDOCUMENT_CLOSE'.

* 5) ★ 핵심: 한 트랜잭션으로 일괄 COMMIT — 데이터와 이력이 같이 적재됨
COMMIT WORK.

WRITE: / '결재 1차 승인 처리 완료.'.
WRITE: / 'RSSCD100 또는 자체 화면에서 변경이력 조회 가능.'.

변경이력 조회 화면을 자체 프로그램으로 구현하는 예시는 5단계 방식 B 의 코드 블록을 참고합니다.


요약

단계 작업 핵심
1 SE11 데이터 element Further Characteristics → Change document 체크
2 SCDO 객체 생성 테이블 등록 + 세 documentation 옵션 체크 + Generate
3 SE37 FM 확인 자동 생성된 <obj>_WRITE_DOCUMENT 인자 검토
4 프로그램 FM 호출 OPEN → WRITE_DOCUMENT(OLD · NEW 전달) → CLOSE → COMMIT
5 이력 조회 RSSCD100 또는 자체 화면에서 CHANGEDOCUMENT_READ_HEADERS / POSITIONS 호출

CBO 테이블의 변경이력을 사용자 화면에 노출하려면 CDHDR · CDPOS 기반 Change Document 메커니즘을 직접 구현해야 합니다. 데이터 element 자격 부여, SCDO 객체 정의, 자동 생성된 FM 호출까지 세 단계를 모두 마쳐야 작동하며, 그 결과 표준 트랜잭션의 자동 동작과 같은 화면 경험을 사용자에게 제공할 수 있습니다. 비슷한 이름의 Log Data Changes 옵션은 DBTABLOG 기반 별개 메커니즘이므로 혼동하지 않도록 주의합니다.


Disclaimer — 이 포스트는 실무 정리 노트를 바탕으로 AI 보조로 정리되었습니다.

SCDO · SE11 · SE37 · RSSCD100 트랜잭션 · 프로그램과 CHANGEDOCUMENT_OPEN · CHANGEDOCUMENT_CLOSE · CHANGEDOCUMENT_READ_HEADERS · CHANGEDOCUMENT_READ_POSITIONS 함수 모듈은 SAP NetWeaver 표준 기능으로 시스템 버전 의존 없이 동작합니다. CDHDR · CDPOSTCDOB · TCDRP 마스터에 등록된 객체 구조를 따르며, 변경이력 적재는 COMMIT WORK 시점에 일괄 처리되므로 자체 프로그램에서 중간 COMMIT 호출 시 데이터와 이력의 정합성이 깨질 수 있으니 트랜잭션 경계를 신중히 관리하시기 바랍니다.