24. 절차적 DML (네비게이션) vs 비절차적 DML (선언적, SQL)

핵심 인사이트 (3줄 요약)

  1. 본질: 데이터 조작 언어(DML)는 데이터를 찾는 방법에 따라 "어떻게(How)"를 명시하는 절차적(Procedural) 언어와, "무엇을(What)" 원하는지만 명시하는 비절차적(Non-procedural/Declarative) 언어로 구분된다.
  2. 가치: 관계형 데이터베이스(RDBMS)의 표준인 SQL은 비절차적 언어로서, 사용자가 데이터의 물리적 접근 경로를 몰라도 내부의 옵티마이저(Optimizer)가 최적의 실행 계획을 찾아주는 혁신을 이뤄냈다.
  3. 융합: 최신 데이터베이스 환경에서는 SQL의 선언적 장점을 유지하면서도 복잡한 비즈니스 로직을 제어하기 위해 PL/SQL과 같은 절차적 확장 언어를 융합하여 사용한다.

Ⅰ. 개요 및 필요성 (Context & Necessity)

과거의 망형(Network)이나 계층형(Hierarchical) 데이터베이스 시스템에서는 데이터를 찾기 위해 포인터를 따라 레코드 단위로 하나씩 이동해야 했다. 이를 '네비게이션(Navigation) 방식'이라고 하며, 프로그래머가 인덱스 구조와 데이터의 물리적 위치를 완벽하게 이해하고 순서대로 코딩해야만 결과를 얻을 수 있었다. 이러한 방식은 데이터 구조가 조금만 바뀌어도 모든 애플리케이션 코드를 재작성해야 하는 치명적인 데이터 종속성(Data Dependency) 문제를 낳았다.

이러한 한계를 극복하기 위해 에드거 코드(E.F. Codd)의 관계형 모델을 기반으로 한 선언적(Declarative) 언어, 즉 비절차적 DML(SQL)이 등장했다. 사용자는 단지 결과물의 조건만 집합적으로 요구할 뿐, 데이터베이스 내부의 엔진이 접근 경로를 대신 고민한다. 이는 개발 생산성을 폭발적으로 향상시켰다.

다음은 두 언어의 접근 패러다임 차이를 보여주는 비교 도식이다.

[절차적 DML (Navigation)]
1. EMP 파일의 첫 번째 레코드로 이동하라.
2. 부서코드가 '10'인지 검사하라.
3. 맞으면 출력하고, 아니면 다음 포인터로 이동하라.
4. EOF(End of File)까지 반복(Loop)하라.
   => ❌ 개발자가 알고리즘(How)을 직접 통제 (고비용)

[비절차적 DML (Declarative / SQL)]
SELECT * FROM EMP WHERE 부서코드 = '10';
   => ✅ 사용자는 결과(What)만 요구, DB 엔진이 실행 경로(How) 결정 (저비용)

[도식 해설] 이 도식의 핵심은 제어권의 역전(Inversion of Control)이다. 절차적 방식에서는 루프(Loop)와 조건문 처리가 애플리케이션 계층에서 이루어진다. 반면 비절차적 방식에서는 사용자가 요구사항만 던지면 데이터베이스 엔진이 인덱스 유무, 테이블 크기 등을 종합하여 최적의 경로를 자체적으로 계산한다. 이러한 배치는 개발자가 데이터의 물리적 저장 구조(인덱스, 파티션 등)가 변경되더라도 쿼리를 수정할 필요가 없도록 논리적 데이터 독립성을 완벽하게 보장하기 위함이다.

📢 섹션 요약 비유: 절차적 DML이 목적지까지 "우회전 100m, 좌회전 50m"를 직접 지시하는 것이라면, 비절차적 DML은 목적지 주소만 입력하면 알아서 최적 경로를 찾아주는 내비게이션(GPS)과 같습니다.


Ⅱ. 아키텍처 및 핵심 원리 (Deep Dive)

비절차적 DML이 가능해진 근본적인 이유는 DBMS 내부에 '옵티마이저(Optimizer)'라는 지능적인 두뇌가 존재하기 때문이다.

