🔥 메서드와 포인터 Indirection - 1
이전 두 프로그램을 비교해 보면, 포인터 인자를 가진 함수는 반드시 포인터를 받아야 한다는 것을 알 수 있습니다.
var v Vertex ScaleFunc(v, 5) // 컴파일 에러! ScaleFunc(&v, 5) // OK
go
반면에 포인터 리시버를 가진 메서드는 호출할 때 값이나 포인터 중 어느 것이든 리시버로 받을 수 있답니다.
var v Vertex v.Scale(5) // OK p := &v p.Scale(10) // OK
go
v.Scale(5)
문장에서, v
는 값이고 포인터가 아님에도 불구하고, 포인터 리시버를 가진 메서드가 자동으로 호출됩니다. 즉, Go는 편의상 v.Scale(5)
문장을 (&v).Scale(5)
로 해석하는데, 그 이유는 Scale
메서드가 포인터 리시버를 가지고 있기 때문이에요.
아래 예제 코드를 함께 살펴보면서 더 자세히 알아봅시다!
package main import "fmt" type Vertex struct { X, Y float64 } func (v *Vertex) Scale(f float64) { v.X = v.X * f // v의 X 값에 f를 곱함 v.Y = v.Y * f // v의 Y 값에 f를 곱함 } func ScaleFunc(v *Vertex, f float64) { v.X = v.X * f // v의 X 값에 f를 곱함 v.Y = v.Y * f // v의 Y 값에 f를 곱함 } func main() { v := Vertex{3, 4} // v는 Vertex 구조체 값 v.Scale(2) // v.Scale 메서드 호출. v의 X, Y가 2배로 커짐 ScaleFunc(&v, 10) // ScaleFunc 함수 호출. v의 X, Y가 10배로 커짐 p := &Vertex{4, 3} // p는 Vertex 구조체를 가리키는 포인터 p.Scale(3) // p.Scale 메서드 호출. p가 가리키는 Vertex의 X, Y가 3배로 커짐 ScaleFunc(p, 8) // ScaleFunc 함수 호출. p가 가리키는 Vertex의 X, Y가 8배로 커짐 fmt.Println(v, p) // 변경된 v와 p의 값 출력 }
go
주석을 보면 알 수 있듯이, Scale
메서드는 리시버 v
의 X
, Y
값에 인자 f
를 각각 곱해서 v
를 변경합니다. 마찬가지로 ScaleFunc
함수도 포인터 v
가 가리키는 Vertex
의 X
, Y
에 f
를 곱해서 그 Vertex
를 변경하지요.
main
함수를 보면, v
는 Vertex
값이고 p
는 Vertex
를 가리키는 포인터입니다. v.Scale(2)
를 호출하면 v
의 X
, Y
가 2배로 커지고, ScaleFunc(&v, 10)
을 호출하면 v
의 X
, Y
가 10배로 커집니다.
비슷하게 p.Scale(3)
을 호출하면 p
가 가리키는 Vertex
의 X
, Y
가 3배로 커지고, ScaleFunc(p, 8)
을 호출하면 p
가 가리키는 Vertex
의 X
, Y
가 8배로 커지게 됩니다.
마지막으로 fmt.Println(v, p)
으로 v
와 p
의 최종 값을 출력하면, 예상한 대로 X
, Y
값들이 모두 변경된 것을 확인할 수 있겠죠?
이렇게 Go 언어에서는 메서드가 값 리시버냐 포인터 리시버냐에 따라, 그리고 함수가 값 인자를 받느냐 포인터 인자를 받느냐에 따라 서로 다르게 동작합니다. 하지만 편의를 위해 어느 정도 자동 변환을 지원하기도 하니, 잘 이해하고 활용한다면 더욱 효율적이고 유연한 코드를 작성할 수 있을 거예요!