Table of Contents
Golang - 복합타입 (맵, 구조체) 사용하기
문자열
문자열은 바이트 단위로 표현되는데, 문자열의 각 문자는 글자 집합(character set)에 의해 결정된 바이트 크기로 저장된다. Go에서 기본적으로 UTF-8 글자 집합을 사용하여 문자열을 처리한다.
또한 문자열은 읽기 전용(immutable)이며, 문자열 내용을 변경하려면 새로운 문자열을 생성해야 한다.
문자열 선언
문자열은 다음과 같이 선언할 수 있다.
1var s string = "Hello"
2var b byte = s[6]
3
4var s2 string = s[:3] // "Hel"
배열이나 슬라이스에서 단일 값을 추출하는 것처럼, 문자열도 인덱스 표현으로 단일 값을 꺼내올 수 있다.
문자열 변환
단일 룬 혹은 바이트는 문자열로 변환이 가능하다.
1var a rune = 'x'
2var s string = string(a)
3var b byte = 'y'
4var s2 string = string(b)
맵
맵(map)은 키(key)와 값(value)의 쌍으로 구성된 자료구조이다. 보통 엄격하게 증가하는 순서가 아닌 값들을 구성하는 데이터가 있을 때 사용한다.
특정 종류의 데이터를 저장할 때는 편리하지만, 특정 키만 허용하도록 제한하는 방법이 없기 때문에 API에서는 사용하지 않고, 맵의 값으로 동일한 타입을 사용해야 하는 한계점이 있다.
맵은 다음과 같은 특징이 있다.
- 맵의 키는 비교 가능한 타입이 될 수 있다. → 맵의 키는 슬라이스나 맵이 될 수 없다.
- 맵은 비교 불가능하다. 맵의 값이 동일한지 비교하기 위해
==
혹은!=
를 사용할 수 없다.
맵 선언
문자열 타입의 키와 정수를 값으로 가지는 맵을 선언한다.
1var map1 map[string]int
2
3// 비어있는 맵 리터럴 할당
4map2 := map[string]int{}
5
6// 기본 크기 지정
7map3 := make(map[int][]string, 10)
8
9// 비어있지 않은 맵 리터럴
10map4 := map[string][]string {
11 "Orcas": []string{"Fred", "Ralph", "Bijou"},
12 "Lions": []string{"Sarah", "Peter", "Billie"},
13 "Kittens": []string{"Waldo", "Raul", "Ze"},
14}
map1 의 제로 값은 nil이 되고 길이가 0이 된다. map1 에 값을 쓰려고 하면 panic이 발생한다.
비어있는 맵 리터럴인 map2 는 길이는 0이지만 비어있는 맵 리터럴이 할당된 맵을 읽고 쓸 수 있다.
맵 응용
다음과 같이 맵을 읽고 쓸 수 있다.
1map1 := map[string]int{}
2map1["Orcas"] = 1 // 맵 쓰기
3map1["Lions"] = 2
4fmt.Println(map1["Orcas"]) // 맵 읽기
아직 설정하지 않은 맵 키에 할당된 값을 읽으려고 시도할 경우에는 맵의 값이 되는 타입의 제로 값을 반환한다.
맵에 키가 있는지 확인하고 싶을 때는 다음과 같이 사용한다.
1m := map[string]int{
2 "hello": 5,
3 "world": 0,
4}
5
6v, ok := m["hello"] // 5 true
7v, ok = m["noData"] // 0 false
맵에서 값을 삭제하는 코드이다.
1m := map[string]int{
2 "hello": 5,
3 "world": 10,
4}
5
6delete(m, "hello")
delete 함수는 맵과 키를 받아 해당 키에 해당하는 키-값 쌍을 제거한다. 키가 맵에 없거나 맵이 nil인 경우에는 아무일도 일어나지 않는다.
맵을 셋(Set)처럼 이용하려면 다음과 같이 사용할 수 있다.
1intSet := map[int]bool{}
2vals := []int{5, 10, 2, 5, 8, 7, 3, 9, 1, 2, 10}
3
4for _, v := range vals {
5 intSet[v] = true
6}
7
8fmt.Println(len(vals), len(intSet))
Go는 셋을 지원하지 않지만, 맵을 이용해서 셋처럼 사용할 수 있다. 값을 boolean으로 설정하여 키에 대응되는 값으로 true를 설정한다.
구조체
맵의 한계점인 “맵의 값으로 동일한 타입만 사용해야 한다” 를 해결하고 여러 데이터 타입을 함께 구성하고자 할 때 사용한다.
구조체 선언
type 키워드로 구조체 타입의 이름을 지정하고 중괄호로 구조체를 정의한다.
1type person struct {
2 name string
3 age int
4 pet string
5}
6
7// 구조체 타입으로 변수 선언
8var person1 person
9// 구조체 리터럴
10person2 := person{}
맵 리터럴과는 다르게 항목들 간에 구분을 위한 콤마를 사용하지 않는다. person1, person2 두 변수 모두 구조체 내에 존재하는 모든 항목들이 각 타입에 맞는 제로 값으로 설정된다.
구조체 응용
구조체 타입 이름을 지정하지 않고 구조체 타입을 구현하는 익명 구조체를 선언할 수 있다.
1var person struct {
2 name string
3 age int
4 pet string
5}
6
7person.name = "bob"
8person.age = 50
9person.pet = "dog"
10
11pet := struct {
12 name string
13 kind string
14}{
15 name: "Fido",
16 kind: "dog",
17}
person과 pet 변수는 익명 구조체이다. 익명 구조체는 1) 외부 데이터를 구조체로 전환 혹은 구조체를 외부 데이터로 전환할 때, 2) 테스트 작성할 때 주로 사용한다.
Go에서 다른 기본 타입의 변수들 간의 비교를 허용하지 않는 것처럼, 다른 타입 구성의 구조체의 비교도 허용하지 않는다.
1type firstPerson struct {
2 name string
3 age int
4}
5
6type secondPerson struct {
7 name string
8 age int
9}
10
11firstPerson == secondPerson // 비교 불가능!!
firstPerson 구조체와 secondPerson 구조체는 서로 다른 타입이기 때문에 ==를 사용하여 비교는 불가능하다. 하지만 익명 구조체이면서 두 구조체 모두 같은 이름, 순서, 타입을 가진다면 타입 변환 없이 비교가 가능하다.