** Claude로 작성됨
SAP BW란?
SAP가 만든 엔터프라이즈 데이터 웨어하우스(EDW) 플랫폼입니다.
ERP(SAP ECC/S4HANA) 등 다양한 소스에서 데이터를 추출·변환·적재(ETL)하여 분석/리포팅에 활용합니다.
데이터 레이어 구조
Source Systems → PSA → DSO(ODS) → InfoCube/ADSO → BEx/BO 리포트
↑ ↑
(Staging) (Data Marts)
| Staging | PSA (Persistent Staging Area) | 원천 데이터 원본 보관 - 가공 전 대기장소 |
| Integration | DSO / ADSO | 세부 트랜잭션 데이터 저장의 개념 (일반 원장 / 스타 스키마) |
| Reporting | InfoCube / ADSO | 집계/분석용 멀티차원 모델 |
핵심 객체
- DataSource : 소스시스템 추출 정의 (일반적인 ETL툴의 커넥터 개념 + 데이터 별 처리 방식 정의)
- InfoObject : 가장 기본 단위 (차원 - 텍스트 컬럼 정의/키피겨 - 숫자 컬럼 정의) - 컬럼의 스키마
- DSO (Data Store Object) : 행 기반 상세 데이터
- InfoCube : 스타스키마 기반 OLAP 큐브
- ADSO (Advanced DSO) : BW/4HANA에서 DSO+InfoCube 통합한 차세대 객체
- Transformation / DTP : ETL 변환 및 적재 규칙 (변환 규칙 : 컬럼의 변환, 값의 치환, 고급 ABAP 쿼리 적용 등)
DTP(Data Transfer Process)에서 full load(전체 적재), Delta load(증분 적재)의 개념이 정의되고 실행 조정됨 - Process Chain : Airflow DAG과 같은 개념의 워크플로우
ETL 흐름 (데이터 엔지니어 핵심)
ERP 시스템
↓ (DataSource가 정의한 방식으로 추출)
PSA ← 원본 데이터 그대로 보관, 에러 확인 가능
↓ (Transformation 규칙 적용)
DSO/ADSO ← 정제된 상세 데이터 저장 (트랜잭션 레벨)
↓ (Transformation 규칙 적용)
InfoCube/ADSO ← 분석용 집계 데이터
↓
BEx/BO/SAC ← 리포트, 대시보드
※ 위의 각 화살표 = DTP 실행
※ 전체 흐름 자동화 = Process Chain
데이터 엔지니어 관점 준비사항
| 소스 연결 정의 | DataSource 생성/설정 |
| 테이블/모델 설계 | InfoObject, ADSO 설계 |
| ETL 로직 작성 | Transformation Rule (*ABAP Routine : 아래 별도 설명 추가) |
| 파이프라인 실행 | DTP 실행 (**Delta 적재 설명 아래 별도 설명) |
| 스케줄링/자동화 | Process Chain 설계 (각 단계 작업별 퍼포먼스 튜닝 노하우 필요) |
| 모니터링 | RSA1(BW Admin), SM37(Job 모니터), ST22(덤프) 트랜잭션 코드 사용 |
3. 외부 연동 시 고려사항 (BW를 전사 데이터 플랫폼으로 사용중인 경우)
- BW → 외부 시스템 : Open ODS View, ODP(Operational Data Provisioning)
- 외부 → BW 적재 : BAPI, File Interface, DB Connect
- 클라우드 연동 : SAP BW Bridge (BW/4HANA ↔ Datasphere)
2026년 최근 트렌드
장기적으로 SAP Datasphere(구 SAP DW Cloud)로 전환을 유도 중, BW/4HANA와 Datasphere 간 브리지 연동 파악 필요
* 델타 적재
3가지 초기화 단계
[1단계] Init (초기화) [2단계] Delta (증분) [3단계] Full (필요시)
처음 한 번만 실행 이후 매번 실행 전체 재적재 필요할 때
전체 데이터 가져오기 + 마지막 Init/Delta 이후 Delta 포인터 무시하고
"여기까지 가져왔다" 표시 변경분만 가져오기 전체 다시 가져옴
⚠️ 중요: Init을 실행해야 Delta 포인터가 생성됩니다. Init 없이 Delta 실행하면 에러
델타 방식의 종류 (핵심)
SAP BW에서 델타를 감지하는 방식이 여러 가지입니다.
① After Image (AI) — 가장 일반적
변경 전: 고객A 매출 = 1000
변경 후: 고객A 매출 = 1500
Delta로 오는 것: 1500 (최종값 전체)
→ BW에서 기존 값을 덮어씀(Overwrite)
② Before Image + After Image (AIE) — 수정/삭제 추적
변경 전 레코드: 고객A 매출 1000 (Before Image, 부호 반전해서 -1000으로 전송)
변경 후 레코드: 고객A 매출 1500 (After Image, +1500으로 전송)
BW에서 합산: -1000 + 1500 = +500 반영
→ 수정/취소 트랜잭션 처리에 사용
③ Additive Delta — 누적 합산
월요일 Delta: 판매량 +100
화요일 Delta: 판매량 +50
수요일 Delta: 판매량 +30
BW에서 합산: 100 + 50 + 30 = 180
→ 증가분을 계속 더함 (판매 실적, 로그성 데이터)
④ Timestamp 기반 — 시간으로 변경 감지
%sql
-- 소스 테이블에 변경일시 컬럼이 있을 때
SELECT * FROM SALES_TABLE
WHERE LAST_CHANGED_AT > '마지막_Delta_실행_시각'
⑤ Change Log / Change Pointer — SAP 내부 방식
SAP ERP가 데이터 변경될 때마다
→ 변경 내역을 Change Log 테이블에 자동 기록
→ BW가 이 Log 테이블을 읽어서 Delta 처리
(SAP끼리 연결할 때 가장 안정적인 방법)
Delta 포인터
타임라인:
─────────────────────────────────────────────→ 시간
[Init 실행] [Delta #1] [Delta #2]
↑ ↑ ↑
전체 복사 여기까지 표시 여기까지 표시
"포인터 이동" "포인터 이동"
Delta 포인터 = "어디까지 가져왔는지 기억하는 책갈피"
⚠️ Delta 포인터는 DTP 성공 시에만 이동합니다. 실패하면 포인터가 그대로 → 다음 Delta 실행 시 실패 구간부터 다시 시도
실무에서 자주 겪는 Delta 문제
| Delta인데 데이터가 안 옴 | Init 안 했거나 포인터 손상 | Init 재실행 |
| 데이터 중복 적재 | Delta 실패 후 재실행 시 중복 | DSO의 Key 기반 Overwrite 확인 |
| Delta 누락 | 소스에서 Change Log 삭제됨 | Full Load 후 Init 재설정 |
| 시간 지연 | 소스 시스템 Timestamp 지연 | Safety Interval 설정 (통상 30분~1시간) |
** ABAP Routine
ABAP의 개념
ABAP(Advanced Business Application Programming) 은 SAP가 만든 자체 프로그래밍 언어
Transformation 안의 Routine 종류
┌─────────────────────────────────────────────────┐
│ Transformation │
│ │
│ [Start Routine] → 전체 패키지 단위 전처리 │
│ ↓ │
│ [Field Routine] → 특정 필드 하나씩 변환 │
│ ↓ │
│ [End Routine] → 전체 패키지 단위 후처리 │
│ ↓ │
│ [Expert Routine] → 위 3개를 모두 직접 제어 │
└─────────────────────────────────────────────────┘
Start Routine — "들어오는 데이터 묶음을 통째로 전처리"
%abap
" 전체 소스 데이터 패키지가 내부 테이블로 들어옴
" RESULT_PACKAGE = 소스에서 온 데이터 전체 묶음
START-OF-ROUTINE.
" 예시: 소스 데이터 중 취소 건(CANCEL_FLAG = 'X')은 아예 제거
DELETE RESULT_PACKAGE WHERE CANCEL_FLAG = 'X'.
" 예시: 특정 날짜 이전 데이터 제거
DELETE RESULT_PACKAGE WHERE POSTING_DATE < '20200101'.
END-OF-ROUTINE.
💡 언제 쓰나?
- 특정 조건의 레코드를 통째로 필터링할 때
- 소스 데이터를 일괄 전처리 해야 할 때
- 외부 테이블 한 번만 조회해서 전체에 활용할 때 (성능상 유리)
Field Routine — "필드 하나의 변환 로직"
%abap
" RESULT = 목적지 필드에 넣을 값
" SOURCE_FIELDS = 소스의 모든 필드
" ── 예시 1: 단순 매핑 ──────────────────────────
RESULT = SOURCE_FIELDS-CUSTOMER_ID.
" ── 예시 2: 조건부 변환 ─────────────────────────
IF SOURCE_FIELDS-SALES_REGION = 'KR'.
RESULT = '한국'.
ELSEIF SOURCE_FIELDS-SALES_REGION = 'US'.
RESULT = '미국'.
ELSE.
RESULT = '기타'.
ENDIF.
" ── 예시 3: 날짜 포맷 변환 ──────────────────────
" 소스: 'DD.MM.YYYY' → BW: 'YYYYMMDD'
DATA: LV_DATE TYPE CHAR10.
LV_DATE = SOURCE_FIELDS-POSTING_DATE. " '15.06.2024'
RESULT+0(4) = LV_DATE+6(4). " YYYY
RESULT+4(2) = LV_DATE+3(2). " MM
RESULT+6(2) = LV_DATE+0(2). " DD
" 결과: '20240615'
" ── 예시 4: NULL 처리 ───────────────────────────
IF SOURCE_FIELDS-AMOUNT IS INITIAL.
RESULT = 0.
ELSE.
RESULT = SOURCE_FIELDS-AMOUNT.
ENDIF.
End Routine — "적재 직전 최종 가공"
%abap
" RESULT_PACKAGE = 변환이 다 끝난 데이터 묶음
" 여기서 최종 조정 가능
END-OF-ROUTINE.
" 예시: 중복 제거 (같은 KEY를 가진 레코드)
SORT RESULT_PACKAGE BY CUSTOMER_ID PRODUCT_ID POSTING_DATE.
DELETE ADJACENT DUPLICATES FROM RESULT_PACKAGE
COMPARING CUSTOMER_ID PRODUCT_ID POSTING_DATE.
" 예시: 금액 합산 후 특정 임계값 이하 제거
DELETE RESULT_PACKAGE WHERE SALES_AMOUNT < 1000.
END-OF-ROUTINE.
실무에서 자주 쓰는 ABAP 패턴
패턴 1 — 다른 테이블 값 조회 (Lookup)
%abap
" 고객번호로 고객 등급을 별도 테이블에서 가져오는 경우
DATA: LV_GRADE TYPE CHAR2.
SELECT SINGLE CUST_GRADE
INTO LV_GRADE
FROM ZCUST_MASTER " Z로 시작 = 커스텀 테이블
WHERE CUSTOMER_ID = SOURCE_FIELDS-CUSTOMER_ID.
IF SY-SUBRC = 0. " SY-SUBRC: 조회 성공 여부 (0 = 성공)
RESULT = LV_GRADE.
ELSE.
RESULT = '미분류'.
ENDIF.
⚠️ 주의: Field Routine 안에서 DB 조회는 레코드 건수만큼 반복 실행됨 → 데이터가 많으면 Start Routine에서 한 번에 조회 후 내부 테이블로 캐싱 권장
패턴 2 — Start Routine에서 캐싱 (성능 최적화)
%abap
" Start Routine에서 마스터 테이블 전체를 한 번만 읽어서 캐싱
DATA: GT_CUST_MASTER TYPE TABLE OF ZCUST_MASTER. " 내부 테이블 선언
SELECT CUSTOMER_ID CUST_GRADE CUST_NAME
INTO TABLE GT_CUST_MASTER
FROM ZCUST_MASTER.
" 이후 Field Routine에서 GT_CUST_MASTER를 READ TABLE로 조회
" → DB 반복 조회 없이 메모리에서 빠르게 처리
패턴 3 — 에러 레코드 제거
%abap
" Start Routine에서 필수값 없는 레코드 제거
LOOP AT RESULT_PACKAGE ASSIGNING FIELD-SYMBOL(<LS_ROW>).
IF <LS_ROW>-CUSTOMER_ID IS INITIAL
OR <LS_ROW>-POSTING_DATE IS INITIAL.
DELETE RESULT_PACKAGE. " 현재 루프 레코드 삭제
ENDIF.
ENDLOOP.
ABAP 기본 문법 요약 (데이터 엔지니어용)
%abap
" ── 변수 선언 ────────────────────────────────────
DATA: LV_NAME TYPE CHAR50, " 문자열 50자
LV_AMOUNT TYPE P DECIMALS 2, " 소수점 2자리 숫자
LV_DATE TYPE DATS, " 날짜 (YYYYMMDD)
LV_FLAG TYPE CHAR1. " 플래그 (X / 공백)
" ── 조건문 ──────────────────────────────────────
IF LV_FLAG = 'X'.
" 참일 때
ELSEIF LV_FLAG = ' '.
" 다른 조건
ELSE.
" 나머지
ENDIF.
" ── 반복문 ──────────────────────────────────────
LOOP AT RESULT_PACKAGE INTO LS_ROW.
" 각 행 처리
ENDLOOP.
" ── DB 조회 ─────────────────────────────────────
SELECT SINGLE FIELD1 FIELD2
INTO (LV_VAR1, LV_VAR2)
FROM TABLE_NAME
WHERE KEY_FIELD = LV_KEY.
" ── 문자열 합치기 ────────────────────────────────
CONCATENATE LV_YEAR LV_MONTH LV_DAY INTO LV_DATE.
" 또는 (신문법)
LV_DATE = |{ LV_YEAR }{ LV_MONTH }{ LV_DAY }|.
데이터 엔지니어가 실수하기 쉬운 포인트
| Field Routine에서 매 레코드 SELECT | 100만번 DB 조회 → 극심한 성능 저하 | Start Routine에서 조회 후 내부 캐싱 |
| SY-SUBRC 확인 안 함 | DB 조회 실패해도 모르고 넘어감 | SELECT 후 항상 IF SY-SUBRC = 0 체크 |
| RESULT_PACKAGE 직접 수정 중 인덱스 오류 | LOOP 중 DELETE 잘못 쓰면 건너뜀 발생 | FIELD-SYMBOL 방식 사용 또는 역순 LOOP |
| 날짜 타입 혼용 | CHAR vs DATS 타입 다르면 비교 오류 | 타입 통일 후 비교 |