만능재주꾼

[Golang] go-training 1-10 본문

💻 Programming/Go

[Golang] go-training 1-10

유고랭 2024. 3. 10. 21:37

🍀 내용 정리

ex01 & ex02

package main

import(
	"fmt"
	"github.com/yugyeongh/cc-5th-go-docker/controller"
)

func main() {
	fmt.Println("hi")

	a := 1
	if a == 1 {
		fmt.Println("a==1")
	}

	fmt.Println(controller.HelloWorld("yugyeong"))

}
package controller

import "fmt"

func HelloWorld(name string) string {
	return fmt.Sprintf("hello, %s", name)
}
  • 소문자로 시작하는 함수는 private, 대문자로 시작하는 함수는 public

ex03

package main

import "fmt"

const (
	a = iota
	b
)

func main() {
	switch a := 1; {
	case a >= 0:
		fmt.Println("a i true")
		fallthrough
	case a > 200:
		fmt.Println("a is false")
	}

	stringSlice1 := []string{"1", "2", "3", "4"} // 슬라이스 만들기
	fmt.Println(stringSlice1[:2])
	stringSlice1 = append(stringSlice1, "6") // 슬라이스에 요소 추가
	fmt.Println(stringSlice1[3:])
}

<aside> ☝🏻 더 알아보기 - fallthrough : case 조건을 계층으로 구성할 때 효과적으로 어떤 값의 레벨에 따라 이전 레벨에서 수행한 작업에 추가 작업을 더 수행해야 할 때 fallthrough를 사용하면 다음 case로 이동해 추가 작업을 할 수 있음

switch i {
case 0: 
	fallthrough
case 1:
	fmt.Println("1")
}

But, 다음 case로 넘겨서 한번에 처리할 때는 fallthrough를 사용하는 것보다 case에 여러 조건을 넣는게 더 낫다

switch i {
case 0,1: 
	fmt.Println("1")
}

</aside>

<aside> ☝🏻 더 알아보기 - pointer : go의 포인터는 값의 메모리 주소를 보유

package main

import "fmt"

