ABAP

SELECT 결과 30초 만에 — @DATA() 인라인 선언 #shorts #SAP #ABAP

이 글에서 다루는 것

ABAP 7.40 이상에서 가장 많이 쓰이는 문법 중 하나가 바로 인라인 선언(Inline Declaration)입니다. 특히 Open SQL의 SELECT ... INTO 구문에서 @DATA()를 활용하면, 결과를 담을 internal table이나 work area를 미리 TYPESDATA로 선언하지 않아도 컴파일러가 자동으로 타입을 추론해 줍니다. 이 글은 해당 문법의 동작 원리, 실무 적용 패턴, 그리고 사용 시 주의해야 할 함정을 다룹니다.

  • 인라인 선언이 컴파일 타임에 어떻게 타입을 결정하는지 이해
  • SELECT SINGLE, SELECT *, JOIN 결과 각각의 인라인 패턴 익히기
  • 이스케이프 문자 @(at-sign)의 의미와 strict mode 동작
  • 실무 환경에서 발생하는 흔한 오류와 해결 전략
  • 레거시 코드에서 신문법으로 마이그레이션할 때 고려할 점

읽기 전에 알아두면 좋은 것

이 글을 이해하려면 ABAP에서 internal table과 structure를 다뤄본 경험, 그리고 기본적인 SELECT ... FROM ... WHERE 형태의 Open SQL 문법을 알고 있어야 합니다. TYPES 키워드로 사용자 정의 타입을 선언해 본 경험과, LOOP AT itab INTO wa 같은 work area 패턴에 익숙하다면 차이를 더 빠르게 체감할 수 있습니다.

실습 환경 및 시스템 요건

인라인 선언은 ABAP 언어 버전 7.40 SP08부터 도입되었고, Open SQL의 @DATA() 호스트 변수 이스케이프 문법은 7.40 SP05 이후 strict mode에서 사용됩니다. 따라서 다음 환경을 권장합니다.

  • NetWeaver 7.50 이상 또는 SAP S/4HANA 1709 이상 (가능하면 최신 릴리스)
  • ABAP Development Tools(ADT, Eclipse 기반) — 인라인 선언에 대한 Quick Fix·Refactoring 지원이 SE80보다 우수
  • ABAP Cloud / RAP 환경에서는 신문법이 사실상 기본값
  • 샘플 테이블로는 SAP에서 기본 제공하는 SFLIGHT, SCARR 또는 사용자 정의 ZTABLE을 사용

SE80(클래식 워크벤치)에서도 작성은 가능하나, 일부 구버전 SP에서는 strict syntax 검사가 약해 @ 없는 호스트 변수까지 허용하므로, 실무에서는 ADT로 작성하길 권장합니다.

핵심 개념: 인라인 선언은 어떻게 동작하는가

전통적인 ABAP에서는 SELECT 결과를 받기 전에 반드시 다음과 같은 절차를 거쳤습니다.

  1. 결과 구조에 맞는 TYPES를 선언
  2. 해당 타입의 DATA 변수(구조체 또는 internal table)를 정의
  3. SELECT ... INTO TABLE lt_xxx 형태로 데이터 수신

이 과정은 단순한 조회 한 번에도 5~10줄을 차지하게 만들었습니다. 인라인 선언은 이 과정을 한 줄로 압축합니다. @DATA(lt_orders)처럼 작성하면 컴파일러가 SELECT 절의 컬럼 목록을 분석해 자동으로 적절한 internal table 타입을 만들어 변수에 바인딩합니다.

비유하자면, 기존 방식이 "이삿짐을 옮기기 전에 빈 박스를 사이즈별로 미리 만들어 두는 것"이라면, 인라인 선언은 "짐을 보고 그 자리에서 박스를 만들어 주는 자동 포장기"에 가깝습니다. 박스(타입) 제작 책임이 개발자에서 컴파일러로 넘어간 것입니다.

여기서 중요한 두 가지 규칙이 있습니다.

  • 이스케이프 문자 @: Open SQL strict mode에서는 ABAP 변수(호스트 변수) 앞에 반드시 @를 붙여야 합니다. 이는 DB 컬럼명과 ABAP 변수명을 명확히 구분하기 위한 장치입니다. @DATA()@도 같은 맥락입니다.
  • 타입 추론 시점: @DATA()의 타입은 컴파일 타임에 SELECT 절의 필드 목록으로 결정됩니다. 따라서 동적 SELECT((field_list)처럼 변수로 컬럼을 주입하는 경우)에서는 사용할 수 없습니다.

