앱서버 vs HANA 중 어디서 처리해야 하는가
SAP BTP 기반 애플리케이션에서 대용량 데이터 처리 로직을 어디에 배치하느냐가 성능을 결정합니다. 애플리케이션 서버(CAP, Java, Node.js)에서 데이터를 전부 가져와 처리하는 방식은 네트워크 전송량과 메모리 사용량 면에서 비효율적입니다. HANA SQLScript로 데이터 가까이에서 처리하면 극적인 성능 향상이 가능합니다.
패턴 1: 앱서버 루프 → SQLScript 집합 연산으로 전환
// 잘못된 패턴: CAP Node.js에서 행 단위 처리
this.on('recalculateAllOrders', async (req) => {
const db = await cds.connect.to('db');
// 모든 주문 가져오기 (대용량)
const orders = await db.run(
SELECT.from('SalesOrders').where({ status: 'OPEN' })
);
// 앱서버에서 행 단위 처리 — 네트워크 왕복 + 메모리 낭비
for (const order of orders) {
const items = await db.run(
SELECT.from('OrderItems').where({ orderId: order.orderId })
);
const total = items.reduce((sum, i) => sum + i.amount, 0);
await db.run(
UPDATE('SalesOrders')
.set({ totalAmount: total })
.where({ orderId: order.orderId })
);
}
// 주문 10000건 = DB 왕복 30000번 이상
});
// 올바른 패턴: SQLScript 프로시저로 HANA에서 처리
// HANA에서 실행되는 프로시저
CREATE OR REPLACE PROCEDURE proc_recalc_order_totals()
LANGUAGE SQLSCRIPT AS
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN ROLLBACK; RESIGNAL; END;
-- 아이템 합계를 HANA에서 집계
lt_totals = SELECT order_id, SUM(item_amount) AS total_amount
FROM zbtp_order_items
GROUP BY order_id;
-- HANA 내에서 직접 업데이트 (네트워크 왕복 없음)
UPDATE zbtp_sales_orders AS o
SET total_amount = (SELECT total_amount FROM :lt_totals AS t
WHERE t.order_id = o.order_id)
WHERE status = 'OPEN';
COMMIT;
END;
// CAP에서 프로시저 호출 (1번의 DB 왕복)
this.on('recalculateAllOrders', async (req) => {
const db = await cds.connect.to('db');
await db.run(`CALL PROC_RECALC_ORDER_TOTALS()`);
});
패턴 2: 복잡한 집계를 HANA Calculation View로 오프로드
-- HANA Calculation View (SAP HANA Studio / Web IDE)
-- 월별/지역별 매출 집계를 앱서버 대신 HANA에서 계산
-- Calculation View: ZCV_MONTHLY_SALES_REPORT
-- - 데이터 소스: zbtp_sales_doc, zbtp_customer
-- - 집계 노드: SUM(net_amount) GROUP BY region, month
-- - 환율 변환 내장
-- CAP에서 Calculation View 조회 (읽기만)
const salesReport = await db.run(
SELECT.from('ZCV_MONTHLY_SALES_REPORT')
.where({ fiscal_year: '2026' })
.orderBy('region', 'month_num')
);
// HANA가 모든 집계를 컬럼 스토어에서 처리
패턴 3: 배치 DML을 SQLScript로 대체
-- 패턴: 대량 데이터 상태 변경
CREATE OR REPLACE PROCEDURE proc_close_expired_quotes(
IN iv_cutoff_date DATE
)
LANGUAGE SQLSCRIPT AS
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN ROLLBACK; RESIGNAL; END;
-- 만료된 견적 한 번에 마감 (행 단위 루프 없음)
UPDATE zbtp_quotation
SET status = 'EXPIRED',
expired_at = :iv_cutoff_date
WHERE status = 'OPEN'
AND valid_until < :iv_cutoff_date;
-- 처리 건수 로그
INSERT INTO zbtp_proc_log(proc_name, affected_rows, executed_at)
VALUES('CLOSE_EXPIRED_QUOTES', ::ROWCOUNT, CURRENT_TIMESTAMP);
COMMIT;
END;
// CAP에서 스케줄러로 호출
const cron = require('node-cron');
cron.schedule('0 2 * * *', async () => {
const db = await cds.connect.to('db');
const today = new Date().toISOString().split('T')[0];
await db.run(`CALL PROC_CLOSE_EXPIRED_QUOTES(DATE '${today}')`);
console.log('만료 견적 마감 완료');
});
앱서버 vs SQLScript 판단 기준
- 처리 대상이 수천 건 이상인가? → SQLScript
- 복잡한 집계(SUM, AVG, GROUP BY)가 필요한가? → SQLScript 또는 Calculation View
- 외부 API 호출이 필요한가? → 앱서버에서 처리
- 비즈니스 규칙이 복잡하고 단위 테스트가 필요한가? → 앱서버
- 단순 데이터 변환/집계이면서 대용량인가? → SQLScript가 유리
공식 문서
SAP HANA Performance Guide는 HANA Cloud Performance Guide에서 확인하세요. SQLScript Best Practices는 SAP Note 2394949를 참고하세요.
댓글 0
아직 댓글이 없습니다.