Skip to content

대용량 파일 처리

대형 JSON 파일 (예: 로그, 설정, 데이터 내보내기) 을 메모리에 직접 로드하면 메모리 부족이 발생할 수 있습니다. json 라이브러리는 다양한 효율적인 처리 방법을 제공합니다.

경고

ForeachFileForeachFileChunked는 반복 전에 전체 파일을 메모리에 로드합니다. "청크" 동작은 메모리 내 데이터의 반복 방식에만 영향을 미치며, 파일 읽기 방식에는 영향을 주지 않습니다. 메모리를 진정으로 제어해야 하는 초대용량 파일 처리의 경우 NDJSONProcessor를 JSONL 형식과 함께 사용하거나 StreamIterator를 사용하세요.

대체 방안

방안적용 시나리오메모리 사용량
Processor.ForeachFile구조화된 반복 처리전체 파일 로드, 항목별 반복
Processor.ForeachFileChunked배치 청크 반복 처리전체 파일 로드, 청크별 반복
NDJSONProcessorJSONL 파일 행 단위 처리메모리 제어 가능, 진정한 스트림 처리

통합 API: Processor

설정 옵션

대용량 파일 처리 설정은 Config에 통합되어 있습니다:

go
type Config struct {
    // ... 기타 설정 ...

    // 대용량 파일 처리 설정
    ChunkSize       int64 // 청크 크기 (기본 1MB)
    MaxMemory       int64 // 최대 메모리 사용량 (기본 100MB)
    BufferSize      int   // 읽기 버퍼 크기 (기본 64KB)
    SamplingEnabled bool  // 샘플링 활성화 여부 (기본 true)
    SampleSize      int   // 샘플링 수량 (기본 1000)
}

기본 사용

go
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)
}

배치 처리

go
// 방법 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
})

중단 제어 포함

go
// 방법 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 // 반복 계속
})

객체 파일 처리

go
// 방법 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
})

커스텀 설정

go
// 대용량 파일 처리 설정 커스텀
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()

경로 탐색 지원

go
city := item.GetString("profile.address.city")      // 중첩 객체
firstTag := item.GetString("tags[0]")               // 배열 인덱스
lastTag := item.GetString("tags[-1]")               // 음수 인덱스 (마지막)
nested := item.GetString("data.items[0].name")      // 복잡한 경로

스트림 처리 설정

Config를 통해 스트림 처리 매개변수를 설정합니다:

go
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 제네릭 함수 사용

go
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 을 사용할 수 있습니다:

go
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()
}

성능 최적화 제안

메모리 제어

go
// 가용 메모리에 따라 설정
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()

모범 사례

  1. 파일 크기 예측: 처리 전 파일 크기를 확인하여 적절한 전략 선택
  2. 메모리 제한 설정: MaxMemory를 사용하여 OOM 방지
  3. 배치 커밋: 일정 수량 누적 후 데이터베이스에 배치 쓰기
  4. 오류 처리: JSONLContinueOnErr 구현 또는 실패 항목 기록
  5. 진행 상황 모니터링: 정기적으로 처리 진행 상황 출력

선택 가이드

파일 크기추천 방안예제
< 10MB직접 로드json.ParseAny + Get
10-100MBProcessor.ForeachFile항목별 처리
100MB-1GBProcessor.ForeachFileChunked청크 반복 처리
> 1GBNDJSONProcessor / JSONL 형식진정한 스트림 처리, 메모리 제어 가능

API 레퍼런스

이 섹션은 대용량 파일 처리 API 의 함수 시그니처와 매개변수 표를 요약하여 빠르게 조회할 수 있도록 합니다.

Processor 메서드

ForeachFile

시그니처: func (p *Processor) ForeachFile(filePath string, fn func(key any, item *IterableValue) error, cfg ...Config) error

대용량 파일의 JSON 배열 요소를 하나씩 반복합니다. 기본 사용중단 제어를 참조하세요.

매개변수

이름타입설명
filePathstringJSON 파일 경로
fnfunc(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)

대용량 파일을 배치로 처리합니다. 배치 처리를 참조하세요.

매개변수

이름타입설명
filePathstringJSON 파일 경로
chunkSizeint배치당 요소 수
fnfunc(chunk []*IterableValue) error배치 처리 콜백

ForeachFileWithPath

시그니처: func (p *Processor) ForeachFileWithPath(filePath, path string, fn func(key any, item *IterableValue) error, cfg ...Config) error

파일에서 지정된 경로의 JSON 배열 또는 객체를 처리합니다.

매개변수

이름타입설명
filePathstringJSON 파일 경로
pathstringJSON 경로 표현식
fnfunc(key any, item *IterableValue) error처리 콜백
go
// 파일에서 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 구조를 재귀적으로 순회합니다.

go
// 모든 중첩 요소 재귀 순회
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 을 로드하고 반복합니다.

go
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 을 로드하고 경로로 반복합니다.

go
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 배열을 청크 단위로 반복합니다.

go
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 을 로드하고 모든 중첩 구조를 재귀적으로 반복합니다.

go
err := json.ForeachFileNested("config.json", func(key any, item *json.IterableValue) error {
    fmt.Printf("경로: %v, 타입: %T\n", key, item.GetData())
    return nil
})

관련 문서

다음 단계