From Zero to SAP
[RAP 프로젝트 #2] ERD를 RAP Business Object로 바라보기: Root, Composition, Association 본문
[RAP 프로젝트 #2] ERD를 RAP Business Object로 바라보기: Root, Composition, Association
권쌥 2026. 4. 23. 21:01지난 글에서는 Fitness CRM 프로젝트의 핵심 도메인을 분석하고 ERD를 설계했다.
이번 글에서는 ERD에서 정의한 관계를 RAP 관점에서 다시 바라보려고 한다.
처음에는 단순히 테이블 간 FK 관계만 잘 잡으면 된다고 생각했다. 하지만 RAP에서는 데이터 모델을 단순 테이블 구조로만 보는 것이 아니라, 하나의 업무 흐름을 표현하는 Business Object 단위로 바라봐야 한다.
이번 글의 목표는 다음 질문에 답하는 것이다.
1. 왜 Member를 Root Business Object로 볼 수 있는가?
2. Membership은 왜 Member의 Composition인가?
3. Product는 왜 Composition이 아니라 Association인가?
4. Payment는 왜 Membership의 하위 객체인가?
5. 이 구조가 ERP 개발 관점에서 어떤 의미를 가지는가?
1. ERD와 RAP Business Object의 차이
ERD는 테이블 간 관계를 표현한다.
예를 들어 Fitness CRM의 ERD는 다음과 같은 관계를 가진다.
Member 1 : N Membership
Product 1 : N Membership
Membership 1 : N Payment
Member 1 : N Attendance
이 관계만 보면 "어떤 테이블이 어떤 테이블을 참조하는가"는 알 수 있다.
하지만 RAP에서는 여기서 한 단계 더 들어가야 한다.
RAP에서 중요한 것은 단순 FK 관계가 아니라, 하나의 업무 객체가 어떤 하위 데이터를 가지고 있고, 어떤 외부 데이터를 참조하는지 구분하는 것이다.
즉 ERD가 데이터의 관계를 보여준다면, RAP Business Object는 업무 흐름의 단위를 보여준다.
ERD
- 테이블 간 관계
- PK, FK 중심
- 데이터 구조 중심
RAP Business Object
- 업무 객체 단위
- Root, Composition, Association 중심
- 생성, 변경, 상태 변화 흐름 중심
이번 프로젝트에서는 ERD를 다음과 같은 RAP 구조로 해석하기로 했다.
Member Root Business Object
├─ Membership Composition
│ └─ Payment Composition
└─ Attendance Composition
Membership
└─ Product Association
2. Root Business Object란 무엇인가
RAP에서 Root Business Object는 단순히 가장 중요한 테이블을 의미하지 않는다.
또한 무조건 가장 먼저 생성되는 테이블을 의미하는 것도 아니다.
Root Business Object는 하나의 업무 시나리오에서 사용자가 가장 먼저 다루는 중심 객체이며, 관련 하위 데이터의 생성과 변경 흐름을 묶어주는 기준 객체라고 볼 수 있다.
예를 들어 Fitness CRM의 회원 관리 업무 흐름을 생각해보면 사용자는 보통 다음과 같은 순서로 시스템을 사용한다.
회원 조회
-> 회원 상세 확인
-> 회원의 이용권 확인
-> 회원의 결제 상태 확인
-> 회원의 출석 기록 확인
이 흐름에서 중심이 되는 객체는 Member다.
따라서 Fitness CRM의 회원 관리 시나리오에서는 Member를 Root Business Object로 두는 것이 자연스럽다.
3. 왜 Member를 Root로 두는가
Fitness CRM에서 Member를 Root Business Object로 두는 이유는 회원 관리 업무 흐름의 중심이 회원이기 때문이다.
사용자는 보통 특정 회원을 기준으로 이용권, 결제 이력, 출석 기록을 조회한다.
예를 들어 운영자가 "김OO 회원의 상태를 확인한다"고 하면, 단순히 회원 이름만 보는 것이 아니다.
김OO 회원은 어떤 이용권을 가지고 있는가?
이용권은 결제가 완료되었는가?
현재 출석 가능한 상태인가?
최근 출석 기록은 어떻게 되는가?
환불된 이용권은 없는가?
이 질문들은 모두 회원을 기준으로 이어진다.
따라서 Member를 Root로 두면 회원을 중심으로 Membership, Payment, Attendance 같은 관련 데이터를 하나의 업무 객체 흐름 안에서 다룰 수 있다.
여기서 주의할 점은 Member가 시스템에서 항상 가장 먼저 생성되는 데이터라는 뜻은 아니라는 것이다.
실제로는 Product가 먼저 등록될 수 있다.
예를 들어 관리자는 회원이 가입하기 전에도 헬스 1개월권, PT 10회권, 락커 1개월권 같은 상품을 미리 등록해둘 수 있다.
그럼에도 회원 관리 시나리오에서는 Member가 중심 객체이기 때문에 Root Business Object로 볼 수 있다.
4. Membership은 왜 Member의 Composition인가
Membership은 특정 회원이 특정 상품을 구매해서 얻게 된 이용권 또는 사용권이다.
즉 Membership은 독립적으로 존재하는 상품 정보가 아니라, 특정 회원에게 속한 사용 권리다.
회원이 존재하지 않는다면 "회원의 이용권"도 업무적으로 의미를 갖기 어렵다.
예를 들어 다음과 같은 데이터는 자연스럽다.
김OO 회원의 헬스 1개월권
이OO 회원의 PT 10회권
박OO 회원의 락커 1개월권
하지만 회원이 없는 이용권은 의미가 불분명하다.
누군가의 헬스 1개월권
소유자가 없는 PT 10회권
따라서 회원 관리 시나리오에서는 Membership을 Member의 하위 객체, 즉 Composition으로 보는 것이 자연스럽다.
RAP에서 Composition은 부모 객체와 자식 객체의 생명주기가 강하게 묶여 있는 관계다.
Member
└─ Membership
회원 상세 화면에서 해당 회원의 이용권을 함께 생성하거나 조회하는 흐름도 자연스럽다.
예를 들어 신규 회원을 등록한 뒤 바로 이용권을 추가하는 업무 흐름은 실제 운영에서도 충분히 발생할 수 있다.
회원 등록
-> 이용권 선택
-> Membership 생성
-> 결제 대기 상태
이처럼 Membership은 Member의 업무 흐름 안에서 생성되고 관리되므로 Composition 관계로 볼 수 있다.
5. Product는 왜 Association인가
처음에는 Membership 안에 상품명, 가격, 기간, 횟수 같은 정보를 모두 넣어도 되지 않을까 생각했다.
하지만 실제 운영을 생각하면 판매 가능한 상품은 별도의 기준 정보로 관리하는 것이 자연스럽다.
예를 들어 헬스장에는 다음과 같은 상품들이 있을 수 있다.
헬스 1개월권
헬스 3개월권
PT 10회권
락커 1개월권
운동복 대여권
이 상품들은 특정 회원이 가입하기 전에도 독립적으로 존재한다.
즉 Product는 특정 회원에게 종속된 데이터가 아니라, 판매 가능한 상품을 정의하는 마스터 데이터다.
만약 Product를 Member의 Composition으로 둔다면 이상한 구조가 된다.
회원이 등록되어야 상품이 존재할 수 있다는 의미가 되기 때문이다.
하지만 현실에서는 반대다.
헬스장에는 회원이 오기 전부터 이미 판매할 상품이 준비되어 있다.
또한 하나의 Product는 여러 회원의 Membership에서 반복적으로 참조될 수 있다.
Product: 헬스 1개월권
├─ 김OO 회원의 Membership
├─ 이OO 회원의 Membership
└─ 박OO 회원의 Membership
따라서 Product는 Membership의 일부가 아니라, Membership이 참조하는 기준 정보다.
이런 관계는 Composition이 아니라 Association으로 보는 것이 적절하다.
Membership
└─ Product 참조
정리하면 다음과 같다.
Composition
- 부모 객체의 일부
- 생명주기가 강하게 묶임
- 예: Member - Membership
Association
- 독립적인 객체를 참조
- 각자 생명주기를 가짐
- 예: Membership - Product
6. Payment는 왜 Membership의 하위 객체인가
Payment는 단순히 "회원이 돈을 냈다"는 사실만 기록하는 테이블이 아니다.
중요한 것은 어떤 이용권 구매 건에 대한 결제인지다.
Fitness CRM에서 이용권 구매는 Membership으로 표현된다.
그리고 Payment는 해당 Membership에 대한 결제 기록을 담당한다.
Member: 누가 구매했는가
Membership: 어떤 상품을 구매해서 어떤 사용 권리를 얻었는가
Payment: 그 구매 건에 대해 어떻게 결제했는가
Payment를 Member에 직접 연결하지 않아도 되는 이유는 Payment에서 Membership을 따라가면 결국 회원을 알 수 있기 때문이다.
Payment -> Membership -> Member
더 중요한 것은 결제 상태가 Membership의 상태 변화와 직접 연결된다는 점이다.
예를 들어 결제 대기 상태에서는 이용권을 사용할 수 없다.
결제가 완료되어야 이용권이 활성화되고, 환불되면 이용권은 더 이상 사용할 수 없는 상태가 된다.
Payment PENDING
-> Membership PENDING_PAYMENT
Payment COMPLETED
-> Membership ACTIVE
Payment REFUNDED
-> Membership REFUNDED
즉 Payment는 단순 조회용 참조 데이터가 아니라, Membership의 업무 흐름과 상태 변화를 함께 만들어가는 하위 객체다.
그래서 Payment는 Membership의 Composition으로 보는 것이 자연스럽다.
7. Attendance는 왜 Member의 하위 객체인가
Attendance는 회원의 출석 기록이다.
처음에는 출석 기록을 Membership에 연결하는 구조도 생각했다.
어떤 이용권을 사용해서 출석했는지 알 수 있으면 사용 횟수 차감이나 환불 정책을 더 엄밀하게 처리할 수 있기 때문이다.
하지만 실제 피트니스 센터의 출입 상황을 생각하면 출석은 특정 이용권 사용 기록이라기보다 회원의 입장 이벤트에 가깝다.
예를 들어 한 회원이 다음 상품들을 동시에 보유할 수 있다.
헬스 1개월권
PT 10회권
락커 1개월권
이 회원이 입장했을 때, 입장 행위가 헬스 이용권 때문인지 PT 수업 때문인지 항상 명확하게 구분되는 것은 아니다.
따라서 출석 기록은 Membership이 아니라 Member에 직접 연결하기로 했다.
Member
└─ Attendance
다만 출석 가능 여부를 판단할 때는 Membership을 확인해야 한다.
즉 Attendance는 회원의 출석 이벤트를 기록하고, Membership은 출석 가능 여부를 검증하는 근거 데이터로 사용된다.
회원이 입장한다.
-> Member 기준으로 Attendance를 생성한다.
-> 해당 Member가 ACTIVE Membership을 가지고 있는지 확인한다.
-> 유효한 이용권이 있으면 CHECKED_IN 처리한다.
-> 유효한 이용권이 없으면 FAILED 처리하고 사유를 기록한다.
8. 마스터 데이터와 트랜잭션 데이터
이번 설계를 하면서 가장 크게 와닿았던 부분은 마스터 데이터와 트랜잭션 데이터를 구분하는 관점이다.
Product는 마스터 데이터다.
현재 판매 중인 상품의 기준 정보를 관리한다.
상품명
기본 가격
기본 기간
기본 사용 횟수
상품 상태
반면 Membership, Payment, Attendance는 실제 업무에서 발생한 기록에 가깝다.
어떤 회원이 어떤 상품을 구매했는가
실제로 얼마를 결제했는가
언제 결제했는가
언제 출석했는가
출석이 성공했는가 실패했는가
여기서 중요한 점은 마스터 데이터가 나중에 변경되어도 과거 거래 기록이 흔들리면 안 된다는 것이다.
예를 들어 현재 Product의 가격이 100,000원이었다가 다음 달에 120,000원으로 변경될 수 있다.
그렇다고 과거에 100,000원으로 결제한 기록까지 120,000원으로 바뀌면 안 된다.
따라서 실제 결제 금액은 Payment에 남겨야 한다.
Product.price
- 현재 기준 판매 가격
Payment.amount
- 실제 거래 시점에 확정된 결제 금액
이 차이가 ERP 개발에서 매우 중요하다고 느꼈다.
ERP 시스템은 단순히 현재 화면에 데이터를 보여주는 시스템이 아니라, 실제 업무에서 발생한 거래와 상태 변화를 나중에도 정확히 추적할 수 있어야 하는 시스템이기 때문이다.
9. RAP 구현 관점에서의 예상 구조
아직 실제 CDS View Entity를 작성하기 전이지만, 지금까지 정리한 내용을 RAP 구조로 옮기면 다음과 같은 형태가 될 것 같다.
ZR_Member
- Root View Entity
- Membership Composition
- Attendance Composition
ZR_Membership
- Member의 Child View Entity
- Payment Composition
- Product Association
ZR_Payment
- Membership의 Child View Entity
ZR_Attendance
- Member의 Child View Entity
ZR_Product
- 별도 마스터 데이터 View Entity
- Membership에서 Association으로 참조
개념적으로는 다음과 같은 모습이다.
define root view entity ZR_Member
as select from zmember
composition [0..*] of ZR_Membership as _Membership
composition [0..*] of ZR_Attendance as _Attendance
{
key member_uuid,
member_no,
name,
status,
_Membership,
_Attendance
}
define view entity ZR_Membership
as select from zmembership
association to parent ZR_Member as _Member
composition [0..*] of ZR_Payment as _Payment
association [1] to ZR_Product as _Product
{
key membership_uuid,
member_uuid,
product_uuid,
start_date,
end_date,
status,
_Member,
_Payment,
_Product
}
위 코드는 최종 구현 코드라기보다는, 이번 설계를 RAP 구조로 옮기면 어떤 모양이 될지 이해하기 위한 초안이다.
다음 단계에서 실제 ABAP Dictionary Table과 CDS View Entity를 만들면서 정확한 문법과 활성화 오류를 함께 확인할 예정이다.
10. 이번 글에서 정리한 기준
이번 글에서 내가 잡은 기준은 다음과 같다.
Composition
이 데이터는 부모 객체의 일부인가?
부모 없이는 업무적으로 의미가 약한가?
부모 화면 안에서 함께 생성, 변경, 조회되는가?
상태 변화와 생명주기가 강하게 묶여 있는가?
Association
이 데이터는 독립적으로 존재할 수 있는가?
여러 객체에서 공통으로 참조되는가?
각자 별도의 생명주기를 가지는가?
마스터 데이터 성격이 강한가?
이 기준을 Fitness CRM에 적용하면 다음과 같다.
Member - Membership
-> Composition
-> 회원의 이용권은 특정 회원에게 종속된다.
Membership - Payment
-> Composition
-> 결제는 특정 이용권 구매 건의 하위 기록이다.
Member - Attendance
-> Composition
-> 출석 기록은 특정 회원의 입장 이벤트다.
Membership - Product
-> Association
-> 상품은 독립적인 마스터 데이터이며, 여러 Membership에서 참조된다.
11. 회고
이번 글을 정리하면서 ERD와 RAP 모델링은 비슷해 보이지만 관점이 다르다는 것을 느꼈다.
ERD를 설계할 때는 테이블을 어떻게 나눌지, FK를 어디에 둘지에 집중했다.
하지만 RAP Business Object 관점에서는 어떤 데이터가 하나의 업무 객체 안에서 함께 움직이는지, 어떤 데이터는 독립적으로 존재하면서 참조만 되는지 생각해야 했다.
특히 Product와 Membership을 나누는 과정에서 ERP 개발의 관점을 조금 느낄 수 있었다.
Product는 현재 기준 정보이고, Payment는 실제 거래 시점의 기록이다.
상품 가격이 나중에 바뀌더라도 과거 결제 금액은 바뀌면 안 된다.
이런 차이를 설계에 반영하는 것이 ERP 시스템에서 중요하다는 것을 알게 되었다.
다음 글에서는 이번에 정리한 Business Object 구조를 바탕으로 실제 ABAP Dictionary Table과 CDS View Entity를 생성해볼 예정이다.
'Project' 카테고리의 다른 글
| [RAP 프로젝트 #1] Fitness CRM 도메인 분석과 ERD 설계 (0) | 2026.04.22 |
|---|---|
| [RAP 프로젝트 #0] SAP BAS 환경 구축과 피트니스 CRM 프로젝트 시작 (0) | 2026.04.21 |