Go에서 메서드는 값 수신자와 타입에 대한 포인터 수신자로 선언할 수 있습니다. 이 특성은 데이터 변경을 누가 담당할지를 명확히 하기 위해 언어의 초기 버전부터 유지되고 있습니다. 전형적인 문제는 값 의미(복사, 변경하지 않음)와 포인터 의미(데이터 공유 및 수정 가능성) 간의 간격입니다.
문제 — 값 수신자로 메서드를 선언하고 기대하는 효과를 얻지 못하거나 포인터 변수에서 값 메서드를 호출할 때 쉽게 실수할 수 있습니다.
해결책 — 다음 규칙을 따르세요:
코드 예제:
type Counter struct { Value int } func (c Counter) IncCopy() { c.Value++ } // 값 수신자 func (c *Counter) IncPointer() { c.Value++ } // 포인터 수신자 c := Counter{} c.IncCopy() // Value는 0으로 남습니다 c.IncPointer() // Value는 1로 변경됩니다
주요 특징:
포인터에서 값 수신자 메서드를 호출할 수 있고, 값에서 포인터 메서드를 호출할 수 있나요?
Go는 "내부적으로" 포인터를 자동으로 역참조하거나 주소를 가져오므로, 유형이 호환되는 경우 호출이 허용됩니다. 그러나 항상 그런 것은 아닙니다 — 인터페이스와 관련해서는 예측할 수 없게 작동할 수 있습니다.
var c Counter (&c).IncCopy() // 포인터를 통해 값을 메서드 호출 가능 c.IncPointer() // 포인터 메서드를 호출할 수 있으며, Go는 자동으로 주소를 가져갑니다
구조체가 포인터 메서드만 구현하고, 값을 인터페이스에 전달하면 어떻게 되나요?
그런 객체는 포인터 메서드를 요구 할 경우 인터페이스를 구현하지 않으며, 따라서 panic이나 컴파일 오류가 발생할 수 있습니다.
type D interface { IncPointer() } func f(d D) {} c := Counter{} f(c) // 오류! Counter는 값으로 인터페이스를 구현하지 않음 f(&c) // 올바름
포인터 수신자 메서드를 호출할 때 포인터의 복사본이 전달되면 구조체가 변경되나요?
예, 포인터를 복사하더라도 동일한 객체를 참조하기 때문에 결과는 동일하게 됩니다.
c := Counter{} p := &c p2 := p p2.IncPointer() // Value가 증가합니다
엔지니어가 값 수신자 메서드 "Update"가 있는 구조체를 구현합니다. 인터페이스를 통해 구조체가 전달되지만 변경 사항이 "사라집니다" — 복사본으로 작업하고 있기 때문입니다.
장점:
단점:
팀 내 명시적 협의: 상태를 변경하는 모든 메서드는 포인터 수신자만 사용하고, 인터페이스는 오직 포인터로만 구현되며, 값은 "확장"과 유틸리티를 위해 사용됩니다.
장점:
단점: