1. 개요 및 이 글에서 다룰 것
ABAP 리포트를 만들다 보면 데이터 조회 → 가공 → 출력이라는 동일한 순서가 끝없이 반복됩니다. 매출 리포트, 재고 리포트, 미수금 리포트 모두 큰 흐름은 같고, 안에서 다루는 SELECT 문과 출력 양식만 달라집니다. 이 반복 구조에서 골격은 한 곳에서 고정하고, 변하는 단계만 자식 클래스가 채워 넣게 하는 객체지향 디자인 패턴이 바로 Template Method 패턴입니다.
이 글에서 다룰 것은 다음과 같습니다.
- Template Method 패턴이 무엇이며, 왜 ABAP OO와 잘 맞는지
ABSTRACT METHODS와REDEFINITION으로 골격을 고정하는 방법- 매출 리포트 클래스를 단계별로 작성하는 실전 예제
- 현업에서 자주 만나는 에러와 처리 방식
- 전략(Strategy) 패턴 / RAP 핸들러와의 차이
2. 이 글을 보기 전에
이 글은 ABAP Objects의 클래스 선언, 인스턴스 생성(NEW), 메서드 호출 문법을 알고 있다고 가정합니다. 또한 Open SQL의 SELECT ... INTO TABLE @DATA(lt_...) 형태, 그리고 cl_demo_output 같은 간단한 출력 클래스를 한 번이라도 다뤄봤다면 따라오기 편합니다. ABAP 770 이상에서 도입된 인라인 선언 문법을 사용하지만, 핵심 개념은 7.40 이전 환경에도 그대로 적용할 수 있습니다.
3. 환경 / 버전 / 준비물
- ABAP 릴리스: 7.55 이상 권장 (인라인
@DATA,NEW연산자 사용). 7.40 SP08부터도 대부분 동작 - 에디션: SAP S/4HANA On-Premise, SAP BTP ABAP Environment(Steampunk) 모두 가능. 단, Steampunk에서는 릴리즈된 API만 사용해야 하므로 예제의 테이블/CDS는 사용자 정의 또는 릴리즈된 객체로 바꿔 사용하세요.
- 개발 도구: ABAP Development Tools(ADT) for Eclipse 권장. SE80/SE24 로컬 클래스 풀에서도 동일하게 작성 가능
- 권한: 클래스 생성 및 패키지 할당 권한, 테스트용 트랜잭션 또는 ADT의 F9 실행 권한
- 샘플 테이블: 예제에서는 가상의
zsales테이블을 사용합니다. 실습 시에는vbak이나 사내 매출 테이블로 치환하세요.
4. 핵심 개념
Template Method 패턴은 GoF 디자인 패턴 중 행위(Behavioral) 패턴에 속합니다. 핵심 아이디어를 한 줄로 요약하면 다음과 같습니다.
알고리즘의 큰 흐름은 상위(추상) 클래스가 정의하고, 그 안의 변하는 세부 단계는 하위 클래스가 재정의(REDEFINITION)한다.
레시피에 비유하면 이해가 쉽습니다. 카페에서 음료를 만드는 과정은 물 끓이기 → 재료 추출 → 컵에 따르기 → 토핑 올리기로 늘 같습니다. 커피냐 차냐에 따라 "재료 추출"과 "토핑"의 구체적인 동작만 달라질 뿐, 순서 자체는 바뀌지 않습니다. 카페 매뉴얼이 "순서"를 강제하고, 바리스타는 "무엇을 추출할지"만 결정하는 구조입니다.
ABAP에서는 이 골격을 추상 클래스의 공개 메서드 하나(보통 run 또는 execute)로 만들고, 그 안에서 단계별 메서드를 순서대로 호출합니다. 각 단계 메서드는 ABSTRACT로 선언해 "하위 클래스가 반드시 구현해야 한다"는 계약을 강제합니다.
골격 메서드와 훅(Hook) 메서드
- Template Method: 호출 순서가 박혀 있는 메서드. 일반적으로
FINAL로 닫아 자식이 순서를 바꾸지 못하게 합니다. - Abstract Step: 반드시 구현해야 하는 단계.
ABSTRACT METHODS로 선언합니다. - Hook: 기본 구현은 비어 있지만 자식이 선택적으로 끼워 넣을 수 있는 확장 지점. 예)
before_run,after_run
다른 패턴과의 비교
전략(Strategy) 패턴은 "알고리즘 전체를 통째로 교체"하지만, Template Method는 "틀은 그대로 두고 일부 단계만 교체"합니다. 상속 기반(Template Method)인지 컴포지션 기반(Strategy)인지가 결정적인 차이입니다. 작은 변형이 많고 흐름이 명확히 고정될 때는 Template Method가, 알고리즘이 자주 통째로 갈아끼워질 때는 Strategy가 어울립니다.
5. 실전 코드 3단계
5-1단계: 추상 베이스 클래스 선언
먼저 모든 리포트 클래스의 공통 흐름을 정의합니다. run( )이 골격을 들고 있고, 세 단계 fetch_data, process, output은 추상 메서드로만 선언합니다.
CLASS lcl_report_base DEFINITION ABSTRACT.
PUBLIC SECTION.
METHODS run FINAL.
PROTECTED SECTION.
" 하위 클래스가 반드시 구현해야 하는 단계
METHODS fetch_data ABSTRACT.
METHODS process ABSTRACT.
METHODS output ABSTRACT.
" 선택적으로 끼워 넣는 훅 (기본은 빈 구현)
METHODS before_run.
METHODS after_run.
ENDCLASS.
CLASS lcl_report_base IMPLEMENTATION.
METHOD run.
before_run( ).
fetch_data( ).
process( ).
output( ).
after_run( ).
ENDMETHOD.
METHOD before_run.
" 기본은 비어 있음. 자식이 필요할 때만 REDEFINITION
ENDMETHOD.
METHOD after_run.
ENDMETHOD.
ENDCLASS.run을 FINAL로 닫은 점이 중요합니다. 자식이 흐름 자체를 바꾸지 못하도록 막아 패턴의 의도를 지킵니다. 추상 메서드는 PROTECTED SECTION에 두는 게 일반적입니다. 외부에서 직접 단계만 호출하는 것을 막고, 상속 체인 안에서만 보이게 하기 위함입니다.
5-2단계: REDEFINITION으로 매출 리포트 구현 (에러/로깅 포함)
실제 매출 리포트 클래스를 만들어 봅니다. 데이터 조회 단계에서 권한 체크와 예외 처리를, 가공 단계에서 합계 계산을, 출력 단계에서 cl_demo_output을 사용합니다.
CLASS lcx_report_error DEFINITION INHERITING FROM cx_static_check.
ENDCLASS.
CLASS lcl_sales_report DEFINITION INHERITING FROM lcl_report_base.
PUBLIC SECTION.
METHODS constructor IMPORTING iv_year TYPE i.
PROTECTED SECTION.
METHODS fetch_data REDEFINITION.
METHODS process REDEFINITION.
METHODS output REDEFINITION.
METHODS before_run REDEFINITION.
PRIVATE SECTION.
DATA mv_year TYPE i.
DATA mt_sales TYPE STANDARD TABLE OF zsales WITH EMPTY KEY.
DATA mv_total TYPE p LENGTH 15 DECIMALS 2.
ENDCLASS.
CLASS lcl_sales_report IMPLEMENTATION.
METHOD constructor.
super->constructor( ).
mv_year = iv_year.
ENDMETHOD.
METHOD before_run.
" 시작 시점 로깅
cl_demo_output=>write( |Sales report start: year { mv_year }| ).
ENDMETHOD.
METHOD fetch_data.
TRY.
SELECT customer, amount, currency
FROM zsales
WHERE year = @mv_year
INTO TABLE @mt_sales.
IF sy-subrc <> 0 OR mt_sales IS INITIAL.
RAISE EXCEPTION TYPE lcx_report_error.
ENDIF.
CATCH cx_sy_open_sql_db INTO DATA(lo_db_err).
" DB 단계 오류는 비즈니스 예외로 래핑
RAISE EXCEPTION TYPE lcx_report_error.
ENDTRY.
ENDMETHOD.
METHOD process.
mv_total = 0.
LOOP AT mt_sales ASSIGNING FIELD-SYMBOL(<ls>).
mv_total = mv_total + <ls>-amount.
ENDLOOP.
ENDMETHOD.
METHOD output.
cl_demo_output=>write_data( mt_sales ).
cl_demo_output=>write( |Total: { mv_total NUMBER = USER }| ).
cl_demo_output=>display( ).
ENDMETHOD.
ENDCLASS.주목할 부분은 세 가지입니다. 첫째, REDEFINITION 키워드만 붙이면 부모의 추상 메서드를 구현한 것으로 인정됩니다. 둘째, DB 예외를 그대로 던지지 않고 lcx_report_error로 래핑해 호출자가 일관된 예외만 보도록 합니다. 셋째, before_run 훅을 재정의해 시작 로그를 남겼습니다. 다른 리포트는 이 훅을 안 건드리면 그만입니다.
5-3단계: 실행 및 프로덕션 고려사항
실제 호출은 한 줄입니다. 추상 클래스인 lcl_report_base는 직접 인스턴스화할 수 없고, 반드시 자식 클래스를 통해야 합니다.
START-OF-SELECTION.
TRY.
DATA(lo_report) = NEW lcl_sales_report( iv_year = 2025 ).
lo_report->run( ).
CATCH lcx_report_error INTO DATA(lo_err).
MESSAGE 'Sales report failed' TYPE 'E'.
ENDTRY.프로덕션 환경에서는 다음 항목을 추가로 고려하세요.
- 성능:
fetch_data에서 큰 테이블을 다룰 때는PACKAGE SIZE커서 루프나 CDS 뷰로 옮기는 편이 안전합니다. 골격이 바뀌지 않으므로 자식만 교체하면 됩니다. - 테스트: ABAP Unit으로
process메서드만 따로 검증하려면 자식 클래스를 만들고fetch_data를 모의(mock) 구현으로 채우면 됩니다. Template Method가 단위 테스트 가독성을 크게 높여 줍니다. - 보안: 권한 체크(
AUTHORITY-CHECK)는 베이스의before_run에서 호출하도록 강제하면, 모든 리포트가 자동으로 권한 검사를 통과합니다. - 일관성: 새 리포트(예:
lcl_inventory_report)를 추가할 때 흐름을 다시 짤 필요 없이 세 메서드만 구현하면 됩니다. 코드 리뷰 포인트도 "세 단계 구현이 빠짐없이 있는가"로 단순해집니다.
6. 흔한 실수 / 트러블슈팅
Q1. "Method ... is not implemented" 컴파일 오류가 납니다.
부모의 ABSTRACT METHODS 중 하나라도 자식에서 REDEFINITION하지 않으면 자식 클래스도 추상이 되어 인스턴스화할 수 없습니다. 세 단계 모두 구현했는지 다시 확인하세요. 자식을 다시 추상으로 두려면 클래스 선언에 ABSTRACT를 명시해야 합니다.
Q2. run 안에서 단계 순서를 바꾸고 싶은데 막혀 있습니다.
run이 FINAL이라 의도된 동작입니다. 만약 순서가 자주 바뀌어야 한다면 Template Method가 잘못 선택된 신호입니다. 그 경우 Strategy 패턴이나 Pipeline 구조(단계 객체 리스트를 순회)를 검토하세요.
Q3. 추상 메서드를 PRIVATE SECTION에 두면 안 되나요?
안 됩니다. ABAP에서 추상 메서드는 자식이 재정의해야 하므로 PRIVATE일 수 없습니다. PROTECTED가 일반적인 선택이며, 외부에서 단계만 호출할 일이 있다면(테스트 등) PUBLIC도 가능합니다.
Q4. 훅(Hook) 메서드를 추상으로 만들면 안 되나요?
훅의 정의 자체가 "기본 동작은 비어 있고 선택적으로 재정의"이므로 추상으로 두면 의도가 깨집니다. 빈 구현을 가진 일반 인스턴스 메서드로 두세요.
Q5. 베이스 클래스에서 자식의 데이터 멤버에 접근하고 싶습니다.
잘못된 방향입니다. 베이스는 자식을 몰라야 합니다. 대신 추상 메서드의 RETURNING 또는 EXPORTING 파라미터로 결과를 부모로 올리거나, 베이스의 PROTECTED 속성에 자식이 값을 채워 넣게 설계하세요.
7. 다음 단계 / 관련 주제
Template Method를 익혔다면 다음 주제로 확장해 보세요.
- Strategy 패턴: 단계가 아니라 알고리즘 전체를 교체할 때. ABAP
INTERFACES로 구현 - Factory Method: 자식 리포트 클래스를 동적으로 골라 반환하는 팩토리
- RAP의 핸들러 클래스: Behavior Definition의 액션 핸들러도 사실상 "틀은 RAP, 동작은 클래스"라는 Template Method적 사고를 따릅니다.
- ABAP Unit + Test Double: 추상 베이스를 활용해 단위 테스트용 가짜 자식 클래스를 만드는 패턴
- Clean ABAP 가이드: 상속보다 컴포지션(Composition over Inheritance) 원칙과 Template Method를 언제 써야 할지의 균형
8. 참고 자료 / 핵심 한 줄
핵심 한 줄: 알고리즘의 "무엇을 언제"는 부모가 못 박고, "어떻게"만 자식이 채운다.
댓글 0
아직 댓글이 없습니다.