Skip to content

반복기 타입

json 패키지는 순차 순회, 스트림 처리, 배치 처리 및 병렬 처리 사례를 아우르는 다양한 반복기 타입을 제공합니다. 반복 함수(Foreach/ForeachFile 등) 는 패키지 레벨 반복 함수Processor 반복 메서드를 참조하세요.

IteratorControl 상수

IteratorControl은 반복 제어 플래그를 나타내며, ForeachWithPathAndControlForeachWithPathAndIterator에서 반복 흐름을 제어하는 데 사용됩니다.

상수설명
IteratorNormal정상적으로 반복 계속 (기본값)
IteratorContinue현재 항목 건너뛰고 반복 계속
IteratorBreak반복 중단

사용 시나리오

시나리오추천 반환값설명
정상적으로 요소 처리IteratorNormal다음 요소 계속 처리
무효한 데이터 필터링IteratorContinue현재 요소 건너뛰고 반복 계속
대상 찾은 후 종료IteratorBreak필요한 데이터를 찾은 후 즉시 중단
오류 발생 시 중단IteratorBreak심각한 오류 발생 시 반복 중단

Iterator 타입

Iterator 는 JSON 배열 또는 객체를 순회하는 저수준 반복기입니다.

NewIterator

시그니처: func NewIterator(data any, cfg ...Config) *Iterator

반복기 인스턴스를 생성합니다.

go
data := []any{"apple", "banana", "cherry"}
it := json.NewIterator(data)
for it.HasNext() {
    val, _ := it.Next()
    fmt.Println(val)
}

메서드

메서드시그니처설명
HasNextfunc (it *Iterator) HasNext() bool더 많은 요소가 있는지 확인
Nextfunc (it *Iterator) Next() (any, bool)다음 요소 가져오기
Resetfunc (it *Iterator) Reset()반복기 상태와 캐시를 지우고 재사용 준비
ResetWithfunc (it *Iterator) ResetWith(data any)상태를 지우고 새 데이터로 초기화

Reset

반복기 상태를 지우고 캐시된 키를 해제합니다. 호출 후 ResetWith로 재초기화할 수 있습니다.

go
it := json.NewIterator(data1)
for it.HasNext() {
    it.Next()
}

it.Reset() // 캐시 지우기

ResetWith

반복기 상태를 지우고 새 데이터로 초기화하여 반복기를 재사용합니다.

go
it := json.NewIterator(data1)
// ... data1 순회 ...

it.ResetWith(data2) // 반복기를 재사용하여 새 데이터 순회
for it.HasNext() {
    val, _ := it.Next()
    fmt.Println(val)
}

IterableValue 타입

IterableValue 는 반복 과정의 현재 요소를 캡슐화하여 편리한 값 접근 메서드를 제공합니다. Foreach 계열 함수의 콜백이 *IterableValue를 받습니다.

메서드

GetData

시그니처: func (iv *IterableValue) GetData() any

기본 데이터를 반환합니다.

Get

시그니처: func (iv *IterableValue) Get(path string) any

경로로 값을 가져옵니다 (점 표기법과 배열 인덱스 지원).

go
val := iv.Get("user.address.city")
val = iv.Get("users[0].name")

GetString

시그니처: func (iv *IterableValue) GetString(key string) string

문자열 값을 가져옵니다.

go
name := item.GetString("name")

GetInt

시그니처: func (iv *IterableValue) GetInt(key string) int

정수 값을 가져옵니다.

go
age := item.GetInt("age")

GetFloat64

시그니처: func (iv *IterableValue) GetFloat64(key string) float64

부동소수점 값을 가져옵니다.

go
price := item.GetFloat64("price")

GetBool

시그니처: func (iv *IterableValue) GetBool(key string) bool

불리언 값을 가져옵니다.

go
enabled := item.GetBool("enabled")

GetArray

시그니처: func (iv *IterableValue) GetArray(key string) []any

배열 값을 가져옵니다.

go
items := item.GetArray("items")

GetObject

시그니처: func (iv *IterableValue) GetObject(key string) map[string]any

객체 값을 가져옵니다.

go
profile := item.GetObject("profile")

GetWithDefault

시그니처: func (iv *IterableValue) GetWithDefault(key string, defaultValue any) any

값을 가져오며, 키가 존재하지 않으면 기본값을 반환합니다.

go
// 선택적 필드 가져오기, 누락 시 기본값 사용
timeout := item.GetWithDefault("timeout", 30)
mode := item.GetWithDefault("mode", "default")

GetStringWithDefault

시그니처: func (iv *IterableValue) GetStringWithDefault(key string, defaultValue string) string

문자열 값을 가져오며, 키가 존재하지 않으면 기본값을 반환합니다.