1. 선언적 언어를 실행하는 내부 파이프라인

사용자가 비절차적 DML을 던지면, 엔진은 이를 절차적인 '실행 계획'으로 번역한다.

[사용자 쿼리 (What)] : SELECT * FROM A JOIN B ON A.id = B.id WHERE A.val = 1
         ↓
[1. Parser / Semantic Checker] : 문법 및 객체 유효성 검증
         ↓
[2. Query Transformer] : 서브쿼리 언네스팅(Unnesting), 뷰 머징(View Merging)
         ↓
[3. Optimizer (Cost-Based)] ──+ 통계 정보 (Dictionary Statistics)
   (실행 경로 A: Hash Join)   │ - A 테이블 건수: 10,000건
   (실행 경로 B: NL Join)     │ - B 테이블 인덱스 존재 여부
         ↓                    +--> 가장 비용(Cost)이 낮은 경로 B 선택
[4. Execution Plan (How)] : 내부적으로 절차적 스텝 생성
         ↓
[5. Execution Engine] : 데이터 추출

[도식 해설] 이 파이프라인의 핵심은 3번의 옵티마이저 단계이다. 사용자는 테이블 A와 B를 조인하라고만(What) 지시했지만, 옵티마이저는 통계 정보를 바탕으로 어떤 테이블을 먼저 읽을지(Driving), 어떤 조인 알고리즘(NL, Hash, Sort Merge)을 사용할지 구체적인 절차(How)를 생성해낸다. 즉, 비절차적 DML은 마법이 아니라, '절차적 계산을 데이터베이스 커널이 대신 수행하는 고도의 추상화 메커니즘'이다. 실무에서는 통계 정보가 과거 데이터에 머물러 있으면 옵티마이저가 멍청한 판단을 내려 시스템이 마비될 수 있으므로 통계 정보의 주기적인 갱신이 필수적이다.

2. 절차적 요소의 결합 (PL/SQL, T-SQL)

비절차적 DML은 집합 연산에 강력하지만, "만약 재고가 0이면 발주 테이블에 Insert하고, 아니면 Update하라"와 같은 복잡한 분기(IF-THEN) 처리는 불가능하다. 이를 해결하기 위해 RDBMS는 비절차적 언어를 감싸는 절차적 블록(PL/SQL, Stored Procedure)을 지원한다.

구성 요소역할내부 동작 메커니즘실무 비유
SQL Engine비절차적 쿼리 수행집합 기반 데이터 I/O 및 조인 처리공장 생산 라인
PL/SQL Engine절차적 로직 통제IF, FOR, WHILE 루프 및 변수 할당 제어공장 관리자
Cursor (커서)집합 결과를 순차 탐색SQL 엔진이 던져준 결과 집합(Set)을 메모리에 올려 1행씩(Row-by-Row) Fetch컨베이어 벨트
Context Switch두 엔진 간의 제어권 전환반복 루프 내에서 SQL 호출 시 발생하는 오버헤드관리자와 작업자 간의 대화

📢 섹션 요약 비유: 비절차적 SQL이 한 번에 요리를 완성하는 자동 조리 기계라면, 절차적 언어(PL/SQL)는 간을 보며 소금을 더 넣을지 판단하는 주방장으로, 이 둘이 협력해야 복잡한 코스 요리가 완성됩니다.


Ⅲ. 융합 비교 및 다각도 분석 (Comparison & Synergy)

두 방식은 단순히 과거와 현재의 대비가 아니다. 실무에서는 데이터 처리 목적에 따라 두 방식을 전략적으로 혼용해야 한다.

1. 절차적 vs 비절차적 DML 심층 비교

