RAP

RAP FCL 없이 3컬럼? — 마스터-디테일 구현법 #shorts #SAP #RAP

▶ YouTube에서 보기

📖 개요: 이 글에서 다루는 내용

SAP Fiori에서 마스터-디테일 화면을 만들 때 가장 흔한 고민은 "리스트에서 항목을 클릭하면 전체 화면이 Object Page로 넘어가 버려서 목록 맥락을 잃는다"는 점입니다. Flexible Column Layout(이하 FCL)을 적용하면 별도의 커스텀 페이지나 추가 네비게이션 개발 없이, List Report와 Object Page가 한 화면에 나란히 열리는 마스터-디테일 UX를 구현할 수 있습니다. 이 글은 RAP(ABAP RESTful Application Programming Model) 기반 구매주문(Purchase Order) 헤더-아이템 시나리오를 예제로, CDS 어노테이션과 manifest.json 설정만으로 FCL을 완성하는 과정을 다룹니다.

  • FCL의 동작 원리와 Fiori Elements 라우팅 구조 이해
  • RAP CDS 뷰에 UI.Facets, UI.SelectionPresentationVariant 어노테이션 적용
  • manifest.json에서 2컬럼/3컬럼 레이아웃 활성화
  • Draft 편집·검증·성능까지 프로덕션 수준으로 확장

📚 시작 전에 알아두면 좋은 것들

이 글은 advanced 난이도로, 다음 내용을 이미 경험했다는 전제로 진행합니다. RAP의 기본 구조(Interface View → Consumption View → Behavior Definition → Service Binding), CDS 어노테이션 문법, Fiori Elements List Report/Object Page의 기본 생성 흐름, 그리고 SAP Business Application Studio 또는 VS Code에서 Fiori 앱 프로젝트를 생성해 본 경험이 있으면 충분합니다. OData V4 서비스 바인딩 경험이 있다면 더 수월합니다.

🔧 환경 · 버전 · 준비물

이 예제는 다음 환경을 기준으로 작성되었습니다.

  • 백엔드: SAP BTP ABAP Environment 또는 SAP S/4HANA 2022 이상 (ABAP Platform 2022+, RAP strict mode 2 지원)
  • 서비스 프로토콜: OData V4 (Service Binding: OData V4 - UI) — FCL은 Fiori Elements for OData V4 템플릿에서 manifest 설정만으로 활성화되므로 일반적으로 V4를 권장합니다
  • 개발 도구: ABAP Development Tools(ADT) for Eclipse, SAP Business Application Studio(SAP Fiori 개발 스페이스)
  • 프런트엔드: SAPUI5 1.108 이상 (sap.f.FlexibleColumnLayout 안정 버전)
  • 데이터 모델: 구매주문 헤더 테이블 zpo_header, 아이템 테이블 zpo_item (1:N 컴포지션)

S/4HANA 온프레미스 구버전(1909 이하)에서는 strict mode나 일부 Draft 문법이 다를 수 있으므로 버전별 문법 차이를 확인하는 것이 좋습니다.

💡 핵심 개념: FCL은 "책상 위에 서류를 나란히 펼치는 것"

일반적인 Fiori 풀스크린 네비게이션이 서류를 한 장씩 넘겨 보는 방식이라면, FCL은 책상 위에 서류 여러 장을 나란히 펼쳐 놓고 비교하는 방식입니다. 왼쪽에는 목록(List Report), 가운데에는 선택한 문서의 상세(Object Page), 오른쪽에는 그 문서의 하위 항목 상세(Sub-Object Page)가 열립니다.

List Report(Begin Column) → 헤더 Object Page(Mid Column) → 아이템 Object Page(End Column)

FCL이 백엔드 개발 대상이 아니라는 점이 핵심입니다. RAP 쪽에서는 컴포지션 모델과 UI 어노테이션으로 "어떤 데이터가 어떤 계층으로 보이는가"만 정의하고, 실제 컬럼 분할은 Fiori Elements 템플릿의 sap.f.FlexibleColumnLayout 컨트롤이 manifest 설정을 읽어 자동 처리합니다.

계층담당 요소정의 내용
RAP CDSUI.Facets, UI.LineItem헤더-아이템 계층, 목록/상세 필드
RAP BDEFComposition, Draft헤더-아이템 트랜잭션 단위
manifest.jsonrouting/config컬럼 수, 기본 레이아웃 타입
SAPUI5 런타임sap.f.FlexibleColumnLayout실제 화면 분할·리사이즈·전환

