Go에서 make와 new 중에서 선택하기
Daniel Hayes
Full-Stack Engineer · Leapcell

Go 언어는 메모리를 할당하는 데 일반적으로 사용되는 두 가지 방법, make
와 new
를 제공합니다. 둘 다 메모리 할당에 사용되지만, 그 역할과 사용 시나리오는 상당히 다릅니다. 이 둘의 차이점을 이해하는 것은 효율적이고 유지 관리 가능한 Go 코드를 작성하는 데 매우 중요합니다. 이 글에서는 make
와 new
의 차이점, 적합한 시나리오를 철저히 분석하고 몇 가지 사용 팁을 제공합니다.
make
와 new
의 기본 차이점
new: 포인터 타입의 제로 값 생성
new
는 Go에서 메모리 할당에 사용되는 키워드입니다. 이 함수의 역할은 타입에 대한 메모리 블록을 할당하고 해당 메모리에 대한 포인터를 반환하는 것입니다. new
에 의해 할당된 메모리는 해당 타입의 제로 값으로 초기화됩니다.
예제: new
사용
package main import "fmt" func main() { var p *int = new(int) fmt.Println(*p) // 출력: 0, `new`에 의해 int에 할당된 메모리는 제로 값으로 초기화됨 }
- 반환 타입:
new
는 해당 타입에 대한 포인터를 반환합니다. - 제로 값 초기화:
new
는 할당된 메모리를 해당 타입의 제로 값으로 초기화합니다. 예를 들어,int
타입의 경우 제로 값은0
이고,string
타입의 경우 빈 문자열""
입니다.
make: 슬라이스, 맵, 채널 초기화
make
는 Go의 특수한 내장 함수로, 슬라이스, 맵, 채널의 세 가지 내장 데이터 타입을 초기화하는 데 특별히 사용됩니다. new
와 달리 make
는 할당된 메모리에 대한 포인터를 반환하지 않고, 초기화된 객체 자체를 반환합니다.
예제: make
사용
package main import "fmt" func main() { // 슬라이스 초기화 s := make([]int, 5) fmt.Println(s) // 출력: [0 0 0 0 0] // 맵 초기화 m := make(map[string]int) m["age"] = 30 fmt.Println(m) // 출력: map[age:30] // 채널 초기화 ch := make(chan int, 2) ch <- 1 fmt.Println(<-ch) // 출력: 1 }
- 반환 타입:
make
는 포인터가 아닌 객체 자체(슬라이스, 맵 또는 채널)를 반환합니다. - 메모리 할당 및 초기화:
make
는 메모리를 할당할 뿐만 아니라 데이터 구조 자체도 초기화합니다. 예를 들어, 슬라이스를 초기화할 때make
는 기본 배열을 할당하고 해당 길이와 용량을 설정합니다.
주요 차이점 요약
-
목적:
new
: 메모리를 할당하고 해당 타입에 대한 포인터를 반환합니다.make
: 슬라이스, 맵 또는 채널 객체를 초기화하고 반환합니다.
-
반환 값:
new
: 해당 타입에 대한 포인터를 반환합니다.make
: 초기화된 객체 자체를 반환합니다.
-
적용 가능한 타입:
new
: 모든 타입.make
: 슬라이스, 맵, 채널.
-
초기화:
new
: 제로 값을 반환합니다.make
: 데이터 구조의 타입에 따라 초기화합니다.
make
와 new
사용 팁
new
사용 팁
구조체 타입에 적합:
new
는 종종 구조체에 대한 메모리를 할당하고 해당 포인터를 반환하는 데 사용됩니다. 구조체 포인터의 초기 값은 구조체의 제로 값이라는 점에 유의해야 합니다.
예제: new
를 사용하여 구조체에 대한 메모리 할당
type Person struct { Name string Age int } func main() { p := new(Person) fmt.Println(p) // 출력: &{ 0} fmt.Println(p.Name) // 출력: 빈 문자열 fmt.Println(p.Age) // 출력: 0 }
new
로 생성된 포인터:new
는 구조체에 대한 포인터를 반환하므로p.Name
또는p.Age
로 해당 필드를 직접 수정할 수 있습니다.
make
사용 팁
지정된 용량으로 슬라이스 초기화:
make
를 사용하여 지정된 길이와 용량으로 슬라이스를 초기화할 수 있습니다. make
를 사용하면 기본 배열을 효율적으로 할당하고 슬라이스를 초기화할 수 있습니다.
예제: make
를 사용하여 용량으로 슬라이스 초기화
// 길이 5, 용량 10으로 슬라이스 초기화 s := make([]int, 5, 10) fmt.Println(len(s), cap(s)) // 출력: 5 10
- 초기화 시 맵 용량 지정:
make
로 맵을 생성할 때 초기 용량을 지정할 수 있으며, 이는 요소가 삽입될 때 여러 번의 메모리 확장을 방지하여 성능을 최적화하는 데 도움이 됩니다.
예제: make
를 사용하여 맵 초기화
m := make(map[string]int, 10) // 초기 용량을 10으로 설정 m["age"] = 30 m["height"] = 175 fmt.Println(m) // 출력: map[age:30 height:175]
- 버퍼링된 채널 초기화:
make
를 사용하여 버퍼링된 채널을 생성하고 채널의 버퍼 크기를 지정합니다. 이는 동시성 프로그래밍에서 매우 유용합니다.
예제: make
를 사용하여 버퍼링된 채널 생성
ch := make(chan int, 2) ch <- 1 ch <- 2 fmt.Println(<-ch) // 출력: 1
적절한 메모리 할당 방법 선택
- 구조체 사용 시나리오: 구조체에 대한 포인터만 필요하고 초기화 중에 특별한 요구 사항이 없는 경우
new
를 사용하는 것이 간단하고 일반적인 접근 방식입니다. - 슬라이스, 맵, 채널 사용 시나리오: 슬라이스, 맵 또는 채널을 초기화해야 하고 해당 내용을 수정해야 하는 경우
make
가 더 적절한 선택입니다. 특히 용량을 미리 지정해야 하는 경우에 그렇습니다.
make
와 new
의 성능 고려 사항
- 메모리 할당 오버헤드: 슬라이스, 맵 및 채널을 초기화할 때
make
는 메모리를 할당할 뿐만 아니라 타입 초기화도 수행하므로 추가 오버헤드가 발생할 수 있습니다. 반면,new
는 메모리만 할당하고 제로 값으로 초기화하므로 오버헤드가 비교적 적습니다. - 불필요한 메모리 할당 방지: 슬라이스, 맵 또는 채널과 같은 타입의 경우
make
를 사용할 때 적절한 용량을 지정하여 메모리 재할당 횟수를 줄이는 것이 좋습니다.
일반적인 오용
- 슬라이스 또는 맵을 생성하는 데
new
를 잘못 사용하는 경우:new
가 슬라이스, 맵 또는 채널과 함께 사용되면 해당 타입의 제로 값만 반환하고 초기화를 수행하지 않습니다. 따라서new
를 사용하여 슬라이스, 맵 또는 채널을 생성하고 해당 내용을 직접 액세스하려고 하면 런타임 오류가 발생합니다.
잘못된 예: 맵을 생성하는 데 new
를 잘못 사용하는 경우
m := new(map[string]int) // 잘못됨: 초기화된 맵이 아닌 포인터를 반환합니다. m["age"] = 30 // 런타임 오류: m은 nil입니다.
올바른 예: 맵을 초기화하려면 make
를 사용해야 합니다.
m := make(map[string]int) m["age"] = 30
요약
Go에서 make
와 new
는 모두 메모리 할당을 위한 키워드이며, 그 기능은 유사하지만 명확한 차이점이 있습니다.
new
는 타입에 대한 메모리를 할당하고 포인터를 반환하는 데 사용되며, 대부분의 타입에 적합합니다.
반면, make
는 주로 슬라이스, 맵 및 채널을 초기화하는 데 사용되며, 더 강력한 초기화 기능을 제공합니다.
new
: 구조체 타입 및 기타 기본 타입에 대한 포인터를 생성하는 데 적합하며, 메모리를 제로 값으로 초기화합니다.make
: 슬라이스, 맵 및 채널을 초기화하는 데 사용되며, 용량 지정을 지원하고 내부 초기화를 완료합니다.
이 두 가지의 다양한 사용 시나리오와 성능 영향을 이해하면 더 효율적이고 유지 관리 가능한 Go 코드를 작성하는 데 도움이 됩니다.
Go 프로젝트 호스팅을 위한 최고의 선택, Leapcell입니다.
Leapcell은 웹 호스팅, 비동기 작업 및 Redis를 위한 차세대 서버리스 플랫폼입니다.
다국어 지원
- Node.js, Python, Go 또는 Rust로 개발하세요.
무제한 프로젝트를 무료로 배포
- 사용량에 대해서만 비용을 지불합니다. 요청도 없고, 요금도 없습니다.
타의 추종을 불허하는 비용 효율성
- 유휴 요금 없이 사용한 만큼 지불합니다.
- 예: 25달러로 평균 응답 시간 60ms에서 694만 건의 요청을 지원합니다.
간소화된 개발자 경험
- 간편한 설정을 위한 직관적인 UI.
- 완전 자동화된 CI/CD 파이프라인 및 GitOps 통합.
- 실행 가능한 통찰력을 위한 실시간 메트릭 및 로깅.
손쉬운 확장성 및 고성능
- 높은 동시성을 쉽게 처리할 수 있도록 자동 확장됩니다.
- 운영 오버헤드가 없으므로 빌드에만 집중할 수 있습니다.
설명서에서 자세히 알아보세요!
X에서 팔로우하세요: @LeapcellHQ