CodeGym /행동 /JAVA 25 SELF /StAX (Streaming API for XML) — 스트리밍 파싱

StAX (Streaming API for XML) — 스트리밍 파싱

JAVA 25 SELF
레벨 47 , 레슨 2
사용 가능

1. 스트리밍 파싱 소개

Java에서는 역사적으로 XML을 다루는 두 가지 접근 방식이 자리 잡았습니다.

이미 알고 있듯, DOM (Document Object Model)은 메모리에 문서 트리를 구성합니다. 요소를 탐색하고 수정하기는 편리하지만, 큰 파일에는 메모리 비용이 너무 큽니다.

반면 SAX (Simple API for XML)는 파일을 순차적으로 처리하며 태그를 만나면 이벤트를 발생시킵니다. 메모리를 절약할 수 있어 매우 큰 문서도 처리할 수 있습니다. 하지만 핸들러를 작성하기 불편하고, 구조를 거슬러 되돌아갈 수 없습니다.

StAX (Streaming API for XML)는 절충안으로 등장했습니다. SAX처럼 스트리밍 방식이지만, 개발자에게 더 많은 제어권을 줍니다. 필요할 때 스트림에서 이벤트를 직접 “끌어” 옵니다. 이러한 방식을 pull 파싱이라고 하며, 더 이해하기 쉽고 유연한 코드를 작성할 수 있게 합니다.

StAX 입문

StAX (Streaming API for XML) — Java용 현대적인 XML 스트리밍 파서로, JDK 6+에서 도입되었습니다.
핵심 아이디어: pull 파싱(“당겨오는” 파서).

SAX에서는 파서가 알아서 메서드를 호출하는 반면(push 모델), StAX에서는 여러분이 과정을 직접 제어합니다:
파서에게 직접 “다음 이벤트를 줘!”라고 요청합니다.

비유:
SAX — 텔레비전과 같습니다. 이벤트가 일방적으로 흘러들어오며, 여러분은 반응만 하면 됩니다.
StAX — 준비되었을 때 직접 “다음 동영상”을 누르는 서비스와 같습니다.

StAX의 핵심 클래스

StAX를 사용하려면 javax.xml.stream 패키지의 두 핵심 클래스가 필요합니다:

  • XMLInputFactory — 파서를 생성하는 팩토리.
  • XMLStreamReader — XML을 ‘조각’ 단위로 읽는 스트리밍 파서.

기본 예제:

import javax.xml.stream.*;
import java.io.FileInputStream;

XMLInputFactory factory = XMLInputFactory.newInstance();
XMLStreamReader reader = factory.createXMLStreamReader(new FileInputStream("data.xml"));

while (reader.hasNext()) {
    int event = reader.next();
    // 이벤트 처리
}
reader.close();

2. StAX 작동 원리: pull 모델

StAX에서는 XML 읽기를 직접 제어합니다:

  1. 스트림을 엽니다(예: 파일).
  2. XMLStreamReader를 생성합니다.
  3. 루프에서 reader.next()를 호출해 다음 이벤트를 가져옵니다.
  4. 이벤트 유형을 확인합니다(START_ELEMENT, END_ELEMENT, CHARACTERS 등).
  5. 원하는 위치에 도달하면 데이터를 처리합니다.
  6. 파서를 닫습니다.

작동 흐름:

flowchart TD
    A[XMLStreamReader 열기] --> B{hasNext?}
    B -- 예 --> C["next()"]
    C --> D{이벤트 유형?}
    D -- START_ELEMENT --> E[요소 시작 처리]
    D -- CHARACTERS --> F[텍스트 처리]
    D -- END_ELEMENT --> G[요소 종료 처리]
    D -- END_DOCUMENT --> H[종료]
    B -- 아니요 --> H

무엇이 편리한가요?

  • 다음 요소를 언제 읽을지 스스로 결정할 수 있습니다.
  • 필요한 지점에서 “멈추고”, 파일의 일부만 처리할 수 있습니다.
  • SAX처럼 수많은 핸들러를 작성할 필요가 없습니다.

3. StAX의 이벤트 유형

reader.next()를 호출하면, 파서는 이벤트 유형을 반환합니다 — 정수(인터페이스 XMLStreamConstants의 상수). 주요 이벤트 유형은 다음과 같습니다:

  • START_ELEMENT — XML 요소의 시작(<tag>).
  • END_ELEMENT — XML 요소의 끝(</tag>).
  • CHARACTERS — 태그 사이의 텍스트 내용.
  • END_DOCUMENT — 문서의 끝.

이벤트 처리 예시:

while (reader.hasNext()) {
    int event = reader.next();
    switch (event) {
        case XMLStreamConstants.START_ELEMENT:
            String name = reader.getLocalName();
            System.out.println("요소 시작: " + name);
            break;
        case XMLStreamConstants.CHARACTERS:
            String text = reader.getText().trim();
            if (!text.isEmpty()) {
                System.out.println("텍스트: " + text);
            }
            break;
        case XMLStreamConstants.END_ELEMENT:
            System.out.println("요소 끝: " + reader.getLocalName());
            break;
    }
}

4. 유용한 팁

StAX를 언제 사용할까?

다음과 같은 경우 StAX가 최적입니다:

  • XML 파일이 매우 큼(기가바이트 단위) — 파일 전체를 메모리에 올리고 싶지 않을 때.
  • 문서의 일부만 처리하면 될 때(예: 특정 요소를 찾은 뒤 중지).
  • 간단하고 이해하기 쉬운 코드가 필요할 때: StAX는 SAX보다 단순하며, 수많은 핸들러를 작성할 필요가 없습니다.

예시 과제:

  • 대용량 XML 데이터 가져오기(예: 1C 내보내기, 은행 거래 내역, 상품 카탈로그).
  • 필요한 요소만 검색 및 처리(예: 백만 건 중에서 오직 <transaction>만).
  • XML을 실시간으로 변환(예: 필터링, 집계).

DOM, SAX, StAX 비교

접근 방식 메모리 단순성 유연성 언제 사용
DOM 높음(모두 메모리에 적재) 매우 쉬움 트리를 변경 가능 XML 편집이 필요할 때의 소/중형 파일
SAX 최소 복잡함(이벤트 핸들러) 읽기 전용, 역추적 불가 매우 큰 파일, 단순 처리
StAX 최소 보통(pull 모델) 부분 읽기 가능, 중간 중지 용이 유연성과 단순성이 모두 필요한 대용량 파일

StAX — 황금 균형점:
— DOM처럼 메모리를 낭비하지 않습니다.
— SAX처럼 복잡한 핸들러가 필요하지 않습니다.
— 파싱 과정을 직접 제어할 수 있습니다.

5. 예제: StAX로 대용량 XML 파일 읽기

예를 들어, "books.xml" 파일이 있다고 합시다:

<library>
    <book>
        <title>초보자를 위한 Java</title>
        <author>이반 이바노프</author>
    </book>
    <book>
        <title>고급 Java</title>
        <author>표트르 페트로프</author>
    </book>
    <!-- ... 많은 책 ... -->
</library>

과제: 모든 책 제목을 출력합니다.

StAX 코드:

import javax.xml.stream.*;
import java.io.*;

public class StaxDemo {
    public static void main(String[] args) throws Exception {
        XMLInputFactory factory = XMLInputFactory.newInstance();
        XMLStreamReader reader = factory.createXMLStreamReader(new FileInputStream("books.xml"));

        while (reader.hasNext()) {
            int event = reader.next();
            if (event == XMLStreamConstants.START_ELEMENT && "title".equals(reader.getLocalName())) {
                reader.next(); // CHARACTERS로 이동
                System.out.println("도서: " + reader.getText());
            }
        }
        reader.close();
    }
}

장점:

  • 파일 전체를 메모리에 로드하지 않습니다.
  • 백만 권이라도 처리 가능 — 프로그램이 ‘터지지’ 않습니다.

6. StAX 사용 시 흔한 실수

오류 №1: 파서나 스트림을 닫지 않음. 리소스 누수를 막기 위해 항상 XMLStreamReader와 스트림(InputStream)을 닫으세요.

오류 №2: 이벤트 유형을 확인하지 않음. 모든 이벤트가 요소의 시작/끝은 아닙니다. 이벤트 유형을 확인하지 않으면 빈 문자열을 얻거나 필요한 데이터를 놓칠 수 있습니다.

오류 №3: 요소의 중첩을 고려하지 않음. XML 구조가 복잡한 경우(예: 섹션 안의 책) 현재 중첩 수준을 추적해 요소를 혼동하지 않도록 하세요.

오류 №4: 큰 파일에 DOM 사용. 파일이 크면 DOM을 사용하지 마세요. OutOfMemoryError가 발생할 수 있습니다. 큰 파일에는 StAX 또는 SAX를 사용하세요.

오류 №5: 예외를 처리하지 않음. 파일과 XML을 다룰 때는 예외가 발생할 수 있습니다(XMLStreamException, IOException). 적절히 처리하거나 상위로 던지세요.

코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION