type User struct {
Name string `json:"name,omitempty"`
Age int `json:"age,omitempty"`
}
GoLang의 구조체(struct)의 필드는 Tag를 이용해서 메타정보를 추가하는 것으로 의미를 확장할 수 있다. 위의 구조체에서 Name과 Age 필드는 각각 json 필드와 매핑되며, nil이 허용(omitempt) 될 수 있음을 의미한다. 이 정보는 Golang의 encoding/json 패키지가 json 데이터를 마샬/언마샬(marshal, unmarshal) 할 때 사용한다. 이 문서에서 태그를 어디에 어떻게 사용 할 수 있는지 살펴볼 것이다.
GoLang struct
Go언어에서 구조체는 일련의 필드들로 구성된다. 각 필드들은 다시 이름(name)과 타입(type)으로 구성된다. 아래 코드를 보자.
package main
import "fmt"
type T1 struct {
f1 string
}
type T2 struct {
T1
f2 int64
f3, f4 float64
}
func main() {
fmt.Println("vim-go")
t := T2{T1{"foo"}, 1, 2, 3}
fmt.Println(t.f1)
fmt.Println(t.T1.f1)
fmt.Println(t.f2)
}
T2 구조체의 T1 필드는 임베디드(embedded)필드로 이름이 선언되지 않는 필드를 의미한다. 이외에 F2는 f2, f3, f4 3개의 필드들을 더 가지고 있다. 세미콜론을 사용 할 경우에 하나의 라인에 같은 타입의 필드들을 선언 할 수 있다.
T1.f1 infomation
Tag f one
Name f1
Type string
========
T1.f2 infomation
Tag f two
Name f2
Type int64
Go는 reflect를 이용해서 프로그램의 런타임에 변수와 값을 검사하고 타입을 체크하고 구조체의 메타정보를 가져올 수 있다. Tag도 구조체의 메타정보 중 하나다. 위의 예제에서 T1 구조체에 있는 각 변수의 Tag 값을 읽어오는 것을 확인 할 수 있다.
Tag의 사용
Tag는 보통 Key1:"value" key2:"value" 와 같은 key/value 목록의 형식으로 작성한다. 아래는 Go의 ORM 라이브러리인 Gorm 에서의 스트럭쳐 선언이다.
type User struct {
gorm.Model
Name string
Age sql.NullInt64
Birthday *time.Time
Email string `gorm:"type:varchar(100);unique_index"`
Role string `gorm:"size:255"`
MemberNumber *string `gorm:"unique;not null"`
Num int `gorm:"AUTO_INCREMENT"`
Address string `gorm:"index:addr"`
IgnoreMe int `gorm:"-"`
}
Gorm은 스트럭처의 Tag를 읽어서 정확한 데이터베이스 작업을 수행 할 수 있을 것이다.
Tag는 reflex의 Lookup과 Get 메서드로 사용 할 수 있다. 아래 코드를 보자.
package main
import (
"fmt"
"reflect"
)
type T struct {
f string `one:"1" two:"2" size:"100"`
}
func main() {
t := reflect.TypeOf(T{})
f, _ := t.FieldByName("f")
fmt.Println(f.Tag) // one:"1" two:"2" size:"100"
v, ok := f.Tag.Lookup("one")
fmt.Printf("%s %t\n", v, ok) // 1 true
v, ok = f.Tag.Lookup("size")
fmt.Printf("%s %t\n", v, ok) // 100 true
v, ok = f.Tag.Lookup("blank") // false
fmt.Printf("%s %t\n", v, ok)
}
Get 메서드는 Lookup의 간단한 wrapper 함수로 boolean flag를 무시한다.
package main
import (
"fmt"
"reflect"
"time"
"database/sql"
"github.com/jinzhu/gorm"
)
type User struct {
gorm.Model
Name string
Age sql.NullInt64
Birthday *time.Time
Email string `gorm:"type:varchar(100);unique_index"`
Role string `gorm:"size:255"`
MemberNumber *string `gorm:"unique;not null"`
Num int `gorm:"AUTO_INCREMENT"`
Address string `gorm:"index:addr"`
IgnoreMe int `gorm:"-"`
}
func main() {
t := reflect.TypeOf(User{})
for i := 0; i < t.NumField(); i++ {
v, ok := t.Field(i).Tag.Lookup("gorm")
if ok {
fmt.Printf("(%14s) is ORM Field : %s\n", t.Field(i).Name, v)
}
}
}
실행결과다.
( Email) is ORM Field : type:varchar(100);unique_index
( Role) is ORM Field : size:255
( MemberNumber) is ORM Field : unique;not null
( Num) is ORM Field : AUTO_INCREMENT
( Address) is ORM Field : index:addr
( IgnoreMe) is ORM Field : -
이제 ORM을 처리하는 패키지는 구조체에서 key가 "gorm"인 필드를 읽을 수 있을 것이다. 이 필드는 ORM처리를 하라는 의미이미로 value를 읽어서 적당한 처리를 하면 된다.
예를들어 구조체로 부터 테이블을 Create하는 메서드라면, addr 인덱스를 만들고, Num int 필드는 AUTO_INCREMENT인 sql문을 만들어서 실행 할 수 있을 것이다.
CREATE TABLE user (
Num int auto_increment,
Email VARCHAR(100),
Role VARCHAR(255),
Address text,
MemberNumber text NOT NULL,
index addr(ADDRESS),
UNIQUE KEY(Email)
);
Multi Tag
Go언어는 multi tag를 허용한다. 구조체를 여러 개의 형태로 마샬링 할 필요가 있을 때 사용한다. API의 반환 값 형태를 파라미터에 따라서 JSON 혹은 XML로 리턴해야 하는 경우가 대표적인 예다.
package main
import (
"fmt"
"reflect"
)
type User struct {
Name string `json:"name" xml:"name"`
Address string `json:"address" xml:"address"`
Age string `json:"age"`
}
func main() {
t := reflect.TypeOf(User{})
for i := 0; i < t.NumField(); i++ {
v, ok := t.Field(i).Tag.Lookup("json")
if ok {
fmt.Printf("(%14s) is Json Field : %s\n", t.Field(i).Name, v)
}
v, ok = t.Field(i).Tag.Lookup("xml")
if ok {
fmt.Printf("(%14s) is xml Field : %s\n", t.Field(i).Name, v)
}
}
}
실행결과
( Name) is Json Field : name
( Name) is xml Field : name
( Address) is Json Field : address
( Address) is xml Field : address
( Age) is Json Field : age
Contents
Go 언어와 태그
GoLang struct
Tag
Tag의 사용
Multi Tag
Tag를 사용하는 패키지들
정리
Recent Posts
Archive Posts
Tags