15 minute read

개요

Go의 reflect 패키지는 런타임에 타입과 값 정보를 검사하고 조작할 수 있는 리플렉션 기능을 제공합니다.

주요 특징:

  • 타입 검사: TypeOf로 런타임 타입 확인
  • 값 검사: ValueOf로 값 정보 조회
  • Kind 분류: 기본 타입 카테고리 구분
  • 구조체 분석: 필드, 메소드, 태그 검사
  • 동적 호출: 함수/메소드 런타임 실행
  • 타입 변환: 인터페이스 간 동적 변환
  • 성능 비용: 리플렉션은 느리므로 신중히 사용

기본 타입 검사

1. TypeOf - 타입 확인

import (
    "fmt"
    "reflect"
)

func main() {
    // 기본 타입
    fmt.Println(reflect.TypeOf(1))        // int
    fmt.Println(reflect.TypeOf(1.2))      // float64
    fmt.Println(reflect.TypeOf("hello"))  // string
    fmt.Println(reflect.TypeOf('a'))      // int32 (rune)
    fmt.Println(reflect.TypeOf(true))     // bool
    
    // 복합 타입
    fmt.Println(reflect.TypeOf([]int{1, 2}))        // []int
    fmt.Println(reflect.TypeOf(map[string]int{}))   // map[string]int
    fmt.Println(reflect.TypeOf(struct{ X int }{}))  // struct { X int }
}

2. Kind - 타입 카테고리

func main() {
    type MyInt int
    
    var x MyInt = 10
    
    t := reflect.TypeOf(x)
    fmt.Println(t)        // main.MyInt (타입 이름)
    fmt.Println(t.Kind()) // int (기본 종류)
    
    // Kind는 기본 분류
    fmt.Println(t.Kind() == reflect.Int)  // true
}

3. Type 메소드

func main() {
    type Person struct {
        Name string
        Age  int
    }
    
    t := reflect.TypeOf(Person{})
    
    // 타입 정보
    fmt.Println(t.Name())       // Person
    fmt.Println(t.PkgPath())    // main
    fmt.Println(t.String())     // main.Person
    fmt.Println(t.Kind())       // struct
    fmt.Println(t.Size())       // 24 (바이트)
    fmt.Println(t.NumField())   // 2 (필드 개수)
}

4. 포인터와 Elem

func main() {
    var x int = 42
    var p *int = &x
    
    // 포인터 타입
    t := reflect.TypeOf(p)
    fmt.Println(t)        // *int
    fmt.Println(t.Kind()) // ptr
    
    // 포인터가 가리키는 타입
    elem := t.Elem()
    fmt.Println(elem)        // int
    fmt.Println(elem.Kind()) // int
}

값 검사

1. ValueOf - 값 정보

func main() {
    x := 42
    
    v := reflect.ValueOf(x)
    fmt.Println(v)          // 42
    fmt.Println(v.Type())   // int
    fmt.Println(v.Kind())   // int
    fmt.Println(v.Int())    // 42
    
    // 타입별 변환
    s := "hello"
    v2 := reflect.ValueOf(s)
    fmt.Println(v2.String()) // hello
}

2. Interface - 원본 값 추출

func main() {
    x := 42
    v := reflect.ValueOf(x)
    
    // 인터페이스로 변환
    i := v.Interface()
    
    // 타입 단언
    num := i.(int)
    fmt.Println(num)  // 42
}

3. 수정 가능 여부

func main() {
    x := 42
    
    // 값으로 전달 (수정 불가)
    v := reflect.ValueOf(x)
    fmt.Println(v.CanSet())  // false
    
    // 포인터로 전달 (수정 가능)
    v2 := reflect.ValueOf(&x).Elem()
    fmt.Println(v2.CanSet())  // true
    
    v2.SetInt(100)
    fmt.Println(x)  // 100
}

4. 값 설정

func main() {
    var x interface{} = 42
    
    v := reflect.ValueOf(&x).Elem()
    
    // 타입 확인 후 설정
    if v.CanSet() {
        switch v.Elem().Kind() {
        case reflect.Int:
            v.Set(reflect.ValueOf(100))
        case reflect.String:
            v.Set(reflect.ValueOf("hello"))
        }
    }
    
    fmt.Println(x)  // 100
}

구조체 검사

