Menu
Coddy logo textTech

임베딩된 메서드 섀도잉

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

때로는 외부 구조체가 임베디드된 타입이 이미 가지고 있는 메서드에 대해 자체적인 구현을 제공해야 할 때가 있습니다. 이것을 섀도잉(shadowing)이라고 하며, 이를 통해 승격된 메서드를 사용자 정의 동작으로 오버라이드할 수 있습니다.

임베디드 메서드와 동일한 이름의 메서드를 외부 구조체에 정의하면, 외부 메서드가 우선순위를 갖습니다:

type Animal struct {
    Name string
}

func (a Animal) Speak() string {
    return "Some sound"
}

type Dog struct {
    Animal
    Breed string
}

func (d Dog) Speak() string {
    return "Woof!"
}

이제 Dog에서 Speak()를 호출하면, 외부 메서드가 사용됩니다:

func main() {
    d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Labrador"}
    
    fmt.Println(d.Speak())        // Woof! - Dog의 메서드
    fmt.Println(d.Animal.Speak()) // Some sound - Animal의 메서드
}

임베디드된 메서드는 사라지지 않습니다. 임베디드된 타입 이름을 통해 명시적으로 접근할 수 있습니다. 이 패턴은 원래의 구현을 유지하면서 동작을 커스터마이징하고 싶을 때 유용하며, 아마도 오버라이딩하는 메서드 내부에서 이를 호출하기 위해 사용될 수 있습니다:

func (d Dog) Speak() string {
    return d.Animal.Speak() + " (but actually Woof!)"
}

섀도잉은 명시적 한정을 통해 원본에 대한 접근을 유지하면서 어떤 동작을 노출할지 제어할 수 있게 해줍니다.

challenge icon

챌린지

쉬움

다양한 알림 채널이 메시지를 형식화하고 전달하는 방식을 맞춤 설정할 수 있는 알림 시스템을 구축해 보겠습니다. 메서드 섀도잉(method shadowing)을 사용하여 특정 채널이 기본 동작을 재정의하도록 하면서도, 필요한 경우 원래의 구현에 여전히 접근할 수 있도록 할 것입니다.

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

  • base.go: TitlePriority 필드(둘 다 문자열)를 가진 BaseNotification 구조체를 생성합니다. 이 구조체에 표준 형식인 [Priority] Title을 반환하는 Format() string 메서드를 추가합니다.
  • channels.go: BaseNotification을 임베딩하고 Format 메서드를 각자의 구현으로 섀도잉하는 두 가지 알림 채널 타입을 생성합니다:
    • Recipient 필드를 가진 EmailNotification—이 타입의 Format()Email to [Recipient]: [Priority] Title을 반환해야 합니다.
    • Channel 필드를 가진 SlackNotification—이 타입의 Format()은 임베딩된 BaseNotification.Format() 메서드를 호출하고 그 결과 앞에 #[Channel]: 를 붙여야 합니다.
  • main.go: 입력으로부터 알림 세부 정보를 읽고, 두 타입의 알림을 모두 생성한 후 다음을 출력하여 섀도잉을 시연합니다:
    1. EmailNotification의 형식화된 출력 (섀도잉된 메서드 사용)
    2. EmailNotification의 기본 형식 (임베딩된 메서드를 명시적으로 호출)
    3. SlackNotification의 형식화된 출력 (내부적으로 기본 메서드를 사용)

다음 입력이 제공됩니다:

  • 1행: 알림 제목 (Notification title)
  • 2행: 우선순위 레벨 (Priority level)
  • 3행: 이메일 수신자 (Email recipient)
  • 4행: Slack 채널 이름 (Slack channel name)

예를 들어, Server Alert, HIGH, admin@company.com, ops-alerts가 주어지면 출력은 다음과 같아야 합니다:

Email to admin@company.com: HIGH Server Alert
[HIGH] Server Alert
#ops-alerts: [HIGH] Server Alert

EmailNotification이 기본 포맷팅을 완전히 대체하는 방식과, SlackNotification이 원래 메서드를 호출하고 채널 컨텍스트를 추가하여 이를 강화하는 방식의 차이를 확인해 보세요. 두 접근 방식 모두 메서드 섀도잉을 효과적으로 사용하는 서로 다른 방법을 보여줍니다.

치트 시트

외부 구조체에 임베디드 타입의 메서드와 동일한 이름의 메서드를 정의하면, 외부 메서드가 임베디드 메서드를 가립니다(재정의합니다):

type Animal struct {
    Name string
}

func (a Animal) Speak() string {
    return "Some sound"
}

type Dog struct {
    Animal
    Breed string
}

func (d Dog) Speak() string {
    return "Woof!"
}

외부 구조체에서 메서드를 호출할 때, 외부 메서드가 우선순위를 갖습니다:

d := Dog{Animal: Animal{Name: "Rex"}, Breed: "Labrador"}

fmt.Println(d.Speak())        // Woof! - Dog의 메서드
fmt.Println(d.Animal.Speak()) // Some sound - Animal의 메서드

임베디드 메서드는 임베디드 타입 이름을 사용한 명시적 한정을 통해 여전히 접근 가능합니다. 재정의하는 메서드 내에서 원래의 구현을 호출할 수 있습니다:

func (d Dog) Speak() string {
    return d.Animal.Speak() + " (but actually Woof!)"
}

직접 해보기

package main

import (
	"bufio"
	"fmt"
	"os"
)

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	// 알림 제목 읽기
	scanner.Scan()
	title := scanner.Text()
	
	// 우선순위 레벨 읽기
	scanner.Scan()
	priority := scanner.Text()
	
	// 이메일 수신자 읽기
	scanner.Scan()
	recipient := scanner.Text()
	
	// 슬랙 채널 이름 읽기
	scanner.Scan()
	channel := scanner.Text()
	
	// TODO: 기본 알림, 제목, 우선순위 및 수신자를 사용하여 EmailNotification 생성
	
	// TODO: 기본 알림, 제목, 우선순위 및 채널을 사용하여 SlackNotification 생성
	
	// TODO: EmailNotification의 형식화된 출력 인쇄 (섀도잉된 메서드 사용)
	
	// TODO: EmailNotification의 기본 형식 인쇄 (임베디드 메서드를 명시적으로 호출)
	
	// TODO: SlackNotification의 형식화된 출력 인쇄
	
	fmt.Println() // 자리 표시자 - 구현 시 제거
}
quiz icon실력 점검

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

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