ABAP SQL 성능 최적화 — 실행 계획 분석부터 AI 기반 쿼리 튜닝까지

Moderator · 조회 1

ABAP SQL 성능 최적화 -- 실행 계획 분석부터 AI 기반 쿼리 튜닝까지

ABAP SQL 성능 최적화 타이틀 ABAP SQL 성능 최적화 핵심 포인트 ABAP SQL 성능 최적화 CTA

1. 도입: 왜 ABAP SQL 성능이 중요한가

SAP 시스템에서 성능 문제의 대부분은 비효율적인 ABAP SQL에서 시작됩니다. 데이터베이스 호출이 전체 응답 시간의 60~80%를 차지하는 경우가 흔하며, 특히 SAP S/4HANA 전환 이후에는 Code Pushdown 원칙에 따라 가능한 한 많은 연산을 데이터베이스 계층에서 처리해야 합니다.

잘못 작성된 단 하나의 SELECT 문이 수십만 건의 불필요한 데이터 전송을 유발하고, 이는 사용자 체감 성능을 극적으로 저하시킵니다. 이 튜토리얼에서는 ABAP SQL 성능 최적화의 핵심 원칙을 코드 예제와 함께 살펴보고, 최근 주목받는 AI 기반 쿼리 튜닝 기법까지 다룹니다.

이 글을 읽고 나면 다음을 할 수 있습니다:

대상 환경: SAP S/4HANA 2023 이상, SAP BTP ABAP Environment, ABAP 7.50+ (일부 문법은 7.40 SP08 이상에서 사용 가능)

2. 비효율 패턴 탐지: SELECT *, 중첩 루프, 동적 SQL

성능 최적화의 첫 단계는 문제를 찾는 것입니다. SAP에서 제공하는 SE30(런타임 분석), SAT(ABAP Trace), ABAP Profiler(Eclipse ADT)를 사용하면 병목 구간을 정확히 식별할 수 있습니다. 다음은 가장 빈번하게 발견되는 세 가지 비효율 패턴입니다.

패턴 1: SELECT * -- 불필요한 컬럼 전송

테이블의 모든 컬럼을 가져오면 네트워크 전송량이 불필요하게 증가합니다. 특히 컬럼이 100개가 넘는 SAP 표준 테이블에서는 치명적입니다.

" [BEFORE] 비효율: 모든 컬럼을 가져와서 ABAP에서 필터링
SELECT * FROM zdemo_abap_tab1 INTO TABLE @DATA(it_all).
LOOP AT it_all INTO DATA(wa) WHERE key_field < 500.
  " 필요한 처리
ENDLOOP.

" [AFTER] 효율: 필요한 컬럼만, WHERE 절로 DB에서 필터링
SELECT key_field, char1, char2
  FROM zdemo_abap_tab1
  WHERE key_field < 500
  INTO TABLE @DATA(it_filtered).

위 개선에서 두 가지가 동시에 적용되었습니다. 첫째, 필요한 컬럼만 명시적으로 선택했습니다. 둘째, 필터링 조건을 ABAP LOOP가 아닌 DB의 WHERE 절로 이동시켰습니다. 이 원칙 하나만 지켜도 대부분의 SELECT 문 성능이 크게 개선됩니다.

패턴 2: 중첩 루프 내 SELECT -- N*M 문제

LOOP 안에서 SELECT를 실행하면 외부 루프 건수만큼 DB 호출이 반복됩니다. 10,000건의 루프라면 10,000번의 DB 라운드트립이 발생합니다.

" [BEFORE] 비효율: 루프 내부 SELECT (N번 DB 호출)
LOOP AT lt_orders INTO DATA(ls_order).
  SELECT SINGLE customer_name
    FROM zcustomer
    WHERE customer_id = @ls_order-customer_id
    INTO @ls_order-customer_name.
ENDLOOP.

" [AFTER] 효율: JOIN으로 한 번에 조회
SELECT o~order_id, o~customer_id, c~customer_name
  FROM zorders AS o
  INNER JOIN zcustomer AS c
    ON o~customer_id = c~customer_id
  INTO TABLE @DATA(lt_order_with_cust).

패턴 3: 동적 SQL의 성능 비용

동적 SQL은 런타임에 구문을 파싱해야 하므로 정적 SQL보다 느립니다. DB 옵티마이저가 실행 계획을 캐시하기 어려울 수 있습니다.

" [BEFORE] 동적 SQL -- 런타임 파싱 오버헤드
SELECT SINGLE ('CARRID, CONNID')
  FROM ('ZDEMO_ABAP_FLSCH')
  WHERE (`CARRID <> 'LH'`)
  INTO NEW @DATA(dref_dynamic).

" [AFTER] 정적 SQL -- 컴파일 타임 최적화 가능
SELECT SINGLE carrid, connid
  FROM zdemo_abap_flsch
  WHERE carrid <> 'LH'
  INTO NEW @DATA(dref_static).

동적 SQL이 불가피한 경우(범용 리포트 등)에도, 가능한 한 WHERE 절이나 필드 목록 중 일부만 동적으로 처리하고 나머지는 정적으로 유지하는 것이 권장됩니다.

3. WHERE 절 최적화 & 컬럼 선택

DB 옵티마이저가 효율적인 실행 계획을 수립하려면 WHERE 절이 명확해야 합니다. 다음 원칙을 기억하세요.

" [BEFORE] 인덱스 무효화 -- 컬럼에 함수 적용
SELECT order_id, order_date
  FROM zorders
  WHERE SUBSTRING( order_id, 1, 2 ) = 'AB'
  INTO TABLE @DATA(lt_bad).

" [AFTER] 인덱스 활용 가능 -- LIKE 패턴 사용
SELECT order_id, order_date
  FROM zorders
  WHERE order_id LIKE 'AB%'
  INTO TABLE @DATA(lt_good).

컬럼 선택도 중요합니다. SELECT * 대신 필요한 컬럼만 명시하면, DB는 해당 컬럼만 포함하는 인덱스(Covering Index)만으로 쿼리를 처리할 수 있습니다. 이 경우 실제 테이블 데이터에 접근할 필요가 없어 I/O가 크게 줄어듭니다.

4. JOIN vs FOR ALL ENTRIES vs EXISTS 비교

여러 테이블의 데이터를 결합할 때 선택할 수 있는 세 가지 방법을 비교합니다.

방법 성능 적합한 상황 주의사항
INNER JOIN 가장 효율적 두 테이블 간 명확한 관계가 있을 때 결과 집합이 커질 수 있음
WHERE EXISTS 중간 존재 여부만 확인할 때 서브쿼리 결과를 가져오지 않음
FOR ALL ENTRIES 상황 의존적 내부 테이블 기반 조회 시 빈 테이블 체크 필수, 중복 자동 제거
" 방법 1: INNER JOIN (권장)
SELECT a~order_id, a~amount, b~customer_name
  FROM zorders AS a
  INNER JOIN zcustomer AS b
    ON a~customer_id = b~customer_id
  WHERE a~order_date >= '20260101'
  INTO TABLE @DATA(lt_join_result).

" 방법 2: WHERE EXISTS
SELECT order_id, amount
  FROM zorders AS a
  WHERE EXISTS ( SELECT 1 FROM zcustomer AS b
                   WHERE b~customer_id = a~customer_id
                     AND b~country = 'KR' )
  INTO TABLE @DATA(lt_exists_result).

" 방법 3: FOR ALL ENTRIES (빈 테이블 체크 필수!)
IF lt_customers IS NOT INITIAL.
  SELECT order_id, amount
    FROM zorders
    FOR ALL ENTRIES IN @lt_customers
    WHERE customer_id = @lt_customers-customer_id
    INTO TABLE @DATA(lt_fae_result).
ENDIF.

FOR ALL ENTRIES 사용 시 반드시 기억할 것: 내부 테이블이 비어 있으면 WHERE 조건이 무시되어 전체 테이블 데이터가 조회됩니다. 이는 운영 환경에서 심각한 장애를 유발할 수 있으므로, 반드시 IS NOT INITIAL 검사를 수행해야 합니다. 또한 FOR ALL ENTRIES는 결과에서 중복 행을 자동으로 제거하므로, 중복이 필요한 경우에는 사용할 수 없습니다.

5. DML 블록 처리 & 집계 함수 Push-down

블록 단위 DML

