대용량 파일 처리
대형 JSON 파일 (예: 로그, 설정, 데이터 내보내기) 을 메모리에 직접 로드하면 메모리 부족이 발생할 수 있습니다. json 라이브러리는 다양한 효율적인 처리 방법을 제공합니다.
경고
ForeachFile 및 ForeachFileChunked는 반복 전에 전체 파일을 메모리에 로드합니다. "청크" 동작은 메모리 내 데이터의 반복 방식에만 영향을 미치며, 파일 읽기 방식에는 영향을 주지 않습니다. 메모리를 진정으로 제어해야 하는 초대용량 파일 처리의 경우 NDJSONProcessor를 JSONL 형식과 함께 사용하거나 StreamIterator를 사용하세요.
대체 방안
| 방안 | 적용 시나리오 | 메모리 사용량 |
|---|---|---|
| Processor.ForeachFile | 구조화된 반복 처리 | 전체 파일 로드, 항목별 반복 |
| Processor.ForeachFileChunked | 배치 청크 반복 처리 | 전체 파일 로드, 청크별 반복 |
| NDJSONProcessor | JSONL 파일 행 단위 처리 | 메모리 제어 가능, 진정한 스트림 처리 |
통합 API: Processor
설정 옵션
대용량 파일 처리 설정은 Config에 통합되어 있습니다:
type Config struct {
// ... 기타 설정 ...
// 대용량 파일 처리 설정
ChunkSize int64 // 청크 크기 (기본 1MB)
MaxMemory int64 // 최대 메모리 사용량 (기본 100MB)
BufferSize int // 읽기 버퍼 크기 (기본 64KB)
SamplingEnabled bool // 샘플링 활성화 여부 (기본 true)
SampleSize int // 샘플링 수량 (기본 1000)
}기본 사용
package main
import (
"log"
"github.com/cybergodev/json"
)
func main() {
// Processor 생성 (기본 설정 사용)
processor, err := json.New()
if err != nil {
log.Fatal(err)
}
defer processor.Close()
// 방법 1: 항목별 처리 (권장)
count := 0
err = processor.ForeachFile("large-data.json", func(key any, item *json.IterableValue) error {
count++
// IterableValue 편의 메서드로 필드 접근
id := item.GetInt("id")
name := item.GetString("name")
email := item.GetString("email")
// 경로로 중첩 속성 접근 지원
city := item.GetString("profile.city")
interests := item.GetArray("profile.interests")
if count%10000 == 0 {
log.Printf("처리한 레코드 %d건, 예시: id=%d name=%s email=%s city=%s 관심사=%d",
count, id, name, email, city, len(interests))
}
return nil
})
if err != nil {
log.Fatal(err)
}
log.Printf("처리 완료, 총 %d건 레코드", count)
}배치 처리
// 방법 2: 배치 처리 (데이터베이스 배치 쓰기에 적합)
err := processor.ForeachFileChunked("large-data.json", 1000, func(chunk []*json.IterableValue) error {
log.Printf("배치 처리: %d건 레코드", len(chunk))
// 데이터베이스에 배치 쓰기
for _, item := range chunk {
id := item.GetInt("id")
name := item.GetString("name")
// ... 데이터 처리
}
return nil
})중단 제어 포함
// 방법 3: 중단 제어 포함 (특정 데이터를 찾은 후 중지)
// item.Break() 반환으로 반복 중지, nil 반환으로 반복 계속
err := processor.ForeachFile("large-data.json", func(key any, item *json.IterableValue) error {
id := item.GetInt("id")
if id == targetID {
// 대상 찾음, 반복 중지
fmt.Printf("대상 발견: ID=%d, Name=%s\n", id, item.GetString("name"))
return item.Break() // 반복 중지 (중단 신호 반환)
}
return nil // 반복 계속
})객체 파일 처리
// 방법 4: JSON 객체 파일 처리 (키 - 값 쌍 구조)
// 파일 형식: {"user1": {...}, "user2": {...}, ...}
err := processor.ForeachFile("config-map.json", func(key any, item *json.IterableValue) error {
fmt.Printf("Key: %s, Name: %s\n", key, item.GetString("name"))
return nil
})커스텀 설정
// 대용량 파일 처리 설정 커스텀
cfg := json.DefaultConfig()
cfg.ChunkSize = 10 * 1024 * 1024 // 10MB 청크
cfg.MaxMemory = 500 * 1024 * 1024 // 500MB 메모리 제한
cfg.BufferSize = 128 * 1024 // 128KB 버퍼
processor, err := json.New(cfg)
if err != nil {
panic(err)
}
defer processor.Close()IterableValue 편의 메서드
ForeachFile* 시리즈 메서드는 IterableValue 인터페이스를 제공하여 편리한 데이터 접근을 지원합니다:
| 메서드 | 설명 | 예제 |
|---|---|---|
Get(path) | 값 가져오기 | item.Get("field") |
GetString(path) | 문자열 가져오기 | item.GetString("name") |
GetInt(path) | 정수 가져오기 | item.GetInt("id") |
GetFloat64(path) | 실수 가져오기 | item.GetFloat64("score") |
GetBool(path) | 불리언 가져오기 | item.GetBool("active") |
GetArray(path) | 배열 가져오기 | item.GetArray("tags") |
GetObject(path) | 객체 가져오기 | item.GetObject("profile") |
Exists(path) | 필드 존재 여부 확인 | item.Exists("email") |
IsNull(path) | null 여부 확인 | item.IsNull("deleted_at") |
IsEmpty(path) | 비어 있는지 확인 | item.IsEmpty("notes") |
Break() | 중단 신호 반환 | return item.Break() |
경로 탐색 지원
city := item.GetString("profile.address.city") // 중첩 객체
firstTag := item.GetString("tags[0]") // 배열 인덱스
lastTag := item.GetString("tags[-1]") // 음수 인덱스 (마지막)
nested := item.GetString("data.items[0].name") // 복잡한 경로스트림 처리 설정
Config를 통해 스트림 처리 매개변수를 설정합니다:
cfg := json.DefaultConfig()
// 대용량 파일 처리 설정
cfg.ChunkSize = 10 * 1024 * 1024 // 10MB 청크
cfg.MaxMemory = 500 * 1024 * 1024 // 500MB 메모리 제한
cfg.BufferSize = 128 * 1024 // 128KB 버퍼
processor, err := json.New(cfg)
if err != nil {
panic(err)
}
defer processor.Close()StreamLinesInto 제네릭 함수 사용
type User struct {
Name string `json:"name"`
}
file, _ := os.Open("users.jsonl")
defer file.Close()
_, err := json.StreamLinesInto[User](file, func(lineNum int, user User) error {
fmt.Printf("처리: %s\n", user.Name)
return nil
})병렬 처리
병렬 처리가 가능한 작업의 경우 멀티 goroutine 을 사용할 수 있습니다:
package main
import (
"sync"
"github.com/cybergodev/json"
)
func main() {
processor, err := json.New()
if err != nil {
panic(err)
}
defer processor.Close()
// worker pool 사용
workers := 4
items := make(chan any, 100)
var wg sync.WaitGroup
// workers 시작
for i := 0; i < workers; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
for item := range items {
// item 처리
_ = item
}
}(i)
}
// 스트림 읽기 및 분배
processor.ForeachFile("large-data.json", func(key any, item *json.IterableValue) error {
items <- item.Get("")
return nil
})
close(items)
wg.Wait()
}성능 최적화 제안
메모리 제어
// 가용 메모리에 따라 설정
cfg := json.DefaultConfig()
cfg.MaxMemory = 500 * 1024 * 1024 // 500MB
cfg.ChunkSize = 10 * 1024 * 1024 // 10MB
processor, err := json.New(cfg)
if err != nil {
panic(err)
}
defer processor.Close()모범 사례
- 파일 크기 예측: 처리 전 파일 크기를 확인하여 적절한 전략 선택
- 메모리 제한 설정:
MaxMemory를 사용하여 OOM 방지 - 배치 커밋: 일정 수량 누적 후 데이터베이스에 배치 쓰기
- 오류 처리:
JSONLContinueOnErr구현 또는 실패 항목 기록 - 진행 상황 모니터링: 정기적으로 처리 진행 상황 출력
선택 가이드
| 파일 크기 | 추천 방안 | 예제 |
|---|---|---|
| < 10MB | 직접 로드 | json.ParseAny + Get |
| 10-100MB | Processor.ForeachFile | 항목별 처리 |
| 100MB-1GB | Processor.ForeachFileChunked | 청크 반복 처리 |
| > 1GB | NDJSONProcessor / JSONL 형식 | 진정한 스트림 처리, 메모리 제어 가능 |
API 레퍼런스
이 섹션은 대용량 파일 처리 API 의 함수 시그니처와 매개변수 표를 요약하여 빠르게 조회할 수 있도록 합니다.
Processor 메서드
ForeachFile
시그니처: func (p *Processor) ForeachFile(filePath string, fn func(key any, item *IterableValue) error, cfg ...Config) error
대용량 파일의 JSON 배열 요소를 하나씩 반복합니다. 기본 사용과 중단 제어를 참조하세요.
매개변수
| 이름 | 타입 | 설명 |
|---|---|---|
filePath | string | JSON 파일 경로 |
fn | func(key any, item *IterableValue) error | 처리 콜백 |
콜백 반환값
| 반환값 | 설명 |
|---|---|
nil | 다음 항목으로 계속 처리 |
item.Break() | 반복 중지, 오류 반환 안 함 |
기타 error | 반복 중지하고 오류 반환 |
ForeachFileChunked
시그니처: func (p *Processor) ForeachFileChunked(filePath string, chunkSize int, fn func(chunk []*IterableValue) error, cfg ...Config) (err error)
대용량 파일을 배치로 처리합니다. 배치 처리를 참조하세요.
매개변수
| 이름 | 타입 | 설명 |
|---|---|---|
filePath | string | JSON 파일 경로 |
chunkSize | int | 배치당 요소 수 |
fn | func(chunk []*IterableValue) error | 배치 처리 콜백 |
ForeachFileWithPath
시그니처: func (p *Processor) ForeachFileWithPath(filePath, path string, fn func(key any, item *IterableValue) error, cfg ...Config) error
파일에서 지정된 경로의 JSON 배열 또는 객체를 처리합니다.
매개변수
| 이름 | 타입 | 설명 |
|---|---|---|
filePath | string | JSON 파일 경로 |
path | string | JSON 경로 표현식 |
fn | func(key any, item *IterableValue) error | 처리 콜백 |
// 파일에서 users 배열의 각 요소 처리
err := p.ForeachFileWithPath("data.json", "users", func(key any, item *json.IterableValue) error {
fmt.Printf("Name: %s\n", item.GetString("name"))
return nil
})ForeachFileNested
시그니처: func (p *Processor) ForeachFileNested(filePath string, fn func(key any, item *IterableValue) error, cfg ...Config) error
파일의 모든 중첩 JSON 구조를 재귀적으로 순회합니다.
// 모든 중첩 요소 재귀 순회
err := p.ForeachFileNested("data.json", func(key any, item *json.IterableValue) error {
fmt.Printf("Key: %v, Type: %T\n", key, item.GetData())
return nil
})패키지 레벨 함수
Processor 메서드 외에도, 다음 함수는 Processor 인스턴스를 생성하지 않고 직접 호출할 수 있습니다. 내부적으로 전역 프로세서를 사용합니다.
ForeachFile (패키지 레벨 함수)
시그니처: func ForeachFile(filePath string, fn func(key any, item *IterableValue) error, cfg ...Config) error
파일에서 JSON 을 로드하고 반복합니다.
err := json.ForeachFile("data.json", func(key any, item *json.IterableValue) error {
fmt.Printf("[%v] %v\n", key, item.GetData())
return nil
})ForeachFileWithPath (패키지 레벨 함수)
시그니처: func ForeachFileWithPath(filePath, path string, fn func(key any, item *IterableValue) error, cfg ...Config) error
파일에서 JSON 을 로드하고 경로로 반복합니다.
err := json.ForeachFileWithPath("data.json", "users", func(key any, item *json.IterableValue) error {
name := item.GetString("name")
fmt.Printf("사용자: %s\n", name)
return nil
})ForeachFileChunked (패키지 레벨 함수)
시그니처: func ForeachFileChunked(filePath string, chunkSize int, fn func(chunk []*IterableValue) error, cfg ...Config) error
파일의 JSON 배열을 청크 단위로 반복합니다.
err := json.ForeachFileChunked("large_data.json", 100, func(chunk []*json.IterableValue) error {
for _, item := range chunk {
processItem(item)
}
return nil
})ForeachFileNested (패키지 레벨 함수)
시그니처: func ForeachFileNested(filePath string, fn func(key any, item *IterableValue) error, cfg ...Config) error
파일에서 JSON 을 로드하고 모든 중첩 구조를 재귀적으로 반복합니다.
err := json.ForeachFileNested("config.json", func(key any, item *json.IterableValue) error {
fmt.Printf("경로: %v, 타입: %T\n", key, item.GetData())
return nil
})관련 문서
- NDJSON 프로세서 — JSONL/NDJSON 스트리밍 처리
- JSONLWriter — JSONL 쓰기
다음 단계
- API 문서 — 완전한 API 참조하세요