
Go 언어 딕셔너리 완전 정복: 맵(map)으로 데이터 관리 마스터하기!
들어가며: 원하는 정보를 쏙쏙! Go 언어의 만능 보관함, 맵(map)
우리가 실생활에서 사전을 사용하듯, 프로그래밍에서도 특정 '키(Key)'를 이용해 원하는 '값(Value)'을 빠르게 찾아낼 수 있는 자료구조가 필요할 때가 많습니다.
마치 단어를 찾으면 그 뜻이 나오는 사전처럼 말이죠!
Go 언어에서는 이러한 기능을 '맵(map)'이라는 내장 타입을 통해 제공하는데요.
맵을 잘 활용하면 데이터를 효율적으로 저장하고 관리하는 데 아주 큰 도움이 된답니다.
이번 글에서는 Go 언어의 맵이 무엇인지, 어떻게 사용하고 어떤 점들을 주의해야 하는지 쉽고 재미있게 알아보겠습니다.
마치 잘 정리된 서랍장처럼 데이터를 깔끔하게 관리하는 비법을 함께 배워볼까요?
맵(map)이란 무엇일까요? 키(Key)와 값(Value)의 환상적인 짝꿍!
Go 언어에서 맵은 '키(Key)'와 '값(Value)'을 하나의 쌍으로 묶어서 저장하는 자료구조입니다.
여기서 중요한 점은, 각 키는 고유해야 한다는 것인데요.
마치 우리 각자의 주민등록번호처럼, 맵 안의 키들은 서로 중복되지 않아야 합니다.
이 고유한 키를 이용하면 해당 키에 연결된 값을 아주 빠르게 찾아낼 수 있습니다.
맵 선언하고 초기화하기: 첫 단추를 잘 꿰어야죠!
Go 언어에서 맵을 사용하려면 먼저 선언을 해야 합니다.
맵을 선언하는 기본적인 방법은 다음과 같습니다.
var m map[KeyType]ValueType
여기서 KeyType은 키의 데이터 타입을, ValueType은 값의 데이터 타입을 의미합니다.
예를 들어, 문자열을 키로 갖고 정수 값을 저장하는 맵을 만들고 싶다면 이렇게 선언할 수 있습니다.
var m map[string]int
하지만 여기서 아주 중요한 점이 있습니다! 이렇게 선언만 한 맵은 아직 'nil' 상태, 즉 아무것도 없는 텅 빈 상태입니다.
이 상태의 맵에 값을 추가하려고 하면 프로그램 실행 중에 '패닉(panic)'이라는 무시무시한 오류가 발생할 수 있습니다.
그래서 맵을 사용하기 전에는 반드시 '초기화' 과정을 거쳐야 하는데요.
초기화는 make 함수를 사용해서 할 수 있습니다.
m = make(map[string]int)
또는, 선언과 동시에 값을 넣어 초기화하는 '맵 리터럴(map literal)' 방식도 있습니다.
이게 더 간편할 때도 많죠.
package main
import "fmt"
func main() {
// 문자열을 키로, 정수를 값으로 갖는 맵을 선언하고 초기화합니다.
m := map[string]int{
"앨리스": 30, // "앨리스"라는 키에 30이라는 값을 저장합니다.
"밥": 25, // "밥"이라는 키에 25라는 값을 저장합니다.
}
fmt.Println(m) // 맵의 내용을 출력합니다.
}
맵에 데이터 추가하고 꺼내 쓰기: 자유자재로 다뤄봐요!
맵에 새로운 키-값 쌍을 추가하거나 기존 값을 변경하는 것은 아주 간단합니다.
마치 변수에 값을 할당하듯이 하면 되는데요.
m["찰리"] = 35 // "찰리"라는 키에 35라는 값을 추가 (또는 이미 있다면 수정)
맵에서 특정 키에 해당하는 값을 가져오는 것도 쉽습니다.
age := m["앨리스"] // "앨리스" 키에 해당하는 값을 age 변수에 저장
그런데 만약 맵에 존재하지 않는 키로 값을 가져오려고 하면 어떻게 될까요?
예를 들어 m["존재하지않는키"]처럼요.
이럴 때는 해당 맵의 값 타입에 대한 '제로 값(zero value)'이 반환됩니다.
만약 맵의 값 타입이 int라면 0이, string이라면 빈 문자열("")이 반환되는 식이죠.
"이 키, 맵 안에 있나요?" 존재 여부 확실하게 확인하기!
앞서 말했듯이, 존재하지 않는 키로 값을 조회하면 제로 값이 반환됩니다.
그런데 만약 값 자체가 0이거나 빈 문자열일 수도 있다면, 단순히 제로 값이 나왔다는 것만으로는 해당 키가 원래 없었던 건지, 아니면 값이 원래 제로 값이었던 건지 구분하기 어렵겠죠?
그래서 Go 언어의 맵은 키의 존재 여부를 함께 알려주는 아주 편리한 기능을 제공합니다.
package main
import "fmt"
func main() {
m := map[string]int{
"앨리스": 30,
"밥": 25,
}
// "앨리스" 키에 해당하는 값과 존재 여부를 함께 확인합니다.
// age 변수에는 값이, exists 변수에는 키의 존재 여부(true 또는 false)가 저장됩니다.
age, exists := m["앨리스"]
if exists {
fmt.Printf("앨리스의 나이는 %d살입니다.\n", age)
} else {
fmt.Println("앨리스의 정보가 없습니다.")
}
// "데이빗" 키에 해당하는 값과 존재 여부를 함께 확인합니다.
_, exists2 := m["데이빗"] // 값은 사용하지 않으므로 _(블랭크 식별자)로 받습니다.
if exists2 {
fmt.Println("데이빗의 정보가 있습니다.") // 이 부분은 실행되지 않겠죠?
} else {
fmt.Println("데이빗의 정보가 없습니다.")
}
}
이렇게 두 번째 반환 값인 exists 변수를 확인하면, 해당 키가 맵에 정말로 존재하는지 아닌지를 명확하게 알 수 있습니다.
맵에서 특정 요소 삭제하기: 깔끔하게 정리해요!
맵에서 특정 키-값 쌍을 삭제하고 싶을 때는 delete 함수를 사용합니다.
delete(m, "밥") // m 맵에서 "밥"이라는 키와 그 값을 삭제합니다.
만약 존재하지 않는 키를 delete 함수에 전달해도 아무런 오류가 발생하지 않는다는 점도 기억해두면 좋습니다.
맵 안의 모든 요소 훑어보기: 반복문의 마법, !
맵 안에 저장된 모든 키-값 쌍을 하나씩 살펴보고 싶을 때는 for 반복문과 range 키워드를 함께 사용합니다.
package main
import "fmt"
func main() {
m := map[string]int{
"앨리스": 30,
"밥": 25,
"찰리": 35,
}
// 맵 m의 모든 키-값 쌍을 반복합니다.
for key, value := range m {
fmt.Printf("%s의 나이는 %d살입니다.\n", key, value)
}
}
아주 중요한 점! Go 언어에서 맵을 range로 반복할 때, 요소들이 어떤 순서로 나올지는 보장되지 않습니다. 즉, 실행할 때마다 순서가 다를 수 있다는 뜻입니다.
만약 특정한 순서대로 맵의 요소를 처리하고 싶다면, 키들을 따로 슬라이스에 담아 정렬한 후, 그 슬라이스를 기준으로 맵에 접근해야 합니다.
맵을 집합(Set)처럼 활용하기: 중복 없이 관리해요!
맵의 특성을 이용하면 마치 '집합(Set)'처럼 활용할 수도 있습니다.
집합은 중복된 요소를 허용하지 않는 자료구조인데요.
예를 들어, 특정 장소를 방문했는지 안 했는지를 기록하고 싶을 때 맵을 사용할 수 있습니다.
package main
import "fmt"
func main() {
// 방문한 장소를 기록하기 위한 맵 (키: 장소 이름, 값: bool 타입)
visited := make(map[string]bool)
visited["서울"] = true // "서울"을 방문했다고 표시
visited["부산"] = true // "부산"을 방문했다고 표시
// "광주"를 방문했는지 확인
if visited["광주"] {
fmt.Println("광주에 방문한 적이 있습니다.")
} else {
// visited["광주"]는 false를 반환 (맵에 없는 키이므로 bool 타입의 제로 값)
fmt.Println("광주에는 아직 방문하지 않았습니다.")
}
}
이 코드에서 visited["광주"]는 맵에 "광주"라는 키가 없기 때문에 bool 타입의 제로 값인 false를 반환합니다.
이를 통해 "광주"를 아직 방문하지 않았다는 것을 알 수 있죠.
맵 안에 또 다른 맵? 중첩된 맵 활용하기!
Go 언어는 맵 안에 또 다른 맵을 넣는, 즉 '중첩된 맵(Nested Map)'도 지원합니다.
이를 통해 더 복잡하고 구조적인 데이터를 표현할 수 있습니다.
예를 들어, 학생별로 과목 점수를 저장하는 경우를 생각해볼 수 있습니다.
package main
import "fmt"
func main() {
// 학생 이름을 키로, [과목 이름을 키로, 점수를 값으로 갖는 맵]을 값으로 갖는 중첩 맵
nestedMap := make(map[string]map[string]int)
// 앨리스 학생의 수학, 과학 점수 추가
nestedMap["앨리스"] = map[string]int{"수학": 90, "과학": 85}
// 밥 학생의 국어 점수 추가
nestedMap["밥"] = make(map[string]int) // 내부 맵도 초기화해야 합니다!
nestedMap["밥"]["국어"] = 80
fmt.Println(nestedMap)
fmt.Println("앨리스의 수학 점수:", nestedMap["앨리스"]["수학"])
}
중첩된 맵을 사용할 때는, 안쪽 맵에 값을 할당하기 전에 반드시 안쪽 맵도 초기화를 해주어야 런타임 패닉을 피할 수 있다는 점을 꼭 기억해주세요!
마무리하며: Go 언어 맵, 데이터 관리의 든든한 지원군!
지금까지 Go 언어의 맵(map) 타입에 대해 자세히 알아봤습니다.
맵은 키-값 쌍을 효율적으로 저장하고 관리하는 데 아주 강력하고 유연한 도구입니다.
맵을 선언하고 초기화하는 방법부터, 요소를 추가, 조회, 삭제하고 반복하는 방법, 그리고 집합처럼 활용하거나 중첩해서 사용하는 방법까지 익혔으니, 이제 여러분은 Go 언어로 더욱 풍부하고 효율적인 데이터 관리를 할 수 있게 되었습니다!
앞으로 Go 프로그래밍 여정에서 맵을 자유자재로 활용하며 멋진 프로그램을 만들어나가시길 바랍니다!
'Go' 카테고리의 다른 글
| Go 언어의 청소부, 가비지 컬렉터 파헤치기: 메모리 관리, 이젠 맡겨주세요! (0) | 2025.06.03 |
|---|---|
| Go 언어에 클래스가 없다고? 걱정 마세요! 구조체와 인터페이스로 다 됩니다! (0) | 2025.06.03 |
| Go 언어 클로저 완전 정복: 변수를 '기억'하는 똑똑한 함수 만들기! (0) | 2025.06.03 |
| Go 언어에서 배열에 특정 값이 있는지 확인하는 꿀팁! (feat. 슬라이스 활용법) (1) | 2025.06.03 |
| Go 언어, 상속 대신 '조합'으로 더 유연하게! 구조체 상속 완벽 이해하기 (2) | 2025.06.03 |