Menu
Coddy logo textTech

임베딩 vs 집약

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

여러분은 임베딩(embedding)이 필드와 메서드를 외부 구조체로 승격시킨다는 것을 배웠습니다. 하지만 Go는 다른 구조체를 이름이 있는 필드로 포함하는 집합(aggregation) 방식도 지원합니다. 각 방식을 언제 사용해야 하는지 이해하는 것이 좋은 설계의 핵심입니다.

임베딩을 사용하면 필드 이름을 생략할 수 있으며, 내부 타입의 멤버들에 직접 접근할 수 있게 됩니다:

type Writer struct{}
func (w Writer) Write() string { return "writing" }

type Document struct {
    Writer  // 임베딩 - 필드 이름 없음
}

d := Document{}
d.Write()  // 직접 접근

애그리게이션(aggregation)을 사용하면 필드에 명시적인 이름을 부여하게 되며, 내부 타입에 접근하려면 해당 이름이 필요합니다:

type Document struct {
    writer Writer  // 애그리게이션 - 명명된 필드
}

d := Document{writer: Writer{}}
d.writer.Write()  // 필드 이름을 사용해야 함

핵심적인 차이점은 표현하고자 하는 관계입니다. 임베딩(Embedding)은 외부 타입이 내부 타입의 정체성을 갖게 되는 "is-a" 관계를 나타냅니다. 집합(Aggregation)은 외부 타입이 단순히 내부 타입을 포함하는 "has-a" 관계를 나타냅니다.

외부 타입이 내부 타입에 의해 구현된 인터페이스를 만족하도록 하거나, 직접적인 메서드 접근을 통해 API를 더 깔끔하게 만들고 싶을 때 임베딩(embedding)을 사용하세요. 내부 타입이 노출되어서는 안 되는 구현 세부 사항이거나, 내부 타입에 접근하는 방식을 명시적으로 제어하고 싶을 때는 어그리게이션(aggregation)을 사용하세요.

challenge icon

챌린지

쉬움

임베딩(embedding)과 집합(aggregation)을 언제 사용해야 하는지 보여주는 보고서 생성 시스템을 구축해 보겠습니다. 일부 구성 요소는 직접 액세스할 수 있어야 하고(임베딩), 다른 구성 요소는 내부 구현 세부 사항으로 유지되어야 하는(집합) 시스템을 만들 것입니다.

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

  • formatter.go: Style 필드(string)와 Format(text string) string 메서드를 가진 Formatter 구조체를 생성합니다. 이 메서드는 [Style] text [/Style]와 같이 텍스트를 스타일로 감싸서 반환해야 합니다. 이것은 직접 노출되지 않아야 하는 내부 헬퍼가 될 것입니다.
  • report.go: 보고서 생성을 다르게 처리하는 두 개의 구조체를 생성합니다:
    • AuthorDate 필드를 가진 Metadata 구조체 — 이것은 직접 액세스할 수 있어야 하는 공개 정보를 나타냅니다.
    • Title 필드를 가진 Report 구조체 — 이 구조체는 Metadata임베드(작성자와 날짜에 직접 액세스하기 위해)하지만, Formatter집합(formatter라는 이름의 필드를 가진 내부 구현 세부 사항으로)을 사용합니다.
    내부 포매터를 사용하여 제목의 형식을 지정하고 메타데이터를 포함하는 Generate() string 메서드를 Report에 추가합니다.
  • main.go: 입력에서 보고서 세부 정보를 읽고, 임베드된 메타데이터와 집합된 포매터를 모두 가진 Report를 생성합니다. 보고서에서 작성자에게 직접 액세스하고(임베딩을 통해), 형식이 지정된 보고서를 생성(내부적으로 숨겨진 포매터를 사용)함으로써 차이점을 보여줍니다.

다음 입력이 제공됩니다:

  • 1행: 보고서 제목
  • 2행: 작성자 이름
  • 3행: 날짜
  • 4행: 포매터 스타일

Format 메서드는 다음을 반환해야 합니다:

[Style] text [/Style]

Generate 메서드는 다음을 반환해야 합니다:

[formatted title]
Author: [Author], Date: [Date]

메인 파일에서 다음 세 줄을 출력합니다:

  1. Report에서 직접 액세스한 작성자 (임베딩이 필드를 승격시킴을 보여줌)
  2. 임베드된 타입 이름을 통해 액세스한 날짜 (report.Metadata.Date)
  3. Generate() 호출 결과

예를 들어, Quarterly Results, Jane Doe, 2024-01-15, BOLD가 주어지면 출력은 다음과 같아야 합니다:

Jane Doe
2024-01-15
[BOLD] Quarterly Results [/BOLD]
Author: Jane Doe, Date: 2024-01-15

임베딩을 통해 AuthorDate는 Report에서 직접 액세스할 수 있는 반면, 포매터는 숨겨져 있는 방식에 주목하세요. 포매터는 이름이 있는 필드로 집합되어 있기 때문에 report.Format()을 직접 호출할 수 없습니다. 이것은 실제 사례에서 "is-a" 관계와 "has-a" 관계의 차이를 보여줍니다.

치트 시트

Go는 한 구조체를 다른 구조체 내에 포함하는 두 가지 방법을 지원합니다: 임베딩(embedding)애그리게이션(aggregation).

임베딩(Embedding)은 필드 이름을 생략하여, 내부 타입의 필드와 메서드를 외부 구조체로 승격(promote)시킵니다:

type Writer struct{}
func (w Writer) Write() string { return "writing" }

type Document struct {
    Writer  // embedding - no field name
}

d := Document{}
d.Write()  // direct access to embedded method

애그리게이션(Aggregation)은 명시적인 필드 이름을 사용하며, 내부 타입에 접근하기 위해 해당 이름이 필요합니다:

type Document struct {
    writer Writer  // aggregation - named field
}

d := Document{writer: Writer{}}
d.writer.Write()  // must use field name

각각을 사용하는 경우:

  • 임베딩(Embedding): "is-a" 관계를 표현합니다. 외부 타입이 내부 타입의 인터페이스를 충족해야 하거나, 직접적인 메서드 접근이 API를 개선할 때 사용합니다.
  • 애그리게이션(Aggregation): "has-a" 관계를 표현합니다. 내부 타입이 노출되어서는 안 되는 구현 세부 사항이거나, 접근에 대한 명시적인 제어를 원할 때 사용합니다.

임베딩을 사용하더라도 필요한 경우 임베딩된 타입 이름을 통해 필드에 접근할 수 있습니다:

type Metadata struct {
    Author string
}

type Report struct {
    Metadata  // embedded
}

r := Report{Metadata: Metadata{Author: "Jane"}}
r.Author              // direct access via promotion
r.Metadata.Author     // explicit access via type name

직접 해보기

package main

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

func main() {
	scanner := bufio.NewScanner(os.Stdin)
	
	scanner.Scan()
	title := scanner.Text()
	
	scanner.Scan()
	author := scanner.Text()
	
	scanner.Scan()
	date := scanner.Text()
	
	scanner.Scan()
	style := scanner.Text()
	
	// TODO: 작성자와 날짜를 포함하는 Metadata 구조체 생성
	
	// TODO: 주어진 스타일을 사용하는 Formatter 생성
	
	// TODO: Metadata를 임베드하고 Formatter를 집합(aggregate)하는 Report 생성
	
	// TODO: Report에서 직접 접근한 작성자 출력 (임베딩은 필드를 승격시킴)
	
	// TODO: 임베드된 타입 이름을 통해 접근한 날짜 출력 (report.Metadata.Date)
	
	// TODO: Generate() 호출 결과 출력
	
	// 사용하지 않는 변수 경고 억제 (변수를 사용할 때 이 줄들을 제거하세요)
	_ = title
	_ = author
	_ = date
	_ = style
}
quiz icon실력 점검

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

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