GO · 패턴
리플렉션 기초 — reflect 패키지
reflect 패키지는 런타임에 타입 정보를 조회하고 값을 동적으로 조작합니다. ORM, JSON 직렬화, DI 프레임워크 등 범용 라이브러리 개발에 필수적이지만, 성능과 타입 안전성을 트레이드오프합니다.
패턴고급reflectreflectionruntimestruct-tagsdynamic
핵심 설명
reflect 패키지는 런타임에 타입 정보를 조회하고 값을 동적으로 조작합니다. ORM, JSON 직렬화, DI 프레임워크 등 범용 라이브러리 개발에 필수적이지만, 성능과 타입 안전성을 트레이드오프합니다.
Go code
package main
import (
"fmt"
"reflect"
"strings"
)
type User struct {
Name string // json:"name" validate:"required"
Email string // json:"email" validate:"required,email"
Age int // json:"age" validate:"min=0,max=150"
}
// 참고: 실제 코드에서는 백틱 태그를 사용합니다
// Name string `json:"name" validate:"required"`
// 1. 타입 정보 조회
func inspectType(v interface{}) {
t := reflect.TypeOf(v)
fmt.Printf("타입: %s, 종류: %s\n", t.Name(), t.Kind())
if t.Kind() == reflect.Struct {
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
tag := f.Tag.Get("json")
validate := f.Tag.Get("validate")
fmt.Printf(" 필드: %-6s 타입: %-6s json:%s validate:%s\n",
f.Name, f.Type, tag, validate)
}
}
}
// 2. 동적 값 조작
func setField(obj interface{}, fieldName string, value interface{}) error {
v := reflect.ValueOf(obj)
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
return fmt.Errorf("포인터가 필요합니다")
}
field := v.Elem().FieldByName(fieldName)
if !field.IsValid() {
return fmt.Errorf("필드 '%s'를 찾을 수 없습니다", fieldName)
}
if !field.CanSet() {
return fmt.Errorf("필드 '%s'를 설정할 수 없습니다", fieldName)
}
val := reflect.ValueOf(value)
if field.Type() != val.Type() {
return fmt.Errorf("타입 불일치: %s != %s", field.Type(), val.Type())
}
field.Set(val)
return nil
}
// 3. 간단한 구조체 → Map 변환 (JSON 태그 기반)
func structToMap(v interface{}) map[string]interface{} {
result := make(map[string]interface{})
val := reflect.ValueOf(v)
typ := reflect.TypeOf(v)
for i := 0; i < val.NumField(); i++ {
field := typ.Field(i)
jsonTag := field.Tag.Get("json")
if jsonTag == "" || jsonTag == "-" {
continue
}
key := strings.Split(jsonTag, ",")[0]
result[key] = val.Field(i).Interface()
}
return result
}
func main() {
user := User{Name: "홍길동", Email: "hong@go.dev", Age: 30}
// 타입 조회
fmt.Println("=== 타입 정보 ===")
inspectType(user)
// 동적 값 설정
fmt.Println("\n=== 동적 값 변경 ===")
fmt.Printf("변경 전: %+v\n", user)
setField(&user, "Name", "김철수")
setField(&user, "Age", 25)
fmt.Printf("변경 후: %+v\n", user)
// 구조체 → Map
fmt.Println("\n=== Map 변환 ===")
m := structToMap(user)
for k, v := range m {
fmt.Printf(" %s: %v\n", k, v)
}
}학습 팁
리플렉션은 일반 코드보다 10~100배 느립니다. 핫 패스(hot path)에서는 피하고, 초기화 시점에 리플렉션 결과를 캐싱하거나 코드 생성(go generate)을 대안으로 고려하세요.
주의할 점
미내보내 필드(소문자)는 reflect.Value.Set()으로 변경할 수 없습니다. CanSet()으로 먼저 확인하지 않으면 패닉이 발생합니다.
자주 묻는 질문
리플렉션 기초 — reflect 패키지란 무엇인가요?
reflect 패키지는 런타임에 타입 정보를 조회하고 값을 동적으로 조작합니다. ORM, JSON 직렬화, DI 프레임워크 등 범용 라이브러리 개발에 필수적이지만, 성능과 타입 안전성을 트레이드오프합니다.
리플렉션 기초 — reflect 패키지 학습 시 주의할 점은 무엇인가요?
미내보내 필드(소문자)는 reflect.Value.Set() 으로 변경할 수 없습니다. CanSet() 으로 먼저 확인하지 않으면 패닉이 발생합니다.
Continue Learning
Go 학습을 이어가세요
총 200개의 독립 HTML 학습 문서 중 하나입니다. 각 문서는 고유 URL과 canonical 메타데이터를 가집니다.