[Go 공식문서 한국어 정리] ⓪9. Go 슬라이스의 사용법과 내부 구조
[Go 공식문서 한국어 정리] ⓪9. Go 슬라이스의 사용법과 낭부 구조
원문 제목: Go Slices: usage and internals
작성자: Andrew Gerrand
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📌 1. 서론 — 이 문서가 다루는 내용
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Go의 슬라이스(slice)는 타입화된 데이터 시퀀스를 다루는 편리하고 효율적인 수단입니다.
다른 언어의 배열과 유사하지만 몇 가지 독특한 특성을 가집니다.
이 문서에서는 슬라이스가 무엇인지, 어떻게 사용하는지, 그리고 내부적으로 어떻게 동작하는지 살펴봅니다.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📌 2. 핵심 개념
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
① 배열(Array) — 슬라이스의 기초
슬라이스는 Go의 배열 위에 구축된 추상화입니다.
배열은 길이와 요소 타입이 고정된 타입입니다. 예를 들어 [4]int는 4개의 정수 배열입니다.
배열의 크기는 타입의 일부이므로 [4]int와 [5]int는 서로 다른 타입입니다.
② 슬라이스 — 배열 위의 강력한 추상화
슬라이스 타입은 []T로 표현되며, T는 요소 타입입니다.
배열과 달리 길이가 고정되지 않습니다.
make 함수로 생성할 수 있으며, make([]T, len, cap) 형태를 가집니다.
③ 슬라이스 내부 구조 — 포인터, 길이, 용량
슬라이스는 배열 세그먼트를 기술하는 구조체입니다.
포인터(배열 시작 주소), 길이(세그먼트 내 요소 수), 용량(기본 배열의 최대 길이)로 구성됩니다.
슬라이싱은 데이터를 복사하지 않고 새로운 슬라이스 값을 생성합니다.
④ 복사와 append — 슬라이스 확장
copy 함수는 소스 슬라이스에서 대상 슬라이스로 데이터를 복사합니다.
append 함수는 슬라이스 끝에 요소를 추가하며, 필요하면 용량을 자동으로 늘립니다.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📌 3. 주요 내용 상세
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Go에서 배열은 값(value)입니다.
배열 변수는 전체 배열을 나타내며, 첫 번째 요소에 대한 포인터가 아닙니다(C와 다름).
따라서 배열을 할당하거나 전달하면 내용 전체가 복사됩니다.
슬라이스 리터럴은 배열 리터럴과 유사하지만 요소 개수를 생략합니다.
letters := []string{"a", "b", "c", "d"}
make 함수는 배열을 할당하고 해당 배열을 참조하는 슬라이스를 반환합니다.
var s []byte
s = make([]byte, 5, 5)
// s == []byte{0, 0, 0, 0, 0}
슬라이스의 제로 값은 nil입니다.
len과 cap 함수는 nil 슬라이스에 대해 0을 반환합니다.
슬라이싱은 기존 슬라이스나 배열에서 half-open range(반개구간)로 지정합니다.
b := []byte{'g', 'o', 'l', 'a', 'n', 'g'}
// b[1:4] == []byte{'o', 'l', 'a'}
시작과 끝 인덱스는 선택적입니다. 기본값은 각각 0과 슬라이스 길이입니다.
// b[:2] == []byte{'g', 'o'}
// b[2:] == []byte{'l', 'a', 'n', 'g'}
// b[:] == b
s = s[2:4]와 같이 슬라이싱하면 새로운 슬라이스가 생성되지만 데이터는 복사되지 않습니다.
따라서 재슬라이싱(re-slicing)으로 요소를 수정하면 원본 슬라이스의 요소도 변경됩니다.
d := []byte{'r', 'o', 'a', 'd'}
e := d[2:]
// e == []byte{'a', 'd'}
e[1] = 'm'
// d == []byte{'r', 'o', 'a', 'm'}
용량이 허용하는 한 슬라이스를 확장할 수 있습니다.
s = s[:cap(s)]
하지만 용량을 초과해서 확장하려면 런타임 패닉이 발생합니다.
copy 함수는 소스와 대상 슬라이스 간 데이터를 복사합니다.
서로 다른 길이의 슬라이스 간에도 복사 가능하며, 겹치는 슬라이스도 안전하게 처리합니다.
용량을 늘리려면 더 큰 새 슬라이스를 만들고 기존 내용을 복사해야 합니다.
t := make([]byte, len(s), (cap(s)+1)*2)
copy(t, s)
s = t
append는 슬라이스 끝에 요소를 추가하는 내장 함수입니다.
필요하면 자동으로 재할당하여 용량을 늘립니다.
a := make([]int, 1)
a = append(a, 1, 2, 3)
한 슬라이스를 다른 슬라이스에 추가하려면 ...를 사용합니다.
a := []string{"John", "Paul"}
b := []string{"George", "Ringo"}
a = append(a, b...)
nil 슬라이스는 길이가 0인 슬라이스처럼 동작합니다.
따라서 nil 슬라이스 변수에 append를 호출할 수 있습니다.
재슬라이싱은 기본 배열을 복사하지 않습니다.
따라서 작은 슬라이스가 큰 배열을 계속 참조하면 메모리 누수가 발생할 수 있습니다.
FindDigits 함수 예시에서 파일 전체를 읽고 일부만 반환하면,
반환된 슬라이스가 파일 전체를 메모리에 유지합니다.
이를 방지하려면 copy로 필요한 데이터만 새 슬라이스에 복사해야 합니다.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📌 4. 실전 활용 / 예시
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
슬라이스는 Go 코드에서 가장 많이 사용하는 데이터 구조입니다.
JSON 디코딩, 데이터 필터링, 버퍼 관리 등 거의 모든 작업에서 슬라이스가 쓰입니다.
append와 copy를 적절히 조합하면 메모리 효율적인 데이터 파이프라인을 구축할 수 있습니다.
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📌 5. 정리
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
슬라이스는 배열 위의 가벼운 추상화입니다.
포인터, 길이, 용량 세 가지 정보로 배열 세그먼트를 기술합니다.
슬라이싱은 데이터 복사 없이 뷰를 생성하며, append와 copy로 동적 확장이 가능합니다.
📎 출처 링크
https://go.dev/blog/slices-intro
#Go #Golang #slice #array #공식문서

오뉴노노 님의 최근 댓글
ㅋㅋㅋㅋㅋ 2019 01.14 잘 읽었습니다 2018 12.30 포인트가 없어서 아직 시작을 못하고있는데요! 글은 잘 읽었습니다! 포인트 쌓고 도전할거에요 2018 12.30