자바스크립트에서는 object 필드에 동적으로 접근하기가 매우 간단하다.
const person = {
gretting: 'hello',
name: 'John Doe',
}
// 통상적인 방법
console.log(person.gretting);
// 동적인 접근법
console.log(person['gretting']);
그래서 자바스크립트를 이용해서 개발할 때에는 이러한 방식을 많이 이용했었는데, Go로 개발을 시작하고나서는 이 방식을 쓸 수 없어서 한동안 불편했었다.
그런데 reflect
패키지를 사용하면 동적으로 구조체 필드에 접근 가능하다.
먼저 위에서 사용한 것과 같이 Person
이라는 구조체가 있다고 하고 그 객체를 만들었을 때,
type Person struct {
Gretting string
Name string
}
person := Person{
Gretting: "hello",
Name: "John Doe",
}
먼저 코드는 위와 같이 된다.
그리고 위 person
객체에서 Gretting
필드를 동적으로 가져오고 싶다고 하면 다음과 같은 코드를 이용하면 된다.
reflect.Indirect(reflect.ValueOf(구조체변수)).FieldByName(필드명).String()
// example
field := "Name"
value := reflect.Indirect(reflect.ValueOf(person)).FieldByName(field).String()
fmt.Println(value) // John Doe
이런 식으로 따로 인터페이스를 이용하지 않고도 다이나믹한 프로그래밍이 가능하다.
또, 전체 필드 리스트를 가져오는 법은 다음과 같다.
e := reflect.ValueOf(&구조체{}).Elem()
fields := []string{} // 필드명들이 담길 배열
for i := 0; i < e.NumField(); i++ {
columns = append(fields, e.Type().Field(i).Name)
}
// example
e := reflect.ValueOf(&Person{}).Elem()
fields := []string{}
for i := 0; i < e.NumField(); i++ {
fields = append(fields, e.Type().Field(i).Name)
}
fmt.Println(fields) // ["Gretting", "Name"]
이 두 방식을 조합하면 전체 필드값을 출력하는 등의 방식이 가능하다.
type Person struct {
Gretting string
Name string
}
func main() {
e := reflect.ValueOf(&Person{}).Elem()
fields := []string{}
for i := 0; i < e.NumField(); i++ {
fields = append(fields, e.Type().Field(i).Name)
}
person := Person{Gretting: "hello", Name: "John Doe"}
for _, field := range fields {
value := reflect.Indirect(reflect.ValueOf(person)).FieldByName(field).String()
fmt.Printf("%v: %v\n", field, value)
}
}
// 실행 결과
Gretting: hello
Name: John Doe
하지만 reflect
패키지를 자주 사용하는 것은 Go스럽지 않다고 한다.