Menu
Coddy logo textTech

여러 구조체 임베딩하기

Coddy GO 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 107개 중 37번째.

Go에서는 단일 구조체 내에 여러 구조체를 임베드하여 다양한 소스의 동작을 결합할 수 있습니다. 이는 다른 언어의 다중 상속과 유사하지만, Go의 구성(composition) 방식을 따릅니다.

type Logger struct{}

func (l Logger) Log(msg string) string {
    return "LOG: " + msg
}

type Notifier struct{}

func (n Notifier) Notify(msg string) string {
    return "NOTIFY: " + msg
}

type Service struct {
    Name string
    Logger
    Notifier
}

Service 구조체는 이제 두 임베디드 타입의 메서드에 모두 접근할 수 있습니다:

func main() {
    s := Service{Name: "OrderService"}
    fmt.Println(s.Log("started"))      // LOG: started
    fmt.Println(s.Notify("new order")) // NOTIFY: new order
}

여러 구조체를 임베딩할 때, 두 개의 임베딩된 타입이 동일한 이름의 메서드를 가지고 있으면 이름 충돌이 발생할 수 있습니다. Go는 이러한 모호성을 자동으로 해결하지 않습니다:

type A struct{}
func (A) Greet() string { return "Hello from A" }

type B struct{}
func (B) Greet() string { return "Hello from B" }

type Combined struct {
    A
    B
}

func main() {
    c := Combined{}
    // c.Greet() - 컴파일 에러: 모호한 선택자(ambiguous selector)
    fmt.Println(c.A.Greet())  // Hello from A
    fmt.Println(c.B.Greet())  // Hello from B
}

충돌이 발생할 때, 타입 이름을 한정자로 사용하여 호출하려는 임베디드 타입의 메서드를 명시적으로 지정해야 합니다.

challenge icon

챌린지

쉬움

구조체 임베딩(struct embedding)을 통해 여러 기능을 결합한 스마트 홈 장치 시스템을 구축해 보겠습니다. 서로 다른 컨트롤러 타입을 임베딩하여 조명을 제어하고 음악을 재생할 수 있는 장치를 만들 것입니다.

코드는 다음 세 개의 파일로 구성됩니다:

  • controllers.go: 서로 다른 기능을 제공하는 두 개의 독립적인 컨트롤러 구조체를 생성합니다:
    • Brightness 필드(int)와 Light set to [level]%를 반환하는 SetLight(level int) string 메서드를 가진 LightController
    • Volume 필드(int)와 Volume set to [level]%를 반환하는 SetVolume(level int) string 메서드를 가진 AudioController
    두 컨트롤러 모두 Status() string 메서드를 가져야 합니다. 이로 인해 발생하는 이름 충돌을 처리해야 합니다. LightController.Status()Brightness: [Brightness]%를 반환해야 하고, AudioController.Status()Volume: [Volume]%를 반환해야 합니다.
  • device.go: Name 필드를 가지고 LightControllerAudioController를 모두 임베딩하는 SmartDevice 구조체를 생성합니다. 각 임베딩된 타입의 Status() 메서드를 명시적으로 호출하여 모호성을 해결하고, 장치 이름과 두 컨트롤러의 상태를 함께 반환하는 FullStatus() string 메서드를 추가합니다.
  • main.go: 입력으로부터 장치 설정을 읽고, SmartDevice를 생성한 뒤, 승격된(promoted) 메서드를 사용하여 조명과 볼륨 레벨을 설정하고 전체 상태를 표시합니다.

다음 입력이 제공됩니다:

  • 1행: 장치 이름
  • 2행: 초기 밝기 레벨 (정수)
  • 3행: 초기 볼륨 레벨 (정수)
  • 4행: 새로 설정할 밝기 레벨 (정수)
  • 5행: 새로 설정할 볼륨 레벨 (정수)

FullStatus() 메서드는 다음을 반환해야 합니다:

[Name] - [LightController.Status()], [AudioController.Status()]

임베딩된 타입에서 승격된 SetLightSetVolume 호출 결과를 출력한 다음, 전체 상태를 출력하세요.

예를 들어, Living Room Hub, 50, 30, 75, 60이 주어지면 출력은 다음과 같아야 합니다:

Light set to 75%
Volume set to 60%
Living Room Hub - Brightness: 75%, Volume: 60%

SetLightSetVolume은 메서드 승격을 통해 SmartDevice에서 직접 접근할 수 있지만, Status()는 두 임베딩된 타입 모두 해당 메서드를 가지고 있기 때문에 명시적인 지정이 필요하다는 점에 유의하세요.

치트 시트

Go는 단일 구조체 내에 여러 구조체를 임베딩하여 서로 다른 소스의 동작을 구성을 통해 결합할 수 있도록 합니다:

type Logger struct{}

func (l Logger) Log(msg string) string {
    return "LOG: " + msg
}

type Notifier struct{}

func (n Notifier) Notify(msg string) string {
    return "NOTIFY: " + msg
}

type Service struct {
    Name string
    Logger
    Notifier
}

Service 구조체는 임베딩된 두 타입의 메서드에 모두 접근할 수 있습니다:

s := Service{Name: "OrderService"}
fmt.Println(s.Log("started"))      // LOG: started
fmt.Println(s.Notify("new order")) // NOTIFY: new order

임베딩된 타입들이 동일한 이름의 메서드를 가지고 있을 때 이름 충돌(Naming conflicts)이 발생합니다. 이 경우 어떤 임베딩된 타입의 메서드를 호출할지 명시적으로 지정해야 합니다:

type A struct{}
func (A) Greet() string { return "Hello from A" }

type B struct{}
func (B) Greet() string { return "Hello from B" }

type Combined struct {
    A
    B
}

// c.Greet() - compile error: ambiguous selector
c := Combined{}
fmt.Println(c.A.Greet())  // Hello from A
fmt.Println(c.B.Greet())  // Hello from B

직접 해보기

package main

import (
	"fmt"
)

func main() {
	// 입력 읽기
	var name string
	fmt.Scanln(&name)

	var initialBrightness int
	fmt.Scanln(&initialBrightness)

	var initialVolume int
	fmt.Scanln(&initialVolume)

	var newBrightness int
	fmt.Scanln(&newBrightness)

	var newVolume int
	fmt.Scanln(&newVolume)

	// TODO: 주어진 이름과 초기값을 사용하여 SmartDevice 생성

	// TODO: 승계된 SetLight 메서드를 사용하고 결과 출력

	// TODO: 승계된 SetVolume 메서드를 사용하고 결과 출력

	// TODO: FullStatus() 메서드를 사용하여 전체 상태 출력
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