1. 필드 순회

type Person struct {
    Name string
    Age  int
    City string
}

func main() {
    p := Person{"Alice", 30, "Seoul"}
    
    t := reflect.TypeOf(p)
    v := reflect.ValueOf(p)
    
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        value := v.Field(i)
        
        fmt.Printf("%s: %v (type: %v)\n",
            field.Name,
            value.Interface(),
            field.Type)
    }
    // Name: Alice (type: string)
    // Age: 30 (type: int)
    // City: Seoul (type: string)
}

2. 필드 정보

type Person struct {
    Name string `json:"name" validate:"required"`
    Age  int    `json:"age" validate:"min=0,max=120"`
}

func main() {
    t := reflect.TypeOf(Person{})
    
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        
        fmt.Printf("Name: %s\n", field.Name)
        fmt.Printf("Type: %v\n", field.Type)
        fmt.Printf("Tag: %s\n", field.Tag)
        fmt.Printf("JSON tag: %s\n", field.Tag.Get("json"))
        fmt.Println()
    }
}

3. 필드 값 수정

type Person struct {
    Name string
    Age  int
}

func main() {
    p := Person{"Alice", 30}
    
    v := reflect.ValueOf(&p).Elem()
    
    // 이름으로 필드 찾기
    nameField := v.FieldByName("Name")
    if nameField.IsValid() && nameField.CanSet() {
        nameField.SetString("Bob")
    }
    
    // 인덱스로 필드 찾기
    ageField := v.Field(1)
    if ageField.CanSet() {
        ageField.SetInt(25)
    }
    
    fmt.Println(p)  // {Bob 25}
}

4. 중첩 구조체

type Address struct {
    City    string
    Country string
}

type Person struct {
    Name    string
    Address Address
}

func main() {
    p := Person{
        Name: "Alice",
        Address: Address{
            City:    "Seoul",
            Country: "Korea",
        },
    }
    
    v := reflect.ValueOf(p)
    
    // 중첩 필드 접근
    addr := v.FieldByName("Address")
    city := addr.FieldByName("City")
    
    fmt.Println(city.String())  // Seoul
}

메소드 검사

1. 메소드 목록

type Calculator struct{}

func (c Calculator) Add(a, b int) int {
    return a + b
}

func (c Calculator) Multiply(a, b int) int {
    return a * b
}

func main() {
    calc := Calculator{}
    t := reflect.TypeOf(calc)
    
    fmt.Println("Methods:")
    for i := 0; i < t.NumMethod(); i++ {
        method := t.Method(i)
        fmt.Printf("- %s: %v\n", method.Name, method.Type)
    }
    // Methods:
    // - Add: func(main.Calculator, int, int) int
    // - Multiply: func(main.Calculator, int, int) int
}

2. 메소드 호출

type Calculator struct{}

func (c Calculator) Add(a, b int) int {
    return a + b
}

func main() {
    calc := Calculator{}
    v := reflect.ValueOf(calc)
    
    // 메소드 찾기
    method := v.MethodByName("Add")
    
    // 인자 준비
    args := []reflect.Value{
        reflect.ValueOf(10),
        reflect.ValueOf(20),
    }
    
    // 호출
    result := method.Call(args)
    fmt.Println(result[0].Int())  // 30
}

3. 포인터 리시버

type Counter struct {
    count int
}

func (c *Counter) Increment() {
    c.count++
}

func (c *Counter) Get() int {
    return c.count
}

func main() {
    counter := &Counter{}
    v := reflect.ValueOf(counter)
    
    // Increment 호출
    v.MethodByName("Increment").Call(nil)
    v.MethodByName("Increment").Call(nil)
    
    // Get 호출
    result := v.MethodByName("Get").Call(nil)
    fmt.Println(result[0].Int())  // 2
}

실전 예제

1. JSON 직렬화 (간단 버전)

import (
    "fmt"
    "reflect"
    "strings"
)

func SimpleJSON(v interface{}) string {
    val := reflect.ValueOf(v)
    typ := reflect.TypeOf(v)
    
    if typ.Kind() != reflect.Struct {
        return fmt.Sprintf("%v", v)
    }
    
    var parts []string
    for i := 0; i < val.NumField(); i++ {
        field := typ.Field(i)
        value := val.Field(i)
        
        // json 태그 읽기
        name := field.Tag.Get("json")
        if name == "" {
            name = field.Name
        }
        
        var valueStr string
        switch value.Kind() {
        case reflect.String:
            valueStr = fmt.Sprintf(`"%s"`, value.String())
        case reflect.Int, reflect.Int64:
            valueStr = fmt.Sprintf("%d", value.Int())
        case reflect.Bool:
            valueStr = fmt.Sprintf("%t", value.Bool())
        default:
            valueStr = fmt.Sprintf("%v", value.Interface())
        }
        
        parts = append(parts, fmt.Sprintf(`"%s":%s`, name, valueStr))
    }
    
    return "{" + strings.Join(parts, ",") + "}"
}

type User struct {
    Name  string `json:"name"`
    Age   int    `json:"age"`
    Email string `json:"email"`
}

func main() {
    user := User{"Alice", 30, "alice@example.com"}
    json := SimpleJSON(user)
    fmt.Println(json)
    // {"name":"Alice","age":30,"email":"alice@example.com"}
}

2. 구조체 검증기

import "strings"

func Validate(v interface{}) error {
    val := reflect.ValueOf(v)
    typ := reflect.TypeOf(v)
    
    for i := 0; i < val.NumField(); i++ {
        field := typ.Field(i)
        value := val.Field(i)
        
        // required 태그 확인
        if field.Tag.Get("validate") == "required" {
            if isZero(value) {
                return fmt.Errorf("field %s is required", field.Name)
            }
        }
    }
    
    return nil
}

func isZero(v reflect.Value) bool {
    switch v.Kind() {
    case reflect.String:
        return v.String() == ""
    case reflect.Int, reflect.Int64:
        return v.Int() == 0
    case reflect.Bool:
        return !v.Bool()
    default:
        return false
    }
}

type User struct {
    Name  string `validate:"required"`
    Email string `validate:"required"`
    Age   int
}

func main() {
    // 유효한 경우
    user1 := User{Name: "Alice", Email: "alice@example.com", Age: 30}
    if err := Validate(user1); err != nil {
        fmt.Println(err)
    } else {
        fmt.Println("Valid")  // Valid
    }
    
    // 유효하지 않은 경우
    user2 := User{Name: "Bob"}
    if err := Validate(user2); err != nil {
        fmt.Println(err)  // field Email is required
    }
}

3. 구조체 복사

func CopyStruct(src, dst interface{}) error {
    srcVal := reflect.ValueOf(src)
    dstVal := reflect.ValueOf(dst)
    
    if dstVal.Kind() != reflect.Ptr {
        return fmt.Errorf("dst must be a pointer")
    }
    
    dstVal = dstVal.Elem()
    
    if srcVal.Kind() != reflect.Struct || dstVal.Kind() != reflect.Struct {
        return fmt.Errorf("both must be structs")
    }
    
    srcType := srcVal.Type()
    
    for i := 0; i < srcVal.NumField(); i++ {
        srcField := srcType.Field(i)
        srcValue := srcVal.Field(i)
        
        dstField := dstVal.FieldByName(srcField.Name)
        if dstField.IsValid() && dstField.CanSet() {
            if srcValue.Type() == dstField.Type() {
                dstField.Set(srcValue)
            }
        }
    }
    
    return nil
}

type Source struct {
    Name  string
    Age   int
    Email string
}

type Destination struct {
    Name  string
    Age   int
    Phone string
}

func main() {
    src := Source{Name: "Alice", Age: 30, Email: "alice@example.com"}
    dst := Destination{}
    
    CopyStruct(src, &dst)
    
    fmt.Printf("%+v\n", dst)  // {Name:Alice Age:30 Phone:}
}

4. 맵을 구조체로 변환

func MapToStruct(m map[string]interface{}, result interface{}) error {
    val := reflect.ValueOf(result)
    
    if val.Kind() != reflect.Ptr || val.Elem().Kind() != reflect.Struct {
        return fmt.Errorf("result must be a pointer to struct")
    }
    
    val = val.Elem()
    typ := val.Type()
    
    for i := 0; i < val.NumField(); i++ {
        field := typ.Field(i)
        fieldVal := val.Field(i)
        
        if !fieldVal.CanSet() {
            continue
        }
        
        // 맵에서 값 찾기
        mapKey := strings.ToLower(field.Name)
        mapValue, exists := m[mapKey]
        if !exists {
            continue
        }
        
        // 타입 변환
        mapVal := reflect.ValueOf(mapValue)
        if mapVal.Type().ConvertibleTo(fieldVal.Type()) {
            fieldVal.Set(mapVal.Convert(fieldVal.Type()))
        }
    }
    
    return nil
}

type Person struct {
    Name string
    Age  int
    City string
}

func main() {
    m := map[string]interface{}{
        "name": "Alice",
        "age":  30,
        "city": "Seoul",
    }
    
    var p Person
    MapToStruct(m, &p)
    
    fmt.Printf("%+v\n", p)  // {Name:Alice Age:30 City:Seoul}
}

5. 함수 타입 검사

func InspectFunc(f interface{}) {
    t := reflect.TypeOf(f)
    
    if t.Kind() != reflect.Func {
        fmt.Println("Not a function")
        return
    }
    
    fmt.Printf("Function: %v\n", t)
    
    // 입력 파라미터
    fmt.Printf("Inputs (%d):\n", t.NumIn())
    for i := 0; i < t.NumIn(); i++ {
        fmt.Printf("  %d: %v\n", i, t.In(i))
    }
    
    // 출력 파라미터
    fmt.Printf("Outputs (%d):\n", t.NumOut())
    for i := 0; i < t.NumOut(); i++ {
        fmt.Printf("  %d: %v\n", i, t.Out(i))
    }
    
    // 가변 인자
    fmt.Printf("Variadic: %v\n", t.IsVariadic())
}

func Add(a, b int) int {
    return a + b
}

func Printf(format string, args ...interface{}) {
    fmt.Printf(format, args...)
}

func main() {
    InspectFunc(Add)
    // Function: func(int, int) int
    // Inputs (2):
    //   0: int
    //   1: int
    // Outputs (1):
    //   0: int
    // Variadic: false
    
    fmt.Println()
    
    InspectFunc(Printf)
    // Function: func(string, ...interface {})
    // Inputs (2):
    //   0: string
    //   1: []interface {}
    // Outputs (0):
    // Variadic: true
}

6. 제네릭 슬라이스 필터

func Filter(slice interface{}, predicate interface{}) interface{} {
    sliceVal := reflect.ValueOf(slice)
    if sliceVal.Kind() != reflect.Slice {
        return nil
    }
    
    predicateVal := reflect.ValueOf(predicate)
    if predicateVal.Kind() != reflect.Func {
        return nil
    }
    
    resultType := sliceVal.Type()
    result := reflect.MakeSlice(resultType, 0, 0)
    
    for i := 0; i < sliceVal.Len(); i++ {
        elem := sliceVal.Index(i)
        
        // predicate 호출
        args := []reflect.Value{elem}
        retVals := predicateVal.Call(args)
        
        if len(retVals) > 0 && retVals[0].Bool() {
            result = reflect.Append(result, elem)
        }
    }
    
    return result.Interface()
}

func main() {
    numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    
    // 짝수만 필터링
    evens := Filter(numbers, func(n int) bool {
        return n%2 == 0
    })
    
    fmt.Println(evens)  // [2 4 6 8 10]
    
    // 문자열 필터링
    words := []string{"apple", "banana", "apricot", "cherry"}
    
    aWords := Filter(words, func(s string) bool {
        return strings.HasPrefix(s, "a")
    })
    
    fmt.Println(aWords)  // [apple apricot]
}

7. 타입별 처리 디스패처

type Handler struct {
    handlers map[reflect.Type]func(interface{})
}

func NewHandler() *Handler {
    return &Handler{
        handlers: make(map[reflect.Type]func(interface{})),
    }
}

func (h *Handler) Register(example interface{}, handler func(interface{})) {
    t := reflect.TypeOf(example)
    h.handlers[t] = handler
}

func (h *Handler) Handle(v interface{}) {
    t := reflect.TypeOf(v)
    if handler, exists := h.handlers[t]; exists {
        handler(v)
    } else {
        fmt.Printf("No handler for type: %v\n", t)
    }
}

type Message struct {
    Text string
}

type Command struct {
    Name string
}

func main() {
    handler := NewHandler()
    
    // 타입별 핸들러 등록
    handler.Register(Message{}, func(v interface{}) {
        msg := v.(Message)
        fmt.Printf("Message received: %s\n", msg.Text)
    })
    
    handler.Register(Command{}, func(v interface{}) {
        cmd := v.(Command)
        fmt.Printf("Command executed: %s\n", cmd.Name)
    })
    
    // 처리
    handler.Handle(Message{Text: "Hello"})     // Message received: Hello
    handler.Handle(Command{Name: "Start"})     // Command executed: Start
    handler.Handle("unknown")                   // No handler for type: string
}

8. 구조체 비교

func DeepEqual(a, b interface{}) bool {
    valA := reflect.ValueOf(a)
    valB := reflect.ValueOf(b)
    
    return deepEqual(valA, valB)
}

func deepEqual(a, b reflect.Value) bool {
    if !a.IsValid() || !b.IsValid() {
        return a.IsValid() == b.IsValid()
    }
    
    if a.Type() != b.Type() {
        return false
    }
    
    switch a.Kind() {
    case reflect.Struct:
        for i := 0; i < a.NumField(); i++ {
            if !deepEqual(a.Field(i), b.Field(i)) {
                return false
            }
        }
        return true
        
    case reflect.Slice, reflect.Array:
        if a.Len() != b.Len() {
            return false
        }
        for i := 0; i < a.Len(); i++ {
            if !deepEqual(a.Index(i), b.Index(i)) {
                return false
            }
        }
        return true
        
    case reflect.Map:
        if a.Len() != b.Len() {
            return false
        }
        for _, key := range a.MapKeys() {
            if !deepEqual(a.MapIndex(key), b.MapIndex(key)) {
                return false
            }
        }
        return true
        
    default:
        return a.Interface() == b.Interface()
    }
}

type Person struct {
    Name    string
    Age     int
    Friends []string
}

func main() {
    p1 := Person{
        Name:    "Alice",
        Age:     30,
        Friends: []string{"Bob", "Carol"},
    }
    
    p2 := Person{
        Name:    "Alice",
        Age:     30,
        Friends: []string{"Bob", "Carol"},
    }
    
    p3 := Person{
        Name:    "Alice",
        Age:     31,
        Friends: []string{"Bob", "Carol"},
    }
    
    fmt.Println(DeepEqual(p1, p2))  // true
    fmt.Println(DeepEqual(p1, p3))  // false
}

일반적인 실수

1. nil 체크 누락

// ❌ 나쁜 예 (nil 패닉)
func GetType(v interface{}) reflect.Type {
    return reflect.TypeOf(v)  // v가 nil이면 nil 반환
}

func main() {
    t := GetType(nil)
    fmt.Println(t.Name())  // 패닉!
}

// ✅ 좋은 예 (nil 체크)
func GetType(v interface{}) reflect.Type {
    t := reflect.TypeOf(v)
    if t == nil {
        return nil
    }
    return t
}

func main() {
    t := GetType(nil)
    if t != nil {
        fmt.Println(t.Name())
    }
}

2. CanSet 확인 누락

// ❌ 나쁜 예 (패닉)
func main() {
    x := 42
    v := reflect.ValueOf(x)
    v.SetInt(100)  // 패닉: reflect: reflect.Value.SetInt using unaddressable value
}

// ✅ 좋은 예 (CanSet 확인)
func main() {
    x := 42
    v := reflect.ValueOf(&x).Elem()
    
    if v.CanSet() {
        v.SetInt(100)
        fmt.Println(x)  // 100
    }
}

3. 타입 변환 오류

// ❌ 나쁜 예 (패닉)
func main() {
    v := reflect.ValueOf("hello")
    n := v.Int()  // 패닉: reflect: call of reflect.Value.Int on string Value
}

// ✅ 좋은 예 (Kind 확인)
func main() {
    v := reflect.ValueOf("hello")
    
    switch v.Kind() {
    case reflect.String:
        fmt.Println(v.String())
    case reflect.Int:
        fmt.Println(v.Int())
    }
}

4. 포인터 역참조 누락

// ❌ 나쁜 예 (구조체 필드 접근 실패)
type Person struct {
    Name string
}

func main() {
    p := &Person{Name: "Alice"}
    v := reflect.ValueOf(p)
    
    // 패닉: reflect: call of reflect.Value.NumField on ptr Value
    for i := 0; i < v.NumField(); i++ {
        // ...
    }
}

// ✅ 좋은 예 (Elem으로 역참조)
func main() {
    p := &Person{Name: "Alice"}
    v := reflect.ValueOf(p).Elem()
    
    for i := 0; i < v.NumField(); i++ {
        fmt.Println(v.Field(i))
    }
}

5. 성능 고려 부족

// ❌ 나쁜 예 (반복문에서 리플렉션)
func Sum(items interface{}) int {
    sum := 0
    v := reflect.ValueOf(items)
    
    for i := 0; i < v.Len(); i++ {
        // 매번 리플렉션 사용
        sum += int(v.Index(i).Int())
    }
    
    return sum
}

// ✅ 좋은 예 (타입 단언 사용)
func Sum(items interface{}) int {
    // 타입 단언으로 직접 처리
    if nums, ok := items.([]int); ok {
        sum := 0
        for _, n := range nums {
            sum += n
        }
        return sum
    }
    
    // 리플렉션은 최후의 수단
    return sumReflect(items)
}

func sumReflect(items interface{}) int {
    // 리플렉션 로직...
    return 0
}

6. unexported 필드 접근

type person struct {
    name string  // unexported
    Age  int     // exported
}

// ❌ 나쁜 예 (unexported 필드 수정 시도)
func main() {
    p := person{name: "Alice", Age: 30}
    v := reflect.ValueOf(&p).Elem()
    
    nameField := v.FieldByName("name")
    // 패닉: reflect: reflect.Value.SetString using value obtained using unexported field
    nameField.SetString("Bob")
}

// ✅ 좋은 예 (exported 필드만 사용)
type Person struct {
    Name string  // exported
    Age  int
}

func main() {
    p := Person{Name: "Alice", Age: 30}
    v := reflect.ValueOf(&p).Elem()
    
    nameField := v.FieldByName("Name")
    if nameField.CanSet() {
        nameField.SetString("Bob")
        fmt.Println(p)  // {Bob 30}
    }
}

7. 메소드 호출 인자 오류

type Calculator struct{}

func (c Calculator) Add(a, b int) int {
    return a + b
}

// ❌ 나쁜 예 (잘못된 인자)
func main() {
    calc := Calculator{}
    v := reflect.ValueOf(calc)
    method := v.MethodByName("Add")
    
    // 패닉: reflect: Call with too few input arguments
    method.Call([]reflect.Value{reflect.ValueOf(10)})
}

// ✅ 좋은 예 (올바른 인자)
func main() {
    calc := Calculator{}
    v := reflect.ValueOf(calc)
    method := v.MethodByName("Add")
    
    // 올바른 개수의 인자
    args := []reflect.Value{
        reflect.ValueOf(10),
        reflect.ValueOf(20),
    }
    
    result := method.Call(args)
    fmt.Println(result[0].Int())  // 30
}

베스트 프랙티스

1. 타입 스위치 우선

// ✅ 리플렉션보다 타입 스위치 우선
func Process(v interface{}) {
    switch val := v.(type) {
    case int:
        fmt.Println("Integer:", val)
    case string:
        fmt.Println("String:", val)
    case []int:
        fmt.Println("Int slice:", val)
    default:
        // 마지막 수단으로 리플렉션
        processReflect(v)
    }
}

2. 에러 처리

// ✅ 안전한 리플렉션 래퍼
func SafeSet(ptr interface{}, value interface{}) error {
    v := reflect.ValueOf(ptr)
    
    if v.Kind() != reflect.Ptr {
        return fmt.Errorf("ptr must be a pointer")
    }
    
    v = v.Elem()
    
    if !v.CanSet() {
        return fmt.Errorf("value cannot be set")
    }
    
    newVal := reflect.ValueOf(value)
    
    if !newVal.Type().AssignableTo(v.Type()) {
        return fmt.Errorf("type mismatch: %v vs %v", newVal.Type(), v.Type())
    }
    
    v.Set(newVal)
    return nil
}

3. 캐싱 활용

// ✅ 타입 정보 캐싱
var typeCache sync.Map

func GetCachedType(v interface{}) reflect.Type {
    t := reflect.TypeOf(v)
    
    if cached, ok := typeCache.Load(t); ok {
        return cached.(reflect.Type)
    }
    
    typeCache.Store(t, t)
    return t
}

4. 문서화

// ✅ 명확한 문서화
// SetField sets the value of a struct field by name.
// ptr must be a pointer to a struct.
// Returns an error if the field doesn't exist or cannot be set.
func SetField(ptr interface{}, fieldName string, value interface{}) error {
    v := reflect.ValueOf(ptr)
    
    if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
        return fmt.Errorf("ptr must be a pointer to struct")
    }
    
    field := v.Elem().FieldByName(fieldName)
    if !field.IsValid() {
        return fmt.Errorf("field %s not found", fieldName)
    }
    
    if !field.CanSet() {
        return fmt.Errorf("field %s cannot be set", fieldName)
    }
    
    newVal := reflect.ValueOf(value)
    if !newVal.Type().AssignableTo(field.Type()) {
        return fmt.Errorf("type mismatch")
    }
    
    field.Set(newVal)
    return nil
}

5. 벤치마크

// ✅ 성능 측정
func BenchmarkReflect(b *testing.B) {
    type Person struct {
        Name string
        Age  int
    }
    
    p := Person{Name: "Alice", Age: 30}
    
    b.Run("Direct", func(b *testing.B) {
        for i := 0; i < b.N; i++ {
            _ = p.Name
        }
    })
    
    b.Run("Reflect", func(b *testing.B) {
        v := reflect.ValueOf(p)
        for i := 0; i < b.N; i++ {
            _ = v.FieldByName("Name").String()
        }
    })
}

6. IsValid 확인

// ✅ IsValid 체크
func GetFieldValue(v interface{}, fieldName string) (interface{}, error) {
    val := reflect.ValueOf(v)
    
    if val.Kind() == reflect.Ptr {
        val = val.Elem()
    }
    
    field := val.FieldByName(fieldName)
    
    if !field.IsValid() {
        return nil, fmt.Errorf("field %s not found", fieldName)
    }
    
    return field.Interface(), nil
}

7. 제네릭 헬퍼

// ✅ 제네릭 유틸리티
func IsNil(v interface{}) bool {
    if v == nil {
        return true
    }
    
    val := reflect.ValueOf(v)
    switch val.Kind() {
    case reflect.Ptr, reflect.Map, reflect.Slice,
         reflect.Chan, reflect.Func, reflect.Interface:
        return val.IsNil()
    }
    
    return false
}

func main() {
    var p *int
    var m map[string]int
    
    fmt.Println(IsNil(p))  // true
    fmt.Println(IsNil(m))  // true
    fmt.Println(IsNil(42)) // false
}

8. 테스트

// ✅ 리플렉션 코드 테스트
func TestSetField(t *testing.T) {
    type Person struct {
        Name string
        Age  int
    }
    
    tests := []struct {
        name      string
        ptr       interface{}
        fieldName string
        value     interface{}
        wantErr   bool
    }{
        {
            name:      "valid string field",
            ptr:       &Person{},
            fieldName: "Name",
            value:     "Alice",
            wantErr:   false,
        },
        {
            name:      "invalid field",
            ptr:       &Person{},
            fieldName: "NotExists",
            value:     "test",
            wantErr:   true,
        },
        {
            name:      "type mismatch",
            ptr:       &Person{},
            fieldName: "Name",
            value:     123,
            wantErr:   true,
        },
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            err := SetField(tt.ptr, tt.fieldName, tt.value)
            if (err != nil) != tt.wantErr {
                t.Errorf("wantErr %v, got %v", tt.wantErr, err)
            }
        })
    }
}

정리

  • 기본: TypeOf (타입), ValueOf (값), Kind (카테고리), Elem (역참조)
  • 구조체: 필드 순회, 태그 읽기, 값 수정, 중첩 접근
  • 메소드: 목록 조회, 동적 호출, 리시버 처리
  • 실전: JSON 직렬화, 검증기, 구조체 복사, 맵 변환, 함수 검사, 제네릭 필터, 디스패처, 비교
  • 실수: nil 체크, CanSet 확인, 타입 변환, 포인터 역참조, 성능, unexported 필드, 인자 오류
  • 베스트: 타입 스위치 우선, 에러 처리, 캐싱, 문서화, 벤치마크, IsValid 확인, 제네릭 헬퍼, 테스트