go
name := item.GetStringWithDefault("name", "알 수 없음")

GetIntWithDefault

시그니처: func (iv *IterableValue) GetIntWithDefault(key string, defaultValue int) int

정수 값을 가져오며, 키가 존재하지 않으면 기본값을 반환합니다.

go
age := item.GetIntWithDefault("age", 0)
port := item.GetIntWithDefault("port", 8080)

GetFloat64WithDefault

시그니처: func (iv *IterableValue) GetFloat64WithDefault(key string, defaultValue float64) float64

부동소수점 값을 가져오며, 키가 존재하지 않으면 기본값을 반환합니다.

go
price := item.GetFloat64WithDefault("price", 0.0)
rate := item.GetFloat64WithDefault("rate", 1.0)

GetBoolWithDefault

시그니처: func (iv *IterableValue) GetBoolWithDefault(key string, defaultValue bool) bool

불리언 값을 가져오며, 키가 존재하지 않으면 기본값을 반환합니다.

go
enabled := item.GetBoolWithDefault("enabled", false)
debug := item.GetBoolWithDefault("debug", true)

Exists

시그니처: func (iv *IterableValue) Exists(key string) bool

지정된 키가 존재하는지 확인합니다.

go
if item.Exists("email") {
    email := item.GetString("email")
    fmt.Printf("이메일: %s\n", email)
}

ForeachNested

시그니처: func (iv *IterableValue) ForeachNested(path string, fn func(key any, item *IterableValue))

지정된 경로 아래의 중첩 구조를 재귀적으로 순회합니다.

IsNullData

시그니처: func (iv *IterableValue) IsNullData() bool

전체 값이 null 인지 확인합니다.

go
if item.IsNullData() {
    fmt.Println("값이 null 입니다")
}

IsNull

시그니처: func (iv *IterableValue) IsNull(key string) bool

지정된 키의 값이 null 인지 확인합니다.

go
if item.IsNull("optional_field") {
    fmt.Println("선택적 필드가 null 입니다")
}

IsEmptyData

시그니처: func (iv *IterableValue) IsEmptyData() bool

전체 값이 비어 있는지 확인합니다 (nil, 빈 문자열, 빈 배열 또는 빈 객체).

go
if item.IsEmptyData() {
    fmt.Println("값이 비어 있습니다")
}

IsEmpty

시그니처: func (iv *IterableValue) IsEmpty(key string) bool

지정된 키의 값이 비어 있는지 확인합니다.

go
if item.IsEmpty("tags") {
    fmt.Println("태그 목록이 비어 있습니다")
}

Break

시그니처: func (iv *IterableValue) Break() error

반복 중단 신호를 반환합니다. 반복 콜백에서 호출하면 순회를 조기에 종료할 수 있습니다.

go
// 주의: Break() 는 콜백이 error 를 반환하는 반복 함수 (ForeachWithError,
// ForeachNestedWithError 등) 에서만 적용됩니다. 일반 Foreach 콜백은 error 를
// 반환하지 않으므로, 그 안에서 item.Break() 를 호출해도 반복이 중지되지 않습니다.
err := json.ForeachNestedWithError(data, func(key any, item *json.IterableValue) error {
    if item.GetString("status") == "stop" {
        // 대상을 찾은 후 반복 중단
        return item.Break()
    }
    // 계속 처리
    return nil
})

Release

시그니처: func (iv *IterableValue) Release()

IterableValue 를 객체 풀에 반환하여 내부 데이터 참조를 해제합니다.

go
json.Foreach(data, func(key any, item *json.IterableValue) {
    // 데이터 처리...
    fmt.Println(item.GetData())
    // 처리 완료 후 해제, GC 부담 감소
    item.Release()
})

StreamIterator 타입

StreamIterator 는 메모리 효율적인 스트림 반복을 제공하며, 대용량 JSON 배열에 적합합니다. 요소 단위로 처리하여 전체 배열을 메모리에 로드할 필요가 없습니다.

NewStreamIterator

시그니처: func NewStreamIterator(reader io.Reader, cfg ...Config) *StreamIterator

스트림 반복기를 생성합니다. Config.BufferSize로 버퍼 크기를 설정합니다.

go
file, _ := os.Open("large-array.json")
defer file.Close()

// 설정 없이 사용
it := json.NewStreamIterator(file)
for it.Next() {
    val := it.Value()
    fmt.Printf("인덱스 %d: %v\n", it.Index(), val)
}
if err := it.Err(); err != nil {
    panic(err)
}

// 설정과 함께 사용
cfg := json.DefaultConfig()
cfg.BufferSize = 64 * 1024 // 64KB 버퍼
it2 := json.NewStreamIterator(file, cfg)

메서드

메서드시그니처설명
Nextfunc (si *StreamIterator) Next() bool다음 요소로 이동
Valuefunc (si *StreamIterator) Value() any현재 요소 반환
Indexfunc (si *StreamIterator) Index() int현재 인덱스 반환
Errfunc (si *StreamIterator) Err() error반복 중 오류 반환

StreamObjectIterator 타입

StreamObjectIterator 는 메모리 효율적인 스트림 반복을 제공하며, 대용량 JSON 객체에 적합합니다.

NewStreamObjectIterator

시그니처: func NewStreamObjectIterator(reader io.Reader, cfg ...Config) *StreamObjectIterator

스트림 객체 반복기를 생성합니다.

go
file, _ := os.Open("large-object.json")
defer file.Close()

it := json.NewStreamObjectIterator(file)
for it.Next() {
    fmt.Printf("키: %s, 값: %v\n", it.Key(), it.Value())
}
if err := it.Err(); err != nil {
    panic(err)
}

메서드

메서드시그니처설명
Nextfunc (soi *StreamObjectIterator) Next() bool다음 키 - 값 쌍으로 이동
Keyfunc (soi *StreamObjectIterator) Key() string현재 키 반환
Valuefunc (soi *StreamObjectIterator) Value() any현재 값 반환
Errfunc (soi *StreamObjectIterator) Err() error반복 중 오류 반환

BatchIterator 타입

BatchIterator 는 대용량 배열을 효율적으로 배치 처리하는 데 사용되며, 단일 요소 처리 오버헤드를 줄입니다.

NewBatchIterator

시그니처: func NewBatchIterator(data []any, cfg ...Config) *BatchIterator

배치 반복기를 생성합니다. Config.MaxBatchSize로 배치 크기를 설정합니다.

go
data := make([]any, 10000)
// 데이터 채우기...

cfg := json.DefaultConfig()
cfg.MaxBatchSize = 100 // 배치당 100 개 요소
it := json.NewBatchIterator(data, cfg)
for it.HasNext() {
    batch := it.NextBatch()
    // 배치 처리
    processBatch(batch)
    fmt.Printf("%d개 요소 처리, 남은 %d\n", len(batch), it.Remaining())
}

메서드

메서드시그니처설명
NextBatchfunc (it *BatchIterator) NextBatch() []any다음 배치 요소 반환
HasNextfunc (it *BatchIterator) HasNext() bool더 많은 배치가 있는지 확인
Resetfunc (it *BatchIterator) Reset()반복기를 시작 위치로 재설정
TotalBatchesfunc (it *BatchIterator) TotalBatches() int전체 배치 수 반환
CurrentIndexfunc (it *BatchIterator) CurrentIndex() int현재 위치 반환
Remainingfunc (it *BatchIterator) Remaining() int남은 요소 수 반환

ParallelIterator 타입

ParallelIterator 는 배열을 병렬로 처리하는 데 사용되며, 멀티코어 CPU 를 활용하여 처리 속도를 높입니다.

NewParallelIterator

시그니처: func NewParallelIterator(data []any, cfg ...Config) *ParallelIterator

병렬 반복기를 생성합니다. Config.MaxConcurrency로 작업 고루틴 수를 설정합니다.

go
data := make([]any, 10000)
// 데이터 채우기...

cfg := json.DefaultConfig()
cfg.MaxConcurrency = 8 // 8 개 작업 고루틴
it := json.NewParallelIterator(data, cfg)
err := it.ForEach(func(idx int, val any) error {
    // 각 요소를 병렬로 처리
    return processItem(idx, val)
})
if err != nil {
    panic(err)
}

ForEach

시그니처: func (it *ParallelIterator) ForEach(fn func(int, any) error) error

각 요소를 병렬로 처리하며, 처음 만난 오류를 반환합니다.

go
err := it.ForEach(func(idx int, val any) error {
    // 이 함수는 여러 고루틴에서 병렬로 실행됩니다
    return nil
})

ForEachWithContext

시그니처: func (it *ParallelIterator) ForEachWithContext(ctx context.Context, fn func(int, any) error) error

컨텍스트가 있는 병렬 처리로, 작업 취소를 지원합니다.

go
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

err := it.ForEachWithContext(ctx, func(idx int, val any) error {
    select {
    case <-ctx.Done():
        return ctx.Err()
    default:
        return processItem(idx, val)
    }
})

ForEachBatch

시그니처: func (it *ParallelIterator) ForEachBatch(batchSize int, fn func(int, []any) error) error

병렬 배치 처리입니다.

go
err := it.ForEachBatch(100, func(batchIdx int, batch []any) error {
    // 각 배치가 하나의 고루틴에서 처리됩니다
    return processBatch(batchIdx, batch)
})

ForEachBatchWithContext

