데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      Custom Controller 1 - 개발에 필요한 Go 언어 기초 문법

      seungkyua 23.06.13
      3,201 13 0

      Kubernetes custom controller 개발에 가장 잘 맞는 프로그래밍 언어는 Go 이다.

      Kubernetes 가 Go 로 개발된 S/W 이다 보니 Custom controller 도 Go 로 만드는 것이 좋을 것 같다는 생각이다.


      그래서 겸사겸사 Custom Controller 개발에 필요한 Go 문법만 정리해 보기로 했다.

      • 변수 선언 : var, Short Variable Declaration Operator(:=)

      • Package 선언 및 활용

      • Struct (json 으로 변환)

      • Receiver function

      • Interface 선언, 활용


      변수 선언

      변수는 var 키워드로 쉽게 선언할 수 있다.

      // var 변수명 변수타입
      var message string

      변수를 선언하면서 값을 대입하면 마지막의 변수 타입은 생략 가능하다.

      var message = "Hi, Welcome!"

      := operator 를 사용하면 shortcut 으로 선언하여 var 도 생략할 수 있다.

      message := "Hi, Welcome!"


      Package 선언 및 활용

      모듈로 만들어서 import 하여 사용할 수 있다.

      아래와 같이 디렉토리를 만들어보자.

      greetings 는 모듈로 선언하고 basic 에서 greetings 모듈을 import 하여 사용할 예정이다.

      $ mkdir -p go-sample
      $ cd go-sample
      
      $ mkdir -p {basic,greetings}
      $ cd greetings

      go-sample 이라는 프로젝트 아래에 greetings 라는 모듈을 만든다.


      모듈을 만들기 위해서 go.mod 파일을 만든다.

      $ go mod init github.com/seungkyua/go-sample/greetings

      go.mod 파일이 생성되며 파일 내용은 아래와 같다.

      module github.com/seungkyua/go-sample/greetings
      
      go 1.19

      greetings.go 파일을 만들어서 아래와 같이 입력한다.


      여기도 error 핸들링과 string format 을 위해서 모듈을 import 하고 있는데 이를 이해할려고 하지 말고 그냥 Hello function 이 있다는 것만 이해하자.

      package greetings
      
      import (
      	"errors"
      	"fmt"
      )
      
      func Hello(name string) (string, error) {
      	if name == "" {
      		return "", errors.New("empty name")
      	}
      
      	message := fmt.Sprintf("Hi, %v. Welcome!", name)
      	return message, nil
      }

      go-sample 홈디렉토리에서 보는 greetings 디렉토리 구조는 아래와 같다.

      greetings
      ├── go.mod
      └── greetings.go

      golang 1.19 버전 부터는 로컬 하위 디렉토리를 인식하기 위해서 [go.work](http://go.work) 를 사용한다.

      아래와 같이 파일을 만들어 보자.

      $ go work use ./basic
      $ go work use ./greetins

      go-sample 홈디렉토리 아래에 [go.work](http://go.work) 파일이 아래와 같이 만들어 진다.

      go 1.19
      
      use (
      	./greetings
      	./basic
      )

      이제 basic 디렉토리로 가서 똑같이 go.mod 파일을 만들고 main.go 파일도 만들어 본다.

      $ cd basic
      
      $ go mod init github.com/seungkyua/go-sample/basic

      go.mod 파일이 아래와 같이 생성되었음을 알 수 있다.

      module github.com/seungkyua/go-sample/basic
      
      go 1.19

      greetings 모듈을 로컬로 인식하게 변경한다.

      $ go mod edit -replace github.com/seungkyua/go-sample/greetings=../greetings

      그리고 로컬 버전을 사용하기 위해서 pseudo 버전을 사용하게 tidy 명령을 활용한다.

      $ go mod tidy

      그럼 최종 go.mod 는 다음과 같다.

      module github.com/seungkyua/go-sample/basic
      
      go 1.19
      
      // go mod edit 으로 로컬 경로를 보도록 수정
      replace github.com/seungkyua/go-sample/greetings => ../greetings
      
      // 로컬이므로 pseudo 버전을 만든다 
      require github.com/seungkyua/go-sample/greetings v0.0.0-00010101000000-000000000000

      main.go 파일을 만들어서 greetings 모듈을 import 하여 활용해 본다.

      package main
      
      import (
      	"fmt"
      	"log"
      
      	"github.com/seungkyua/go-sample/greetings"
      )
      
      func main() {
      	message, err := greetings.Hello("Seungkyu")
      	if err != nil {
      		log.Fatal(err)
      	}
      	fmt.Println(message)
      }

      go-sample 디렉토리 구조는 아래와 같다.

      basic
      ├── go.mod
      └── main.go
      greetings
      ├── go.mod
      └── greetings.go
      go.work


      Struct 활용 (json 변환)

      struct 를 json data 로 변환하는 것을 marshal (encoding) 이라고 하고 json data를 struct 로 변환하는 것을 unmarshal (decoding) 이라고 한다.


      json 패키지의 Marshal function 은 다음과 같다.

      func Marshal(v interface{}) ([]byte, error)

      Struct 를 만들어서 json data (byte slice) 로 변환해 보자.

      type album struct {
      	ID     string  `json:"id"`
      	Title  string  `json:"title"`
      	Artist string  `json:"artist"`
      	Price  float64 `json:"price"`
      }
      
      a := album{ID: "1", Title: "Blue Train", Artist: "John Coltrane", Price: 56.99}
      b, err := json.Marshal(a)
      if err != nil {
      	log.Fatal(err)
      }
      
      b2 := []byte(`{"id":"1","title":"Blue Train","artist":"John Coltrane","price":56.99}`)
      fmt.Println(bytes.Equal(b, b2))

      Unmarshal function 은 다음과 같다.

      func Unmarshal(data []byte, v interface{}) error

      json data (byte slice) 를 struct 로 다시 변환한다.

      var a2 album
      err = json.Unmarshal(b, &a2)
      if err != nil {
      	log.Fatal(err)
      }
      fmt.Println(a2)


      Receiver Function

      Receiver function 은 struct 의 멤버 변수를 활용할 수 있는 function 이다.

      Vertex struct 를 만들고 그에 속한 멤버 변수를 활용하는 function 을 만들면 된다.

      func 와 함수명 사이에 Struct 를 변수와 함께 넣으면 Receiver function 이 된다.

      type Vertex struct {
      	X, Y float64
      }
      
      func (v Vertex) Abs() float64 {
      	return math.Sqrt(v.X*v.X + v.Y*v.Y)
      }
      
      func (v *Vertex) Scale(f float64) {
      	v.X = v.X * f
      	v.Y = v.Y * f
      }

      아래와 같이 main 함수를 실행하면 값은 50 이 나온다.

      func main() {
      	v := Vertex{3, 4}
      	v.Scale(10)
      	fmt.Println(v.Abs())
      }

      그런데 만약 위의 func (v *Vertex) Scale(f float64)에서 Vertex 를 포인터가 아닌 value 로 만들면 어떤 결과가 나올까?

      위의 함수에서 * 를 지우고 다시 실행해 보자.


      결과는 5 가 된다.


      즉, Struct 의 멤버 변수의 값을 바꾸고 싶으면 Pointer Receiver 를 사용해야 한다.


      Interface 선언, 활용

      Interface 는 method 를 모아둔 것이라 할 수 있다.

      interface 역시 type 키워드로 선언하기 때문에 interface 타입이라고도 말한다.


      아래와 같이 Abs() 메소드를 선언하고 나서 Abs 를 Receiver function 으로 구현했다면 Abs 를 구현한 타입은 Abser 타입이라고 할 수 있다.

      type Abser interface {
      	Abs() float64
      }

      아래는 MyFloat 타입도 Abser 타입이라고 할 수 있다. 하지만 Abs 의 Receiver 는 value Receiver 이다.

      type MyFloat float64
      
      func (f MyFloat) Abs() float64 {
      	if f < 0 {
      		return float64(-f)
      	}
      	return float64(f)
      }

      Vertex 타입도 역시 Abser 타입이며 pointer Receiver 이다.

      type Vertex struct {
      	X, Y float64
      }
      
      func (v *Vertex) Abs() float64 {
      	return math.Sqrt(v.X*v.X + v.Y*v.Y)
      }

      main 함수에서 interface 를 활용해 본다.


      a = v 에서 에러가 발생한다.

      func main() {
      	var a Abser
      	f := MyFloat(-math.Sqrt2)
      	v := Vertex{3, 4}
      
      	a = f  // MyFloat 은 Abser 인터페이스를 구현했으니 가능함
      	a = &v // *Vertex 는 Abser 인터페이스를 구현했으니 가능함
      
      	a = v  // v 는 Vertext 타입임 (*Vertex 타입이 아님), value receiver 는 구현안했으니 에러 
      
      	fmt.Println(a.Abs())
      }


      Interface 선언, 활용 (2)

      interface 는 interface 를 포함할 수 있다.


      아래와 같이 Client interface 가 Reader interface 를 가지면 Reader interface 에 선언된 함수가 그 대로 Client 에도 속하게 된다.

      package interfaces
      
      import (
      	"fmt"
      )
      
      type Reader interface {
      	Get(obj string) error
      	List(list []string) error
      }
      
      type Client interface {
      	Reader
      }
      
      var ReconcileClient Client = &client{}
      
      type client struct {
      	name string
      }
      
      func (c *client) Get(obj string) error {
      	fmt.Println(obj)
      	return nil
      }
      
      func (c *client) List(list []string) error {
      	fmt.Println(list)
      	return nil
      }

      그런 다음 struct 에서 interface 를 또 다시 포함할 수 있다.


      아래와 같이 GuestbookReconciler struct 에 Client interface 를 포함하면 GuestbookReconciler 는 마치 Client interface 가 가지는 함수를 자신의 메소드 처럼 사용할 수 있다.

      package main
      
      import "github.com/seungkyua/go-sample/interfaces"
      
      type GuestbookReconciler struct {
      	interfaces.Client
      }
      
      func main() {
      	g := GuestbookReconciler{interfaces.ReconcileClient}
      	g.Get("Seungkyu")
      	g.Client.Get("Seungkyu")
      }

      이 때 메소드 활용은 g.Get 혹은 g.Client.Get 둘 다 가능하다.

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      seungkyua 님의 최신 블로그

      더보기
      동영상 기고하기