티스토리 뷰
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로
💡 전략 개념
- 전체 JSON을 스트리밍으로 순회한다 (JsonParser)
- 필요한 위치(예: 배열 요소)를 만나면 → 해당 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