또한 결과 구조에 따라 추론되는 타입이 달라집니다. INTO TABLE @DATA(lt)는 standard internal table, INTO @DATA(ls)는 단일 구조체(SELECT SINGLE의 결과), INTO @DATA(lv)는 단일 필드 SELECT일 때 elementary 변수가 됩니다.

1단계: 가장 단순한 인라인 SELECT

먼저 판매주문 헤더 테이블을 가정해 보겠습니다. 우리가 만든 ZSALES_HEADER라는 테이블에서 특정 고객의 주문 목록을 조회한다고 합시다. 전통적 방식과 비교해 보면 차이가 분명합니다.

" 전통적 방식 - 타입과 변수를 미리 선언
TYPES: BEGIN OF ty_sales,
         order_id   TYPE zsales_header-order_id,
         customer   TYPE zsales_header-customer,
         net_amount TYPE zsales_header-net_amount,
       END OF ty_sales.

DATA: lt_sales TYPE STANDARD TABLE OF ty_sales,
      ls_sales TYPE ty_sales.

SELECT order_id customer net_amount
  FROM zsales_header
  INTO TABLE lt_sales
 WHERE customer = lv_customer.

이를 인라인 선언으로 다시 쓰면 다음과 같이 간결해집니다.

" 인라인 선언 방식
DATA(lv_customer) = 'C00010'.

SELECT order_id, customer, net_amount
  FROM zsales_header
  INTO TABLE @DATA(lt_sales)
 WHERE customer = @lv_customer.

LOOP AT lt_sales INTO DATA(ls_sales).
  WRITE: / ls_sales-order_id, ls_sales-customer, ls_sales-net_amount.
ENDLOOP.

주의할 점은, 신문법 SELECT에서는 컬럼 사이에 콤마(,)가 반드시 필요하고, ABAP 변수 앞에 @ 이스케이프가 강제된다는 점입니다. 이 두 가지가 strict mode의 핵심 표식입니다.

2단계: 실무 시나리오 — JOIN과 단일 행 조회

실제 업무에서는 단일 테이블 조회보다 JOIN과 집계가 더 자주 등장합니다. 판매주문과 고객 마스터를 JOIN해 이름까지 함께 가져오는 예시를 보겠습니다. 이 과정에서 에러 처리와 로깅도 함께 다룹니다.

DATA(lv_plant) = '1000'.

SELECT h~order_id,
       h~customer,
       c~name1 AS customer_name,
       h~net_amount,
       h~currency
  FROM zsales_header AS h
  INNER JOIN kna1 AS c
    ON c~kunnr = h~customer
  INTO TABLE @DATA(lt_joined)
 WHERE h~plant = @lv_plant
   AND h~status = 'O'.

IF sy-subrc <> 0.
  MESSAGE |No open orders found for plant { lv_plant }| TYPE 'I'.
  RETURN.
ENDIF.

SELECT SINGLE order_id, customer, net_amount
  FROM zsales_header
  INTO @DATA(ls_first_order)
 WHERE plant = @lv_plant
 ORDER BY created_at DESCENDING.

IF sy-subrc = 0.
  WRITE: / 'Latest order:', ls_first_order-order_id.
ENDIF.

이 코드의 포인트는 세 가지입니다. 첫째, JOIN 결과의 컬럼 별칭(AS customer_name)이 그대로 추론된 구조체의 컴포넌트 이름이 됩니다. 둘째, SELECT SINGLEINTO TABLE이 아니라 INTO @DATA()로 받아 자동으로 구조체 타입이 됩니다. 셋째, sy-subrc 점검을 빼먹지 않는 것은 신문법이라도 변함없는 기본기입니다.

3단계: 프로덕션 패턴 — 성능, 단위 테스트, 보안

현업 코드에서는 단순히 동작하는 것을 넘어 성능, 테스트 가능성, 보안 측면을 고려해야 합니다. 다음은 자재 마스터를 페이지 단위로 조회하면서 SQL Injection을 막고 단위 테스트도 가능한 형태로 작성한 예시입니다.

