Menu
Coddy logo textTech

리플렉션 기초

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

리플렉션(Reflection)은 프로그램이 런타임에 자신의 구조를 조사하고 조작할 수 있게 해줍니다. Go의 reflect 패키지는 이 기능을 제공하여, 타입을 검사하고 구조체 필드를 읽으며 메서드를 동적으로 호출할 수 있게 합니다.

리플렉션의 두 가지 핵심 타입은 reflect.Typereflect.Value입니다. reflect.TypeOf()reflect.ValueOf()를 사용하여 이들을 얻을 수 있습니다:

import "reflect"

type Person struct {
    Name string
    Age  int
}

p := Person{Name: "Alice", Age: 30}

t := reflect.TypeOf(p)
v := reflect.ValueOf(p)

fmt.Println(t.Name())       // Person
fmt.Println(t.Kind())       // struct
fmt.Println(v.NumField())   // 2

구조체 필드를 반복하여 이름, 타입 및 값을 검사할 수 있습니다:

for i := 0; i < t.NumField(); i++ {
    field := t.Field(i)
    value := v.Field(i)
    fmt.Printf("%s: %v\n", field.Name, value)
}
// 출력:
// Name: Alice
// Age: 30

리플렉션(Reflection)은 강력하지만 트레이드오프가 있습니다. 컴파일 타임 타입 체크를 우회하고, 직접 접근보다 실행 속도가 느리며, 코드를 이해하기 어렵게 만듭니다. JSON 마샬링, ORM 매핑 또는 컴파일 타임에 타입을 알 수 없는 제네릭 유틸리티를 구축하는 것과 같은 작업에만 제한적으로 사용하세요.

challenge icon

챌린지

쉬움

리플렉션(reflection)을 사용하여 어떤 구조체든 검사하고 그 구조를 보고하는 구조체 인스펙터(struct inspector)를 만들어 봅시다! 이는 컴파일 타임에 알 수 없는 타입을 다루는 유틸리티를 만드는 리플렉션의 실용적인 사용 사례입니다.

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

  • inspector.go: 리플렉션 기반의 검사 유틸리티를 작성합니다.

    전달된 모든 구조체를 검사하기 위해 reflect 패키지를 사용하는 InspectStruct(v any) string 함수를 만듭니다. 함수는 다음 내용을 포함하는 형식화된 보고서를 반환해야 합니다:

    • 구조체의 타입 이름 (type name)
    • 구조체의 종류 (kind, "struct"여야 함)
    • 필드 개수
    • 각 필드에 대해: 필드 이름, 타입, 그리고 현재 값

    출력 형식은 다음과 같아야 합니다:

    Type: [TypeName]
    Kind: struct
    Fields: [count]
    - [FieldName] ([FieldType]): [Value]
    - [FieldName] ([FieldType]): [Value]
    ...

    타입 정보를 얻으려면 reflect.TypeOf()를 사용하고, 필드 값에 접근하려면 reflect.ValueOf()를 사용하세요. 메타데이터를 위해 타입에서 NumField()Field(i)를 사용하고, 실제 값을 위해 값에서 Field(i)를 사용하여 필드를 순회합니다.

  • main.go: 샘플 구조체를 정의하고 인스펙터를 사용합니다.

    구조체 타입(product 또는 employee)과 해당 필드 값들을 읽은 다음, 적절한 구조체를 생성하고 검사합니다.

    product의 경우: Name (string), Price (float64), InStock (bool) 필드를 가진 Product 구조체를 생성합니다. 이름, 가격, 재고 상태를 읽습니다.

    employee의 경우: ID (int), Name (string), Department (string) 필드를 가진 Employee 구조체를 생성합니다. ID, 이름, 부서를 읽습니다.

    생성된 구조체를 InspectStruct에 전달하고 결과를 출력합니다.

다음과 같은 입력이 제공됩니다:

  • 1행: 구조체 타입 (product 또는 employee)
  • 다음 행들: 해당 구조체 타입의 필드 값들

예를 들어, 다음과 같이 입력되면:

product
Laptop
999.99
true

