개발바닥

구조체(Struct) 본문

GO 언어

구조체(Struct)

라이언 2022. 2. 28. 23:11
반응형

Go Struct란?

Go의 struct는 필드들의 집합체이며 필드들의 컨테이너이다. Go에서 struct는 필드 데이터만을 가지며, 메서드를 갖지 않는다.

Go 언어에서는 class가 없지만 객체지향 프로그램(OOP) 스타일로 프로그램을 할 수 있다. go의 struct는 메서드에 붙일 수도 있고, embedding으로 상속도 흉내 낼 수 있다.

즉, Go에는 전통적인 OOP 언어가 가지는 클래스, 객체, 상속 개념이 없다.

 

구조체 정의

구조체 정의하는 방식은 3가지 방식이 있다.

1. 일반 구조체

2. 중첩된 구조체 (nested struct)

3. 임베디드 필드 구조체  (embedded struct)

// 1. 구조체 정의
type User struct{
   필드명 타입
   ...
   필드명 타입
}

// 2. nested struct
type ClassInfo struct{
   Class int
   No int
}

type Student struct{
   Class ClassInfo   // 구조체 안에 구조체 선언
   Name string
}

// 3. embedded struct
type ClassInfo struct{
   Class int
   No int
}

type Student struct{
   ClassInfo  // 구조체 임베디드
   Name string
}

 

그러면 여기서 의문이 든다. embedded struct 와 nested struct의 차이점이 무엇일까?

접근하는 방식이 달라진다.

nested struct 는 선언한 변수명을 통해 접근을 한다.

embedded struct  내장되므로 바로 dot(.) 으로 접근이 가능하다.

만약에 상위 구조체와 내장된 구조체와 필드가 겹친다면 상위 구조체가 우선순위를 갖고 내장된 필드를 접근하고 싶다면 타입명을 통해서 접근을 해야된다.

 

package main

import "fmt"

type User struct {
	Name string
	No   int
}

// embedded struct
type VipUser struct {
	*User
	No int
}

// nested struct
type VVipUser struct {
	UserInfo User
	No       int
}

func main() {
	vipUser := VipUser{No: 1, User: &User{"jo", 2}}
	fmt.Println("vipNo : ", vipUser.No, " vipUserName : ", vipUser.Name, " vipUserNo : ", vipUser.User.No)
	vvipUser := VVipUser{No: 2, UserInfo: User{"park", 3}}
	fmt.Println("vvipNo : ", vvipUser.No, " vvipUserName : ", vvipUser.UserInfo.Name, " vvipUserNo : ", vvipUser.UserInfo.No)
}

결과 화면

vipNo :  1  vipUserName :  jo  vipUserNo :  2
vvipNo :  2  vvipUserName :  park  vvipUserNo :  3

 

Go struct 태그로 인코딩 제어하기 

구조체 필드의 태그는 `태그명:"내용"` 형식으로 지정한다.

각 필드에 struct 태그를 추가하여 json 출력을 제어하는 값이 있다. 필드 이름의 낙타 대문자 버전을 json키에 대한 값으로 배치하면 인코더가 대신 해당 이름을 사용한다. (json 뿐만 아니라 xml, bson 다양한 인코더 형식이 존재한다.)

용어 정리!
인코딩이란? 사람이 인지하지 못하고 컴퓨터가 이해할 수 있도록 바꿔주는 것을 의미한다.
디코딩이란? 인코딩의 반대로서 사람이 이해 할 수 있도록 바꿔주는 것을 의미한다.

그러면 이 태그는 주로 언제 사용할까요??

서버에서 클라이언트 요청 처리 완료 후 json 형식으로 응답을 해줄 때 필드와 보내고자 하는 키 값이 대소문자가 다를 때 사용할 수 있고,

해당 필드를 항상 생략하고자 할 때나 구조체 유효성 검사를 체크할 때 태그를 사용할 수 있다.

아래 소스 코드를 통해서 확인을 해봅시다.

package main

import (
	"encoding/json"
	"fmt"
	"os"
)

type ColorGroup struct {
	ID     int      `json:"id"`
	Name   string   `json:"name"`
	Colors []string `json:"colors"`
	No     int      `json:"-"` // "-" 을 하게 되면 항상 생략하게 된다.
}

func main() {
	group := ColorGroup{
		ID:     1,
		Name:   "Reds",
		Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
		No:     5,
	}
	b, err := json.Marshal(group) //json 인코딩
	if err != nil {
		fmt.Println("error:", err)
	}
	os.Stdout.Write(b)
}

위 컬러그룹 구조체 필드들은 앞에 대문자로 시작하지만 응답으로는 소문자로 보내고 싶을 때 위 코드 처럼 사용하면 된다.

또한 특정 필드를 항상 생략하고자 할 때 "-"를 선언해주므로써 생략을 할 수 있다.

결과 화면

{"id":1,"name":"Reds","colors":["Crimson","Red","Ruby","Maroon"]}

태그를 자동으로 생성해주는 라이브러리가 존재한다.

※ gomodifytags Github 주소 : https://github.com/fatih/gomodifytags

 

구조체를 받았을 때 구조체 필드 유효성 검사하는 라이브러리

※ validator Github 주소 : https://github.com/go-playground/validator

validator는 현재 실무에서도 구조체 필드 검사를 위해서 사용하고 있는 유용한 라이브러리이다.

예를 들어서 특정 필드에 값이 반드시 존재해야 된다면 태그에 required를 선언해주면 된다.(zero value이면 안된다.)

이거 상당히 중요하다. 무심코 required를 사용했다가 zero value를 못쓰는 상황이 발생할 수 있기 때문이다.... (찾는데 고생할 수 있습니다. ㅠ.ㅠ)

또한, 해당 필드 값이 10이상이여야 된다. 그러면 gte=10 이런식으로 10 밑으로 값이 들어오면 유효하지 않도록 판단 할 수 있다.

 

package main

import (
	"fmt"
	"github.com/go-playground/validator"
)

type User struct {
	ID    string `json:"id" validate:"required"`
	Name  string `json:"name" validate:"required"`
	Email string `json:"email" validate:"required"`
	Age   uint64 `json:"age" validate:"gte=20"`
}

func main() {
	validate := validator.New()
	user := &User{
		ID:    "test",
		Name:  "ryan",
		Email: "ryan@example.com",
		Age:   15,
	}
	err := validate.Struct(user)
	if err != nil {
		for _, err := range err.(validator.ValidationErrors) {
			fmt.Println(err)
		}
		return
	}
	fmt.Println("구조체 유효성 검사 통과!!")
}

나이가 20살 미만이기 때문에 에러를 발생하게 된다.

결과 화면

Key: 'User.Age' Error:Field validation for 'Age' failed on the 'gte' tag
반응형

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

인터페이스 (interface)  (0) 2022.03.06
모듈(Module)  (0) 2022.03.06
슬라이스(Slice)  (0) 2022.02.26
채널(Channel)  (0) 2022.02.22
고루틴(goroutine)  (0) 2022.02.21
Comments