INSERT, UPDATE, MODIFY, DELETE 작업을 행 단위로 반복하면 매번 DB 라운드트립이 발생합니다. 내부 테이블을 활용한 블록 처리로 전환하면 DB 호출 횟수를 극적으로 줄일 수 있습니다.

" [BEFORE] 비효율: 행 단위 MODIFY (N번 DB 호출)
LOOP AT lt_carriers INTO DATA(ls_carrier).
  ls_carrier-carrier_name = to_upper( ls_carrier-carrier_name ).
  MODIFY zdemo_abap_carr FROM @ls_carrier.
ENDLOOP.

" [AFTER] 효율: 블록 단위 MODIFY (1번 DB 호출)
LOOP AT lt_carriers ASSIGNING FIELD-SYMBOL().
  -carrier_name = to_upper( -carrier_name ).
ENDLOOP.
MODIFY zdemo_abap_carr FROM TABLE @lt_carriers.

특정 컬럼만 변경해야 한다면 UPDATE SET 구문이 더 효율적입니다. 전체 행을 읽어서 수정한 뒤 다시 쓰는 대신, 변경할 컬럼만 지정합니다.

" UPDATE SET: 특정 컬럼만 업데이트 (가장 효율적)
UPDATE zdemo_abap_tab1
  SET char1 = 'X'
  WHERE key_field < 20.

집계 함수 Push-down

합계, 평균, 건수 등의 집계를 ABAP 루프에서 계산하지 말고 DB에 위임하세요. DB 엔진은 이런 연산에 최적화되어 있습니다.

" [BEFORE] 비효율: ABAP에서 집계
SELECT amount FROM zorders INTO TABLE @DATA(lt_amounts).
DATA(lv_sum) = VALUE #( ).
LOOP AT lt_amounts INTO DATA(lv_amt).
  lv_sum = lv_sum + lv_amt.
ENDLOOP.

" [AFTER] 효율: DB에서 집계 (Push-down)
SELECT AVG( amount ) AS avg_amount,
       SUM( amount ) AS total_amount,
       COUNT( * )    AS order_count
  FROM zorders
  WHERE order_date >= '20260101'
  INTO @DATA(ls_aggregated).

SAP HANA 환경에서는 이러한 집계 연산이 In-Memory 엔진에서 병렬 처리되므로, Push-down 효과가 더욱 극대화됩니다.

6. 버퍼링 & 인덱스 전략

테이블 버퍼링

DDIC(데이터 딕셔너리)에서 테이블 버퍼링을 설정하면, ABAP SQL이 DB 대신 애플리케이션 서버의 로컬 버퍼에서 데이터를 읽습니다. 자주 읽히지만 거의 변경되지 않는 설정 테이블(Customizing Table)에 적합합니다.

주의: 버퍼링된 테이블에 SELECT DISTINCT, ORDER BY, GROUP BY, JOIN 등을 사용하면 버퍼를 우회하여 직접 DB에 접근합니다(Buffer Bypass). 버퍼링의 이점을 살리려면 단순한 SELECT 문을 사용해야 합니다.

인덱스 전략

인덱스는 읽기 성능을 높이지만 쓰기 성능을 저하시킵니다. 다음 기준으로 보조 인덱스를 설계하세요.

내부 테이블 타입 선택

DB에서 가져온 데이터를 ABAP에서 처리할 때도 내부 테이블 타입 선택이 성능에 영향을 줍니다.

테이블 타입 검색 성능 적합한 사용 시나리오
표준 테이블(STANDARD) 선형 검색 O(n) 소규모 데이터, 순차 처리
정렬 테이블(SORTED) 이진 검색 O(log n) 범위 검색, 순차 처리 병행
해시 테이블(HASHED) 상수 시간 O(1) 대규모 데이터의 키 기반 접근

10,000건 이상의 데이터에서 READ TABLE을 반복 수행한다면, 표준 테이블 대신 해시 테이블이나 정렬 테이블(BINARY SEARCH)을 사용하는 것이 권장됩니다.

7. AI 기반 쿼리 튜닝: Claude/ChatGPT로 최적화

최근 AI 도구를 활용한 ABAP SQL 최적화가 실무에서 빠르게 확산되고 있습니다. 특히 레거시 코드 리팩토링이나 성능 병목 분석에서 AI의 도움이 효과적입니다.

활용 시나리오 1: 비효율 쿼리 자동 변환