출력은 다음과 같아야 합니다:

Type: Product
Kind: struct
Fields: 3
- Name (string): Laptop
- Price (float64): 999.99
- InStock (bool): true

그리고 다음과 같이 입력되면:

employee
42
Alice Johnson
Engineering

출력은 다음과 같아야 합니다:

Type: Employee
Kind: struct
Fields: 3
- ID (int): 42
- Name (string): Alice Johnson
- Department (string): Engineering

그리고 다음과 같이 입력되면:

product
Headphones
79.50
false

출력은 다음과 같아야 합니다:

Type: Product
Kind: struct
Fields: 3
- Name (string): Headphones
- Price (float64): 79.5
- InStock (bool): false

인스펙터가 구조체의 구조를 미리 알지 못해도 서로 다른 구조체 타입에 대해 어떻게 작동하는지 확인해 보세요. 이것이 바로 리플렉션의 힘입니다! 동일한 InspectStruct 함수가 ProductEmployee 구조체를 모두 동적으로 처리합니다.

치트 시트

reflect 패키지는 프로그램이 런타임에 자신의 구조를 검사하고 조작할 수 있게 해줍니다.

두 가지 핵심 타입은 reflect.Typereflect.Value이며, 각각 reflect.TypeOf()reflect.ValueOf()를 사용하여 얻을 수 있습니다:

import "reflect"

type Person struct {
    Name string
    Age  int
}

p := Person{Name: "Alice", Age: 30}

t := reflect.TypeOf(p)
v := reflect.ValueOf(p)

fmt.Println(t.Name())       // Person
fmt.Println(t.Kind())       // struct
fmt.Println(v.NumField())   // 2

구조체 필드를 순회하며 이름, 타입, 값을 검사합니다:

for i := 0; i < t.NumField(); i++ {
    field := t.Field(i)
    value := v.Field(i)
    fmt.Printf("%s: %v\n", field.Name, value)
}
// 출력:
// Name: Alice
// Age: 30

리플렉션은 컴파일 타임 타입 체크를 우회하며, 직접 접근보다 실행 속도가 느리고 코드를 이해하기 어렵게 만듭니다. JSON 마샬링, ORM 매핑 또는 컴파일 타임에 타입을 알 수 없는 제네릭 유틸리티를 구축하는 것과 같은 작업에 사용하세요.

직접 해보기

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

// Name, Price, InStock 필드를 가진 Product 구조체
type Product struct {
	Name    string
	Price   float64
	InStock bool
}

// ID, Name, Department 필드를 가진 Employee 구조체
type Employee struct {
	ID         int
	Name       string
	Department string
}

func main() {
	reader := bufio.NewReader(os.Stdin)

	// 구조체 타입을 읽음
	structType, _ := reader.ReadString('\n')
	structType = strings.TrimSpace(structType)

	if structType == "product" {
		// product 필드들을 읽음
		name, _ := reader.ReadString('\n')
		name = strings.TrimSpace(name)

		priceStr, _ := reader.ReadString('\n')
		priceStr = strings.TrimSpace(priceStr)
		price, _ := strconv.ParseFloat(priceStr, 64)

		inStockStr, _ := reader.ReadString('\n')
		inStockStr = strings.TrimSpace(inStockStr)
		inStock, _ := strconv.ParseBool(inStockStr)

		// TODO: 읽어온 값들로 Product 구조체 생성
		// TODO: product와 함께 InspectStruct를 호출하고 결과 출력

	} else if structType == "employee" {
		// employee 필드들을 읽음
		idStr, _ := reader.ReadString('\n')
		idStr = strings.TrimSpace(idStr)
		id, _ := strconv.Atoi(idStr)

		name, _ := reader.ReadString('\n')
		name = strings.TrimSpace(name)

		department, _ := reader.ReadString('\n')
		department = strings.TrimSpace(department)

		// TODO: 읽어온 값들로 Employee 구조체 생성
		// TODO: employee와 함께 InspectStruct를 호출하고 결과 출력

	}
}
quiz icon실력 점검

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

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