🔥 값 또는 포인터 리시버 선택하기
Go 언어에서 메서드의 리시버로 값(value) 또는 포인터(pointer)를 사용하는 데에는 두 가지 이유가 있습니다.
첫 번째 이유는 메서드가 리시버가 가리키는 값을 수정할 수 있도록 하기 위함입니다. 다음 예제 코드를 살펴보겠습니다.
func (v *Vertex) Scale(f float64) { // 포인터 리시버를 사용하여 Vertex 구조체의 필드 값을 수정합니다. v.X = v.X * f v.Y = v.Y * f }
go
Scale
메서드는 포인터 리시버를 사용하여 Vertex
구조체의 X
와 Y
필드 값을 수정합니다. 만약 값 리시버를 사용한다면, 메서드 내에서 수정한 값은 메서드 호출이 끝난 후에 사라지게 됩니다.
두 번째 이유는 메서드 호출 시마다 값을 복사하는 것을 피하기 위함입니다. 예를 들어, 리시버가 큰 구조체인 경우에는 값 복사로 인한 오버헤드를 줄일 수 있습니다.
func (v *Vertex) Abs() float64 { // 값을 수정할 필요는 없지만, 포인터 리시버를 사용하여 값 복사를 피합니다. return math.Sqrt(v.X*v.X + v.Y*v.Y) }
go
Abs
메서드는 리시버의 값을 수정할 필요가 없지만, 포인터 리시버를 사용하여 값 복사를 피하고 있습니다.
일반적으로 한 타입의 모든 메서드는 값 또는 포인터 리시버 중 하나만 사용하는 것이 좋습니다. 값과 포인터 리시버를 혼용하는 것은 혼란을 야기할 수 있기 때문입니다.
다음은 위 예제 코드의 전체 내용입니다.
package main import ( "fmt" "math" ) type Vertex struct { X, Y float64 } func (v *Vertex) Scale(f float64) { // 포인터 리시버를 사용하여 Vertex 구조체의 필드 값을 수정합니다. v.X = v.X * f v.Y = v.Y * f } func (v *Vertex) Abs() float64 { // 값을 수정할 필요는 없지만, 포인터 리시버를 사용하여 값 복사를 피합니다. return math.Sqrt(v.X*v.X + v.Y*v.Y) } func main() { v := &Vertex{3, 4} fmt.Printf("Before scaling: %+v, Abs: %v\n", v, v.Abs()) // 크기 조정 전 Vertex 값과 Abs 결과를 출력합니다. v.Scale(5) // Scale 메서드를 호출하여 Vertex 값을 5배로 크기 조정합니다. fmt.Printf("After scaling: %+v, Abs: %v\n", v, v.Abs()) // 크기 조정 후 Vertex 값과 Abs 결과를 출력합니다. }
go
main
함수에서는 Vertex
구조체의 포인터를 생성하고, 크기 조정 전후의 값과 Abs
메서드의 결과를 출력합니다. Scale
메서드를 호출하여 Vertex
의 X
와 Y
값을 5배로 크기 조정한 후, 다시 한 번 값과 Abs
결과를 출력하여 변경 사항을 확인할 수 있습니다.
이 예제를 통해 값 또는 포인터 리시버를 선택하는 기준과 그 차이점을 이해할 수 있습니다. 메서드가 리시버의 값을 수정해야 하는 경우나 큰 구조체의 값 복사를 피하고자 할 때는 포인터 리시버를 사용하는 것이 효과적입니다.