AI에게 기존의 비효율적인 ABAP SQL 코드를 제공하고, 최적화된 버전으로 변환을 요청할 수 있습니다.

" AI에게 제공하는 프롬프트 예시:
" "다음 ABAP 코드를 SAP HANA Push-down 원칙에 맞게 최적화해줘.
"  FOR ALL ENTRIES를 JOIN으로 변환하고,
"  ABAP 루프 내 집계를 DB 집계 함수로 이동시켜줘."

" [AI 변환 전] 레거시 코드
SELECT * FROM zorders INTO TABLE @DATA(lt_orders).
DATA: lv_total TYPE p DECIMALS 2.
LOOP AT lt_orders INTO DATA(ls_ord) WHERE status = 'OPEN'.
  lv_total = lv_total + ls_ord-amount.
ENDLOOP.

" [AI 변환 후] 최적화 코드
SELECT SUM( amount ) AS total_amount
  FROM zorders
  WHERE status = 'OPEN'
  INTO @DATA(lv_total_optimized).

활용 시나리오 2: 실행 계획 해석

SAT 또는 SQL Monitor(트랜잭션 SQLM)에서 수집한 실행 계획 데이터를 AI에게 제공하면, 병목 원인과 개선 방안을 자연어로 설명받을 수 있습니다. 예를 들어 "Full Table Scan이 발생하는 이유가 뭐야?"와 같은 질문에 인덱스 추가, WHERE 절 수정 등 구체적인 조치를 제안받을 수 있습니다.

활용 시나리오 3: FOR ALL ENTRIES를 JOIN으로 자동 변환

레거시 시스템에는 FOR ALL ENTRIES 패턴이 광범위하게 사용되어 있습니다. AI 도구에 해당 코드와 테이블 구조를 함께 제공하면, INNER JOIN 또는 CDS View 기반의 최적화 코드를 생성할 수 있습니다.

AI 활용 시 주의사항

8. 실무 체크리스트 & 마무리

아래 체크리스트를 코드 리뷰나 성능 점검 시 활용하세요.

ABAP SQL 성능 최적화 체크리스트

  1. SELECT *를 사용하고 있지 않은가? -- 필요한 컬럼만 명시적으로 선택한다
  2. LOOP 내부에 SELECT가 있지 않은가? -- JOIN 또는 FOR ALL ENTRIES로 대체한다
  3. WHERE 절이 인덱스를 활용하고 있는가? -- 기본 키 또는 보조 인덱스 필드를 조건에 포함한다
  4. FOR ALL ENTRIES 사용 시 빈 테이블 체크를 하고 있는가? -- IS NOT INITIAL 검사 필수
  5. DML을 행 단위로 실행하고 있지 않은가? -- 블록 단위(FROM TABLE) 처리로 전환한다
  6. 집계를 ABAP 루프에서 하고 있지 않은가? -- SUM, AVG, COUNT 등 DB 집계 함수를 사용한다
  7. 동적 SQL이 불필요하게 사용되고 있지 않은가? -- 가능한 한 정적 SQL을 사용한다
  8. 내부 테이블 타입이 적절한가? -- 대량 키 검색에는 해시 테이블을 사용한다
  9. 변경 전후 성능을 측정했는가? -- SE30/SAT/ABAP Profiler로 반드시 검증한다
  10. 버퍼링 대상 테이블을 식별했는가? -- 읽기 빈도가 높고 변경이 적은 테이블에 버퍼링을 설정한다

핵심 요약

ABAP SQL 성능 최적화의 근본 원칙은 단순합니다. DB에서 할 수 있는 일은 DB에서 하고, ABAP으로 가져오는 데이터는 최소화하라. SAP HANA 환경에서는 이 원칙이 더욱 중요해졌으며, Code Pushdown을 통해 DB 엔진의 In-Memory 처리 능력을 최대한 활용해야 합니다.

레거시 코드를 한꺼번에 전부 바꿀 수는 없지만, 가장 빈번하게 실행되는 프로그램부터 위 체크리스트를 적용하면 체감할 수 있는 성능 개선을 달성할 수 있습니다. AI 도구를 활용하면 이 과정을 더욱 가속할 수 있으니, 적극적으로 도입을 검토해 보시기 바랍니다.

참고 자료