func main(){
i,j := 42, 2701

p := &i //i는 피연산자이고, 연산자&를 통해 i값을 참조하는 포인터 p 생성
fmt.Println(*p) //연산자*를 통해 i값 출력
*p = 21 //포인터 p를 통해 i값 변경 (p가 가리키는 주소에 있는 값이 변경)
fmt.Println(i) //result: 21

</aside>

ex04

package main
import (
	"fmt"
	"reflect" // 데이터 타입과 값에 대한 정보를 확인하고 값을 바꿔주는 패키지
)

type Element interface{}
type List []Element

type Person struct {
	name string
	age int
}

func (p Person) String() string { 
	return fmt.Sprintf("I am %s and %d years old", p.name, p.age)
}

func main() {
	list := make(List,4)
	list[0] = 1
	list[1] = "hello" // string
	list[2] = Person{"Jack", 29}
	list[3] = 1.12

	for index, element := range list {
		if value, ok := element.(int); ok {
			fmt.Printf("list[%d] is an int and its value is %d\\n", index, value)
		} else if value, ok := element.(string); ok {
			fmt.Printf("list[%d] is an string and its value is %s\\n", index, value)
		} else if value, ok := element.(Person); ok {
			fmt.Printf("list[%d] is an Person and its value is %v\\n", index, value)
		} else {
			fmt.Println("Unknow type")
		}
	}

	// switch
	for index, element := range list {
		switch value := element.(type) {
		case int:
			fmt.Printf("list[%d] is an int and its value is %d\\n", index, value)
		case string:
			fmt.Printf("list[%d] is a string and its value is %s\\n", index, value)
		case Person:
			fmt.Printf("list[%d] is a Person and its value is %s\\n", index, value)
		default:
			fmt.Printf("list[%d] is of a different type %s\\n", index, reflect.ValueOf(value).Kind())
		}
	}
}
  • Element 인터페이스: 빈 인터페이스
  • List: Element 타입의 슬라이스
  • Person 구조체와 String() 메서드 정의
    • Person 구조체: 이름, 나이 필드를 가지는 구조체
    • String() 메서드: Person 타입에 대한 문자열 표현 반환
      • 표준 라이브러리에 포함되어 있는 인터페이스인 Stringer 인터페이스를 따르기 위한 메서드를 정의하는 것으로, 해당 타입의 값을 문자열로 표현하는데 사용
  • 첫번째 반복문
    • 리스트의 각 요소 정보를 출력
    • value, ok := element.(int); ok : 타입 단언을 사용해 인터페이스 타입을 특정 타입으로 변환하는 방법을 보여줌
      • element를 int 타입으로 변환해 value에 저장
      • ok 변수를 통해 변환의 성공 여부를 확인
        • 변환에 성공하면 ok는 true, value에는 값이 저장
        • 변환에 실패하면 ok는 false, value는 해당 타입의 제로값을 가짐

ex06

package main

import (
	"fmt"
	"sync"
)

func main() {
	wg := sync.WaitGroup{}
	wg.Add(1) // 대기해야 할 고루틴의 수를 추가
	go func() {
		defer wg.Done() // 각각의 고루틴이 작업을 완료할 때마다 done 메서드 호출
		fmt.Println("Let's Go")
	}()
	wg.Wait() // 모든 고루틴이 종료될 때까지 대기
}
  • sync 패키지의 WaitGroup 구조체를 생성하는 코드
  • sync.WaitGroup 구조체의 인스턴스를 생성하고 이를 wg에 할당
  • {}를 통해 생성자 함수 호출하고 구조체의 기본값으로 초기화
  • wg.Add(1): 대기해야 할 고루틴의 수를 추가
  • wg.Done(): 각각의 고루틴이 작업을 완료할 때마다 done 메서드 호출
  • wg.Wait(): 모든 고루틴이 종료될 때까지 대기
package main

import (
	"fmt"
	"sync"
)

func cacl(i int, wg *sync.WaitGroup) {
	defer wg.Done()
	t := 0
	for i := 1; i < 1000000; i++ {
		t++
	}

	fmt.Println(i, t)
}

func main() {
	wg := sync.WaitGroup{}
	wg.Add(10)
	for i := 0; i < 10; i++ {
		go cacl(i, &wg)
	}

	wg.Wait()
	fmt.Println("End!!!!")
}
  • 10개의 고루틴 동시 시작끝나는 시점이 항상 다른 걸 확인할 수 있음
package main

import (
	"fmt"
	"time"
)

func main() {
	msg := "Let's Go"
	go func(msg string) {
		fmt.Println(msg)
	}(msg)
	msg = "Let's GoGoGo"
	time.Sleep(1 * time.Second)
}

  • 고루틴이 실행된 후에는 main 함수에서 msg 값이 변경되어도 고루틴 내부의 msg값은 변경되지 않음

ex07 - error handling

package main

import (
	"errors"
	"fmt"
)

func isEnable(enable bool) (bool, error) {
	if enable {
		return false, errors.New("You can't enable this setting")
	}

	return true, nil
}

func isDisable(disable bool) (bool, error) {
	if disable {
		return false, fmt.Errorf("You can't disable this setting")
	}

	return true, nil
}

func main() {
	_, err := isEnable(true)
	if err != nil {
		fmt.Println(err.Error())
	}

	_, err = isDisable(true)
	if err != nil {
		fmt.Println(err.Error())
	}
}

  • 에러가 없을 때는 err에 nil을 반환

☝🏻 더 알아보기 - errors.New vs fmt.Errorf → 포맷이 필요하면 fmt.Errorf를 사용하고, 포맷이 필요없는 static error message면 errors.New를 사용!

 

ex08 - type assertions

✂️ 타입 단언이란?

  • 인터페이스 값이나 값의 타입을 확인하고 해당 값을 다른 타입으로 변환하는 것
  • 타입 단언은 두 가지 형태로 사용 가능
    1. x.(T): x 라는 변수가 T 타입임을 단언, 만약 x가 T 타입이 아니면 런타임 에러 발생
    2. x.(type): switch 문 안에서 사용되며, switch 문의 case 블록에서 변수 x의 타입을 확인하는데 사용
// 1. x.(T) 예시
var x interface{} = 42
value, ok := x.(int) // x가 int 타입인지 확인 | value에는 x값 저장, int면 true, 아니면 false 저장
if ok {
    fmt.Println("x is an int:", value)
} else {
    fmt.Println("x is not an int")
}
// 2. x.(type) 예시
package main

import (
	"fmt"
)

func main() {
	checkType(42)
	checkType("hello")
	checkType(3.14)
}

func checkType(x interface{}) { // checkType 함수가 인터페이스를 받아 해당 값의 타입 확인 -> 인터페이스는 받은 값의 타입을 동적으로 처리하는데 유용
	switch x := x.(type) { 
	case int:
		fmt.Println("Integer:", x)
	case string:
		fmt.Println("String:", x)
	default:
		fmt.Println("Unknown type")
	}
}

 

package main

import "fmt"

func printValue(v interface{}) {
	switch v := v.(type) {
	case string:
		fmt.Printf("%v is a string\\n", v)
	case int:
		fmt.Printf("%v is a int\\n", v)
	default:
		fmt.Printf("The type of v is unknown\\n")
	}
}

func printValue2(v ...interface{}) {
	for _, v := range v {
		switch v := v.(type) {
		case string:
			fmt.Printf("%v is a string\\n", v)
		case int:
			fmt.Printf("%v is a int\\n", v)
		default:
			fmt.Printf("The type of v is unknown\\n")
		}
	}
}

func main() {
	printValue(10)
	printValue("100")
	printValue(123.01)
	printValue2(1000) // 배열로 넣지 않고 단일 요소만 넣어도 됨
	printValue2(300, "400", 500.01)
}

package main

import (
	"fmt"
	"reflect"
	"strconv"
)

func main() {
	var test = map[string]interface{}{
		"test01": []interface{}{"a", "b"},
		"test02": []int{1, 2},
	}

	oids1 := make([]string, len(test["test01"].([]interface{})))
	for i, v := range test["test01"].([]interface{}) {
		oids1[i] = v.(string)
	}
	fmt.Println(oids1)

	oids2 := make([]string, len(test["test02"].([]int)))
	for i, v := range test["test02"].([]int) {
		oids2[i] = strconv.Itoa(v)
	}
	fmt.Println(oids2)
	fmt.Println(reflect.TypeOf(oids2)) // string

	oids3 := make([]int, len(test["test02"].([]int)))
	for i, v := range test["test02"].([]int) {
		oids3[i] = v
	}
	fmt.Println(oids3)
	fmt.Println(reflect.TypeOf(oids3)) // int
}
  • oids1 설명
    • test라는 맵에서 test01 키에 해당하는 값이 있는지 확인
    • 해당 값이 있으면 그 값을 interface{} 타입으로 얻어오기
    • 얻어온 값을 interface{}로 타입 단언
    • 이를 이용해 슬라이스 oids1을 생성
    • 슬라이스 길이는 test01 키에 대응하는 값의 길이로 설정
    • test[”test01”].([]interface{})의 각 요소를 순회하면서 타입 단언을 통해 해당 값이 문자열인지 확인
    • 해당 값이 문자열이면 이를 oids1 슬라이스에 저장, 아니면 빈 슬라이스로 남기기

☝🏻 더 알아보기 - make → make(T, length) 형식으로 지정 → 예를 들어 m := make(map[string]int, 10)은 초기 버킷 수가 10인 문자열을 키로 가지는 정수형 맵 생성

ex09 - cli tool

cobra

  • golang의 CLI 라이브러리 중 하나
  • go get -u [github.com/spf13/cobra로](http://github.com/spf13/cobra로) 설치 | import "github.com/spf13/cobra로 사용
  • kubectl도 cobra로 만들어졌다는 사실!!
cobra로 cli를 만들어보고 싶습니다!! 무슨 cli를 만들어볼까요!!! -> 

 

ex10 - server

Gin

  • 웹 서버 및 웹 애플리케이션을 개발하기 위한 경량화된 웹 프레임워크
  • go get -u github.com/gin-gonic/gin
// http 적용
package main

import (
	"net/http"

	"github.com/gin-gonic/gin"
)

func main() {
	router := gin.Default()

	router.GET("/", func(c *gin.Context) {
		c.String(http.StatusOK, "Hello world")
	})

	router.GET("/ping", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{"message": "pong"})
	})

	router.GET("/hello", func(c *gin.Context) {
		name := c.Query("name")
		c.String(http.StatusOK, "Hello, %s!", name)
	})

	router.Run(":8080")
}
// https 사용
package main