┌────────────┬────────────────────────────┬─────────────────────────────┐ │ 항목 │ 절차적 DML (Procedural) │ 비절차적 DML (Declarative) │ ├────────────┼────────────────────────────┼─────────────────────────────┤ │ 대표 언어 │ PL/SQL, C, Java(JDBC 루프) │ SQL (ANSI 표준) │ │ 처리 단위 │ 레코드 단위 (Row-by-Row) │ 집합 단위 (Set-based) │ │ 최적화 주체│ 애플리케이션 개발자 │ DBMS 내부 옵티마이저 │ │ I/O 오버헤드│ 높음 (네트워크 왕복 잦음) │ 낮음 (엔진 내부 일괄 처리) │ │ 로직 복잡도│ IF/ELSE 등 복잡한 제어 용이│ 단순 조건 필터링에 한정됨 │ └────────────┴────────────────────────────┴─────────────────────────────┘

2. 성능 병목 구조의 차이 (Context Switching)

[안티패턴: 애플리케이션 절차적 루프]
App (Java)                         DB Engine
 FOR 1 to 10,000:
   SELECT * FROM EMP...   -------> (Parse -> Execute -> Fetch) x 1만 번 발생
   (네트워크 지연 발생)   <------- 
 
[권장패턴: 비절차적 집합 처리]
App (Java)                         DB Engine
 UPDATE EMP SET...        -------> (Parse -> Execute -> 집합 처리) 1번 발생
 WHERE dept = 10;         <-------

[도식 해설] 이 비교 도식은 절차적 처리의 가장 큰 약점인 네트워크 왕복(Round-trip) 및 파싱 오버헤드를 보여준다. 개발자가 애플리케이션 단에서 FOR 루프를 돌며 1만 번의 단건 쿼리를 던지면, DB는 매번 쿼리를 분석하고 락을 걸고 해제하는 불필요한 비용을 지불해야 한다. 반면 비절차적 DML은 단 한 번의 요청으로 1만 건의 레코드를 데이터베이스 커널 내부 메모리에서 고속으로 일괄 처리한다. 실무 성능 튜닝의 핵심은 "최대한 루프(절차적)를 없애고 SQL(집합적)로 푸시 다운(Push-down)하는 것"이다.

📢 섹션 요약 비유: 택배 10,000개를 보낼 때, 오토바이로 1개씩 1만 번 왔다 갔다 하는 것(절차적)과 대형 트럭 한 대에 모두 실어 한 번에 보내는 것(비절차적 집합 처리)의 차이입니다.


Ⅳ. 실무 적용 및 기술사적 판단 (Strategy & Decision)

1. 실무 시나리오: 커서(Cursor) 남용에 의한 CPU 100% 장애

초급 개발자가 은행 이자 계산 배치를 작성하면서 모든 고객의 계좌를 커서(Cursor)로 열어 루프를 돌며 UPDATE를 수행했다. 결과적으로 10시간이 지나도 배치가 끝나지 않는 장애가 발생했다.

의사결정 플로우:

  1. 문제 인지: 배치 프로그램의 CPU 사용률이 100%를 찍고 트랜잭션 로그가 꽉 참.
  2. 원인 분석: PL/SQL 내부에서 Row-by-Row(절차적) 처리를 하면서 컨텍스트 스위칭이 수천만 번 발생.
  3. 해결책 적용 (집합 처리 전환):
    • 절차적 루프를 완전히 폐기.
    • 윈도우 함수와 서브쿼리를 활용한 단일 비절차적 UPDATE ... SELECT 구문으로 재작성. (실행 시간 10시간 -> 3분 단축)

2. 도입 체크리스트 및 트레이드오프

  • 언제 절차적 방식(PL/SQL, 루프)을 써야 하는가?
    • 단일 SQL로 작성 시 가독성이 극도로 떨어져 유지보수가 불가능할 때.
    • 레코드마다 외부 API를 호출하거나 다른 시스템과 연동해야 하는 경우.
  • 언제 비절차적 방식(SQL 집합)을 써야 하는가?
    • 대용량 데이터의 집계, 변환, 조인, 필터링 등 순수 데이터 I/O 작업일 때.

3. 절차와 비절차의 타협점: BULK 프로세싱

