Go에서 embedding(구조체 내 삽입)은 명시적 상속 없이 하나의 구조체를 다른 구조체에 삽입하여 구조체의 동작을 "상속하는" 메커니즘입니다. 삽입된 구조체는 새 구조체의 필드(익명의 필드)가 됩니다.
이때 모든 메서드가 삽입된 구조체의 메서드가 되어 마치 "상속된" 것처럼 동작합니다.
예제:
package main import "fmt" type Animal struct { Name string } func (a Animal) Speak() { fmt.Println("내 이름은", a.Name) } type Dog struct { Animal // embedding, 익명 필드 Breed string } func main() { d := Dog{ Animal: Animal{Name: "Rex"}, Breed: "Shepherd", } d.Speak() // 상속된 메서드! fmt.Println(d.Name) // 필드에 직접 접근 가능 }
embedding과 구조체를 이름 있는 필드로 일반적으로 포함시키는 것의 주요 차이점:
"삽입된 구조체와 외부 구조체가 동일한 이름의 필드를 가지고 있다면 어떤 필드가 사용되며 어떻게 접근합니까?"
많은 사람들은 "상위" 필드만 사용된다고 생각하지만, 실제로는 다릅니다:
예제:
type Base struct { Name string } type Child struct { Base // embedding Name string // 일치하는 필드 } c := Child{Base: Base{Name: "Base"}, Name: "Child"} fmt.Println(c.Name) // Child fmt.Println(c.Base.Name) // Base
사례
프로젝트에서 표준 로깅 구조체를 삽입한 후 자식 타입에 필드
Logger를 정의했습니다. 그 결과,d.Logger.Info()호출이 예상대로 작동하지 않았습니다. 왜냐하면 삽입된 필드에 대한 접근이 동일한 이름 사이에서 "사라졌기" 때문입니다.
사례
개발자가 자식 구조체에서 메서드를 재정의하려고 했지만 메서드는 "override"되지 않고 단지 새로운 메서드가 생성되며 부모 메서드는 여전히
Child.Base.Method()로 접근이 가능했습니다. 이로 인해 일부 코드가 "오래된" 메서드 버전을 사용하여 다른 로직을 기대했습니다.
사례
중첩 구조체를 가진 JSON 직렬화에서 예기치 않은 일이 발생했습니다. "삽입된" 구조체의 필드가 루트 객체에 나타났습니다. embedding이 marshaling에 미치는 영향을 모르는 바람에 구조체가 잘못 직렬화되어 API의 하위 호환성이 깨졌습니다.