시그니처: func (it *ParallelIterator) ForEachBatchWithContext(ctx context.Context, batchSize int, fn func(int, []any) error) error

컨텍스트가 있는 병렬 배치 처리입니다.

Map

시그니처: func (it *ParallelIterator) Map(transform func(int, any) (any, error)) ([]any, error)

각 요소를 병렬로 변환하여 새 슬라이스를 반환합니다.

go
results, err := it.Map(func(idx int, val any) (any, error) {
    if num, ok := val.(float64); ok {
        return num * 2, nil
    }
    return nil, fmt.Errorf("unexpected type at index %d", idx)
})

Filter

시그니처: func (it *ParallelIterator) Filter(predicate func(int, any) bool) []any

요소를 병렬로 필터링하여 조건을 만족하는 요소 슬라이스를 반환합니다.

go
even := it.Filter(func(idx int, val any) bool {
    if num, ok := val.(float64); ok {
        return int(num)%2 == 0
    }
    return false
})

Close

시그니처: func (it *ParallelIterator) Close()

ParallelIterator 리소스를 해제합니다.

go
it := json.NewParallelIterator(data, cfg)
defer it.Close()

전체 예제

대용량 파일 스트림 처리

go
package main

import (
    "fmt"
    "os"
    "github.com/cybergodev/json"
)

func main() {
    file, err := os.Open("large-array.json")
    if err != nil {
        panic(err)
    }
    defer file.Close()

    it := json.NewStreamIterator(file)
    count := 0

    for it.Next() {
        val := it.Value()
        // 요소 단위 처리, 메모리 친화적
        count++
        if count%1000 == 0 {
            fmt.Printf("%d개 요소 처리 완료, 현재 값: %v\n", count, val)
        }
    }

    if err := it.Err(); err != nil {
        panic(err)
    }

    fmt.Printf("총 %d개 요소 처리 완료\n", count)
}

병렬 처리

go
package main

import (
    "fmt"
    "sync/atomic"
    "github.com/cybergodev/json"
)

func main() {
    // JSON 배열 파싱
    data := `[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]`
    var arr []any
    json.Unmarshal([]byte(data), &arr)

    // 병렬 반복기 생성 (4 개 작업 고루틴)
    cfg := json.DefaultConfig()
    cfg.MaxConcurrency = 4
    it := json.NewParallelIterator(arr, cfg)

    var sum int64

    err := it.ForEach(func(idx int, val any) error {
        if num, ok := val.(float64); ok {
            atomic.AddInt64(&sum, int64(num))
        }
        return nil
    })

    if err != nil {
        panic(err)
    }

    fmt.Printf("합계: %d\n", sum) // 출력: 합계: 55
}

배치 처리

go
package main

import (
    "fmt"
    "github.com/cybergodev/json"
)

func main() {
    // 대용량 데이터셋 생성
    data := make([]any, 1000)
    for i := range data {
        data[i] = map[string]any{"id": i, "value": i * 10}
    }

    // 배치당 100 개 요소
    cfg := json.DefaultConfig()
    cfg.MaxBatchSize = 100
    it := json.NewBatchIterator(data, cfg)
    batchNum := 0

    for it.HasNext() {
        batch := it.NextBatch()
        batchNum++

        // 배치 처리 (예: 데이터베이스 일괄 쓰기)
        fmt.Printf("배치 %d: %d개 요소 처리\n", batchNum, len(batch))
    }

    fmt.Printf("전체 배치 수: %d\n", it.TotalBatches())
}

Iterator 재사용

go
package main

import (
    "fmt"
    "github.com/cybergodev/json"
)

func main() {
    // 첫 번째 순회
    it := json.NewIterator([]any{"a", "b", "c"})
    for it.HasNext() {
        val, _ := it.Next()
        fmt.Println(val)
    }

    // 동일 반복기를 재사용하여 새 데이터 순회, 재할당 방지
    it.ResetWith([]any{1, 2, 3, 4})
    for it.HasNext() {
        val, _ := it.Next()
        fmt.Println(val)
    }
}

성능 권장 사항

  1. Iterator 재사용 - Reset/ResetWith로 재할당을 피하고 여러 번 순회하는 사례에 적합
  2. 대용량 데이터셋에 스트림 반복기 사용 - StreamIterator/StreamObjectIterator는 요소 단위 처리로 메모리 효율적
  3. 배치 처리로 오버헤드 감소 - BatchIterator는 배치 단위 처리로 단일 요소 오버헤드를 줄임
  4. CPU 집약적 작업은 병렬 처리 - ParallelIterator로 멀티코어 가속 활용
  5. IterableValue 해제 - Foreach 콜백에서 처리 완료 후 Release()를 호출해 GC 부담 감소

관련 문서