import (
	"log"
	"net/http"

	"github.com/gin-gonic/gin"
	"golang.org/x/crypto/acme/autocert"
	"golang.org/x/sync/errgroup"
)

func main() {
	router := gin.Default()

	router.GET("/ping", func(c *gin.Context) {
		c.String(200, "pong")
	})

	var g errgroup.Group

	g.Go(func() error {
		return http.ListenAndServe(":http", http.RedirectHandler("<https://naver.com>", 303))
	})

	g.Go(func() error {
		return http.Serve(autocert.NewListener("naver.com"), router)
	})

	if err := g.Wait(); err != nil {
		log.Fatal(err)
	}
}

  • gin.Default() 호출로 기본 Gin 라우터 생성
  • r.GET()을 통해 엔드포인트 지정
  • errgroup.Group을 사용해 여러 고루틴에서 실행되는 서버 관리
  • g.Go()를 사용해 각각의 고루틴 시작
    • 첫번째 고루틴은 http 서버를 http 요청을 https로 리디렉션하는데 사용
    • 두번째 고루틴은 Let’s Encrypt의 autocert 패키지를 사용해 https 서버를 시작
  • g.Wait()를 호출해 모든 고루틴이 완료될때까지 기다림

🔥 Trouble Shooting

 

📍 package ... is not in GOROOT 오류

  1. 원인
    1. go 컴파일러가 특정 패키지를 goroot 환경변수로 지정된 go 설치 디렉토리 내에서 찾을 수 없을 때 발생
    2. gopath 설정 문제: 패키지가 gopath 경로에 없는 경우에 발생할 수 있는 오류로 해당 패키지가 gopath 경로에 있는지 확인해야 함
    3. 패키지 이름 오타
    4. 등등
  2. 해결
    1. go mod init github.com/yugyeongh/cc-5th-go-docker
      1. go 프로젝트의 모듈을 초기화하는 명령
      2. 현재 디렉토리에 대한 모듈을 만들 수 있음 
      3. module github.com/yugyeongh/cc-5th-go-docker go 1.20
      4. → go.mod 파일 생성 (종속성 요구 사항이 정의된 파일로 모듈 경로와 특정 버전으로 작성)
      5. 이 모듈은 go프로젝트의 의존성을 관리하고 외부 패키지를 가져오는데 사용
    2. go mod tidy
      1. go 모듈에서 사용되는 의존성 그래프 정리 및 최적화

