Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 | 31 |
Tags
- go .env
- 람다 함수 이름 변경
- C++ 시간 초과
- 팔로잉 끊기 구현
- godotenv
- 백준 알고리즘
- node.js 교과서
- 깃허브 코드 업로드
- 출력형식 오류 해결
- 깃허브 clone
- 깃허브 pull
- 열혈 C++
- 깃허브 협업
- 깃허브
- C++ Fast I/O
- GitHub 업로드
- 깃허브 복제
- C++
- 백준 2443
- 라우팅 연결하기
- aws lambda 함수
- node.js
- http 모듈
- Node.js교과서
- go 환경변수
- 백준
- github pull
- C++ 입출력
- 람다 함수 이름 변경 안됨
- lambda 이름 변경
Archives
- Today
- Total
만능재주꾼
[Golang] go-training 1-10 본문
🍀 내용 정리
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
✂️ 타입 단언이란?
- 인터페이스 값이나 값의 타입을 확인하고 해당 값을 다른 타입으로 변환하는 것
- 타입 단언은 두 가지 형태로 사용 가능
- x.(T): x 라는 변수가 T 타입임을 단언, 만약 x가 T 타입이 아니면 런타임 에러 발생
- 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 오류
- 원인
- go 컴파일러가 특정 패키지를 goroot 환경변수로 지정된 go 설치 디렉토리 내에서 찾을 수 없을 때 발생
- gopath 설정 문제: 패키지가 gopath 경로에 없는 경우에 발생할 수 있는 오류로 해당 패키지가 gopath 경로에 있는지 확인해야 함
- 패키지 이름 오타
- 등등
- 해결
- go mod init github.com/yugyeongh/cc-5th-go-docker
- go 프로젝트의 모듈을 초기화하는 명령
- 현재 디렉토리에 대한 모듈을 만들 수 있음
module github.com/yugyeongh/cc-5th-go-docker go 1.20
- → go.mod 파일 생성 (종속성 요구 사항이 정의된 파일로 모듈 경로와 특정 버전으로 작성)
- 이 모듈은 go프로젝트의 의존성을 관리하고 외부 패키지를 가져오는데 사용
- go mod tidy
- go 모듈에서 사용되는 의존성 그래프 정리 및 최적화
- go mod init github.com/yugyeongh/cc-5th-go-docker
💡 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
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는 해당 타입의 제로값을 가짐
- 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값은 변경되지 않음
- 에러가 없을 때는 err에 nil을 반환
- 인터페이스 값이나 값의 타입을 확인하고 해당 값을 다른 타입으로 변환하는 것
- 타입 단언은 두 가지 형태로 사용 가능
- x.(T): x 라는 변수가 T 타입임을 단언, 만약 x가 T 타입이 아니면 런타임 에러 발생
- 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") }
</aside>// 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" "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 슬라이스에 저장, 아니면 빈 슬라이스로 남기기
- golang의 CLI 라이브러리 중 하나
- go get -u [github.com/spf13/cobra로](http://github.com/spf13/cobra로) 설치 | import "github.com/spf13/cobra로 사용
- kubectl도 cobra로 만들어졌다는 사실!!
ex10 - servercobra로 cli를 만들어보고 싶습니다!! 무슨 cli를 만들어볼까요!!! ->
- 웹 서버 및 웹 애플리케이션을 개발하기 위한 경량화된 웹 프레임워크
- 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()를 호출해 모든 고루틴이 완료될때까지 기다림
- 원인
- go 컴파일러가 특정 패키지를 goroot 환경변수로 지정된 go 설치 디렉토리 내에서 찾을 수 없을 때 발생
- gopath 설정 문제: 패키지가 gopath 경로에 없는 경우에 발생할 수 있는 오류로 해당 패키지가 gopath 경로에 있는지 확인해야 함
- 패키지 이름 오타
- 등등
- 해결
- go mod init github.com/yugyeongh/cc-5th-go-docker
- go 프로젝트의 모듈을 초기화하는 명령
- 현재 디렉토리에 대한 모듈을 만들 수 있음
module github.com/yugyeongh/cc-5th-go-docker go 1.20
- → go.mod 파일 생성 (종속성 요구 사항이 정의된 파일로 모듈 경로와 특정 버전으로 작성)
- 이 모듈은 go프로젝트의 의존성을 관리하고 외부 패키지를 가져오는데 사용
- go mod tidy
- go 모듈에서 사용되는 의존성 그래프 정리 및 최적화
- go mod init github.com/yugyeongh/cc-5th-go-docker
- 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
</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는 해당 타입의 제로값을 가짐
- 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값은 변경되지 않음
- 에러가 없을 때는 err에 nil을 반환
- 인터페이스 값이나 값의 타입을 확인하고 해당 값을 다른 타입으로 변환하는 것
- 타입 단언은 두 가지 형태로 사용 가능
- x.(T): x 라는 변수가 T 타입임을 단언, 만약 x가 T 타입이 아니면 런타임 에러 발생
- 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") }
</aside>// 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" "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 슬라이스에 저장, 아니면 빈 슬라이스로 남기기
- golang의 CLI 라이브러리 중 하나
- go get -u [github.com/spf13/cobra로](http://github.com/spf13/cobra로) 설치 | import "github.com/spf13/cobra로 사용
- kubectl도 cobra로 만들어졌다는 사실!!
ex10 - servercobra로 cli를 만들어보고 싶습니다!! 무슨 cli를 만들어볼까요!!! ->
- 웹 서버 및 웹 애플리케이션을 개발하기 위한 경량화된 웹 프레임워크
- 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()를 호출해 모든 고루틴이 완료될때까지 기다림
- 원인
- go 컴파일러가 특정 패키지를 goroot 환경변수로 지정된 go 설치 디렉토리 내에서 찾을 수 없을 때 발생
- gopath 설정 문제: 패키지가 gopath 경로에 없는 경우에 발생할 수 있는 오류로 해당 패키지가 gopath 경로에 있는지 확인해야 함
- 패키지 이름 오타
- 등등
- 해결
- go mod init github.com/yugyeongh/cc-5th-go-docker
- go 프로젝트의 모듈을 초기화하는 명령
- 현재 디렉토리에 대한 모듈을 만들 수 있음
module github.com/yugyeongh/cc-5th-go-docker go 1.20
- → go.mod 파일 생성 (종속성 요구 사항이 정의된 파일로 모듈 경로와 특정 버전으로 작성)
- 이 모듈은 go프로젝트의 의존성을 관리하고 외부 패키지를 가져오는데 사용
- go mod tidy
- go 모듈에서 사용되는 의존성 그래프 정리 및 최적화
- go mod init github.com/yugyeongh/cc-5th-go-docker
- go 모듈 사용
- switch문에서 fallthrough의 쓰임
- golang의 포인터
- cobra가 뭔가요
- cobra로 만드는 cli
- </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
- https://velog.io/@kineo2k/Cobra로-간단한-CLI-만들기</aside>
- </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
- https://velog.io/@kineo2k/Cobra로-간단한-CLI-만들기</aside>
'💻 Programming > Go' 카테고리의 다른 글
[golang] .env 파일 이용해 환경변수 설정하기 (0) | 2024.02.13 |
---|
Comments