이 글에서 다루는 것
ABAP 7.40 이상에서 가장 많이 쓰이는 문법 중 하나가 바로 인라인 선언(Inline Declaration)입니다. 특히 Open SQL의 SELECT ... INTO 구문에서 @DATA()를 활용하면, 결과를 담을 internal table이나 work area를 미리 TYPES와 DATA로 선언하지 않아도 컴파일러가 자동으로 타입을 추론해 줍니다. 이 글은 해당 문법의 동작 원리, 실무 적용 패턴, 그리고 사용 시 주의해야 할 함정을 다룹니다.
- 인라인 선언이 컴파일 타임에 어떻게 타입을 결정하는지 이해
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 결과를 받기 전에 반드시 다음과 같은 절차를 거쳤습니다.
- 결과 구조에 맞는
TYPES를 선언 - 해당 타입의
DATA변수(구조체 또는 internal table)를 정의 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 SINGLE은 INTO 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
아직 댓글이 없습니다.