[FORALL / BULK COLLECT 아키텍처]
PL/SQL (절차적)                   SQL Engine (비절차적)
  Collection 변수에
  데이터 1,000건 적재 
        |
  FORALL i IN 1..1000  ====(1번의 묶음 전달)====> 1,000건 일괄 UPDATE 처리

[도식 해설] 이 아키텍처는 절차적 로직의 복잡성을 유지하면서도 비절차적 엔진의 성능을 취하기 위한 하이브리드 기법(Bulk Processing)을 보여준다. 하나씩 전달하던 데이터를 배열(Array)에 담아 뭉텅이로 SQL 엔진에 던진다. 이를 통해 컨텍스트 스위칭 횟수를 1/1000 수준으로 줄일 수 있다. 대용량 배치 처리 실무에서는 순수 SQL로 풀기 어려운 비즈니스 로직을 마주했을 때 이 패턴을 필수적으로 사용한다.

📢 섹션 요약 비유: 물건을 조립할 때 로봇 팔(SQL)이 할 수 있는 일은 로봇에게 맡기고, 섬세한 검수가 필요한 부분만 사람(절차적 루프)이 개입하는 융합 공정입니다.


Ⅴ. 기대효과 및 결론 (Future & Standard)

기대효과 구분세부 내용향상 지표 / 결과
개발 생산성선언적 구문을 통한 코드량 감소애플리케이션 코드베이스 50% 축소
처리 성능옵티마이저 기반의 집합적 I/O 최적화대용량 배치 처리 시간 90% 단축
독립성 확보물리적 데이터 구조 변경 시 코드 무변경무중단 데이터베이스 마이그레이션 가능

관계형 데이터베이스의 승리는 비절차적 DML(SQL)의 승리라 해도 과언이 아니다. 사용자는 "어떻게"를 고민하는 짐을 내려놓고 비즈니스의 "무엇"에만 집중할 수 있게 되었다. 미래의 데이터 처리 패러다임 역시 Spark SQL, Hive 등 빅데이터와 NoSQL 영역에서도 SQL-like 한 선언적 인터페이스를 모방하는 방향으로 진화하고 있다. 진정한 데이터 엔지니어라면 무조건적인 SQL 맹신을 넘어, 옵티마이저의 한계를 보완할 수 있는 절차적 제어 능력까지 균형 있게 갖추어야 한다.

📢 섹션 요약 비유: 비절차적 DML은 인류가 직접 노를 젓는 배에서 벗어나, 목적지만 입력하면 알아서 바람을 타고 가는 자율주행 돛단배를 발명한 것과 같은 혁신입니다.


📌 관련 개념 맵 (Knowledge Graph)

  • Optimizer (옵티마이저) | 비절차적 요구사항을 물리적인 최적 실행 경로로 변환하는 DBMS 커널의 핵심 두뇌
  • Context Switching | PL/SQL 엔진과 SQL 엔진 사이에서 제어권이 넘어갈 때 발생하는 심각한 성능 오버헤드
  • Set-based Processing | 개별 행이 아닌 조건에 맞는 전체 데이터 집합을 한 번에 처리하는 관계형 대수의 기본 사상
  • Cursor (커서) | 집합 단위로 추출된 메모리상의 데이터를 애플리케이션이 하나씩(Row-by-Row) 처리할 수 있게 돕는 포인터
  • Logical Data Independence | 비절차적 DML의 가장 큰 장점으로, 데이터 구조가 바뀌어도 응용 프로그램은 영향을 받지 않는 특성

👶 어린이를 위한 3줄 비유 설명

  1. 절차적 방식은 로봇에게 "1보 앞으로 가, 손을 뻗어, 사과를 쥐어, 입으로 가져와"라고 하나하나 명령하는 거예요.
  2. 비절차적 방식은 로봇에게 그냥 "나 사과 먹고 싶어!"라고 말만 하면 로봇이 알아서 가장 빠른 방법으로 사과를 가져오는 마법이에요.
  3. 데이터베이스는 너무 똑똑해서, 우리가 사과만 달라고 해도 제일 안 막히는 길로 달려가서 사과를 찾아다 준답니다!