티스토리 뷰

Java

[Java] Jackson Streaming API, TreeModel

snail voyager 2025. 4. 19. 20:03
728x90
반응형

Jackson의 3가지 JSON 처리 모델

모델 특징 장점 단점
Streaming API 빠른 처리, 메모리 효율 가장 빠름, 대용량 데이터에 적합 사용법이 복잡하고 직관적이지 않음
Tree Model 유연한 구조 탐색 가능 구조가 복잡하거나 동적일 때 유용 전체 JSON을 메모리에 올려야 함
Data Binding 자바 객체 ↔ JSON 자동 매핑 가장 사용하기 쉬움 속도는 스트리밍보다 느림

Streaming API (스트림 기반 처리)

Streaming API는 Jackson에서 가장 저수준이며, 빠르고 메모리 효율적입니다. 

JSON을 한 줄씩 읽고 쓰는 방식 (JsonParser, JsonGenerator)으로 동작

스트리밍 방식(Forward-only 방식)을 사용하면 메모리 사용량이 적습니다.

 

  • 대용량 JSON 처리에 적합
  • pull 기반 파서: JsonParser.nextToken() 호출로 다음 토큰을 직접 요청
  • 메모리 사용량이 매우 적음
  • 초대형 JSON 파일을 읽거나 써야 할 때
  • 메모리를 최소화해야 할 때
  • 성능이 매우 중요한 시스템에서
ObjectMapper mapper = new ObjectMapper();
JsonFactory factory = mapper.getFactory();
JsonParser parser = factory.createParser(jsonString);

while (!parser.isClosed()) {
    JsonToken token = parser.nextToken();
    if (JsonToken.FIELD_NAME.equals(token) && "name".equals(parser.getCurrentName())) {
        parser.nextToken(); // move to value
        System.out.println("name: " + parser.getText());
    }
}

 

Forward-only 방식이란?

 

  • JSON을 순차적으로(앞에서 뒤로) 한 줄씩 읽어 나가는 방식
  • 한 번 지나간 토큰(값)은 다시 돌아갈 수 없습니다
  • JsonParser가 사용됨 (Jackson의 Streaming API)
  • 불필요한 구조 파싱 없이 바로 다음 값으로
  • 전체 JSON을 메모리에 안 올려도 됨
  • GB 단위 JSON도 안전하게 처리 가능
{
  "name": "Alice",
  "age": 30,
  "skills": ["Java", "Spring"]
}

JsonFactory factory = new JsonFactory();
JsonParser parser = factory.createParser(json);

while (!parser.isClosed()) {
    JsonToken token = parser.nextToken();
    System.out.println("Token: " + token + ", Value: " + parser.getText());
}


Token: START_OBJECT, Value: {
Token: FIELD_NAME,   Value: name
Token: VALUE_STRING, Value: Alice
Token: FIELD_NAME,   Value: age
Token: VALUE_NUMBER, Value: 30
Token: FIELD_NAME,   Value: skills
Token: START_ARRAY,  Value: [
Token: VALUE_STRING, Value: Java
Token: VALUE_STRING, Value: Spring
Token: END_ARRAY,    Value: ]
Token: END_OBJECT,   Value: }

 

나이가 30 이상인 사용자만 골라내자는 조건으로 필터

import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;

import java.io.*;

