reflect 사용 이유

reflect는 여러 가지의 사용방법이 있겠지만 저는 종종 협업을 하면서 상대방이 보낸 데이터의 타입 및 종류에 대하여 구분이 필요한 경우 사용하였습니다. 

 

reflect를 이용하여 타입 가져오기

인터페이스 형식으로 데이터를 받아 받은 데이터의 타입을 구분할 수 있습니다.

package main

import (
	"fmt"
	"reflect"
)

func ShowType(i interface{}) {
	fmt.Println(reflect.ValueOf(i).Type())
}

func main() {
	num := 100
	ShowType(num)

	job := "student"
	ShowType(job)
}
/* 출력결과
int
string
*/

코드설명

reflect.ValueOf().Type()을 사용하여 해당 타입이 어떤 타입인지 알 수 있습니다.

 

reflect를 이용하여 구조체 멤버 변수 확인하기

reflect를 사용하여 인터페이스로 받은 구조체의 멤버 변수들을 확인하도록 하겠습니다. 여기서 중요한 건 멤버 변수가 없는 일반 변수의 인터페이스를 넘기면 panic이 발생합니다.

package main

import (
	"fmt"
	"reflect"
)

type Person struct {
	Name string `json:"name"`
	Age  int    `json:"age"`
	Job  string `json:"job"`
}

func (p Person) Print() {
	fmt.Println(p.Name, " is ", p.Job)
}

func ShowMetaData(i interface{}) {
	elements := reflect.ValueOf(i).Elem()

	for index := 0; index < elements.NumField(); index++ {
		typeField := elements.Type().Field(index)
		fmt.Println(typeField.Name, typeField.Type, typeField.Tag, elements.Field(index))
	}
}

func main() {
	p := &Person{
		Name: "Han",
		Age:  30,
		Job:  "Developer",
	}
	ShowMetaData(p)

	// panic 발생
	//num := 100
	//ShowMetaData(num)
}

/* 출력결과
Name string json:"name" Han
Age int json:"age" 30
Job string json:"job" Developer
*/

코드설명

ShowMetaData() 함수를 보면 interface의 value를 reflect.Value().Elem()을 통하여 elements들을 가져옵니다. 그리고 

elements.Type().Field()를 통하여 현재 필드의 데이터를 StructField형식으로 가져올 수 있습니다. StructField의 구조는 다음과 같습니다.

 

StructField의 구조

// A StructField describes a single field in a struct.
type StructField struct {
	// Name is the field name.
	Name string
	// PkgPath is the package path that qualifies a lower case (unexported)
	// field name. It is empty for upper case (exported) field names.
	// See https://golang.org/ref/spec#Uniqueness_of_identifiers
	PkgPath string

	Type      Type      // field type
	Tag       StructTag // field tag string
	Offset    uintptr   // offset within struct, in bytes
	Index     []int     // index sequence for Type.FieldByIndex
	Anonymous bool      // is an embedded field
}

Name : 구조체 멤버 변수의 이름

Type : 구조체 맴버변수의 타입

Tag : 구조체 맴버변수의 태그

Offset : 구조체 멤버 변수의 오프셋(맴버변수의 메모리 위치)

Index : 구조체 맴버변수의 선언 순서

 

reflect를 이용하여 멤버 변수 가져오기

package main

import (
	"fmt"
	"reflect"
)

type Person struct {
	Name   string `json:"name"`
	Age    int    `json:"age"`
	Job    string `json:"job"`
	Salary int    `json:"salary"`
}

func (p Person) Print() {
	fmt.Println(p.Name, " is ", p.Job)
}

func (p *Person) SetSalary(salary int) {
	p.Salary = salary
}

func (p Person) GetName() string {
	return p.Name
}

func ShowMethod(i interface{}) {
	value := reflect.ValueOf(i)

	for index := 0; index < value.NumMethod(); index++ {
		fmt.Printf("method name : %s\n", value.Type().Method(index).Name) // 1번
		fmt.Printf("method type : %v\n\n", value.Method(index).Type()) // 2번
	}
}

func main() {
	p := &Person{
		Name: "Han",
		Age:  30,
		Job:  "Developer",
	}
	ShowMethod(p)
}

/* 출력결과
method name : GetName
method type : func() string

method name : Print
method type : func()

method name : SetSalary
method type : func(int)
*/

코드설명

1번 주석을 보면 Type().Method()를 통하여 구조체의 메서드의 타입을 가져올 수 있습니다. 가져온 메서드 타입으로 메서드 명을 가져올 수 있습니다.

2번 주석을 보면 Method().Type()을 통해 실제 메서드의 구성을 알 수 있습니다.

 

 

'Go언어' 카테고리의 다른 글

go work 사용해보기  (0) 2023.07.06
Ubuntu(Linux)에서 Go 재설치  (0) 2022.10.30
Go언어 Cron  (0) 2021.06.15
Prometheus Go언어 Metric label  (0) 2021.06.14
Prometheus Go언어 Metric 생성  (0) 2021.06.13

+ Recent posts