Fiori Elements V4의 라우터는 각 라우트의 컨텍스트 깊이(depth)를 계산합니다. 깊이 0(리스트)이면 1컬럼, 깊이 1(헤더 상세)이면 defaultTwoColumnLayoutType, 깊이 2(아이템 상세)이면 defaultThreeColumnLayoutType이 적용됩니다. URL 해시에 컨텍스트 경로가 쌓일수록 컬럼이 오른쪽으로 하나씩 열리는 구조입니다.

💻 실전 코드: 3단계로 완성하는 구매주문 FCL

1단계 — 기본 예제: CDS 계층 정의와 FCL 활성화

먼저 헤더 Consumption View에 아이템으로 향하는 #LINEITEM_REFERENCE Facet을 선언합니다. 이 Facet이 있어야 헤더 Object Page 안에 아이템 테이블이 생기고, 그 테이블의 행 클릭이 세 번째 컬럼으로 이어집니다.

@Metadata.layer: #CORE
annotate view ZC_PurchOrderTP with
{
  @UI.facet: [
    {
      id:       'HeaderGeneral',
      type:     #IDENTIFICATION_REFERENCE,
      label:    '주문 기본 정보',
      position: 10
    },
    {
      id:            'PoItems',
      type:          #LINEITEM_REFERENCE,
      label:         '구매 아이템',
      position:      20,
      targetElement: '_Item'
    }
  ]

  @UI.lineItem: [{ position: 10, importance: #HIGH }]
  PurchaseOrderNo;

  @UI.lineItem: [{ position: 20 }]
  @UI.identification: [{ position: 10 }]
  SupplierName;

  @UI.lineItem: [{ position: 30 }]
  @UI.identification: [{ position: 20 }]
  NetAmount;
}

다음으로 BAS에서 생성한 Fiori Elements V4 앱의 manifest.json에 FCL 설정 한 블록을 추가합니다.

{
  "sap.ui5": {
    "routing": {
      "config": {
        "routerClass": "sap.f.routing.Router",
        "flexibleColumnLayout": {
          "defaultTwoColumnLayoutType": "TwoColumnsMidExpanded",
          "defaultThreeColumnLayoutType": "ThreeColumnsMidExpanded"
        }
      }
    }
  }
}

이것만으로 리스트 → 헤더 → 아이템의 3컬럼 화면이 동작합니다. TwoColumnsMidExpanded는 상세 컬럼을 넓게, TwoColumnsBeginExpanded는 목록 컬럼을 넓게 표시하는 옵션입니다.

2단계 — 실무 시나리오: Draft 편집과 검증 메시지

FCL에서 편집 버튼을 누르면 화면 전환 없이 Mid Column이 그대로 편집 모드로 바뀌므로, Draft 기반 트랜잭션 처리가 사실상 필수입니다.

managed implementation in class zbp_i_purchorder unique;
strict ( 2 );
with draft;

define behavior for ZI_PurchOrderTP alias PurchOrder
persistent table zpo_header
draft table zpo_header_d
lock master total etag LastChangedAt
authorization master ( instance )
{
  create; update; delete;
  association _Item { create; with draft; }

  field ( readonly ) PurchaseOrderNo, LastChangedAt;
  validation checkSupplier on save { create; field SupplierId; }
  draft determine action Prepare { validation checkSupplier; }
}

검증 실패 시 %element로 필드 단위 타깃을 지정해야 어느 컬럼의 어느 필드가 문제인지 하이라이트됩니다.

METHOD checkSupplier.
  READ ENTITIES OF zi_purchordertp IN LOCAL MODE
    ENTITY PurchOrder
    FIELDS ( SupplierId ) WITH CORRESPONDING #( keys )
    RESULT DATA(lt_orders).

  LOOP AT lt_orders INTO DATA(ls_order)
       WHERE SupplierId IS INITIAL.
    APPEND VALUE #( %tky = ls_order-%tky ) TO failed-purchorder.
    APPEND VALUE #(
      %tky = ls_order-%tky
      %msg = new_message( id       = 'ZPO_MSG'
                          number   = '001'
                          severity = if_abap_behv_message=>severity-error )
      %element-SupplierId = if_abap_behv=>mk-on
    ) TO reported-purchorder.
  ENDLOOP.
ENDMETHOD.

3단계 — 프로덕션: SelectionPresentationVariant와 성능·보안

운영 화면에서는 사용자가 목록을 열자마자 의미 있는 데이터가 정렬·필터된 상태로 보여야 합니다. UI.SelectionPresentationVariant로 "미결 주문을 최신순으로"라는 기본 뷰를 백엔드에서 정의합니다.

annotate view ZC_PurchOrderTP with
{
  @UI.presentationVariant: [{
    qualifier: 'pvOpen',
    sortOrder: [{ by: 'OrderDate', direction: #DESC }],
    visualizations: [{ type: #AS_LINEITEM }],
    requestAtLeast: [ 'OverallStatus' ]
  }]
  @UI.selectionVariant: [{
    qualifier: 'svOpen',
    text: '미결 주문',
    filter: 'OverallStatus eq ''OPEN'''
  }]
  @UI.selectionPresentationVariant: [{
    qualifier: 'spvDefault',
    selectionVariantQualifier: 'svOpen',
    presentationVariantQualifier: 'pvOpen'
  }]
}
{
  "PurchOrderList": {
    "type": "Component",
    "name": "sap.fe.templates.ListReport",
    "options": {
      "settings": {
        "contextPath": "/PurchOrder",
        "defaultTemplateAnnotationPath": "com.sap.vocabularies.UI.v1.SelectionPresentationVariant#spvDefault",
        "initialLoad": "Enabled"
      }
    }
  }
}

프로덕션 체크리스트 세 가지: 첫째 성능 — FCL은 컬럼마다 별도 OData 요청이 발생하므로 @UI.lineItem 필드 수를 최소화하고 계산 필드는 CDS 계산식으로 내립니다. 둘째 보안 — authorization master ( instance )와 DCL로 헤더 접근을 제한하면 컴포지션에 따라 아이템 컬럼도 통제됩니다. 셋째 테스트 — EML 기반 유닛 테스트로 검증 로직을 UI 없이 검증합니다.

METHOD create_without_supplier_fails.
  MODIFY ENTITIES OF zi_purchordertp
    ENTITY PurchOrder
    CREATE FIELDS ( OrderDate ) WITH VALUE #(
      ( %cid = 'C1' OrderDate = cl_abap_context_info=>get_system_date( ) ) )
    MAPPED DATA(mapped) FAILED DATA(failed) REPORTED DATA(reported).
  COMMIT ENTITIES RESPONSES FAILED DATA(commit_failed)
                            REPORTED DATA(commit_reported).
  cl_abap_unit_assert=>assert_not_initial( commit_failed-purchorder ).
ENDMETHOD.

⚠️ 흔한 실수와 트러블슈팅

Q1. manifest에 설정을 넣었는데 화면이 계속 풀스크린으로 전환됩니다.

가장 흔한 원인은 routerClass가 기본값(sap.m.routing.Router 계열)으로 남아 있는 경우입니다. FCL은 sap.f.routing.Router가 필요합니다. 또 Fiori Elements V2(OData V2) 템플릿은 설정 위치가 sap.ui.generic.app 하위로 완전히 다르므로, 어떤 템플릿으로 생성했는지 먼저 확인하세요.

Q2. 두 번째 컬럼까지는 열리는데 아이템을 클릭해도 세 번째 컬럼이 안 열립니다.

아이템 엔터티에 대한 Object Page 라우트/타깃이 manifest에 없거나, CDS에서 헤더→아이템이 association만 있고 composition으로 선언되지 않은 경우입니다. RAP에서 계층 네비게이션은 컴포지션 트리와 Service Definition 노출 여부를 함께 확인해야 합니다. defaultThreeColumnLayoutType 누락도 점검 대상입니다.

Q3. 편집 중 다른 헤더를 클릭하면 작성 중인 내용이 사라질까 걱정됩니다.

Draft를 활성화했다면 일반적으로 자동 저장되어 유실되지 않습니다. FCL + 편집 시나리오에는 Draft 조합을 권장합니다.

Q4. 좁은 화면(태블릿)에서 컬럼이 이상하게 보입니다.

FCL은 뷰포트 폭에 따라 자동으로 컬럼 수를 줄입니다(1280px 미만은 최대 2컬럼, 960px 미만은 1컬럼). 태블릿 사용자 비중이 높다면 defaultTwoColumnLayoutType을 TwoColumnsBeginExpanded로 조정해 목록 가독성을 확보하는 방법도 있습니다.

🚀 확장 주제: 이어서 도전해 볼 것들

FCL 마스터-디테일이 완성되었다면 다음 주제로 확장해 보세요. Side Effects 어노테이션으로 아이템 수량 변경 시 헤더 합계를 즉시 갱신하기, UI.Chart + ALP(Analytical List Page)로 Begin Column을 분석형 목록으로 교체하기, RAP 액션과 Determination으로 승인 워크플로 버튼을 Mid Column 헤더에 배치하기가 자연스러운 다음 코스입니다. Adaptation Project를 통해 표준 S/4HANA 앱의 FCL 레이아웃을 조정하는 시나리오도 실무에서 자주 등장합니다.

댓글 0

아직 댓글이 없습니다.