public class JacksonStreamingFilter {
    public static void main(String[] args) throws IOException {
        String json = """
        {
          "users": [
            { "id": 1, "name": "Alice", "age": 28 },
            { "id": 2, "name": "Bob", "age": 35 },
            { "id": 3, "name": "Charlie", "age": 31 }
          ]
        }
        """;

        JsonFactory factory = new JsonFactory();
        JsonParser parser = factory.createParser(json);
        ObjectMapper mapper = new ObjectMapper();

        // 파싱을 시작
        while (!parser.isClosed()) {
            JsonToken token = parser.nextToken();

            // "users" 필드를 찾음
            if (JsonToken.FIELD_NAME.equals(token) && "users".equals(parser.getCurrentName())) {
                token = parser.nextToken(); // move to START_ARRAY

                // 배열 시작
                if (token == JsonToken.START_ARRAY) {
                    while (parser.nextToken() != JsonToken.END_ARRAY) {
                        // 현재 위치는 user 하나의 START_OBJECT
                        JsonNode userNode = mapper.readTree(parser);

                        int age = userNode.path("age").asInt();
                        if (age >= 30) {
                            System.out.println("Matched User: " + userNode.toString());
                        }
                    }
                }
            }
        }

        parser.close();
    }
}

Tree Model (트리 모델 처리)

Tree Model은 JSON을 트리 형태(JsonNode)로 메모리에 올려놓고 노드 단위로 접근하는 방식입니다. 

트리 탐색처럼 JSON 구조를 다룰 수 있어 동적 구조 처리에 유리

 

  • 전체 JSON을 한 번에 메모리에 로드
  • 계층적인 데이터 탐색이 직관적
  • null 처리에 안전한 path() 메서드 사용 가능
  • 구조가 동적이거나 복잡한 JSON을 다뤄야 할 때
  • 필드 존재 여부를 사전에 알 수 없을 때
  • 특정 필드만 파싱하고 싶을 때
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(jsonString);

String name = rootNode.path("name").asText();
int age = rootNode.path("age").asInt();
System.out.println("Name: " + name + ", Age: " + age);

 

Tree Model과 Streaming API를 혼합해서 사용하는 전략

 

  • 대용량 JSON 배열이 있을 때 → 전체를 한꺼번에 Tree Model로 메모리에 올리면 OOM 위험
  • 부분 탐색은 Streaming으로, 복잡한 객체 파싱은 Tree Model로

 

💡 전략 개념

  1. 전체 JSON을 스트리밍으로 순회한다 (JsonParser)
  2. 필요한 위치(예: 배열 요소)를 만나면 → 해당 JSON 객체를 Tree Model (JsonNode)로 변환
ObjectMapper mapper = new ObjectMapper();
JsonFactory factory = mapper.getFactory();
JsonParser parser = factory.createParser(jsonInput);

// JSON 예시:
// {
//   "status": "ok",
//   "data": [
//     { "id": 1, "name": "Alice" },
//     { "id": 2, "name": "Bob" }
//   ]
// }

while (!parser.isClosed()) {
    JsonToken token = parser.nextToken();

    // 배열 "data"의 시작 위치를 찾음
    if (JsonToken.FIELD_NAME.equals(token) && "data".equals(parser.getCurrentName())) {
        parser.nextToken(); // move to START_ARRAY
        if (parser.currentToken() == JsonToken.START_ARRAY) {

            while (parser.nextToken() != JsonToken.END_ARRAY) {
                // 여기서 parser는 배열 내 객체 하나씩 가리킴
                // 객체 하나를 Tree Model로 읽기
                JsonNode node = mapper.readTree(parser);

                // Tree Model 방식으로 처리
                int id = node.path("id").asInt();
                String name = node.path("name").asText();

                System.out.println("User ID: " + id + ", Name: " + name);
            }
        }
    }
}

 

 

Jackson의 readTree(JsonParser)는 파서가 가리키는 현재 위치의 JSON 객체만 읽어들여 JsonNode로 만들어 줍니다.
이 덕분에 Streaming 중에도 필요한 부분만 트리처럼 사용

728x90
반응형

'Java' 카테고리의 다른 글

[Java] Jackson readValue(), convertValue()  (0) 2025.04.20
[Java] BigInteger  (0) 2025.04.19
[Java] BigDecimal  (0) 2025.04.19
[Java] DIP (Dependency Inversion Principle)  (0) 2025.03.03
[Java] Bounded WildCard  (1) 2025.03.03
반응형
300x250