리플렉션 기초
Coddy GO 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 107개 중 83번째.
리플렉션(Reflection)은 프로그램이 런타임에 자신의 구조를 조사하고 조작할 수 있게 해줍니다. Go의 reflect 패키지는 이 기능을 제공하여, 타입을 검사하고 구조체 필드를 읽으며 메서드를 동적으로 호출할 수 있게 합니다.
리플렉션의 두 가지 핵심 타입은 reflect.Type과 reflect.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 매핑 또는 컴파일 타임에 타입을 알 수 없는 제네릭 유틸리티를 구축하는 것과 같은 작업에만 제한적으로 사용하세요.
챌린지
쉬움리플렉션(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 함수가 Product와 Employee 구조체를 모두 동적으로 처리합니다.
치트 시트
reflect 패키지는 프로그램이 런타임에 자신의 구조를 검사하고 조작할 수 있게 해줍니다.
두 가지 핵심 타입은 reflect.Type과 reflect.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를 호출하고 결과 출력
}
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
8에러 처리와 OOP
error 인터페이스사용자 정의 에러 타입에러 래핑 (fmt.Errorf)센티넬 에러errors.Is()와 errors.As()Panic, Defer, Recover요약 - 파일 파서