💡 References

  • go 모듈 사용
  • switch문에서 fallthrough의 쓰임
  • golang의 포인터
  • cobra가 뭔가요
  • cobra로 만드는 cli
    • https://velog.io/@kineo2k/Cobra로-간단한-CLI-만들기</aside>
      package main
      
      import(
      	"fmt"
      	"github.com/yugyeongh/cc-5th-go-docker/controller"
      )
      
      func main() {
      	fmt.Println("hi")
      
      	a := 1
      	if a == 1 {
      		fmt.Println("a==1")
      	}
      
      	fmt.Println(controller.HelloWorld("yugyeong"))
      
      }
      
      package controller
      
      import "fmt"
      
      func HelloWorld(name string) string {
      	return fmt.Sprintf("hello, %s", name)
      }
      
      • 소문자로 시작하는 함수는 private, 대문자로 시작하는 함수는 public
      ex03 ☝🏻 더 알아보기 - fallthrough : case 조건을 계층으로 구성할 때 효과적으로 어떤 값의 레벨에 따라 이전 레벨에서 수행한 작업에 추가 작업을 더 수행해야 할 때 fallthrough를 사용하면 다음 case로 이동해 추가 작업을 할 수 있음But, 다음 case로 넘겨서 한번에 처리할 때는 fallthrough를 사용하는 것보다 case에 여러 조건을 넣는게 더 낫다</aside>
      package main
      
      import "fmt"
      
      func main(){
      i,j := 42, 2701
      
      p := &i //i는 피연산자이고, 연산자&를 통해 i값을 참조하는 포인터 p 생성
      fmt.Println(*p) //연산자*를 통해 i값 출력
      *p = 21 //포인터 p를 통해 i값 변경 (p가 가리키는 주소에 있는 값이 변경)
      fmt.Println(i) //result: 21
      
      package main
      import (
      	"fmt"
      	"reflect" // 데이터 타입과 값에 대한 정보를 확인하고 값을 바꿔주는 패키지
      )
      
      type Element interface{}
      type List []Element
      
      type Person struct {
      	name string
      	age int
      }
      
      func (p Person) String() string { 
      	return fmt.Sprintf("I am %s and %d years old", p.name, p.age)
      }
      
      func main() {
      	list := make(List,4)
      	list[0] = 1
      	list[1] = "hello" // string
      	list[2] = Person{"Jack", 29}
      	list[3] = 1.12
      
      	for index, element := range list {
      		if value, ok := element.(int); ok {
      			fmt.Printf("list[%d] is an int and its value is %d\\n", index, value)
      		} else if value, ok := element.(string); ok {
      			fmt.Printf("list[%d] is an string and its value is %s\\n", index, value)
      		} else if value, ok := element.(Person); ok {
      			fmt.Printf("list[%d] is an Person and its value is %v\\n", index, value)
      		} else {
      			fmt.Println("Unknow type")
      		}
      	}
      
      	// switch
      	for index, element := range list {
      		switch value := element.(type) {
      		case int:
      			fmt.Printf("list[%d] is an int and its value is %d\\n", index, value)
      		case string:
      			fmt.Printf("list[%d] is a string and its value is %s\\n", index, value)
      		case Person:
      			fmt.Printf("list[%d] is a Person and its value is %s\\n", index, value)
      		default:
      			fmt.Printf("list[%d] is of a different type %s\\n", index, reflect.ValueOf(value).Kind())
      		}
      	}
      }
      
      • Element 인터페이스: 빈 인터페이스
      • List: Element 타입의 슬라이스
      • Person 구조체와 String() 메서드 정의
        • Person 구조체: 이름, 나이 필드를 가지는 구조체
        • String() 메서드: Person 타입에 대한 문자열 표현 반환
          • 표준 라이브러리에 포함되어 있는 인터페이스인 Stringer 인터페이스를 따르기 위한 메서드를 정의하는 것으로, 해당 타입의 값을 문자열로 표현하는데 사용
      • 첫번째 반복문
        • 리스트의 각 요소 정보를 출력
        • value, ok := element.(int); ok : 타입 단언을 사용해 인터페이스 타입을 특정 타입으로 변환하는 방법을 보여줌
          • element를 int 타입으로 변환해 value에 저장
          • ok 변수를 통해 변환의 성공 여부를 확인
            • 변환에 성공하면 ok는 true, value에는 값이 저장
            • 변환에 실패하면 ok는 false, value는 해당 타입의 제로값을 가짐
      ex06
      • sync 패키지의 WaitGroup 구조체를 생성하는 코드
      • sync.WaitGroup 구조체의 인스턴스를 생성하고 이를 wg에 할당
      • {}를 통해 생성자 함수 호출하고 구조체의 기본값으로 초기화
      • wg.Add(1): 대기해야 할 고루틴의 수를 추가
      • wg.Done(): 각각의 고루틴이 작업을 완료할 때마다 done 메서드 호출
      • wg.Wait(): 모든 고루틴이 종료될 때까지 대기
      package main
      
      import (
      	"fmt"
      	"sync"
      )
      
      func cacl(i int, wg *sync.WaitGroup) {
      	defer wg.Done()
      	t := 0
      	for i := 1; i < 1000000; i++ {
      		t++
      	}
      
      	fmt.Println(i, t)
      }
      
      func main() {
      	wg := sync.WaitGroup{}
      	wg.Add(10)
      	for i := 0; i < 10; i++ {
      		go cacl(i, &wg)
      	}
      
      	wg.Wait()
      	fmt.Println("End!!!!")
      }
      
      • 10개의 고루틴 동시 시작끝나는 시점이 항상 다른 걸 확인할 수 있음
      package main
      
      import (
      	"fmt"
      	"time"
      )
      
      func main() {
      	msg := "Let's Go"
      	go func(msg string) {
      		fmt.Println(msg)
      	}(msg)
      	msg = "Let's GoGoGo"
      	time.Sleep(1 * time.Second)
      }
      
      
      • 고루틴이 실행된 후에는 main 함수에서 msg 값이 변경되어도 고루틴 내부의 msg값은 변경되지 않음
      ex07 - error handling
      • 에러가 없을 때는 err에 nil을 반환
      <aside> ☝🏻 더 알아보기 - errors.New vs fmt.Errorf → 포맷이 필요하면 fmt.Errorf를 사용하고, 포맷이 필요없는 static error message면 errors.New를 사용!ex08 - type assertions
      • 인터페이스 값이나 값의 타입을 확인하고 해당 값을 다른 타입으로 변환하는 것
      • 타입 단언은 두 가지 형태로 사용 가능
        1. x.(T): x 라는 변수가 T 타입임을 단언, 만약 x가 T 타입이 아니면 런타임 에러 발생
        2. x.(type): switch 문 안에서 사용되며, switch 문의 case 블록에서 변수 x의 타입을 확인하는데 사용
      // 1. x.(T) 예시
      var x interface{} = 42
      value, ok := x.(int) // x가 int 타입인지 확인 | value에는 x값 저장, int면 true, 아니면 false 저장
      if ok {
          fmt.Println("x is an int:", value)
      } else {
          fmt.Println("x is not an int")
      }
      
      // 2. x.(type) 예시
      package main
      
      import (
      	"fmt"
      )
      
      func main() {
      	checkType(42)
      	checkType("hello")
      	checkType(3.14)
      }
      
      func checkType(x interface{}) { // checkType 함수가 인터페이스를 받아 해당 값의 타입 확인 -> 인터페이스는 받은 값의 타입을 동적으로 처리하는데 유용
      	switch x := x.(type) { 
      	case int:
      		fmt.Println("Integer:", x)
      	case string:
      		fmt.Println("String:", x)
      	default:
      		fmt.Println("Unknown type")
      	}
      }
      
      </aside>
      package main
      
      import (
      	"fmt"
      	"reflect"
      	"strconv"
      )
      
      func main() {
      	var test = map[string]interface{}{
      		"test01": []interface{}{"a", "b"},
      		"test02": []int{1, 2},
      	}
      
      	oids1 := make([]string, len(test["test01"].([]interface{})))
      	for i, v := range test["test01"].([]interface{}) {
      		oids1[i] = v.(string)
      	}
      	fmt.Println(oids1)
      
      	oids2 := make([]string, len(test["test02"].([]int)))
      	for i, v := range test["test02"].([]int) {
      		oids2[i] = strconv.Itoa(v)
      	}
      	fmt.Println(oids2)
      	fmt.Println(reflect.TypeOf(oids2)) // string
      
      	oids3 := make([]int, len(test["test02"].([]int)))
      	for i, v := range test["test02"].([]int) {
      		oids3[i] = v
      	}
      	fmt.Println(oids3)
      	fmt.Println(reflect.TypeOf(oids3)) // int
      }
      
      • oids1 설명
        • test라는 맵에서 test01 키에 해당하는 값이 있는지 확인
        • 해당 값이 있으면 그 값을 interface{} 타입으로 얻어오기
        • 얻어온 값을 interface{}로 타입 단언
        • 이를 이용해 슬라이스 oids1을 생성
        • 슬라이스 길이는 test01 키에 대응하는 값의 길이로 설정
        • test[”test01”].([]interface{})의 각 요소를 순회하면서 타입 단언을 통해 해당 값이 문자열인지 확인
        • 해당 값이 문자열이면 이를 oids1 슬라이스에 저장, 아니면 빈 슬라이스로 남기기
      <aside> ☝🏻 더 알아보기 - make → make(T, length) 형식으로 지정 → 예를 들어 m := make(map[string]int, 10)은 초기 버킷 수가 10인 문자열을 키로 가지는 정수형 맵 생성ex09 - cli tool
      • golang의 CLI 라이브러리 중 하나
      • go get -u [github.com/spf13/cobra로](http://github.com/spf13/cobra로) 설치 | import "github.com/spf13/cobra로 사용
      • kubectl도 cobra로 만들어졌다는 사실!!
      cobra로 cli를 만들어보고 싶습니다!! 무슨 cli를 만들어볼까요!!! -> 
      
      ex10 - server
      • 웹 서버 및 웹 애플리케이션을 개발하기 위한 경량화된 웹 프레임워크
      • go get -u github.com/gin-gonic/gin
      // http 적용
      package main
      
      import (
      	"net/http"
      
      	"github.com/gin-gonic/gin"
      )
      
      func main() {
      	router := gin.Default()
      
      	router.GET("/", func(c *gin.Context) {
      		c.String(http.StatusOK, "Hello world")
      	})
      
      	router.GET("/ping", func(c *gin.Context) {
      		c.JSON(http.StatusOK, gin.H{"message": "pong"})
      	})
      
      	router.GET("/hello", func(c *gin.Context) {
      		name := c.Query("name")
      		c.String(http.StatusOK, "Hello, %s!", name)
      	})
      
      	router.Run(":8080")
      }
      
      • gin.Default() 호출로 기본 Gin 라우터 생성
      • r.GET()을 통해 엔드포인트 지정
      • errgroup.Group을 사용해 여러 고루틴에서 실행되는 서버 관리
      • g.Go()를 사용해 각각의 고루틴 시작
        • 첫번째 고루틴은 http 서버를 http 요청을 https로 리디렉션하는데 사용
        • 두번째 고루틴은 Let’s Encrypt의 autocert 패키지를 사용해 https 서버를 시작
      • g.Wait()를 호출해 모든 고루틴이 완료될때까지 기다림
      <aside> 🔥 Trouble Shooting📍 package ... is not in GOROOT 오류
      1. 원인
        1. go 컴파일러가 특정 패키지를 goroot 환경변수로 지정된 go 설치 디렉토리 내에서 찾을 수 없을 때 발생
        2. gopath 설정 문제: 패키지가 gopath 경로에 없는 경우에 발생할 수 있는 오류로 해당 패키지가 gopath 경로에 있는지 확인해야 함
        3. 패키지 이름 오타
        4. 등등
      2. 해결
        1. go mod init github.com/yugyeongh/cc-5th-go-docker
          1. go 프로젝트의 모듈을 초기화하는 명령
          2. 현재 디렉토리에 대한 모듈을 만들 수 있음
            module github.com/yugyeongh/cc-5th-go-docker
            
            go 1.20
            
          3. → go.mod 파일 생성 (종속성 요구 사항이 정의된 파일로 모듈 경로와 특정 버전으로 작성)
          4. 이 모듈은 go프로젝트의 의존성을 관리하고 외부 패키지를 가져오는데 사용
        2. go mod tidy
          1. go 모듈에서 사용되는 의존성 그래프 정리 및 최적화
      <aside> 💡 References
      • go 모듈 사용
      • switch문에서 fallthrough의 쓰임
      • golang의 포인터
      • cobra가 뭔가요
      • cobra로 만드는 cli
        • https://velog.io/@kineo2k/Cobra로-간단한-CLI-만들기</aside>
          package main
          
          import(
          	"fmt"
          	"github.com/yugyeongh/cc-5th-go-docker/controller"
          )
          
          func main() {
          	fmt.Println("hi")
          
          	a := 1
          	if a == 1 {
          		fmt.Println("a==1")
          	}
          
          	fmt.Println(controller.HelloWorld("yugyeong"))
          
          }
          
          package controller
          
          import "fmt"
          
          func HelloWorld(name string) string {
          	return fmt.Sprintf("hello, %s", name)
          }
          
          • 소문자로 시작하는 함수는 private, 대문자로 시작하는 함수는 public
          ex03<aside> ☝🏻 더 알아보기 - fallthrough : case 조건을 계층으로 구성할 때 효과적으로 어떤 값의 레벨에 따라 이전 레벨에서 수행한 작업에 추가 작업을 더 수행해야 할 때 fallthrough를 사용하면 다음 case로 이동해 추가 작업을 할 수 있음But, 다음 case로 넘겨서 한번에 처리할 때는 fallthrough를 사용하는 것보다 case에 여러 조건을 넣는게 더 낫다</aside>
          package main
          
          import "fmt"
          
          func main(){
          i,j := 42, 2701
          
          p := &i //i는 피연산자이고, 연산자&를 통해 i값을 참조하는 포인터 p 생성
          fmt.Println(*p) //연산자*를 통해 i값 출력
          *p = 21 //포인터 p를 통해 i값 변경 (p가 가리키는 주소에 있는 값이 변경)
          fmt.Println(i) //result: 21
          
          </aside>
          package main
          import (
          	"fmt"
          	"reflect" // 데이터 타입과 값에 대한 정보를 확인하고 값을 바꿔주는 패키지
          )
          
          type Element interface{}
          type List []Element
          
          type Person struct {
          	name string
          	age int
          }
          
          func (p Person) String() string { 
          	return fmt.Sprintf("I am %s and %d years old", p.name, p.age)
          }
          
          func main() {
          	list := make(List,4)
          	list[0] = 1
          	list[1] = "hello" // string
          	list[2] = Person{"Jack", 29}
          	list[3] = 1.12
          
          	for index, element := range list {
          		if value, ok := element.(int); ok {
          			fmt.Printf("list[%d] is an int and its value is %d\\n", index, value)
          		} else if value, ok := element.(string); ok {
          			fmt.Printf("list[%d] is an string and its value is %s\\n", index, value)
          		} else if value, ok := element.(Person); ok {
          			fmt.Printf("list[%d] is an Person and its value is %v\\n", index, value)
          		} else {
          			fmt.Println("Unknow type")
          		}
          	}
          
          	// switch
          	for index, element := range list {
          		switch value := element.(type) {
          		case int:
          			fmt.Printf("list[%d] is an int and its value is %d\\n", index, value)
          		case string:
          			fmt.Printf("list[%d] is a string and its value is %s\\n", index, value)
          		case Person:
          			fmt.Printf("list[%d] is a Person and its value is %s\\n", index, value)
          		default:
          			fmt.Printf("list[%d] is of a different type %s\\n", index, reflect.ValueOf(value).Kind())
          		}
          	}
          }
          
          • Element 인터페이스: 빈 인터페이스
          • List: Element 타입의 슬라이스
          • Person 구조체와 String() 메서드 정의
            • Person 구조체: 이름, 나이 필드를 가지는 구조체
            • String() 메서드: Person 타입에 대한 문자열 표현 반환
              • 표준 라이브러리에 포함되어 있는 인터페이스인 Stringer 인터페이스를 따르기 위한 메서드를 정의하는 것으로, 해당 타입의 값을 문자열로 표현하는데 사용
          • 첫번째 반복문
            • 리스트의 각 요소 정보를 출력
            • value, ok := element.(int); ok : 타입 단언을 사용해 인터페이스 타입을 특정 타입으로 변환하는 방법을 보여줌
              • element를 int 타입으로 변환해 value에 저장
              • ok 변수를 통해 변환의 성공 여부를 확인
                • 변환에 성공하면 ok는 true, value에는 값이 저장
                • 변환에 실패하면 ok는 false, value는 해당 타입의 제로값을 가짐
          ex06
          • sync 패키지의 WaitGroup 구조체를 생성하는 코드
          • sync.WaitGroup 구조체의 인스턴스를 생성하고 이를 wg에 할당
          • {}를 통해 생성자 함수 호출하고 구조체의 기본값으로 초기화
          • wg.Add(1): 대기해야 할 고루틴의 수를 추가
          • wg.Done(): 각각의 고루틴이 작업을 완료할 때마다 done 메서드 호출
          • wg.Wait(): 모든 고루틴이 종료될 때까지 대기
          package main
          
          import (
          	"fmt"
          	"sync"
          )
          
          func cacl(i int, wg *sync.WaitGroup) {
          	defer wg.Done()
          	t := 0
          	for i := 1; i < 1000000; i++ {
          		t++
          	}
          
          	fmt.Println(i, t)
          }
          
          func main() {
          	wg := sync.WaitGroup{}
          	wg.Add(10)
          	for i := 0; i < 10; i++ {
          		go cacl(i, &wg)
          	}
          
          	wg.Wait()
          	fmt.Println("End!!!!")
          }
          
          • 10개의 고루틴 동시 시작끝나는 시점이 항상 다른 걸 확인할 수 있음
          package main
          
          import (
          	"fmt"
          	"time"
          )
          
          func main() {
          	msg := "Let's Go"
          	go func(msg string) {
          		fmt.Println(msg)
          	}(msg)
          	msg = "Let's GoGoGo"
          	time.Sleep(1 * time.Second)
          }
          
          
          • 고루틴이 실행된 후에는 main 함수에서 msg 값이 변경되어도 고루틴 내부의 msg값은 변경되지 않음
          ex07 - error handling
          • 에러가 없을 때는 err에 nil을 반환
          <aside> ☝🏻 더 알아보기 - errors.New vs fmt.Errorf → 포맷이 필요하면 fmt.Errorf를 사용하고, 포맷이 필요없는 static error message면 errors.New를 사용!ex08 - type assertions
          • 인터페이스 값이나 값의 타입을 확인하고 해당 값을 다른 타입으로 변환하는 것
          • 타입 단언은 두 가지 형태로 사용 가능
            1. x.(T): x 라는 변수가 T 타입임을 단언, 만약 x가 T 타입이 아니면 런타임 에러 발생
            2. x.(type): switch 문 안에서 사용되며, switch 문의 case 블록에서 변수 x의 타입을 확인하는데 사용
          // 1. x.(T) 예시
          var x interface{} = 42
          value, ok := x.(int) // x가 int 타입인지 확인 | value에는 x값 저장, int면 true, 아니면 false 저장
          if ok {
              fmt.Println("x is an int:", value)
          } else {
              fmt.Println("x is not an int")
          }
          
          // 2. x.(type) 예시
          package main
          
          import (
          	"fmt"
          )
          
          func main() {
          	checkType(42)
          	checkType("hello")
          	checkType(3.14)
          }
          
          func checkType(x interface{}) { // checkType 함수가 인터페이스를 받아 해당 값의 타입 확인 -> 인터페이스는 받은 값의 타입을 동적으로 처리하는데 유용
          	switch x := x.(type) { 
          	case int:
          		fmt.Println("Integer:", x)
          	case string:
          		fmt.Println("String:", x)
          	default:
          		fmt.Println("Unknown type")
          	}
          }
          
          </aside>
          package main
          
          import (
          	"fmt"
          	"reflect"
          	"strconv"
          )
          
          func main() {
          	var test = map[string]interface{}{
          		"test01": []interface{}{"a", "b"},
          		"test02": []int{1, 2},
          	}
          
          	oids1 := make([]string, len(test["test01"].([]interface{})))
          	for i, v := range test["test01"].([]interface{}) {
          		oids1[i] = v.(string)
          	}
          	fmt.Println(oids1)
          
          	oids2 := make([]string, len(test["test02"].([]int)))
          	for i, v := range test["test02"].([]int) {
          		oids2[i] = strconv.Itoa(v)
          	}
          	fmt.Println(oids2)
          	fmt.Println(reflect.TypeOf(oids2)) // string
          
          	oids3 := make([]int, len(test["test02"].([]int)))
          	for i, v := range test["test02"].([]int) {
          		oids3[i] = v
          	}
          	fmt.Println(oids3)
          	fmt.Println(reflect.TypeOf(oids3)) // int
          }
          
          • oids1 설명
            • test라는 맵에서 test01 키에 해당하는 값이 있는지 확인
            • 해당 값이 있으면 그 값을 interface{} 타입으로 얻어오기
            • 얻어온 값을 interface{}로 타입 단언
            • 이를 이용해 슬라이스 oids1을 생성
            • 슬라이스 길이는 test01 키에 대응하는 값의 길이로 설정
            • test[”test01”].([]interface{})의 각 요소를 순회하면서 타입 단언을 통해 해당 값이 문자열인지 확인
            • 해당 값이 문자열이면 이를 oids1 슬라이스에 저장, 아니면 빈 슬라이스로 남기기
          <aside> ☝🏻 더 알아보기 - make → make(T, length) 형식으로 지정 → 예를 들어 m := make(map[string]int, 10)은 초기 버킷 수가 10인 문자열을 키로 가지는 정수형 맵 생성ex09 - cli tool
          • golang의 CLI 라이브러리 중 하나
          • go get -u [github.com/spf13/cobra로](http://github.com/spf13/cobra로) 설치 | import "github.com/spf13/cobra로 사용
          • kubectl도 cobra로 만들어졌다는 사실!!
          cobra로 cli를 만들어보고 싶습니다!! 무슨 cli를 만들어볼까요!!! -> 
          
          ex10 - server
          • 웹 서버 및 웹 애플리케이션을 개발하기 위한 경량화된 웹 프레임워크
          • go get -u github.com/gin-gonic/gin
          // http 적용
          package main
          
          import (
          	"net/http"
          
          	"github.com/gin-gonic/gin"
          )
          
          func main() {
          	router := gin.Default()
          
          	router.GET("/", func(c *gin.Context) {
          		c.String(http.StatusOK, "Hello world")
          	})
          
          	router.GET("/ping", func(c *gin.Context) {
          		c.JSON(http.StatusOK, gin.H{"message": "pong"})
          	})
          
          	router.GET("/hello", func(c *gin.Context) {
          		name := c.Query("name")
          		c.String(http.StatusOK, "Hello, %s!", name)
          	})
          
          	router.Run(":8080")
          }
          
          • gin.Default() 호출로 기본 Gin 라우터 생성
          • r.GET()을 통해 엔드포인트 지정
          • errgroup.Group을 사용해 여러 고루틴에서 실행되는 서버 관리
          • g.Go()를 사용해 각각의 고루틴 시작
            • 첫번째 고루틴은 http 서버를 http 요청을 https로 리디렉션하는데 사용
            • 두번째 고루틴은 Let’s Encrypt의 autocert 패키지를 사용해 https 서버를 시작
          • g.Wait()를 호출해 모든 고루틴이 완료될때까지 기다림
          <aside> 🔥 Trouble Shooting📍 package ... is not in GOROOT 오류
          1. 원인
            1. go 컴파일러가 특정 패키지를 goroot 환경변수로 지정된 go 설치 디렉토리 내에서 찾을 수 없을 때 발생
            2. gopath 설정 문제: 패키지가 gopath 경로에 없는 경우에 발생할 수 있는 오류로 해당 패키지가 gopath 경로에 있는지 확인해야 함
            3. 패키지 이름 오타
            4. 등등
          2. 해결
            1. go mod init github.com/yugyeongh/cc-5th-go-docker
              1. go 프로젝트의 모듈을 초기화하는 명령
              2. 현재 디렉토리에 대한 모듈을 만들 수 있음
                module github.com/yugyeongh/cc-5th-go-docker
                
                go 1.20
                
              3. → go.mod 파일 생성 (종속성 요구 사항이 정의된 파일로 모듈 경로와 특정 버전으로 작성)
              4. 이 모듈은 go프로젝트의 의존성을 관리하고 외부 패키지를 가져오는데 사용
            2. go mod tidy
              1. go 모듈에서 사용되는 의존성 그래프 정리 및 최적화
          <aside> 💡 References
        • </aside>
        • </aside>
        • // https 사용 package main import ( "log" "net/http" "github.com/gin-gonic/gin" "golang.org/x/crypto/acme/autocert" "golang.org/x/sync/errgroup" ) func main() { router := gin.Default() router.GET("/ping", func(c *gin.Context) { c.String(200, "pong") }) var g errgroup.Group g.Go(func() error { return http.ListenAndServe(":http", http.RedirectHandler("<https://naver.com>", 303)) }) g.Go(func() error { return http.Serve(autocert.NewListener("naver.com"), router) }) if err := g.Wait(); err != nil { log.Fatal(err) } }
        • Gin
        • cobra
        • </aside>
        • package main import "fmt" func printValue(v interface{}) { switch v := v.(type) { case string: fmt.Printf("%v is a string\\n", v) case int: fmt.Printf("%v is a int\\n", v) default: fmt.Printf("The type of v is unknown\\n") } } func printValue2(v ...interface{}) { for _, v := range v { switch v := v.(type) { case string: fmt.Printf("%v is a string\\n", v) case int: fmt.Printf("%v is a int\\n", v) default: fmt.Printf("The type of v is unknown\\n") } } } func main() { printValue(10) printValue("100") printValue(123.01) printValue2(1000) // 배열로 넣지 않고 단일 요소만 넣어도 됨 printValue2(300, "400", 500.01) }
        • <aside> ✂️ 타입 단언이란?
        • </aside>
        • package main import ( "errors" "fmt" ) func isEnable(enable bool) (bool, error) { if enable { return false, errors.New("You can't enable this setting") } return true, nil } func isDisable(disable bool) (bool, error) { if disable { return false, fmt.Errorf("You can't disable this setting") } return true, nil } func main() { _, err := isEnable(true) if err != nil { fmt.Println(err.Error()) } _, err = isDisable(true) if err != nil { fmt.Println(err.Error()) } }
        • package main import ( "fmt" "sync" ) func main() { wg := sync.WaitGroup{} wg.Add(1) // 대기해야 할 고루틴의 수를 추가 go func() { defer wg.Done() // 각각의 고루틴이 작업을 완료할 때마다 done 메서드 호출 fmt.Println("Let's Go") }() wg.Wait() // 모든 고루틴이 종료될 때까지 대기 }
        • ex04
        • <aside> ☝🏻 더 알아보기 - pointer : go의 포인터는 값의 메모리 주소를 보유
        • switch i { case 0,1: fmt.Println("1") }
        • switch i { case 0: fallthrough case 1: fmt.Println("1") }
        • package main import "fmt" const ( a = iota b ) func main() { switch a := 1; { case a >= 0: fmt.Println("a i true") fallthrough case a > 200: fmt.Println("a is false") } stringSlice1 := []string{"1", "2", "3", "4"} // 슬라이스 만들기 fmt.Println(stringSlice1[:2]) stringSlice1 = append(stringSlice1, "6") // 슬라이스에 요소 추가 fmt.Println(stringSlice1[3:]) }
        • ex01 & ex02
    • </aside>
    • </aside>
    • // https 사용 package main import ( "log" "net/http" "github.com/gin-gonic/gin" "golang.org/x/crypto/acme/autocert" "golang.org/x/sync/errgroup" ) func main() { router := gin.Default() router.GET("/ping", func(c *gin.Context) { c.String(200, "pong") }) var g errgroup.Group g.Go(func() error { return http.ListenAndServe(":http", http.RedirectHandler("<https://naver.com>", 303)) }) g.Go(func() error { return http.Serve(autocert.NewListener("naver.com"), router) }) if err := g.Wait(); err != nil { log.Fatal(err) } }
    • Gin
    • cobra
    • </aside>
    • package main import "fmt" func printValue(v interface{}) { switch v := v.(type) { case string: fmt.Printf("%v is a string\\n", v) case int: fmt.Printf("%v is a int\\n", v) default: fmt.Printf("The type of v is unknown\\n") } } func printValue2(v ...interface{}) { for _, v := range v { switch v := v.(type) { case string: fmt.Printf("%v is a string\\n", v) case int: fmt.Printf("%v is a int\\n", v) default: fmt.Printf("The type of v is unknown\\n") } } } func main() { printValue(10) printValue("100") printValue(123.01) printValue2(1000) // 배열로 넣지 않고 단일 요소만 넣어도 됨 printValue2(300, "400", 500.01) }
    • <aside> ✂️ 타입 단언이란?
    • </aside>
    • package main import ( "errors" "fmt" ) func isEnable(enable bool) (bool, error) { if enable { return false, errors.New("You can't enable this setting") } return true, nil } func isDisable(disable bool) (bool, error) { if disable { return false, fmt.Errorf("You can't disable this setting") } return true, nil } func main() { _, err := isEnable(true) if err != nil { fmt.Println(err.Error()) } _, err = isDisable(true) if err != nil { fmt.Println(err.Error()) } }
    • package main import ( "fmt" "sync" ) func main() { wg := sync.WaitGroup{} wg.Add(1) // 대기해야 할 고루틴의 수를 추가 go func() { defer wg.Done() // 각각의 고루틴이 작업을 완료할 때마다 done 메서드 호출 fmt.Println("Let's Go") }() wg.Wait() // 모든 고루틴이 종료될 때까지 대기 }
    • ex04
    • <aside> ☝🏻 더 알아보기 - pointer : go의 포인터는 값의 메모리 주소를 보유
    • switch i { case 0,1: fmt.Println("1") }
    • switch i { case 0: fallthrough case 1: fmt.Println("1") }
    • package main import "fmt" const ( a = iota b ) func main() { switch a := 1; { case a >= 0: fmt.Println("a i true") fallthrough case a > 200: fmt.Println("a is false") } stringSlice1 := []string{"1", "2", "3", "4"} // 슬라이스 만들기 fmt.Println(stringSlice1[:2]) stringSlice1 = append(stringSlice1, "6") // 슬라이스에 요소 추가 fmt.Println(stringSlice1[3:]) }
    • ex01 & ex02

'💻 Programming > Go' 카테고리의 다른 글

[golang] .env 파일 이용해 환경변수 설정하기  (0) 2024.02.13
Comments