-
Go 1.23 iter 패키지 완전 정복Go 2024. 8. 24. 10:15
소개
Go 1.23 버전부터 새롭게 등장한
iter
패키지는 개발자들에게 반복 처리(iteration)를 보다 효율적이고 유연하게 관리할 수 있는 방법을 제공합니다.iter
패키지는 추상화된 반복자(iterator) 개념을 도입하여, 기존의 반복 처리 방식을 개선하고 다양한 활용 가능성을 열어줍니다.이 글에서는
iter
패키지의 기본 개념과 사용법, 그리고 실제 활용 예시를 통해 Go 개발에서의 활용성을 살펴보겠습니다.iter 패키지의 핵심 개념
iter
패키지는 주로for-range
문과 함께 사용됩니다.핵심적인 역할은 컨텍스트(context)를 가진 논리적인 열거 가능 객체를 생성하고, 이를 다른 코드 블록에서
for-range
를 통해 순회하는 데 도움을 주는 것입니다.기존에는 goroutine과 channel을 활용하여 범위를 분할하는 방식으로 이러한 기능을 구현했습니다.
하지만 이 방식은
for-range
루프를 중간에 중단해야 할 경우 goroutine이 남아 처리되지 않는 문제점을 가지고 있었습니다.또한, 단순히 반복 처리를 위해 goroutine과 channel을 사용하는 것은 자원 낭비가 될 수 있습니다.
기존 방식의 한계점: goroutine과 channel을 이용한 반복 처리
다음은 goroutine과 channel을 사용하여 슬라이스를 순회하는 예시입니다.
package main func iter1[T any](a []T) func() (T, bool) { ch := make(chan T) go func() { defer close(ch) for _, v := range a { ch <- v } }() return func() (T, bool) { v, ok := <-ch return v, ok } } func main() { vv := iter1([]int{1, 2, 3}) for { v, ok := vv() if !ok { break } println(v) } }
위 코드에서
for
루프를 중간에break
하더라도,goroutine
은 계속 실행되어 자원을 소모합니다.이러한 문제를 해결하기 위해
context
패키지를 활용하여goroutine
을 제어할 수 있지만, 코드가 더욱 복잡해집니다.context를 활용한 개선
context
를 활용하면goroutine
의 실행을 제어하여 자원 낭비를 줄일 수 있습니다.package main import ( "context" ) func iter1[T any](ctx context.Context, a []T) func() (T, bool) { ch := make(chan T) go func() { defer close(ch) for _, v := range a { select { case ch <- v: case <-ctx.Done(): } } }() return func() (T, bool) { v, ok := <-ch return v, ok } } func main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() vv := iter1(ctx, []int{1, 2, 3}) for { v, ok := vv() if !ok { break } println(v) if v == 2 { cancel() break } } }
하지만 이러한 방식은 반복 처리를 위해 불필요한
goroutine
과channel
을 생성해야 한다는 단점을 여전히 가지고 있습니다.iter 패키지 활용
iter
패키지는 이러한 문제점을 해결하고 보다 효율적인 반복 처리를 가능하게 합니다.iter
패키지는Seq
와Seq2
라는 두 가지 주요 타입을 제공합니다.Seq[V any]
: 슬라이스와 같이 단일 값을 반복하는 경우 사용합니다.Seq2[K, V any]
: 맵과 같이 키-값 쌍을 반복하는 경우 사용합니다.
iter.Seq와 iter.Seq2의 차이
iter.Seq
는 슬라이스를for-range
로 처리하는 방식과 유사합니다.a := []int{1, 2, 3} for v := range a { // v를 사용하는 코드 }
반면
iter.Seq2
는 맵을for-range
로 처리하는 방식과 유사합니다.m := map[string]int{} for k, v := range m { // k 또는 v를 사용하는 코드 }
iter.Seq를 이용한 반복 처리 예시
iter.Seq
를 사용하여 모든 값을 출력하는 함수를 다음과 같이 정의할 수 있습니다.func PrintAll[V any](seq iter.Seq[V]) { for v := range seq { fmt.Println(v) } }
위 함수는 기존의
for-range
문과 동일한 방식으로iter.Seq
를 사용합니다.iter 구현: 파일에서 줄 읽어오기
iter
패키지를 직접 구현하는 방법은 조금 복잡하지만, 일관된 패턴을 따르면 어렵지 않습니다.예를 들어, 파일에서 줄 단위로 데이터를 읽어와
for-range
로 처리하는 함수를 구현해보겠습니다.package main import ( "bufio" "io" "iter" "log" "os" ) func lines(r io.Reader) iter.Seq[string] { scanner := bufio.NewScanner(r) return func(yield func(string) bool) { for scanner.Scan() { if !yield(scanner.Text()) { break } } } }
lines
함수는io.Reader
를 입력받아iter.Seq[string]
을 반환합니다.iter.Seq
의 본체는 함수이며, 이 함수의 인수yield
는 각 줄을 처리하는 코루틴 역할을 합니다.yield
함수는 처리할 값을 입력받고, 반환값bool
은for-range
루프를 계속 진행할지 여부를 결정합니다.false
를 반환하면for-range
루프가 중단됩니다.iter.Seq2를 이용한 반복 처리 예시
iter.Seq2
를 사용하는 예시로, 학급의 학생 성적을 관리하는 구조체를 생각해 볼 수 있습니다.type ClassRoom struct { // ... } func (cr *ClassRoom) Scores() iter.Seq2[string, int] { // ... } func main() { cr, _ := loadClassRoom("3A") for name, score := range cr.Scores() { fmt.Printf("name=%v, score=%v\n", name, score) } }
표준 패키지에서의 iter 활용
slices
와maps
패키지에는iter
패키지와 연동하여 사용할 수 있는 다양한 함수들이 추가되었습니다.예를 들어,
slices.Values
함수는 슬라이스의 모든 값을iter.Seq
로 반환하고,maps.Keys
함수는 맵의 모든 키를iter.Seq
로 반환합니다.iter의 효과: 메모리 효율성
iter
패키지는 단순히 범위를 분리하는 것뿐만 아니라 메모리 효율성도 향상시킵니다.예를 들어, 매우 큰 문자열을 공백으로 분리하여 처리해야 하는 경우,
strings.Split
함수를 사용하면 모든 토큰을 메모리에 저장해야 하므로 메모리 부족 현상이 발생할 수 있습니다.하지만
iter
패키지를 사용하면 필요한 토큰만 메모리에 로드하여 처리할 수 있으므로 메모리 사용량을 줄일 수 있습니다.벤치마크 결과
다음은
strings.Split
함수와iter.Seq
를 사용한 벤치마크 결과입니다.goos: linux goarch: amd64 pkg: iterbench cpu: Intel(R) Core(TM) i5-8250U CPU @ 1.60GHz BenchmarkWithoutIter-8 190472 5547 ns/op 1792 B/op 1 allocs/op BenchmarkWithIter-8 492627 2129 ns/op 24 B/op 2 allocs/op PASS ok iterbench 2.743s
결과에서 확인할 수 있듯이,
iter
패키지를 사용하면 실행 속도와 메모리 사용량을 모두 개선할 수 있습니다.마무리
Go 1.23 버전부터 도입된
iter
패키지는 반복 처리를 보다 효율적이고 유연하게 관리할 수 있는 강력한 도구입니다.처음에는 함수 시그니처가 다소 복잡해 보일 수 있지만, 익숙해지면
iter
패키지를 활용하여 코드를 더욱 깔끔하고 효율적으로 작성할 수 있습니다.iter
패키지를 적극 활용하여 견고하고 유지보수하기 쉬운 Go 애플리케이션을 개발해 보시기 바랍니다.'Go' 카테고리의 다른 글
AriaSQL: Go 언어로 만든 새로운 관계형 데이터베이스의 탄생 (2) 2024.09.07 Argon/Bcrypt가 CPU를 100% 사용하는 이유와 해결책 (2) 2024.09.07 Go 1.23 이터레이터 완벽 정리 (0) 2024.08.20 Go 1.22의 새로운 기능: cmp.Or (0) 2024.07.29 Go 1.22의 새로운 기능: slices.Concat (0) 2024.07.29