업무에 사용하는 Go 언어 8 – make

Go 언어에서 중요한 내장 함수 중 make에 대해 다루도록 하겠습니다.
make 함수는 slice, map, channel 같이 Go 언어의 참조 타입 중 컬렉션 타입을 초기화할 때 사용합니다.
메모리에 변수의 구조를 알려주는 행위라고 이해하시면 되겠습니다.

업무에 사용하는 Go 언어 썸네일
업무에 사용하는 Go 언어 썸네일

약간의 여담

nil 상태에 대해서 저번 글에 아주 상세히 다루어보았다.
“리터럴이 딱히 없는데 어떡하지? 아무 값으로 일단 초기화 해야하나?”라는 의문을 가진 분들이 있을 것이다. 그런 분들을 위해 Go 언어에서 지원하는 함수가 make 함수라고 생각하면 좋을 것 같다.

make 함수의 정의

Go 언어의 make 내장 함수는 기본적으로 지원하는 함수입니다.
slice, map, channel의 초기화를 위해 사용합니다.
우리가 이미 알아봤을 때 이 세 가지 타입은 참조형 타입이기 때문에 nil 상태로는 사용 자체가 불가능하다는 것을 알 수 있었습니다.
그렇기에 리터럴로 초기화를 하여 선언하면 사용할 수 있었는데요, 리터럴 대신 make 함수를 통해 초기화 후 사용할 수 있겠습니다.

이전 내용 복습

slice, map, channel 타입은 값 자체를 가지는 것이 아니라 참조를 하는 타입이므로,
초기화하지 않으면 nil 상태로 존재하기 때문에 사용할 수 없다.

make 함수 기본 문법과 예시

make 함수의 기본 문법은 다음과 같습니다.

Go
make(type, length, capacity)
type생성하고자 하는 타입
length(option) 슬라이스의 초기 길이
혹은
(option) 채널의 버퍼 크기
capacity(option) 슬라이스의 초기 용량
-> 미지정 시 length와 동일

기본 문법에 따라 다음과 같이 slice, map, channel을 생성할 수 있습니다.

Go
s := make([]int, 5, 10) // 길이가 5, 용량이 10인 int 슬라이스 생성
m := make(map[string]int) // 빈 맵 생성
c := make(chan int, 3) // 버퍼 크기가 3인 int 채널 생성

make 함수가 필요한 이유

Go 언어에서 make 함수는 단순하게 초기화만 하는 것은 아닙니다.
초기화를 수행하는 동시에 slice, map, channel을 사용하기 위해 내부적인 데이터를 구성합니다.
내부적인 데이터에는 각 타입에 따라 메모리 주소, 길이, 용량, 버퍼와 같은 정보가 포함됩니다.
make 함수의 이런 원리 덕분에 참조 타입을 사용할 수 있게 됩니다.

실제 업무에서 어떻게 사용되는가

필자의 경우 참조 타입의 어떤 변수를 초기화 할 때 리터럴이 확실히 정해져있는 경우 굳이 make 함수를 사용하지는 않는다.
그러나 경험 상 데이터를 따져보면 상수가 아닌 이상 리터럴이 확실히 정해져있는 경우는 별로 없는 것 같다.
make 함수를 사용하여 초기화를 하게 되면 사용할 준비만 확실하게 해두는 것이기 때문에 코드의 유연성이 확보될 수 있다.
개인적으로 생각했을 때 가독성도 make 함수가 더 좋다고 생각한다.

make 사용 예시

slice, map, channel에 대해 make 함수를 간단하게 사용해보도록 하겠습니다.
channel에 대해서는 아직 다루지 않은 내용입니다. 초기화하는 방법만 확인해보고 넘어가셔도 좋습니다.

slice

Go
package main

import "fmt"

func main() {
	s := make([]int, 3, 5)
	fmt.Println(len(s)) // 3
	fmt.Println(cap(s)) // 5
	fmt.Println(s)      // [0 0 0]
}

make 함수로 slice를 생성하게 되면 요소의 값은 타입의 기본 값으로 초기화됩니다.
여러 타입으로 변경하면서 꼭 확인해보도록 합시다.

map

Go
package main

import "fmt"

func main() {
	m := make(map[string]int)
	m["article_number"] = 8
	fmt.Println(m)
}

make 함수로 map을 생성하면 초기화된 빈 맵이 생성되며 키-값에 대한 데이터를 저장하고 사용할 수 있습니다.

channel

Go
package main

import "fmt"

func main() {
	c := make(chan int, 2)
	c <- 1
	c <- 2
	fmt.Println(<-c)
	fmt.Println(<-c)

}

버퍼 크기가 2인 channel을 생성하여 값을 전송하고 수신하는 코드 예시입니다.

make 함수 사용 시 주의 사항

앞서 make 함수는 참조 타입의 변수에 대하여 길이, 용량, 버퍼와 같이 크기를 결정한다고 설명했습니다.
이는 프로그램(혹은 애플리케이션)의 성능에 직결되는 부분이기 때문에 꼭 적절히 설정하는 것이 좋습니다.

다음 글에서 알아볼 내용

Go 언어 함수