CLASS zcl_material_reader DEFINITION PUBLIC FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    TYPES: BEGIN OF ty_material_view,
             matnr TYPE mara-matnr,
             mtart TYPE mara-mtart,
             matkl TYPE mara-matkl,
             ersda TYPE mara-ersda,
           END OF ty_material_view,
           tt_material_view TYPE STANDARD TABLE OF ty_material_view WITH KEY matnr.

    METHODS read_by_type
      IMPORTING iv_mtart        TYPE mara-mtart
                iv_max_rows     TYPE i DEFAULT 100
      RETURNING VALUE(rt_result) TYPE tt_material_view.
ENDCLASS.

CLASS zcl_material_reader IMPLEMENTATION.
  METHOD read_by_type.
    IF iv_mtart IS INITIAL.
      RETURN.
    ENDIF.

    SELECT matnr, mtart, matkl, ersda
      FROM mara
      INTO TABLE @DATA(lt_inline)
      UP TO @iv_max_rows ROWS
     WHERE mtart = @iv_mtart
     ORDER BY matnr.

    rt_result = CORRESPONDING #( lt_inline ).
  ENDMETHOD.
ENDCLASS.

여기서 의도적으로 외부에 노출되는 반환 타입은 명시적으로 선언하고, 내부 임시 변수만 @DATA()로 받았습니다. 이렇게 하면 메서드 시그니처(공개 API)는 안정적으로 유지되고, 내부 구현은 간결해집니다. 단위 테스트에서도 반환 타입이 고정되어 있어야 assertion이 쉬워집니다.

성능 관점에서 UP TO n ROWS와 적절한 ORDER BY를 함께 사용했고, SQL Injection은 호스트 변수 바인딩(@iv_mtart)으로 자연스럽게 방어됩니다. 동적 컬럼 리스트를 문자열로 조립해야 한다면 @DATA() 대신 명시적 타입과 cl_abap_dyn_prg의 화이트리스트 검증을 병행해야 합니다.

자주 마주치는 함정과 해결 패턴

Q1. "The host variable must be escaped using @"라는 구문 오류가 납니다.
A. strict syntax check가 활성화된 SELECT에서 ABAP 변수 앞에 @를 빼먹은 경우입니다. 신문법(콤마로 컬럼 구분, INTO @DATA)을 쓰는 순간 SELECT 전체가 strict 모드로 평가되므로, WHERE 절의 모든 ABAP 변수에도 @를 붙여야 합니다.

Q2. @DATA()로 받은 변수의 타입을 명시적으로 다시 쓸 수 있나요?
A. 인라인 선언된 변수는 컴파일러가 만든 익명 타입을 가지므로 외부에서 직접 참조하기 어렵습니다. 다른 메서드의 매개변수로 넘기거나 클래스 멤버에 저장해야 한다면, 처음부터 TYPES로 공개 타입을 만들어 두는 편이 좋습니다. 즉 "내부 임시 결과는 인라인, 외부 계약은 명시적"이라는 분리 원칙을 권장합니다.

Q3. SELECT *과 인라인 선언을 같이 써도 되나요?
A. 문법적으로는 SELECT * FROM tab INTO TABLE @DATA(lt)가 가능하지만 권장하지 않습니다. 향후 테이블에 컬럼이 추가되면 추론된 타입이 조용히 바뀌어, 사용 측 코드의 구조체 접근이 의도와 다르게 동작할 수 있습니다. 필요한 컬럼만 명시적으로 나열하는 것이 일반적으로 안전합니다.

Q4. 구버전 SP에서 동작하지 않습니다.
A. 인라인 선언은 7.40 SP08+, Open SQL strict 모드는 SP05+에서 안정적으로 지원됩니다. 시스템 패치 레벨이 낮다면 신문법 사용이 제한되거나 일부 옵션에서 단검(short dump)이 발생할 수 있으니, SYSTEM-CALL로 릴리스를 확인하거나 BASIS팀과 합의 후 적용하세요.

이어서 살펴보면 좋은 주제

인라인 선언에 익숙해졌다면 자연스럽게 다음 주제로 확장할 수 있습니다.

  • FOR 표현식과 VALUE #( ): 인라인 선언과 결합해 internal table을 한 줄로 변환·필터링
  • REDUCE / FILTER 표현식: 함수형 스타일 데이터 가공
  • CDS View 소비: SELECT FROM zcds_view에서도 동일한 인라인 패턴이 그대로 적용
  • ABAP SQL의 WITH 절(CTE): 복잡한 쿼리를 단계적으로 작성
  • RAP(RESTful ABAP Programming Model): behavior implementation에서 인라인 선언이 표준 스타일

댓글 0

아직 댓글이 없습니다.