Skip to content

Date: April 01, 2024

Learn Golang

# fmt ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋งŒ ์‚ฌ์šฉํ• ๋•Œ๋Š” go run ํŒŒ์ผ๋ช….go ์‹คํ–‰ ๊ฐ€๋Šฅ
# thrid party ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ ์‹œ ์—๋Š”

docker run --rm -it golang:1.18
apt-get update && apt-get install vim -y
echo $GOPATH
  /go
# GOPATH ๊ฐ€ ์•„๋‹Œ ๋””๋ ‰ํ† ๋ฆฌ์—์„œ ์‹คํ–‰ /root/TestApp
# fmt ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋งŒ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ์ •์ƒ ์‹คํ–‰
mkdir -p ~/TestApp && cd ~/TestApp

# main.go ์ž‘์„ฑ ํ›„ ์‹คํ–‰ ์„ฑ๊ณต!
go run main.go

cat > main.go
package main
import (
    "fmt"
    "github.com/Pallinder/go-randomdata"
)
func main(){
    fmt.Println("Running the TestApp")
    fmt.Println(randomdata.SillyName())
}

go run main.go

# ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์‚ฌ์šฉ ์‹œ It complains about not finding the package because :
#  1.GOPATH๋ฐ– 2.ํŒจํ‚ค์ง€ ๋‹ค์šด๋กœ๋“œ ์•ˆํ–ˆ๊ธฐ ๋–„๋ฌธ
#  no required module provides package github.com/Pallinder/go-randomdata:
#  go.mod file not found in current directory or any parent directory;
#  use go module to fix this
# Go๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜์—ฌ ํ•ด๊ฒฐ

# go mod init /root/TestApp
# go.mod ํŒŒ์ผ ์ƒ์„ฑ

cd hellogo/cookbook
go mod init github.com/jnuho/jnuho.github.io/hellogo/cookbook


# go.mod์— ํ˜„์žฌ module ์ •์˜ ์™ธ์—๋Š” ๋ณ„ ๋‚ด์šฉ ์—†์Œ
# go build ์‹คํ–‰ ํ•˜์—ฌ ๋””ํŽœ๋˜์‹œ๋ฅผ ๋‹ค์šด๋ฐ›์•„์•ผ ํ•จ
cat go.mod
  module github.com/jnuho/jnuho.github.io/hellogo/TestApp
  go 1.18

# ๋””ํŽœ๋˜์‹œ ๋‹ค์šด๋กœ๋“œ. ๋‹ค๋ฅธ ๋ฒ„์ „ ์‚ฌ์šฉํ•˜๊ณ ์ž ํ•˜๋ฉด,
# go.mod ํŒŒ์ผ ์ˆ˜์ • ํ›„ go build ์žฌ์‹คํ–‰
go build

# `go mod tidy` fetch all the dependencies that you need for testing in your module.
go mod tidy

# go mod why -m <module>
# find out where any of your dependencies are used (consumed)
go mod why -m golang.org/x/text

# ํŠน์ • ๋ฒ„์ „์˜ ํŒจํ‚ค์ง€ ์‚ฌ์šฉ
# ๋””ํดํŠธ : latest
go get github.com/bwmarrin/[email protected]

cat go.mod
  require github.com/bwmarrin/discordgo v0.203

go get github.com/bwmarrin/discordgo@latest

# ๋นŒ๋“œ ํ•˜๊ธฐ ์ „ go.mod ํŒŒ์ผ ํ•„์š”
# go build ์ปค๋ฉ˜๋“œ๋กœ ์‹คํ–‰ ํŒŒ์ผ ์ƒ์„ฑ
go build
  • Vscode > Extension > Go
### CREATE MODULE ###
cd goproject/calculator
# MODULE_NAME should ideally be on the web
go mod init github.com/jnuho/jnuho.github.io/goproject/calculator
cat go.mod

### CREATE PACKAGE ###
# e.g. two packages
mkdir operations display
# to use 'format.go' inside 'simple.go', use:
# import "github.com/jnuho/jnuho.github.io/goproject/calculator/display"
touch operations/simple.go
touch display/format.go

### COMMIT AND PUSH TO REPO ###
### USE ABOVE PACKAGES ###
# mkdir simplego && cd simplego
# cat > main.go
package main

import "github.com/jnuho/jnuho.github.io/goproject/calculator/operations"

func main() {
}
# look through import statements
# and downloads all missing packages/dependencies
# ์™ธ๋ถ€ ํŒจํ‚ค์ง€ ๋‹ค์šด๋กœ๋“œ
go mod tidy

cat go.mod
  • ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด
  • ์–ด์…ˆ๋ธ”๋ฆฌ์–ด vs. ๊ณ ์ˆ˜์ค€ ์–ธ์–ด
    • ์–ด์…ˆ๋ธ”๋ฆฌ์–ด๋Š” ์ฝ”๋“œ์™€ ๊ธฐ๊ณ„์–ด๊ฐ€ 1:1 ๋งค์นญ์ด์ง€๋งŒ ๊ณ ์ˆ˜์ค€์–ธ์–ด ์ฝ”๋“œ๋Š” ์‹คํ–‰ํ•˜๋ ค๋ฉฐ ๊ธฐ๊ณ„์–ด๋กœ ๋ณ€ํ™˜ ํ•„์š”
    • ๊ฐ๊ฐ์˜ ๊ณ ์ˆ˜์ค€ ์–ธ์–ด๋Š” ์ปดํŒŒ์ผ๋Ÿฌ ์žˆ์Œ e.g. C ์ปดํŒŒ์ผ๋Ÿฌ, Go ์ปดํŒŒ์ผ๋Ÿฌ
  • ์ •์  ์ปดํŒŒ์ผ ์–ธ์–ด vs. ๋™์  ์ปดํŒŒ์ผ ์–ธ์–ด
    • ๋ฏธ๋ฆฌ ์ปดํŒŒ์ผ(ํŒŒ๋ฅธ ์†๋„) vs. ์‚ฌ์šฉํ• ๋•Œ ์ปดํŒŒ์ผ(์†๋„ ํฌ์ƒ, ์—ฌ๋Ÿฌ OS ๋ฒ”์šฉ์„ฑ ๋›ฐ์–ด๋‚จ)
    • Go๋Š” ์ €์  ์ปดํŒŒ์ผ ์–ธ์–ด๋กœ, ๋‹ค์–‘ํ•œ ํ”Œ๋žซํผ์— ๋งž๋„๋ก ์‹คํ–‰ ํŒŒ์ผ์„ ๋”ฐ๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•จ
  • ์•ฝ ํƒ€์ž… ์–ธ์–ด vs. ๊ฐ• ํƒ€์ž… ์–ธ์–ด

    • Go๋Š” ๊ฐ• ํƒ€์ž… ์–ธ์–ด.
  • ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ ์œ ๋ฌด

  • ์ž๋™ ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ ์ฒ˜๋ฆฌ ํ•˜์—ฌ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ จ ๋ฌธ์ œ ์ ์Œ
  • CPU๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ ์„ฑ๋Šฅ ์ €ํ•˜ ๋‹จ์ 

  • ์ฝ”๋“œ ์‹คํ–‰ ๋‹จ๊ณ„

  • ํด๋”์ƒ์„ฑ
  • .go ํŒŒ์ผ์ƒ์„ฑ ๋ฐ ์ž‘์„ฑ
  • Go ๋ชจ๋“ˆ ์ƒ์„ฑ (.mod ์ƒ์„ฑ: ๋ชจ๋“ˆ๋ช…,Go๋ฒ„์ „,ํ•„์š”ํŒจํ‚ค์ง€ ๋ชฉ๋ก ๋“ฑ ์ •๋ณด)
  • ๋นŒ๋“œ (go build)
    • GOOS GOARCH ํ™˜๊ฒฝ๋ณ€์ˆ˜ ์กฐ์ •ํ•˜์—ฌ ๋‹ค๋ฅธ OS๋ฐ ์•„ํ‚คํ…์ณ์—์„œ ์‹คํ–‰๊ฐ€๋Šฅํ•œ ํŒŒ์ผ ์ƒ์„ฑ ๊ฐ€๋Šฅ
    • e.g. GOOS=linux GOARCH=amd64 go build
  • ์‹คํ–‰

  • Go1.16๋ฒ„์ „ ๋ถ€ํ„ฐ Go ๋ชจ๋“ˆ ์‚ฌ์šฉ์ด ๊ธฐ๋ณธ์ด ๋จ

  • ๋ชจ๋“ˆ์€ ํŒจํ‚ค์ง€ ์ข…์†์„ฑ ๊ด€๋ฆฌ ๋“ฑ ํŒจํ‚ค์ง€ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ ์—ญํ• 
  • Ver < 1.16: ๋ชจ๋“  Go์ฝ”๋“œ๋Š” $GOPATH/src ์•„๋ž˜
  • Ver >= 1.16: ๋ชจ๋“  Go์ฝ”๋“œ๋Š” Go๋ชจ๋“ˆ ์•„๋ž˜
    • go mod init MODULE_NAME์œผ๋กœ Go๋ชจ๋“ˆ ์ƒ์„ฑ
    • ๋ชจ๋“ˆ์ด๋ฆ„์€ ์œ ๋‹ˆํฌํ•ด์•ผ ํ•จ; GitHub ์ €์žฅ์†Œ ์ฃผ์†Œ๋ฅผ ํ™œ์šฉํ•˜๊ฑฐ๋‚˜ URL์„ ๋ชจ๋“ˆ ์ด๋ฆ„์œผ๋กœ ์‚ฌ์šฉ
์ด๋ฆ„ ์„ค๋ช… ๊ฐ’์˜ ๋ฒ”์œ„
uint8 1๋ฐ”์ดํŠธ ๋ถ€ํ˜ธ ์—†๋Š” ์ •์ˆ˜ 0~255
uint16 2๋ฐ”์ดํŠธ ๋ถ€ํ˜ธ ์—†๋Š” ์ •์ˆ˜ 0~65535
uint32 4๋ฐ”์ดํŠธ ๋ถ€ํ˜ธ ์—†๋Š” ์ •์ˆ˜ 0~4294...95
uint64 8๋ฐ”์ดํŠธ ๋ถ€ํ˜ธ ์—†๋Š” ์ •์ˆ˜ 0~1844...615
int8 1๋ฐ”์ดํŠธ ๋ถ€ํ˜ธ ์žˆ๋Š” ์ •์ˆ˜ -128~127
int16 2๋ฐ”์ดํŠธ ๋ถ€ํ˜ธ ์žˆ๋Š” ์ •์ˆ˜ -32768~32767
int32 4๋ฐ”์ดํŠธ ๋ถ€ํ˜ธ ์žˆ๋Š” ์ •์ˆ˜ -214...648~214..647
int64 8๋ฐ”์ดํŠธ ๋ถ€ํ˜ธ ์žˆ๋Š” ์ •์ˆ˜ -922....808 ~ 922..807
float32 4๋ฐ”์ดํŠธ ์‹ค์ˆ˜ IEEE~754 32๋น„ํŠธ ์‹ค์ˆ˜
float64 8๋ฐ”์ดํŠธ ์‹ค์ˆ˜ IEEE~754 64๋น„ํŠธ ์‹ค์ˆ˜ (a:=3.14 will default to float64 type)
complex64 8๋ฐ”์ดํŠธ ๋ณต์†Œ์ˆ˜(์ง„์ˆ˜,๊ฐ€์ˆ˜) ์ง„์ˆ˜์™€ ๊ฐ€์ˆ˜ ๋ฒ”์œ„๋Š” float32 ๋ฒ”์œ„์™€ ๊ฐ™์Œ
complex128 16๋ฐ”์ดํŠธ ๋ณต์†Œ์ˆ˜(์ง„์ˆ˜,๊ฐ€์ˆ˜) ์ง„์ˆ˜์™€ ๊ฐ€์ˆ˜ ๋ฒ”์œ„๋Š” float64 ๋ฒ”์œ„์™€ ๊ฐ™์Œ
byte uint8์˜ ๋ณ„์นญ 1๋ฐ”์ดํŠธ ๋ฐ์ดํ„ฐ ๋‚˜ํƒ€๋‚ผ๋•Œ ์‚ฌ์šฉ 0~255 rune
int 32๋น„ํŠธ ์ปดํ“จํ„ฐ์—์„œ int32. 64๋น„ํŠธ ์ปดํ“จํ„ฐ์—์„œ int64
uint 32๋น„ํŠธ ์ปดํ“จํ„ฐ์—์„œ uint32. 64๋น„ํŠธ ์ปดํ“จํ„ฐ์—์„œ uint64
  • ๊ทธ์™ธ ํƒ€์ž…
  • boolean
  • ๋ฌธ์ž์—ด
  • ๋ฐฐ์—ด
  • ์Šฌ๋ผ์ด์Šค
  • ๊ตฌ์กฐ์ฒด
  • ํฌ์ธํ„ฐ
  • ํ•จ์ˆ˜ํƒ€์ž…
  • ์ธํ„ฐํŽ˜์ด์Šค
  • ๋งต
  • ์ฑ„๋„

  • ํƒ€์ž…๋ณ„ ๊ธฐ๋ณธ๊ฐ’

ํƒ€์ž… ๊ธฐ๋ณธ๊ฐ’
๋ชจ๋“ ์ •์ˆ˜ํƒ€์ž… (int8, int16, int32, int64, uint8, uint16, uint32, uint64, int, uint, byte, rune) 0
๋ชจ๋“  ์‹ค์ˆ˜ ํƒ€์ž… (float32, float64, complex64, complex128) 0.0
๋ถˆ๋ฆฌ์–ธ false
๋ฌธ์ž์—ด "" (๋นˆ๋ฌธ์ž์—ด)
๊ทธ์™ธ nil (์ •์˜๋˜์ง€ ์•Š์€ ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Œ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” Go ํ‚ค์›Œ๋“œ)
  • ๋””ํดํŠธ ๋ฐ์ดํ„ฐํƒ€์ž…
package main
import (
  "fmt"
  "reflect"
)
func main() {
  // int
  fmt.Println(reflect.TypeOf(10))

  // float64
  fmt.Println(reflect.TypeOf(3.1415))

  // string
  fmt.Println(reflect.TypeOf("LaLaLalla"))

  // bool
  fmt.Println(reflect.TypeOf(true))

  // int32: (runeํƒ€์ž… : 4-byte int32 ํƒ€์ž…๊ณผ ๋ณ„์นญ)
  fmt.Println(reflect.TypeOf('A'))
}
  • ๋ณ€์ˆ˜์„ ์–ธ ํ˜•ํƒœ
package main

import "fmt"

func main() {
  var a int = 3
  var b int // ํƒ€์ž…๋ณ„ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ๋Œ€์ฒด (์ดˆ๊นƒ๊ฐ’ ์ƒ๋žต)
  var c = 4 // ์šฐ๋ณ€๊ฐ’์˜ ํƒ€์ž…์œผ๋กœ ๋Œ€์ฒด (ํƒ€์ž…์ƒ๋žต)
  d := 5    // ์šฐ๋ณ€๊ฐ’์˜ ํƒ€์ž…์œผ๋กœ ๋Œ€์ฒด (var ํ‚ค์›Œ๋“œ์™€ ํƒ€์ž… ์ƒ๋žต)

  fmt.Println(a, b, c ,d)
}

6.์—ฐ์‚ฐ์ž

  • numericOperator.go
package main

import (
  "fmt"
)

func main(){
  var x int32 = 7
  //var y float32 = 3.14

  fmt.Println(x + 1)
  fmt.Println(x - 1)
  fmt.Println(x * 1)
  fmt.Println(x / 1)
  fmt.Println(x % 1)

  fmt.Println(x & 1)
  fmt.Println(x | 1)
  fmt.Println(x ^ 1)
  fmt.Println(x &^ 1)

  fmt.Println(x << 1)
  fmt.Println(x >> 1)
}
  • compOperator.go
package main

import (
  "fmt"
)

func main(){
  var x int32 = 7
  var y float32 = 3.14

  fmt.Println(x == y)
  fmt.Println(x != y)
  fmt.Println(x * y)
  fmt.Println(x < y)
  fmt.Println(x > y)
  fmt.Println(x <= y)
  fmt.Println(x >= y)

}
  • operationErr1.go
  • ์ปดํ“จํ„ฐ๋Š” 2์ง„์ˆ˜๋กœ๋งŒ ํ‘œํ˜„-> ์‹ค์ˆ˜ ํ‘œํ˜„์‹œ ์˜ค์ฐจ๋ฐœ์ƒ (e.g. 0.376 != 2^-2 + 2^-3+ ...)
  • ์„œ๋กœ ๋‹ค๋ฅธ ๊ฐ’์ด ์˜ค์ฐจ์— ์˜ํ•ด ๊ฐ™๋‹ค๊ณ  ๋‚˜์˜ค๋Š” ํ˜„์ƒ ํ…Œ์ŠคํŠธ
  • epsilon ๊ฐ’์„ ์ •์˜ํ•˜์—ฌ ๋‘ ๊ฐ’์˜ ์ฐจ์ด๊ฐ€ ํ•ด๋‹น ๊ฐ’๋ณด๋‹ค ์ž‘์œผ๋ฉด equal์ฒ˜๋ฆฌ
  • operationErr2.go
  • ๋น„ํŠธํ‘œํ˜„์‹œ '์ง€์ˆ˜๋ถ€+์‹ค์ˆ˜๋ถ€' ์ค‘ ๊ฐ€์žฅ ์˜ค๋ฅธ์ชฝ ๋น„ํŠธ ํ•˜๋‚˜์ฐจ์ด ์ด๋‚ด์ด๋ฉด ๊ฐ™๋‹ค๊ณ  ํŒ๋‹จ
  • ๋งˆ์ง€๋ง‰ ๋น„ํŠธ๊ฐ€ 1๋งŒํผ ์ฐจ์ด ๋‚˜๋Š”์ง€ ํ™•์ธ
    • func Nextafter(x,y float64) (r float64)
    • x์—์„œ y๋ฅผํ–ฅ ํ•ด์„œ 1๋น„ํŠธ ์กฐ์ • (+ or - ํ•œ ๊ฐ’์„ ๋ฐ˜ํ™˜)
package main

import (
  "fmt"
  "math"
)

const epsilon = 0.000001

func equal(a, b float64) bool {
  diff := math.Abs(a-b)

  if diff <= epsilon {
    return true
  } else {
    return false
  }

}

func main() {
  var a float64 = 0.1
  var b float64 = 0.2
  var c float64 = 0.3

  fmt.Printf("%0.18f + %0.18f = %0.18f\n", a, b, a+b)
  fmt.Printf("%0.18f == %0.18f : %v\n", c, a+b, equal(a+b,c))

  a= 0.0000000000004
  b= 0.0000000000002
  c= 0.0000000000007

  fmt.Printf("%g == %g : %v\n", c, a+b, equal(a+b,c))

}
  • operationErr2.go

package main

import (
  "fmt"
  "math"
)

func equal(a, b float64) bool {
  return math.Nextafter(a,b) == b
}

func main() {
  var a float64 = 0.1
  var b float64 = 0.2
  var c float64 = 0.3

  fmt.Printf("%0.18f + %0.18f = %0.18f\n", a, b, a+b)
  fmt.Printf("%0.18f == %0.18f : %v\n", c, a+b, equal(a+b,c))

  a= 0.0000000000004
  b= 0.0000000000002
  c= 0.0000000000007

  fmt.Printf("%g == %g : %v\n", c, a+b, equal(a+b,c))
}
  • operationErr3.go

package main

import (
  "fmt"
  "math/big"
)

func main() {
  a, _ :=  new(big.Float).SetString("0.1") // a
  b, _ :=  new(big.Float).SetString("0.2") // b
  c, _ :=  new(big.Float).SetString("0.3") // c

  d := new(big.Float).Add(a,b) // a+b
  fmt.Println(a, b, c, d)
  fmt.Println(c.Cmp(d)) // 0: equals (์ •๋ฐ€๋„ ๋†’์Œ)
}
  • ๋…ผ๋ฆฌ ์—ฐ์‚ฐ์ž
  • || && !

  • ๋Œ€์ž…์—ฐ์‚ฐ์ž

  • a,b =3,4
  • a,b = b,a
  • a=10 b=a

  • ์ฆ๊ฐ์—ฐ์‚ฐ์ž

  • a++ a-- += -=
  • a++ ๋Š” ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š์Œ!

  • ์—ฐ์‚ฐ์ž ์šฐ์„ ์ˆœ์œ„

7.ํ•จ์ˆ˜

package main
import "fmt"

func factorial(n int) int {

  if n <= 1  {
    return 1
  } else {
    return n * factorial(n-1)
  }
}

func main() {

  fmt.Println(factorial(5))
}

8.์ƒ์ˆ˜

const Pig int = 0
const Cow int = 1
const Chicken int = 2

const PI = 3.14
const PI2 float64 = 3.14

// iota๋กœ ๊ฐ„ํŽธํ•˜๊ฒŒ ์—ด๊ฑฐ๊ฐ’ ์‚ฌ์šฉ ํ•˜๊ธฐ
// 1์”ฉ์ฆ๊ฐ€. ์†Œ๊ด„ํ˜ธ ๋ฒ—์–ด๋‚˜๋ฉด ๋‹ค์‹œ ์ดˆ๊ธฐํ™” ๋จ
const (
  Red   int = iota // 0
  Blue  int = iota // 1
  Green int = iota // 2
)

// const๋ฅผ ์†Œ๊ด„ํ˜ธ()๋กœ ๋ฌถ๊ณ  iota ์‚ฌ์šฉํ•˜๋ฉด
// 0๋ถ€ํ„ฐ 1์”ฉ ์ฐจ๋ก€๋กœ ์ฆ๊ฐ€ํ•˜๋ฉฐ ๊ฐ’์ด ์ดˆ๊ธฐํ™” ๋จ
const (
  C1 uint = iota + 1 // 1 = 0 + 1
  C2                 // 2 = 1 + 1
  C3                 // 3 = 2 + 1
)

const (
  BitFlag1 uint = iota + 1 // 1 = 1 << 0
  BitFlag2                 // 2 = 1 << 1
  BitFlag3                 // 4 = 1 << 2
  BitFlag4                 // 8 = 1 << 3
)

func PrintAnimal(animal int) {
  if animal == Pig {
    fmt.Println("๊ฟ€")
  } else if animal == Cow {
    fmt.Println("์Œ๋ฉ”")
  } else if animal == Chicken {
    fmt.Println("๊ผฌ๋ผ์˜ค")
  }

}

func main() {
  PrintAnimal(Pig)
  PrintAnimal(Cow)
  PrintAnimal(Chicken)

  var a int = PI * 100
  // var b int = PI2 * 100 // ERROR!
  fmt.Println(a)
  // fmt.Println(b)

  fmt.Println(C1)
  fmt.Println(C2)
  fmt.Println(C3)

  fmt.Println(BitFlag1)
  fmt.Println(BitFlag2)
  fmt.Println(BitFlag3)
}

9.if์กฐ๊ฑด๋ฌธ

  // ์ดˆ๊ธฐ๋ฌธ์„ ์‹คํ–‰ํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ• ๋•Œ ์กฐ๊ฑด๋ฌธ ์‚ฌ์šฉ
  if ์ดˆ๊ธฐ๋ฌธ; ์กฐ๊ฑด๋ฌธ {
    // ์ดˆ๊ธฐ๋ฌธ์˜ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•˜๋Š” ๋ชฉ์ ์œผ๋กœ ํ™œ์šฉ๊ฐ€๋Šฅ
  }

10.switch๋ฌธ

// From A Tour of Go
Go only runs the selected case, not all the cases that follow.
In effect, the break statement that is needed at the end of each case
in those languages is provided automatically in Go.
Another important difference is that Go's switch cases need not be constants,
and the values involved need not be integers.
package main

import "fmt"


type ColorType int

const (
  Red ColorType = iota // 1
  Blue                 // 2
  Yellow                 // 3
  Green                 // 4
)

func SelectColor(color ColorType) {
  switch color {
    case Red:
      fmt.Println("Red!!!rrrr")
    case Blue:
      fmt.Println("Blue!!!bbb")
    case Yellow:
      fmt.Println("Yellow!!!")
    case Green:
      fmt.Println("Greeen!!!ggg")
  }
}

func main() {
  mycolor := Red
  fmt.Println(mycolor)

}

11.for๋ฌธ

// label์‚ฌ์šฉ์€ ์ง€์–‘ - ํ˜ผ๋™ ์˜ฌ ์ˆ˜ ์žˆ์Œ
func find45(a int) (int, bool) {
  for b := 1; b < 10; b++ {
    if b*a == 45 {
      return b, true
    }
  }
  return 0, false
}

func main() {
  a := 1
  b := 0
  found := false

  for ; a <= 9; a++ {
    // Like for, the if statement can start with a short statement to execute before the condition.
    if b, found = find45(a); found {
      break
    }
  }

  fmt.Printf("%d * %d = %d\n", a, b, a*b)
}
package main

import (
  "fmt"
  "math"
)

func pow(x, n, lim float64) float64 {
  // Like for, the if statement can start with a short statement to execute before the condition.
  if v := math.Pow(x, n); v < lim {
    return v
  }
  return lim
}

func main() {
  fmt.Println(
    pow(3, 2, 10),
    pow(3, 3, 20),
  )
}
  • continue, break
package main

import (
  "bufio"
  "fmt"
  "os"
)

func main() {
  stdin := bufio.NewReader(os.Stdin)

  for {
    // ์ž…๋ ฅ ๋œ ๊ฐ’์ด ๋ณ€์ˆ˜์— ์ €์žฅ๋˜๊ธฐ ์ „์— ๋ฒ„ํผ์— ์ €์žฅ
    // ์ˆซ์ž๋Š” number์— ์ €์žฅ๋˜๊ณ  
    // ์ˆซ์ž + 
    fmt.Print("์ˆซ์ž๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”: [-1๋กœ ์ข…๋ฃŒ]")
    var number int
    _, err := fmt.Scanln(&number)

    if err != nil {
      fmt.Println("์ˆซ์ž๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค!")

      //ํ‚ค๋ณด๋“œ ๋ฒ„ํผ ๋น„์›€
      stdin.ReadString('\n')
      continue
    }

    fmt.Printf("์ž…๋ ฅ ํ•˜์‹  ์ˆซ์ž๋Š” %d ์ž…๋‹ˆ๋‹ค.", number)

    if number%2 == 0 {
      fmt.Printf("์ž…๋ ฅ ํ•˜์‹  ์ˆซ์ž๋Š” ์ง์ˆ˜, %d, ์ž…๋‹ˆ๋‹ค.\n", number)
    } else if number%2 == 1 {
      fmt.Printf("์ž…๋ ฅ ํ•˜์‹  ์ˆซ์ž๋Š” ํ™€์ˆ˜, %d, ์ž…๋‹ˆ๋‹ค.\n", number)
    } else if number == -1 {
      fmt.Printf("for๋ฌธ ์ข…๋ฃŒ\n")
      break
    }
  }
}

12.๋ฐฐ์—ด array

  • array copy
package main

import (
  "fmt"
)

func main() {
  a := [5]int{1,2,3,4,5}
  b := [5]int{500,400,300,200,100}

  // a
  for i,v := range a {
    fmt.Printf("a[%d] = %d\n", i, v)
  }

  // b
  fmt.Println()
  for i, v := range b {
    fmt.Printf("b[%d] = %d\n", i, v)
  }

  b = a

  // a๊ฐ€ ๋ณต์‚ฌ๋œ b
  fmt.Println()
  for i,v := range b {
    fmt.Printf("b[%d] = %d\n", i, v)
  }
}
  • multi-array
package main

import (
  "fmt"
)

func main() {
  a := [2][5] int {
    {1,2,3,4,5},
    {6,7,8,9,10},
    // ํ–‰๋ฐ”๋€œ ์žˆ์„๋•Œ๋Š” ๋งˆ์ง€๋ง‰์— , ํ•„์š”
    // ํ–‰๋ฐ”๋€œ์—†๋Š” inline ์ผ๋•Œ๋Š” ํ•„์š” ์—†์Œ e.g. { {...},{.., 9,10} }
  }

  for _, arr := range a {
    for _, v := range arr {
      fmt.Print(v, "")
    }
    fmt.Println()
  }
}

13.๊ตฌ์กฐ์ฒด struct

  • ์ปดํ“จํ„ฐ๊ฐ€ ๋ฐ์ดํ„ฐ์— ํšจ๊ณผ์ ์œผ๋กœ ์ ‘๊ทผํ•˜๊ณ ์ž, ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ผ์ • ํฌ๊ธฐ ๊ฐ„๊ฒฉ์œผ๋กœ ์ •๋ ฌ
  • 64๋น„ํŠธ ์ปดํ“จํ„ฐ์˜ ๊ฒฝ์šฐ 8๋ฐ”์ดํŠธ ๋‹จ์œ„๋กœ ์ •๋ ฌ ๋˜์–ด ์žˆ๋Š”๊ฒŒ ํšจ์œจ์ 
  • int32, float64ํ•„๋“œ๊ฐ€ ์žˆ๋Š” ๊ตฌ์กฐ์ฒด์˜ ๊ฒฝ์šฐ 12๋ฐ”์ดํŠธ(4+8) ๊ฐ€ ์•„๋‹Œ 16๋ฐ”์ดํŠธ(8+8) ํ• ๋‹น
  • ์ˆœ์„œ๋ฅผ float64, int32๋กœ ํ•˜๋ฉด 12๋ฐ”์ดํŠธ(8+4)
  • ๋ถˆํ•„์š”ํ•œ ๋ฉ”๋ชจ๋ฆฌ ๋‚ญ๋น„๋ฅผ ์ค„์ด๋ ค๋ฉด ์ž‘์€ ํฌ๊ธฐ ํ•„๋“œ๊ฐ’์„ ์•ž์— ๋ฐฐ์น˜
  • ํŒจ๋”ฉ: ๋ฉ”๋ชจ๋ฆฌ ์ •๋ ฌ์„ ์œ„ํ•ด์„œ ํ•„๋“œ๊ฐ„ ๊ฐ„๊ฒฉ์„ ๋„์›€
package main

import (
  "fmt"
  "unsafe"
)


type User struct {
  A int32 // 4๋ฐ”์ดํŠธ
  B float64 // 8๋ฐ”์ดํŠธ
}

type User2 struct {
  A int8    // 1๋ฐ”์ดํŠธ
  B int     // 8๋ฐ”์ดํŠธ
  C int8    // 1๋ฐ”์ดํŠธ
  D int     // 8๋ฐ”์ดํŠธ
  E int8    // 1๋ฐ”์ดํŠธ
}

type User3 struct {
  A int8    // 1๋ฐ”์ดํŠธ
  C int8    // 1๋ฐ”์ดํŠธ
  E int8    // 1๋ฐ”์ดํŠธ
  B int     // 8๋ฐ”์ดํŠธ
  D int     // 8๋ฐ”์ดํŠธ
}

func main() {
  user := User {34, 97.5}
  // 12๋ฐ”์ดํŠธ๊ฐ€ ์•„๋‹Œ 16๋ฐ”์ดํŠธ ์ถœ๋ ฅ : ๋ฉ”๋ชจ๋ฆฌ์ •๋ ฌ
  // ๋ณ€์ˆ˜์‹œ์ž‘์ฃผ์†Œ๋ฅผ ๋ณ€๊ตฌํฌ๊ธฐ์˜ ๋ฐฐ์ˆ˜๋กœ ๋งž์ถค
  fmt.Println("user size : ", unsafe.Sizeof(user))

  user2 := User2{1,2,3,4,5}
  fmt.Println("user2 size : ", unsafe.Sizeof(user2))

  user3 := User3{1,2,3,4,5}
  fmt.Println("user3 size : ", unsafe.Sizeof(user3))

}

14.ํฌ์ธํ„ฐ

func main() {
  var a int = 500
  var p *int

  p = &a

  fmt.Printf("p์˜ ๊ฐ’: %p\n", p) // ๋ฉ”๋ชจ๋ฆฌ ์ฃผ์†Ÿ๊ฐ’
  fmt.Printf("p๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ๋ฉ”๋ชจ๋ฆฌ์˜ ๊ฐ’: %d\n", *p) // p๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ๋ฉ”๋ชจ๋ฆฌ์˜ ๊ฐ’ *p == a

  *p = 100 // p๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ๋ฉ”๋ชจ๋ฆฌ์˜ ๊ฐ’ ๋ณ€๊ฒฝ
  fmt.Printf("===p๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ๋ฉ”๋ชจ๋ฆฌ์˜ ๊ฐ’ ๋ณ€๊ฒฝ->100===\n")
  fmt.Printf("a์˜ ๊ฐ’: %d\n", a) // a๊ฐ’ ๋ณ€ํ™” ํ™•์ธ *p == a

  var p2 *int = &a
  fmt.Printf("p2๋„ a๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” *int ํฌ์ธํ„ฐ ์ž…๋‹ˆ๋‹ค: \n\tp2= %p, *p2=%d, &a=%p\n", p2,*p2, &a)
}
  • == ์—ฐ์‚ฐ์œผ๋กœ ํฌ์ธํ„ฐ๊ฐ€ ๊ฐ™์€ ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„ ๊ฐ€๋ฆฌํ‚ค๋Š”์ง€ ํ™•์ธ๊ฐ€๋Šฅ
func main() {
  var a int = 10
  var b int = 20

  var p1 *int = &a
  var p2 *int = &a
  var p3 *int = &b

  fmt.Printf("p1 == p2 : %v\n", p1==p2)
  fmt.Printf("p2 == p3 : %v\n", p2==p3)


  // ํฌ์ธํ„ฐ์˜ ๊ธฐ๋ณธ๊ฐ’ nil
  var p *int
  if p != nil {
    fmt.Println("ํฌ์ธํ„ฐ p๋Š” ์œ ํšจํ•œ ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„์„ ๊ฐ€๋ฆฌํ‚ด")
  } else {
    fmt.Println("ํฌ์ธํ„ฐ p == nil ์–ด๋–ค ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„๋„ ๊ฐ€๋ฆฌํ‚ค๊ณ  ์žˆ์ง€ ์•Š์Œ")
  }

  // ํฌ์ธํ„ฐ๋Š” ๋ณ€์ˆ˜๋Œ€์ž… ๋˜๋Š” ํ•จ์ˆ˜ ์ธ์ˆ˜์ „๋‹ฌ ์‹œ ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„์„ ์ ๊ฒŒ ์‚ฌ์šฉํ•˜๋ฉด์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
}
package main

import (
  "fmt"
)

type Data struct {
  value int
  data [200]int
}

// ํ˜ธ์ถœํ• ๋•Œ๋งˆ๋‹ค Data ๊ตฌ์กฐ์ฒด ํฌ๊ธฐ๋งŒํผ์˜ ๋ฉ”๋ชจ๋ฆฌ ๋ณต์‚ฌ๋˜๊ณ  ๊ทธ ๊ฐ’์„ ๋ณ€๊ฒฝํ•จ
// ์ „๋‹ฌ๋ฐ›์€ ๋ณ€์ˆ˜์˜ ๊ฐ’์€ ๋ณ€๊ฒฝ ์•ˆ๋จ.
// ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋งค๋ฒˆ ๋ณต์‚ฌํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋งŽ์ด ํ˜ธ์ถœ ์‹œ ์„ฑ๋Šฅ์ €ํ•˜
func ChangeDataNotWorking(arg Data) {
  // ๋งค๊ฐœ'๋ณ€์ˆ˜' arg ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ์•ˆ๋จ...
  arg.value = 999
  arg.data[100] = 999
}

func ChangeData(arg *Data) {
  // ๋งค๊ฐœ'๋ณ€์ˆ˜' arg ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ
  arg.value = 999
  arg.data[100] = 999
}

func main() {
  // ์ธ์Šคํ„ด์Šค: ๋ฉ”๋ชจ๋ฆฌ์— ํ• ๋‹น๋œ '๋ฐ์ดํ„ฐ์˜ ์‹ค์ฒด'
  // ํฌ์ธํ„ฐ๋ฅผ ์ด์šฉํ•˜์—ฌ ์ธ์Šคํ„ด์Šค์— ์ ‘๊ทผ

  // ์ธ์Šคํ„ด์Šค๋Š” ์–ธ์ œ์‚ฌ๋ผ์ง€๋‚˜?
  // ๋ฉ”๋ชจ๋ฆฌ์— ๋ฐ์ดํ„ฐ ํ• ๋‹น๋˜๊ณ  ์‚ฌ๋ผ์ง€์ง€ ์•Š์œผ๋ฉด ๋ฉ”๋ชจ๋ฆฌ๊ณ ๊ฐˆ-> ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ Gc๊ฐ€ ๋‹ด๋‹น
  // Garbage Collector์— ์˜ํ•ด ์ผ์ • ๊ฐ„๊ฒฉ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์“ธ๋ชจ์—†๋Š” ๋ฐ์ดํ„ฐ ์ฒญ์†Œ (์ธ์Šคํ„ด์Šค ๋“ฑ)
  // ์ƒ์„ฑ๋œ ๋ณ€์ˆ˜ (์ธ์Šคํ„ด์Šค) ๋ฉ”๋ชจ๋ฆฌ๋Š” ํ•จ์ˆ˜ ์ข…๋ฃŒ์‹œ ์‚ฌ๋ผ์ง
  // 1.์ธ์Šคํ„ด์Šค๋Š” ๋ฉ”๋ชจ๋ฆฌ์— ์ƒ์„ฑ๋œ ๋ฐ์ดํ„ฐ์˜ ์‹ค์ฒด
  // 2.ํฌ์ธํ„ฐ๋ฅผ ์ด์šฉํ•ด์„œ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ฒŒ ํ•  ์ˆ˜ ์žˆ์Œ
  // 3.ํ•จ์ˆ˜ ํ˜ธ์ถœ ์‹œ ํฌ์ธํ„ฐ ์ธ์ˆ˜๋ฅผ ํ†ตํ•ด์„œ ์ธ์Šคํ„ด์Šค๋ฅผ ์ž…๋ ฅ๋ฐ›๊ณ , ๊ทธ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋จ
  // 4.์“ธ๋ชจ ์—†์–ด์ง„ ์ธ์Šคํ„ด์Šค๋Š” ๊ฐ€๋น„์ง€ ์ปฌ๋ ‰ํ„ฐ๊ฐ€ ์ž๋™์œผ๋กœ ์ง€์›Œ์คŒ

  // Data ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
  var data Data

  // p: ์ƒˆ๋กœ์šด Data์ธ์Šคํ„ด์Šค๊ฐ€ ์ƒ์„ฑ๋œ ๊ฒƒ์ด ์•„๋‹Œ, ๊ธฐ์กด์— ์žˆ๋˜ data ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ํฌ์ธํ„ฐ ๋ณ€์ˆ˜
  var p *Data = &data

  fmt.Printf("p.value = %d\n", p.value)
  fmt.Printf("data.value = %d\n", data.value)

  ChangeData(&data)

  // 0->999 ํ™•์ธ
  fmt.Printf("p.value = %d\n", p.value)
  fmt.Printf("data.value = %d\n", data.value)

  // ์ธ์Šคํ„ด์Šค ํ•˜๋‚˜๋งŒ ์ƒ์„ฑํ•˜๊ณ  ํฌ์ธํ„ฐ 3๊ฐœ๊ฐ€ ํ•˜๋‚˜์˜ ์ธ์Šคํ„ด์Šค ๊ฐ€๋ฆฌํ‚ด
  var p1 *Data = &Data{}
  var p2 *Data = p1
  var p3 *Data = p1

  // initial value
  fmt.Println(*p1)

  fmt.Printf("์ฃผ์†Ÿ๊ฐ’1 = %p\n", p1)
  fmt.Printf("์ฃผ์†Ÿ๊ฐ’2 = %p\n", p2)
  fmt.Printf("์ฃผ์†Ÿ๊ฐ’3 = %p\n", p3)

  // ํฌ์ธํ„ฐ ์ดˆ๊ธฐํ™” ๋ฐฉ๋ฒ•
  // & ์‚ฌ์šฉํ•œ ์ดˆ๊ธฐํ™” ๋ฐฉ๋ฒ•:
  //    ํฌ์ธํ„ฐ ๊ฐ’์„ ๋ณ„๋„์˜ ๋ณ€์ˆ˜๋กœ ์„ ์–ธํ•˜์ง€ ์•Š๊ณ  ์ดˆ๊ธฐํ™” ํ•˜๋Š” ๋ฐฉ๋ฒ•
  // new  ์‚ฌ์šฉํ•œ ์ดˆ๊ธฐํ™” ๋ฐฉ๋ฒ•
  //    new ์ดˆ๊ธฐํ™” ๋ฐฉ๋ฒ•์€ ๋‚ด๋ถ€ ํ•„๋“œ๊ฐ’ ์ดˆ๊ธฐํ™”๋Š” ์•ˆ๋จ
  //    & ์‚ฌ์šฉํ•œ ์ดˆ๊ธฐํ™” ๋ฐฉ๋ฒ•๊ณผ ๋™์ผ &Data{} == new(Data)
  //    & ์‚ฌ์šฉํ•œ ์ดˆ๊ธฐํ™” ๋ฐฉ๋ฒ•์€ ๋‚ด๋ถ€ ํ•„๋“œ๊ฐ’๋„ ์ดˆ๊ธฐํ™” ๊ฐ€๋Šฅ p1 := &Data{3,4}
  p4 := &Data{}
  ChangeData(p4)
  p5 := new(Data)
  ChangeData(p5)

  // var p6 Data
  p6 := Data{}
  ChangeData(&p6)

  fmt.Printf("์ฃผ์†Ÿ๊ฐ’4 = %p\n", p4)
  fmt.Printf("p4.value = %d\n", p4.value)
  fmt.Printf("p4.value = %d\n", p4.value)

  fmt.Printf("์ฃผ์†Ÿ๊ฐ’5 = %p\n", p5)
  fmt.Printf("p5.value = %d\n", p5.value)
  fmt.Printf("p5.value = %d\n", p5.value)

  fmt.Printf("์ฃผ์†Ÿ๊ฐ’6 = %p\n", &p6)
  fmt.Printf("p6.value = %d\n", p6.value)
  fmt.Printf("p6.value = %d\n", p6.value)
}
  • ์Šคํƒ ๋ฉ”๋ชจ๋ฆฌ : ํž™๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ๋ณด๋‹ค ํ›จ์”ฌ ํšจ์œจ์ ์ด์ง€๋งŒ, ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • ํž™ ๋ฉ”๋ชจ๋ฆฌ : ํ•จ์ˆ˜์™ธ๋ถ€๋กœ ๊ณต๊ฐœ๋˜๋Š” ๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„ ํ• ๋‹น ์‹œ, ํž™ ๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ์—์„œ ํ• ๋‹น
package main

import "fmt"

type User struct{
  Name string
  Age int
}

func NewUser(name string, age int) *User{
  var u = User{name, age}
  return &u
}

func main() {
  userPointer := NewUser("AAA", 23)
  fmt.Println(userPointer)
  fmt.Printf("userPointer: %v has an address: %p\n", userPointer, userPointer)
}
* C/C++์—์„œ๋Š” malloc() ํ˜ธ์ถœํ•˜์—ฌ ํž™๋ฉ”๋ชจ๋ฆฌ ๊ณต๊ฐ„ ํ• ๋‹น
* java์—์„œ๋Š” ํด๋ž˜์Šคํƒ€์ž…์„ ํž™์—, ๊ธฐ๋ณธํƒ€์ž…์„ ์Šคํƒ์— ํ• ๋‹น
* Go์–ธ์–ด๋Š” ํƒˆ์ถœ๊ฒ€์‚ฌ(escape analysis)๋กœ ์ธ์Šคํ„ด์Šค(instance)๋ฅผ ์Šคํƒ๋ฉ”๋ชจ๋ฆฌ ๋˜๋Š” ํž™๋ฉ”๋ชจ๋ฆฌ ์ค‘ ์–ด๋Š ๋ฉ”๋ชจ๋ฆฌ์— ํ• ๋‹น ํ•  ์ง€ ๊ฒฐ์ •
* ํ•จ์ˆ˜์™ธ๋ถ€๋กœ ๊ณต๊ฐœ๋˜๋Š” ์ธ์Šคํ„ด์Šค์˜ ๊ฒฝ์šฐ ํ•จ์ˆ˜๊ฐ€ ์ข…๋ฃŒ ๋˜์–ด๋„ ์‚ฌ๋ฆฌ์ง€์ง€ ์•Š์Œ
  Suppose, we define a public method that returns ๋‚ด๋ถ€์—์„œ ์ƒ์„ฑ๋œ ๊ฐ์ฒด
  ์›๋ž˜, ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ ์„ ์–ธ๋œ ๋ณ€์ˆ˜๋Š” ํ•จ์ˆ˜๊ฐ€ ์ข…๋ฃŒ๋˜๋ฉด ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค.
  Go.์–ธ์–ด์—์„œ๋Š” ํƒˆ์ถœ๊ฒ€์‚ฌ์—์„œ u ๋ณ€์ˆ˜์˜ ์ธ์Šคํ„ด์Šค๊ฐ€ ํ•จ์ˆ˜ ์™ธ๋ถ€๋กœ ๊ณต๊ฐœ๋˜๋Š” ๊ฒƒ์„ ๋ถ„์„ํ•ด๋‚ด์„œ,
  u๋ฅผ ์Šคํƒ ๋ฉ”๋ชจ๋ฆฌ๊ฐ€ ์•„๋‹Œ ํž™๋ฉ”๋ชจ๋ฆฌ์—์„œ ํ• ๋‹นํ•˜๊ฒŒ๋จ. ํ•จ์ˆ˜๊ฐ€ ์ข…๋ฃŒ ๋˜์–ด๋„ ๋ฉ”๋ชจ๋ฆฌ์—์„œ ์‚ฌ๋ผ์ง€์ง€ ์•Š์Œ
  (GC๊ฐ€ ํ•ด๋‹น ๊ฐ์ฒด๋ฅผ ๋ถ„๋ณ„ ํ•  ์ˆ˜ ์žˆ์Œ)


* Go์–ธ์–ด ์Šคํƒ ๋ฉ”๋ชจ๋ฆฌ๋Š” ๊ณ„์† ์ฆ๊ฐ€๋˜๋Š” ๋™์  ๋ฉ”๋ชจ๋ฆฌ ํ’€.
*    ์ผ์ •ํ•œ ํฌ๊ธฐ๋ฅผ ๊ฐ–๋Š” C/C++์–ธ์–ด์™€ ๋น„๊ตํ•ด๋„ ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์„ฑ ๋†’๊ณ ,
*   ์žฌ๊ท€ ํ˜ธ์ถœ๋•Œ๋ฌธ์— ์Šคํƒ ๋ฉ”๋ชจ๋ฆฌ ๊ณ ๊ฐˆ๋ฌธ์ œ๋Š” ๋ฐœ์ƒ X

15.๋ฌธ์ž์—ด

s1 := "Hello string variable!"
s2 := `Special \t characters \t\t \n inside backticks are ignored\n
  backticks also ignores new lines
  line3
  line4
  line5`

fmt.Println(s1)
fmt.Println(s2)
  • runeํƒ€์ž… : 4-byte int32ํƒ€์ž…๊ณผ ๋ณ„์นญ
  • type rune int32
  // ๋ฌธ์ž ํ•˜๋‚˜๋ฅผ ํ‘œํ˜„ํ•˜๋Š”๋ฐ rune ํƒ€์ž… ์‚ฌ์šฉ
  // UTF-8์€ 1~3๋ฐ”์ดํŠธ
  // ์•ŒํŒŒ๋ฒณ ๋ฌธ์ž์—ดํฌ๊ธฐ =1, ํ•œ๊ธ€ ๋ฌธ์ž์—ด ํฌ๊ธฐ = 3
  var c rune = 'ํ•œ'

  fmt.Printf("%T\n", c) // ํƒ€์ž… ์ถœ๋ ฅ : int32
  fmt.Println(c)        // int32(rune) ํ˜•์‹ ๊ฐ’ ์ถœ๋ ฅ
  fmt.Printf("%c\n", c) // ๋ฌธ์ž์ถœ๋ ฅ

  // 12 = (1*3) +  (3*3)
  a := "abc๊ฐ€๋‚˜๋‹ค"
  fmt.Println(len(a))


  // string <-> []rune ํƒ€์ž… ๋ณ€ํ™˜
  //     []rune(str)
  //     string([]rune{...})
  str := "Hello World!"
  // []rune{72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100}
  runes := []rune(str)

  fmt.Println(str)
  fmt.Println(runes)
  fmt.Println(string(runes))
  fmt.Println(len(runes))

  for _,v := range runes {
    fmt.Printf("ํƒ€์ž…: %T, ๊ฐ’: %d, ๋ฌธ์ž์—ด: %c\n", v,v,v)
  }

  d := []byte(c)
  e := []rune(c)
  // [65 66 67 235 172 184 236 158 144 236 151 180]
  fmt.Println(d)
  // [65 66 67 47928 51088 50676]
  fmt.Println(e)

  // []byte and []rune are immutable, contrary to string
  d[5] = 23
  fmt.Println(string(d))
  e[3] = 244
  fmt.Println(string(e))

  // strng <-> []byte ๋ณ€ํ™˜
  // ๋ชจ๋“  ๋ฌธ์ž์—ด์€ 1๋ฐ”์ดํŠธ ๋ฐฐ์—ด๋กœ ๋ณ€ํ™˜ ๊ฐ€๋Šฅ
  • Compare String
  s1 := "Hello"
  s2 := "Hell"
  s3 := "Hello"

  fmt.Printf("%s == %s, %v\n", s1, s2, s1 == s2)
  fmt.Printf("%s == %s, %v\n", s1, s3, s1 == s3)

  str1 := "BBB"
  str2 := "aaaaAAA"
  str3 := "BBAD"
  str4 := "ZZZ"

  fmt.Printf("%s > %s, %v\n", str1, str2, str1 > str1)
  fmt.Printf("%s <= %s, %v\n", str3, str4, str3 <= str4)
  • string ๋ผ๋ฆฌ ๋Œ€์ž…ํ•˜๊ธฐ
  • Data, Len ๊ฐ™์€์ง€ ํ™•์ธ
import (
  "fmt"
  "unsafe"
  "reflect"
)

func main() {
  str1 := "Hello World"
  str2 := str1


  // string -> unsafe.Pointer -> *reflect.StringHeader
  stringHeader1 := (*reflect.StringHeader) (unsafe.Pointer(&str1))
  stringHeader2 := (*reflect.StringHeader) (unsafe.Pointer(&str2))

  // ๋‘ ๊ฐ’์ด ๊ฐ™์Œ
  fmt.Println(stringHeader1)
  fmt.Println(stringHeader2)
}
  • string is immutable
func main() {

  var str string = "Hello World"
  str= "How are you"
  // ERROR: string is immutable
  // str[2] = 'a'

  fmt.Println(str)

  var slice []byte = []byte(str)
  slice[2] = 'a'
  fmt.Printf("%s\n", slice)

  // str = string(slice)
  // fmt.Printf("%s\n", str)
}

16.ํŒจํ‚ค์ง€

  • go build ๋กœ ์‹คํ–‰ ํŒŒ์ผ ๋งŒ๋“ค๋•Œ go mod ํ•„์š”
go mod init [ํŒจํ‚ค์ง€๋ช…]
go mod init 'github.com/jnuho/goproject'
# Go๋ชจ๋“ˆ์— ํ•„์š”ํ•œ ํŒจํ‚ค์ง€๋ฅผ ์ฐพ์•„์„œ ๋‹ค์šด๋กœ๋“œ, ํ•„์š”ํ•œ ํŒจํ‚ค์ง€ ์ •๋ณด๋ฅผ go.mod, go.sum ํŒŒ์ผ์— ์ ์–ด์คŒ
go mod tidy
cat go.mod
  • ํŒจํ‚ค์ง€ ๋‚ด ํ•„๋“œ ์™ธ๋ถ€ ๊ณต๊ฐœ ์—ฌ๋ถ€
  • const, ๋ณ€์ˆ˜, ํ•จ์ˆ˜, ๋ณ„์นญ ํƒ€์ž…
# ch16
#   ใ„ด usepkg/usepkg.go
#   ใ„ด usepkg/go.mod
#   ใ„ด usepkg/go.sum
#     ใ„ด custompkg/custompkg.go
cd 16-package/usepkg

# ํผ๋ธ”๋ฆญ github ์žˆ๋Š” ๊ฒฝ์šฐ
# go mod init github.com/reponame/16-package/usepkg
go mod init 16-package/usepkg
go mod tidy

# ch16
#   ใ„ด ex16.2
#     ใ„ด ex16.2.go
#     ใ„ด publicpkg/publicpkg.go
cd 16-package/ex16.2
go mod init 16-package/ex16.2
go mod tidy
  • ํŒจํ‚ค์ง€ ์ดˆ๊ธฐํ™”
  • ์ „์—ญ๋ณ€์ˆ˜ ์ดˆ๊ธฐํ™”
  • init() ํ˜ธ์ถœ (๋งค๊ฐœ๋ณ€์ˆ˜, ๋ฐ˜ํ™˜๊ฐ’ ์—†๋Š” ํ•จ์ˆ˜)
package main

import (
  "fmt"
  "math/rand"

  // ๊ฒน์น˜๋Š” ํŒจํ‚ค์ง€ ์ด๋ฆ„ ๋ณ„์นญ์œผ๋กœ ๋ฌถ๊ธฐ
  "text/template"
  htemplate "html/template"

  // ํŒจํ‚ค์ง€๋ฅผ ์‚ฌ์šฉํ•˜์ง€๋Š” ์•Š์ง€๋งŒ ๋ถ€๊ณผํšจ๊ณผ ๋–„๋ฌธ์— import ํ•˜๋Š” ๊ฒฝ์šฐ
  "database/sql"

  // ๋ฐ‘์ค„ _์„ ์ด์šฉํ•ด์„œ init() ํ•จ์ˆ˜ ํ˜ธ์ถœ
  // ๋ฐ‘์ค„ _ ์ด์šฉํ•˜์—ฌ unused ์—๋Ÿฌ ๋ฐฉ์ง€
  _ "github.com/mattn/go-sqlite3"
)

type Service struct {
  db *sql.DB
}

func main() {

  template.New("foo").Parse(`{{define "T"}}Hello`)
  htemplate.New("foo").Parse(`{{define "T"}}Hello`)

  fmt.Println(rand.Int())
}
  • Go ๋ชจ๋“ˆ๋งŒ๋“ค๊ณ , ์™ธ๋ถ€ ํŒจํ‚ค์ง€ ํ™œ์šฉํ•˜๊ธฐ
cd 16-package/usepkg
go mod init 16-package/usepkg
mkdir custompkg
  • 16-package/usepkg/custompkg/custompkg.go
package custompkg

import "fmt"

func PrintCustom() {
  fmt.Println("This is custom package!")
}
  • 16-package/usepkg/usepkg.go
package main

import (
  "fmt"
  "16-package/usepkg/custompkg" // ๋ชจ๋“ˆ ๋‚ด ํŒจํ‚ค์ง€
  "github.com/guptarohit/asciigraph" // ์™ธ๋ถ€ ์ €์žฅ์†Œ ํŒจํ‚ค์ง€
  "github.com/tuckersGo/musthaveGo/16-package/expkg"
)

func main() {
  custompkg.PrintCustom()
  expkg.PrintSample()

  data := []float64{3,4,5,6,9,7,5,8,5,10,2,7,2,5,6}
  graph := asciigraph.Plot(data)
  fmt.Println(graph)
}
  • ํŒจํ‚ค์ง€๋ช…๊ณผ ํŒจํ‚ค์ง€ ์™ธ๋ถ€ ๊ณต๊ฐœ
cd 16-package/ex16.2/publicpkg
cat publicpkg.go
  package publicpkg

  import "fmt"

  const (
    PI = 3.1415
    pi = 3.1415 // ์™ธ๋ถ€์— ๊ณต๊ฐœ๋˜์ง€ ์•Š์Œ
  )

  var ScreenSize int = 1080 
  var screenHeight int // ์™ธ๋ถ€์— ๊ณต๊ฐœ๋˜์ง€ ์•Š์Œ

  func PublicFunc() {
    const MyConst = 100 // ์™ธ๋ถ€์— ๊ณต๊ฐœ๋˜์ง€ ์•Š์Œ (๋Œ€๋ถ„์ž ๋ณ€์ˆ˜๋ผํ•ด๋„, ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ ์„ ์–ธ๋˜์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์™ธ๋ถ€์— ๊ณต๊ฐœ๋˜์ง€ ์•Š์Œ)
    fmt.Println("This is a public function", MyConst)
  }

  func privateFunc() { // ์™ธ๋ถ€์— ๊ณต๊ฐœ๋˜์ง€ ์•Š์Œ
    fmt.Println("This is a private function")
  }

  type MyInt int
  type myString string // ์™ธ๋ถ€์— ๊ณต๊ฐœ๋˜์ง€ ์•Š์Œ

  type MyStruct struct {
    Age int
    name string // ์™ธ๋ถ€์— ๊ณต๊ฐœ๋˜์ง€ ์•Š์Œ
  }

  func (m MyStruct) PublicMethod() {
    fmt.Println("This is a public method")
  }

  func (m MyStruct) privateMethod() { // ์™ธ๋ถ€์— ๊ณต๊ฐœ๋˜์ง€ ์•Š์Œ
    fmt.Println("This is a private method")
  }

  type myPrivateStruct struct { // ์™ธ๋ถ€์— ๊ณต๊ฐœ๋˜์ง€ ์•Š์Œ
    Age int
    name string
  }

  func (m myPrivateStruct) PrivateMethod() { // ์™ธ๋ถ€์— ๊ณต๊ฐœ๋˜์ง€ ์•Š์Œ
    fmt.Println("This is a private method")
  }
cat 16-package/ex16.2
cat ex16.2.go
  package main

  import (
    "fmt"
    "16-package/ex16.2/publicpkg"
  )

  func main() {
    fmt.Println("PI: ", publicpkg.PI)
    publicpkg.PublicFunc()

    var myint publicpkg.MyInt = 10
    fmt.Println("myint:", myint)

    var mystruct = publicpkg.MyStruct{Age: 18}
    fmt.Println("mystrcut:", mystruct)
  }
  • ํŒจํ‚ค์ง€ ์ž„ํฌํŠธํ•˜๋ฉด ๋‹ค์Œ ์ง„ํ–‰
  • ์ „์—ญ๋ณ€์ˆ˜ ์ดˆ๊ธฐํ™”
  • ํŒจํ‚ค์ง€์— init() ์ •์˜๋˜์–ด ์žˆ๋‹ค๋ฉด ํ˜ธ์ถœ
    • ๋งŒ์•ฝ ์–ด๋–ค ํŒจํ‚ค์ง€์˜ ์ดˆ๊ธฐํ™” ํ•จ์ˆ˜์ธ init() ํ•จ์ˆ˜๊ธฐ๋Šฅ๋งŒ ์‚ฌ์šฉํ•˜๊ธธ ์›ํ•  ๊ฒฝ์šฐ ๋ฐ‘์ค„ _ ๋กœ ์ž„ํฌํŠธ

17. ๋žœ๋ค์ˆซ์ž ๋งž์ถ”๊ธฐ ๊ฒŒ์ž„

package main

import (
    "bufio"
    "fmt"
    "math/rand"
    "os"
    "time"
)

func Print(o ...interface{}) {
    fmt.Println(o...)
}

func InputIntValue(stdin *bufio.Reader) (int, error) {
    var n int
    fmt.Printf("Guess number (1~100): ")
    _, err := fmt.Scanln(&n)
    if err != nil {
        stdin.ReadString('\n')
    }
    return n, err
}

// Scanln reads a small piece of input (like a number or a word) until the first Enter key press (newline).
// stdin.ReadString reads the entire line of input (including spaces) until the Enter key press (newline).
// Remember, buffered streams help manage data flow,
// and Scanln and ReadString are like different ways of listening to your friend's answers
// โ€”either short and quick or long and detailed!
// => stdin is more explicit about handling buffering

// Both methods involve buffering, but Scanln abstracts it away for you.
// ReadString gives you more control over the buffering process.
func main() {
    // Seed random
    rand.New(rand.NewSource(time.Now().UnixNano()))

    stdin := bufio.NewReader(os.Stdin)
    guess := rand.Intn(100) + 1
    cnt := 1

    for {
        n, err := InputIntValue(stdin)
        if err != nil {
            fmt.Println("Only numbers are allowed")
        } else {
            if n == guess {
                fmt.Printf("\nYou're correct! %d was guessed in %d attempts!\n", guess, cnt)
                break
            } else if n < guess {
                fmt.Printf("it is bigger than %d\n", n)
            } else if n > guess {
                fmt.Printf("it is smaller than %d\n", n)
            }
            cnt++
        }
    }
}

18.์Šฌ๋ผ์ด์Šค

  • slice: ๋™์ ๋ฐฐ์—ด
  • ์ผ๋ฐ˜์ ์ธ ๋ฐฐ์—ด var array [10]int์€ ์ผ์ •ํ•œ ๊ธธ์ด์—์„œ ๋Š˜์–ด๋‚˜์ง€ ์•Š๋Š” ๋ฌธ์ œ
  • ๋™์ ๋ฐฐ์—ด ์Šฌ๋ผ์ด์Šค : var slice []int

  • Slice Interview Questions

    1. A slice is a reference data type. Inside there is a pointer to the first element of the slice. This factor is what determines how certain operations, even when performed on copies of the slice, can affect the original slice.
    2. A slice has a length, which describes the number of elements currently stored in the slice, and a capacity, which indicates how many elements can be added to this memory area.
    3. If the inequality len(x) + 1 <= cap(x) is not met when adding a new element, the slice expands into a new area of memory, and capacity doubles (until it reaches the size of 1024, after which they increase by 25% with each expansion).
    4. When you pass a slice as an argument to a function as a copy (not via a pointer), you should remember that the slice contains a pointer to the first element, which allows for modifications to the original slice.
    5. The length and capacity values are passed by copy. If you pass a slice to a function and then the same slice is modified elsewhere in the code (e.g., by adding a new element), it will not affect the length and capacity of the copied slice within the function.
// ์ดˆ๊ธฐํ™” ๋ฐฉ๋ฒ•
var slice1 = []int{1,2,3} //len:3 cap: 3
var slice2 = []int{1, 5:2, 10:3} // [1 0 0 0 0 2 0 0 0 0 3]
var slice3 = make([]int, 3, 5) // len: 3 cap: 5

for i := 0; i<len(slice1); i++ {
  fmt.Print(slice1[i], " ")
}
for i,v := range slice1 {
  slice[i] = v*2
}
  • ์Šฌ๋ผ์ด์Šค ๋™์ž‘์›๋ฆฌ
  • func(arr [5]int) ๋ฐฐ์—ด ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„˜๊ธฐ๋ฉด ์ „์ฒด ๋ฐฐ์—ด COPY : array ์š”์†Œ๋“ค ๋ณ€๊ฒฝ ์ ์šฉ X
  • func(slice []int) ์Šฌ๋ผ์ด์Šค ํŒŒ๋ผ๋ฏธํ„ฐ ํฌ์ธํ„ฐ๊นŒ์ง€ ๋ณต์ œ๋˜๋ฏ€๋กœ: array ์š”์†Œ๋“ค ๋ณ€๊ฒฝ ์ ์šฉ ๋จ

    • reallocation is expensive in terms of time and memory
  • Length: The length of a slice is the number of elements it currently contains. You can get the length of a slice using the built-in len() function.

  • Capacity: The capacity of a slice represents the maximum number of elements it can hold without allocating additional memory. You can get the capacity of a slice using the built-in cap() function. Initially, a slice's capacity is equal to its length, but it can grow as needed. For example:

  • Performance

Understanding the difference between length and capacity is crucial for performance optimization. When appending elements to a slice, Go may need to allocate a new underlying array if the capacity is exceeded. This reallocation can be expensive in terms of time and memory.

  • Avoiding Reallocation

If you know the expected size of your data, you can preallocate a slice with sufficient capacity to avoid frequent reallocations during appends. This can significantly improve performance. For example:

// Preallocate a slice with capacity 100
myData := make([]int, 0, 100)

// Append elements to myData
// ...
// No reallocation needed as long as len(myData) < 100
  • Example of a slice with len=cap=0 and appending
  • When you create a slice without specifying an initial capacity (e.g., []int{}),
  • Go initializes it with a default capacity of 0. However, as you append elements to the slice, Go dynamically manages its capacity.

  • Dynamic Capacity Growth:

  • When you append elements to a slice, Go automatically increases its capacity as needed.
  • Initially, the slice has a capacity of 0 (no underlying array).
  • As you append elements, Go allocates a new underlying array with a larger capacity
    • (usually double the current capacity).
  • The existing elements are copied to the new array, and the slice now points to the new array.

  • Capacity Doubling and Beyond:

  • Initially, the capacity doubles each time it exceeds the current capacity.
  • However, after a certain point (usually when the capacity reaches 1024 elements), Go switches to increasing the capacity by 25% instead of doubling.
  • This strategy balances memory usage and reallocation frequency.

  • Example:

package main

import "fmt"

/**
Length: 1, Capacity: 1
Length: 2, Capacity: 2
Length: 3, Capacity: 4
Length: 4, Capacity: 4
Length: 5, Capacity: 8
Length: 6, Capacity: 8
Length: 7, Capacity: 8
Length: 8, Capacity: 8
Length: 9, Capacity: 16
Length: 10, Capacity: 16
*/
func main() {
    var s []int // Create an empty slice (capacity 0)
    for i := 0; i < 10; i++ {
        s = append(s, i)
        fmt.Printf("Length: %d, Capacity: %d\n", len(s), cap(s))
    }
}

In this example, observe how the capacity grows dynamically as elements are appended to the slice.

  1. Efficient Programming Tips:
  2. If you know the expected maximum size of your slice, consider preallocating capacity using make (e.g., make([]int, 0, 100)).
  3. Avoid unnecessary reallocations by setting an initial capacity that accommodates your use case.
  4. Be mindful of shared underlying arrays when creating multiple slices from the same array.

Go's dynamic capacity management ensures efficient memory usage while allowing you to work with slices without worrying about manual reallocation.

type SliceHeader struct {
  Data uintptr  // ์‹ค์ œ ๋ฐฐ์—ด์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ํฌ์ธํ„ฐ
  Len int       // ์š”์†Œ๊ฐœ์ˆ˜
  Cap int       // ์‹ค์ œ ๋ฐฐ์—ด์˜ ๊ธธ์ด
}
  • ๋ฌธ์ œ๋ฐœ์ƒ CASE1
package main
import "fmt"

func main() {
  slice1 := make([]int, 3, 5)
  slice2 := append(slice1, 4, 5)
    slice3 := append(slice2, 6)

  // len() cap()
  // slice1:  [0 0 0] 3 5
  fmt.Println("slice1: ", slice1, len(slice1), cap(slice1))
  // slice2:  [0 0 0 4 5] 5 5
  fmt.Println("slice2: ", slice2, len(slice2), cap(slice2))
  // slice3:  [0 0 0 4 5 6] 6 10
    fmt.Println("slice3: ", slice3, len(slice3), cap(slice3))

  // ๋ฌธ์ œ1: slice1๋ณ€๊ฒฝ ์‹œ ๋‘˜๋‹ค ๋ฐ”๋€œ
  slice1[1] = 100
  fmt.Println("After slice1[1]=100")
  fmt.Println("slice1: ", slice1, len(slice1), cap(slice1))
  fmt.Println("slice2: ", slice2, len(slice2), cap(slice2))
  fmt.Println("slice3: ", slice3, len(slice3), cap(slice3))

  // ๋ฌธ์ œ2: slice1 append()ํ•˜๋ฉด 1,2 ๋‘˜๋‹ค ๋ฐ”๋€Œ๋Š”๋ฐ,
  // len์— ๋”ฐ๋ผ append ์œ„์น˜๊ฐ€ ๋‹ค๋ฆ„
  slice1 = append(slice1, 500)
  fmt.Println("After append(slice1, 500)")
  fmt.Println("slice1: ", slice1, len(slice1), cap(slice1))
  fmt.Println("slice2: ", slice2, len(slice2), cap(slice2))
}

// slice1:  [0 0 0] 3 5
// slice2:  [0 0 0 4 5] 5 5
// After slice1[1]=100
// slice1:  [0 100 0] 3 5
// slice2:  [0 100 0 4 5] 5 5
// After slice1 = append(slice1, 500)
// slice1:  [0 100 0 500] 4 5
// slice2:  [0 100 0 500 5] 5 5
  • ๋ฌธ์ œ๋ฐœ์ƒ CASE2
// cap์ด ๋‹ค ์ฐผ์„๋•Œ, append() ํ•˜๋ฉด
// 2๋ฐฐ๋งŒํผ len ์ฆ๊ฐ€
package main
import "fmt"

func main() {
  // len=3 cap=3
  slice1 := []int{1,2,3}

  // cap=len์œผ๋กœ ์Šฌ๋ผ์ด์Šค ์šฉ๋Ÿ‰์ด ๋‹ค ์ฐฌ ์ƒํƒœ์—์„œ
  //  append() ์‹คํ–‰ ์‹œ cap*2๋งŒํผ ์šฉ๋Ÿ‰(capacity) ์ฆ๊ฐ€
  //  cap: 3-> 6
  //  ์ƒˆ๋กœ์šด ์šฉ๋Ÿ‰ (6)์˜ ๋ฐฐ์—ด์ด ์ƒ์„ฑ๋˜๋ฉด์„œ,
  //  slice1๊ณผ slice2๋Š” ๋‹ค๋ฅธ๋ฐฐ์—ด์„ ๊ฐ€๋ฆฌํ‚ค๊ฒŒ๋จ
  slice2 := append(slice1, 4, 5)
  fmt.Println("After slice2 := append(slice1, 4, 5) ")
  fmt.Println("slice1: ", slice1, len(slice1), cap(slice1))
  fmt.Println("slice2: ", slice2, len(slice2), cap(slice2))

  slice1[1] = 100
  fmt.Println("After slice1[1] = 100")
  fmt.Println("slice1: ", slice1, len(slice1), cap(slice1))
  fmt.Println("slice2: ", slice2, len(slice2), cap(slice2))

  slice1 = append(slice1, 500)
  fmt.Println("After slice1 = append(slice1, 500)")
  fmt.Println("slice1: ", slice1, len(slice1), cap(slice1))
  fmt.Println("slice2: ", slice2, len(slice2), cap(slice2))
}

// After slice2 := append(slice1, 4, 5) 
// slice1:  [1 2 3] 3 3
// slice2:  [1 2 3 4 5] 5 6
// After slice1[1] = 100
// slice1:  [1 100 3] 3 3
// slice2:  [1 2 3 4 5] 5 6
// After slice1 = append(slice1, 500)
// slice1:  [1 100 3 500] 4 6
// slice2:  [1 2 3 4 5] 5 6

  • ์Šฌ๋ผ์ด์‹ฑ
package main

import "fmt"

func main() {
  array := [5]int{1,2,3,4,5}
  slice := array[1:2]


  // ์Šฌ๋ผ์ด์‹ฑ cap๊ธธ์ด = ์‹œ์ž‘์ธ๋ฑ์Šค ~๋์ธ๋ฑ์Šค ๊นŒ์ง€์˜ ๊ธธ์ด
  //   ์—ฌ๊ธฐ์—์„œ๋Š” cap = ์‹œ์ž‘์ธ๋ฑ์Šค 1๋ถ€ํ„ฐ ๋๊นŒ์ง€ ๊ธธ์ด = 4
  fmt.Println("After slice := array[1:2]")
  fmt.Println("array: ", array)
  fmt.Println("slice: ", slice, len(slice), cap(slice))

  array[1] = 100
  fmt.Println("After array[1] = 100")
  fmt.Println("array: ", array)
  fmt.Println("slice: ", slice, len(slice), cap(slice))

  slice = append(slice, 500)
  fmt.Println("After slice = append(slice, 500)")
  fmt.Println("array: ", array)
  fmt.Println("slice: ", slice, len(slice), cap(slice))


  // IF ์Šฌ๋ผ์ด์Šค append๋ฅผ ๋ฐฐ์—ด ๊ธธ์ด 5์ด์ƒ์œผ๋กœ ์‹คํ–‰ํ•˜๋ฉด
  // array๋Š” ๊ทธ๋Œ€๋กœ, slice๋Š” ๋™์ ์œผ๋กœ ํฌ๊ธฐ ์ฆ๊ฐ€: cap*2 ์šฉ๋Ÿ‰ ์ฆ๊ฐ€ ๋ฐ len ์ฆ๊ฐ€
}


// After slice := array[1:2]
// array:  [1 2 3 4 5]
// slice:  [2] 1 4
// After array[1] = 100
// array:  [1 100 3 4 5]
// slice:  [100] 1 4
// After slice = append(slice, 500)
// array:  [1 100 500 4 5]
// slice:  [100 500] 2 4
  • ์Šฌ๋ผ์ด์Šค๋ฅผ ์Šฌ๋ผ์ด์‹ฑ ํ•˜๊ธฐ
package main

import "fmt"

func main() {
  slice1 := []int{1,2,3,4,5}
  fmt.Println("slice1: ", slice1, len(slice1), cap(slice1))

  // ์ค‘๊ฐ„
  slice2 := slice1[1:3]
  fmt.Println("slice1[1:3]: ", slice2, len(slice2), cap(slice2))

  // ์ฒ˜์Œ๋ถ€ํ„ฐ i๊นŒ์ง€
  slice3 := slice1[:3]
  fmt.Println("slice1[:3]: ", slice3, len(slice3), cap(slice3))

  // i๋ถ€ํ„ฐ ๋๊นŒ์ง€
  slice4 := slice1[1:]
  fmt.Println("slice1[1:]: ", slice4, len(slice4), cap(slice4))

  // ์ „์ฒด
  slice5 := slice1[:]
  fmt.Println("slice1[:]", slice5, len(slice5), cap(slice5))
}
// slice1:  [1 2 3 4 5] 5 5
// slice1[1:3]:  [2 3] 2 4
// slice1[:3]:  [1 2 3] 3 5
// slice1[1:]:  [2 3 4 5] 4 4
// slice1[:] [1 2 3 4 5] 5 5
  • ์Šฌ๋ผ์ด์Šค ๋ณต์ œ (์„œ๋กœ ๋‹ค๋ฅธ ๋ฐฐ์—ด ๊ฐ€๋ฆฌํ‚ค๋„๋ก)
package main
import "fmt"

func main() {
  slice1 := []int{1,2,3,4,5}

  // ๋ณต์ œ
  slice2 := make([]int, len(slice1))
  for i,v := range slice1 {
    slice2[i] = v
  }
  fmt.Println("After slice1-> slice2 ๋ณต์ œ(for๋ฌธ)")
  fmt.Println("slice1", slice1, len(slice1), cap(slice1))
  fmt.Println("slice2", slice2, len(slice2), cap(slice2))

  // slice1,2๋Š” ์„œ๋กœ ๋‹ค๋ฅธ ๋ฐฐ์—ด ๊ฐ€๋ฆฌํ‚ค๋ฏ€๋กœ slice1์˜ ๋ฐฐ์—ด๋งŒ ๋ฐ˜์˜
  slice1[1] = 100
  fmt.Println("After slice1[1] = 100")
  fmt.Println("slice1", slice1, len(slice1), cap(slice1))
  fmt.Println("slice2", slice2, len(slice2), cap(slice2))
}

// After slice1-> slice2 ๋ณต์ œ(for๋ฌธ)
// slice1 [1 2 3 4 5] 5 5
// slice2 [1 2 3 4 5] 5 5
// After slice1[1] = 100
// slice1 [1 100 3 4 5] 5 5
// slice2 [1 2 3 4 5] 5 5
  • ์Šฌ๋ผ์ด์Šค ๋ณต์ œ (append()๋กœ ์ฝ”๋“œ๊ฐœ์„ )
package main
import "fmt"

func main() {
  slice1 := []int{1,2,3,4,5}
  slice2 := append([]int{}, slice1...)
  fmt.Println("After slice2 := append([]int{}, slice1...)")
  fmt.Println("slice1", slice1, len(slice1), cap(slice1))
  fmt.Println("slice2", slice2, len(slice2), cap(slice2))
}
  • ์Šฌ๋ผ์ด์Šค ๋ณต์ œ (copy()๋กœ ์ฝ”๋“œ๊ฐœ์„ )
  • func copy(dest, src []Type) int
package main
import "fmt"

func main() {
  slice1 := []int{1,2,3,4,5}
  slice2 := make([]int, 3, 10) // len:3, cap:10
  slice3 := make([]int, 10) // len:10, cap:10

  // 3๊ฐœ๋งŒ ๋ณต์‚ฌ๋จ
  cnt1 := copy(slice2, slice1)
  // 5๊ฐœ ๋ณต์‚ฌ๋จ
  cnt2 := copy(slice3, slice1)

  fmt.Println("slice2: ", cnt1, slice2)
  fmt.Println("slice3: ", cnt2, slice3)


  // ๊ฐ™์€ cap, len์œผ๋กœ ๋ณต์ œํ•˜๋ ค๋ฉด:
  // for๋ฌธ์œผ๋กœ ์‹คํ–‰ ํ•˜๊ฑฐ๋‚˜
  // copy์ด์šฉ
  slice2 = make([]int, len(slice1))
  copy(slice2, slice1)
  fmt.Println("slice2: ", slice2)
}

// slice2:  3 [1 2 3]
// slice3:  5 [1 2 3 4 5 0 0 0 0 0]
  • ์Šฌ๋ผ์ด์Šค ์š”์†Œ ์‚ญ์ œ
  • ์‚ญ์ œํ•œ ์š”์†Œ ์ธ๋ฑ์Šค ์ดํ›„๋ฅผ 1์นธ์”ฉ ๋‹น๊ธฐ๊ณ , ๋งˆ์ง€๋ง‰ ์š”์†Œ ์Šฌ๋ผ์ด์‹ฑ ๊ธฐ๋Šฅ์œผ๋กœ ์ œ๊ฑฐ
  • ๋˜๋Š” append() ์กฐํ•ฉ์œผ๋กœ ์š”์†Œ์‚ญ์ œ ํ›„ reassign

  • ๋ฐฉ๋ฒ• 1. ํ•œ์นธ์”ฉ ๋‹น๊ธฐ๋Š” ์ž‘์—…: for๋ฌธ

package main
import "fmt"

func main() {
  slice1 := []int{1,2,3,4,5}
  del := 2

  for i:=del; i<len(slice1)-1; i++ {
    slice1[i] = slice1[i+1]
  }
  slice1 = slice1[:len(slice1)-1]

  fmt.Printf("After slice1[%d] is deleted: %v  %d %d\n", del, slice1, len(slice1), cap(slice1))
  • ๋ฐฉ๋ฒ• 2. append() ํ™œ์š”
package main
import "fmt"

func main() {
  slice1 := []int{1,2,3,4,5}
  del := 2

  slice1 = append(slice1[:del], slice1[del+1:]...)

  fmt.Println("slice1: ", slice1, len(slice1), cap(slice1))
}
  • ์Šฌ๋ผ์ด์Šค ์š”์†Œ ์ถ”๊ฐ€
  • ์ถ”๊ฐ€ํ•œ ์š”์†Œ ์ธ๋ฑ์Šค ์ดํ›„๋ฅผ 1์นธ์”ฉ ๋ฐ€๊ธฐ: for๋ฌธ
  • ๋˜๋Š” append() ์กฐํ•ฉ์œผ๋กœ ์š”์†Œ์ถ”๊ฐ€ ํ›„ reassign

  • ๋ฐฉ๋ฒ• 1. ํ•œ์นธ์”ฉ ๋ฏธ๋Š” ์ž‘์—…: for๋ฌธ

  • len+1์ด ๋˜์ง€๋งŒ cap*2 ๊ฐ€ ๋จ!
package main
import "fmt"
func main() {
  slice1 := []int{1,2,3,4,5}
  add := 2
  slice1 = append(slice1, 0)


  for i:= len(slice1)-1 ; i > add; i-- {
    slice1[i] = slice1[i-1]
  }
  slice1[add] = 100
  slice1 = slice1[:len(slice1)]

  // slice1 [1 2 100 3 4 5] 6 10
  fmt.Println("slice1", slice1, len(slice1), cap(slice1))
}
  • ๋ฐฉ๋ฒ• 2. append() ํ™œ์šฉ
package main
import "fmt"
func main() {
  slice1 := []int{1,2,3,4,5}
  add := 2
  slice1 = append(slice1[:add], append([]int{100}, slice1[add:]...)...)

  // slice1 [1 2 100 3 4 5] 6 10
  fmt.Println("slice1", slice1, len(slice1), cap(slice1))
}
  • ์Šฌ๋ผ์ด์Šค ์š”์†Œ (intํ˜•) ์ •๋ ฌ
package main
import (
  "fmt"
  "sort"
)
func main() {
  slice := []int{5, 1, 2, 3, 4}

  sort.Ints(slice)
  fmt.Println(slice)
}
  • ๊ตฌ์กฐ์ฒด ํƒ€์ž… ์Šฌ๋ผ์ด์Šค ์ •๋ ฌ
  • ๊ตฌ์กฐ์ฒด๋ฅผ Sort()๋กœ ์ •๋ ฌ ํ•˜๋ ค๋ฉด '๊ตฌ์กฐ์ฒด ๋ฆฌ์ŠคํŠธ' ๋Œ€ํ•ด Len(), Less(), Swap() ๋ฉ”์†Œ๋“œ ๊ตฌํ˜„ํ•„์š”
  • ๊ตฌ์กฐ์ฒด๋ฆฌ์ŠคํŠธ = []๊ตฌ์กฐ์ฒด
package main
import (
  "fmt"
  "sort"
)

type Student struct {
  Name string
  Age int
}

type Students []Student

func (s Students) Len() int {
  return len(s)
}

func (s Students) Less(i, j int) bool {
  return s[i].Age < s[j].Age
}

func (s Students) Swap(i, j int) {
  s[i], s[j] = s[j], s[i]
}

// ๊ตฌ์กฐ์ฒด ์ •๋ ฌ ์‹œ ํ•„์š”ํ•œ ์ •์˜ ๋ฉ”์†Œ๋“œ
// Len(), Less(), Swap()
func main() {
  // ์Šฌ๋ผ์ด์Šค์•ˆ์— {"A", 1} ์„ ์–ธ์‹œ
  // ๊ตฌ์กฐ์ฒด type ๋ช… ์ƒ๋žต
  slice := []Student {
    {"B", 2},
    {"C", 9},
    {"A", 1},
    {"D", 5},
  }

  sort.Sort(Students(slice))
  fmt.Println(slice)
}

19.๋ฉ”์†Œ๋“œ

  • func (r Rabbit) info() int
  • ๋ฆฌ์‹œ๋ฒ„: (r Rabbit)
  • ๋ฉ”์†Œ๋“œ๋ช…: info

  • ๋ฉ”์†Œ๋“œ์™€ ํ•จ์ˆ˜์˜ ๊ตฌ๋ถ„: '์†Œ์†'

  • ํ•จ์ˆ˜๋Š” ์–ด๋””์—๋„ ์†ํ•˜์ง€ ์•Š์ง€๋งŒ, ๋ฉ”์†Œ๋“œ๋Š” ๋ฆฌ์‹œ๋ฒ„์— ์†ํ•จ
  • ๋ฆฌ์‹œ๋ฒ„๋ฅผ ํ†ตํ•ด ํ•ด๋‹น ๋ฉ”์†Œ๋“œ์˜ ์†Œ์† ๋ช…์‹œ
  • ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด object์™€ ๊ธฐ๋Šฅ์„ ๋ฌถ์„ ์ˆ˜ ์žˆ์Œ
  • ์‘์ง‘๋„ ๋†’ํž˜
  • ๊ฐ์ฒด์ง€ํ–ฅ (์ ˆ์ฐจ์ค‘์‹ฌ -> ๊ด€๊ณ„์ค‘์‹ฌ)
  • ํด๋ž˜์Šค ์ƒ์†์€ ์ง€์› X, ๋ฉ”์†Œ๋“œ์™€ ์ธํ„ฐํŽ˜์ด์Šค O
package main
import "fmt"

type account struct {
  balance int
}

func withdrawFunc(a* account, amount int) { // ์ผ๋ฐ˜ํ•จ์ˆ˜ ํ‘œํ˜„
  a.balance -= amount
}

func (a *account) withdrawMethod(amount int) { // ๋ฉ”์†Œ๋“œ ํ‘œํ˜„
  a.balance -= amount
}

func main() {
  a := &account{100}

  withdrawFunc(a, 30)
  a.withdrawMethod(30)

  fmt.Printf("%d \n", a.balance)
}
  • ๋ณ„์นญ ๋ฆฌ์‹œ๋ฒ„ ํƒ€์ž…
  • int ํƒ€์ž…๋„ ๋ณ„์นญ์„ ํ†ตํ•ด ๋ฆฌ์‹œ๋ฒ„๋กœ ๊ธฐ๋Šฅํ•  ์ˆ˜ ์žˆ์Œ
package main
import "fmt"

// ์‚ฌ์šฉ์ž ์ •์˜ ๋ณ„์นญ ํƒ€์ž…
type myInt int

func (a myInt) add(b int) int {
  return int(a) + b
}

func main() {
  var a myInt = 10 // myIntํƒ€์ž…๋ณ€์ˆ˜
  res := a.add(30)
  fmt.Println("myInt + 30 = ", res)

  var b int = 20
  myInt(b)
}
  • ๊ฐ์ฒด์ง€ํ–ฅ: ์ ˆ์ฐจ์ค‘์‹ฌ -> ๊ด€๊ณ„์ค‘์‹ฌ
  • Student ๋Š” ๋‹จ์ˆœํžˆ ์ด๋ฆ„,๋‚˜์ด์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ์•„๋‹ˆ๋ผ,
  • ๊ณผ๋ชฉ์„ ๋“ฑ๋กํ•˜๊ณ , ๋ฆฌํฌํŠธ๋ฅผ ๋ณด๋‚ด๋Š” ๊ธฐ๋Šฅ์ด ์ถ”๊ฐ€๋œ ๊ฐ์ฒด(Object)๊ฐ€ ๋จ.
type Student struct {
  FirstName string
  LastName string
  Age int
}

// Student ๊ตฌ์กฐ์ฒด์— ์†ํ•˜๋Š” ๋ฉ”์†Œ๋“œ๋“ค
func (s *Student) EnrollClass(c *Subject) {
  // ...
}

func (s *Student) SendReport(p *Professor, r *Report) {
  // ...
}
  • ํฌ์ธํ„ฐ ๋ฉ”์†Œ๋“œ vs ๊ฐ’ํƒ€์ž… ๋ฉ”์†Œ๋“œ
package main
import "fmt"

type account struct {
  balance int
  firstName string
  lastName string
}

// 1. ํฌ์ธํ„ฐํƒ€์ž… ๋ฉ”์†Œ๋“œ
func (a1 *account) withdrawPointer(amount int) {
  a1.balance -= amount
}

// 2. ๊ฐ’ํƒ€์ž… ๋ฉ”์†Œ๋“œ
func (a2 account) withdrawValue(amount int) {
  a2.balance -= amount
}

// 3. ๋ณ€๊ฒฝ๋œ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฐ’ ํƒ€์ž… ๋ฉ”์†Œ๋“œ
//  ๊ฐ’ํƒ€์ž… ๋ฉ”์†Œ๋“œ๋ฅผ ๋ณด์™„ํ•˜์ง€๋งŒ, ๋ฉ”๋ชจ๋ฆฌ์˜ ๋ณต์‚ฌ๊ฐ€ ์—ฌ๋Ÿฌ๋ฒˆ ์ผ์–ด๋‚˜ ๋ฉ”๋ชจ๋ฆฌ ๋น„ํšจ์œจ์ 
func (a3 account) withdrawReturnValue(amount int) account {
  a3.balance -= amount
  return a3
}

func main() {
  var mainA *account = &account{100, "Joe", "Park"}

  mainA.withdrawPointer(30)
  fmt.Println(mainA)

  // ๊ณ ์–ธ์–ด์—์„œ๋Š” (*mainA).withdrawValue(30) ์™€ ๊ฐ™์Œ
  // ๋ณต์‚ฌ๊ฐ€ ์ผ์–ด๋‚˜๋ฏ€๋กœ mainA์— ๋ณ€๊ฒฝ๊ฐ’ ๋ฐ˜์˜์•ˆ๋จ
  mainA.withdrawValue(30)
  fmt.Println(mainA)

  mainB := mainA.withdrawReturnValue(30)
  fmt.Println(mainB)
}

20.์ธํ„ฐํŽ˜์ด์Šค

  • ์ธํ„ฐํŽ˜์ด์Šค ์ค‘๊ด„ํ˜ธ ๋ธ”๋ก์•ˆ์— ๋ฉ”์„œ๋“œ ์ง‘ํ•ฉ ์ •์˜์‹œ ์œ ์˜์‚ฌํ•ญ:

  • ๋ฉ”์†Œ๋“œ๋Š” ๋ฐ˜๋“œ์‹œ ๋ฉ”์†Œ๋“œ๋ช…์ด ์žˆ์–ด์•ผ ํ•จ

  • ๋งค๊ฐœ๋ณ€์ˆ˜์™€ ๋ฐ˜ํ™˜์ด ๋‹ค๋ฅด๋”๋ผ๋„ ์ด๋ฆ„์ด ๊ฐ™์€ ๋ฉ”์†Œ๋“œ ์žˆ์„ ์ˆ˜ ์—†์Œ
  • ์ธํ„ฐํŽ˜์ด์Šค์—์„œ๋Š” ๋ฉ”์†Œ๋“œ ๊ตฌํ˜„ ํฌํ•จ X

  • ์ถ”์ƒํ™”

  • ๋‚ด๋ถ€๋™์ž‘์„ ๊ฐ์ถฐ์„œ ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•˜๋Š” ์ชฝ๊ณผ ์‚ฌ์šฉํ•˜๋Š” ์ชฝ ๋ชจ๋‘์—๊ฒŒ ์ž์œจ๋ฅด ์ฃผ๋Š” ๋ฐฉ์‹
  • ๊ตฌ์ฒดํ™”๋œ ๊ฐ์ฒด๊ฐ€ ์•„๋‹Œ ์ธํ„ฐํŽ˜์ด์Šค๋งŒ ๊ฐ€์ง€๊ณ  ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ ๊ฐ€๋Šฅ.
  • ์ฝ”๋“œ ์ˆ˜์ •์—†์ด ํ•„์š”์— ๋”ฐ๋ผ ๊ตฌ์ฒดํ™”๋œ ๊ฐ์ฒด๋ฅผ ๋ฐ”๊ฟ”์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ.
  • ํ”„๋กœ๊ทธ๋žจ์˜ ๋ณ€๊ฒฝ์š”์ฒญ์— ์œ ์—ฐํ•˜๊ฒŒ ๋Œ€์ฒ˜ ๊ฐ€๋Šฅ.
  • ๋•ํ…Œ์ดํ•‘ - ์„œ๋น„์Šค ์‚ฌ์šฉ์ž ์ค‘์‹ฌ ์ฝ”๋”ฉ
  • ๊ตฌ์กฐ์ฒด ํƒ€์ž…์ด ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค๋Š”๊ฑธ ๋ช…์‹œํ•  ํ•„์š” ์—†์Œ

  • ํฌํ•จ๋œ ์ธํ„ฐํŽ˜์ด์Šค

  • ๋นˆ ์ธํ„ฐํŽ˜์ด์Šค
  • ์ธํ„ฐํŽ˜์ด์Šค ๊ธฐ๋ณธ๊ฐ’

  • Interface as parameter

// NOTE // In Go, interfaces are already reference types. // When you pass an interface as an argument, you're effectively passing a reference\ // to the underlying concrete type that implements the interface. // Even if you pass an interface by value (without a pointer), // it behaves like a reference because it points to the actual implementation. // func SendBook(parcel string, sender Sender) {}

package main

import "fmt"

type Stringer interface {
    String() string
}

type Student struct {
    Name string
    Age  int
}

func (s *Student) String() string {
    return fmt.Sprintf("์•ˆ๋…•, ๋‚˜๋Š” %d์‚ด, %s๋ผ๊ณ  ํ•ด", s.Age, s.Name)
}

func main() {
    student := &Student{"Jake", 9}
  // ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ด์šฉํ•˜๋ฉด ๋ฉ”์„œ๋“œ ๊ตฌํ˜„์„ ํฌํ•จํ•œ ๊ตฌ์ฒด์ ์ธ ๊ฐ์ฒด(concrete object)๊ฐ€ ์•„๋‹Œ,
  // ์ถ”์ƒํ™”๋œ ๊ฐ์ฒด(e.g. stringer ๋ณ€์ˆ˜)๋กœ ์ƒํ˜ธ์ž‘์šฉ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  // Studentํƒ€์ž… ๊ฐ์ฒด๋Š” String()๋ฉ”์„œ๋“œ๋ฅผ ํฌํ•จํ•˜๊ธฐ ๋•Œ๋ฌธ์—,
  // stringer์— student๋ฅผ ๋Œ€์ž… ํ•  ์ˆ˜ ์žˆ๋‹ค. (abstract away)
    var stringer Stringer // ์ธํ„ฐํŽ˜์ด์Šค์˜ ๊ธฐ๋ณธ๊ฐ’์€ nil (e.g. stringer == nil)
  stringer = student

    fmt.Printf("%s\n", stringer.String())
}
  • ์šฐํŽธ์—…์ฒด fedex.FedexSender, koreaPost.PostSender

  • FedexSender

// github.com/tuckersGo/musthaveGo/ch20/fedex
package fedex
import "fmt"

type FedexSender struct {
}

func (f *FedexSender) Send(parcel string) {
  fmt.Printf("Fedex์—์„œ %s๋ฅผ ๋ณด๋ƒ…๋‹ˆ๋‹ค.\n", parcel)
}
  1. PostSender
// github.com/tuckersGo/musthaveGo/ch20/koreaPost
package koreaPost
import "fmt"

type PostSender struct{
}

func (p *PostSender) Send(parcel string) {
  fmt.Printf("Post์šฐํŽธ์—์„œ %s๋ฅผ ๋ณด๋ƒ…๋‹ˆ๋‹ค.\n", parcel)
}
  • ์ธํ„ฐํŽ˜์ด์Šค ์—†์ด ์‚ฌ์šฉ
package main

import (
  "fmt"
  "github.com/tuckersGo/musthaveGo/ch20/fedex"
  "github.com/tuckersGo/musthaveGo/ch20/koreaPost"
)

func SendFedexParcel(parcel string, f *fedex.FedexSender) {
  f.Send(parcel)
}

func SendKoreaPostParcel(parcel string, p *koreaPost.PostSender) {
  p.Send(parcel)
}

func main() {
  f := &fedex.FedexSender{}
  p := &koreaPost.PostSender{}

  // f.Send("fedex parcel")
  // p.Send("koreaPost parcel")
  SendFedexParcel("fedex parcel", f)
  SendKoreaPostParcel("koreaPost parcel", p)
}
  • ์ธํ„ฐํŽ˜์ด์Šค ์‚ฌ์šฉ
  • ๋‘๊ฐœ struct ํƒ€์ž…์ด ๊ธฐ๋Šฅ์„ ๊ณต์œ ํ•˜๋˜ implemntation์ด ์กฐ๊ธˆ ๋‹ค๋ฆ„
package main
import (
  "fmt"
  "github.com/tuckersGo/musthaveGo/ch20/fedex"
  "github.com/tuckersGo/musthaveGo/ch20/koreaPost"
)

type Sender interface {
  Send(parcel string)
}

// In Go, interfaces are already reference types.
// When you pass an interface as an argument, you're effectively passing a reference\
// to the underlying concrete type that implements the interface.
// Even if you pass an interface by value (without a pointer),
// it behaves like a reference because it points to the actual implementation.
func SendBook(parcel string, sender Sender) {
  sender.Send(parcel)
}

func main() {
  f := &fedex.FedexSender{}
  p := &koreaPost.PostSender{}

  SendBook("fedex์†Œํฌ", f)
  SendBook("koreaPost์†Œํฌ", p)
}


- ๋•ํƒ€์ดํ•‘
  - FedexSender, PostSender์— implements Sender๋ผ๋Š” ๋ช…์‹œ๋ฅผ ํ•˜์ง€ ์•Š์•„๋„
  - Sender์ธํ„ฐํŽ˜์ด์Šค ์˜ Send() ๋ฉ”์†Œ๋“œ๋งŒ ์ •์˜ํ–ˆ๋‹ค๋ฉด ์ž๋™์œผ๋กœ duckTyping ๋จ

- ์„œ๋น„์Šค ์‚ฌ์šฉ์ž ์ค‘์‹ฌ ์ฝ”๋”ฉ
  - Sender ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์„œ๋น„์Šค ์ œ๊ณต์ž์ธ Fedex๋‚˜ Post๊ฐ€ ์•„๋‹Œ ํŒจํ‚ค์ง€๋ฅผ ์ด์šฉํ•˜๋Š” ์ชฝ์—์„œ ๋งŒ๋“ฆ
  - ๋•ํƒ€์ดํ•‘์—์„œ๋Š” ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„ ์—ฌ๋ถ€๋ฅผ ํƒ€์ž…์„ ์–ธ์—์„œ ํ•˜๋Š”๊ฒŒ ์•„๋‹ˆ๋ผ, ์ธํ„ฐํŽ˜์ด์Šค๊ฐ€
  - ์‚ฌ์šฉ๋  ๋•Œ, ํ•ด๋‹น ํƒ€์ž…์ด ์ธํ„ฐํŽ˜์ด์Šค์— ์ •์˜๋œ ๋ฉ”์„œ๋“œ๋ฅผ ํฌํ•จํ–ˆ๋Š”์ง€ ์—ฌ๋ถ€๋กœ ๊ฒฐ์ •
  - ์„œ๋น„์Šค ์ œ๊ณต์ž๊ฐ€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ •์˜ํ•  ํ•„์š”์—†์ด, ๊ตฌ์ฒด์ ์ธ ๊ฐ์ฒด๋งŒ ์ œ๊ณตํ•˜๊ณ ,
  - ์„œ๋น„์Šค ์ด์šฉ์ž๊ฐ€ ํ•„์š”์— ๋”ฐ๋ผ ๊ทธ๋•Œ๊ทธ๋–„ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ •์˜ํ•ด์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ
// AํšŒ์‚ฌ: B,CํšŒ์‚ฌ์˜ ์ œํ’ˆ ์„ฑ๋Šฅ ๋น„๊ตํ•˜๊ณ ์ž ํ•จ
//    AํšŒ์‚ฌ๊ฐ€ ์ง์ ‘ Database ์ธํ„ฐํŽ˜์ด์Šค ์ •์˜ํ•˜์—ฌ
//    TotalTime ํ•จ์ˆ˜ ์‚ฌ์šฉํ•˜๋„๋ก ๊ตฌํ˜„
// ๊ตฌ์กฐ์ฒด BDatabase, CDatabase๊ฐ€ ๋‹ฌ๋ผ์„œ
// ํ•œ ํ•จ์ˆ˜์˜ ์ธ์ˆ˜๋กœ ์“ธ์ˆ˜ ์—†๊ธฐ๋•Œ๋ฌธ์—, Database ์ธํ„ฐํŽ˜์ด์Šค ์ •์˜
//    Database ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์‚ฌ์šฉ (reference to address)
//    (In Go, interfaces are already reference types!)
func TotalTime(db Database) int {
  db.Get()
  db.Set()
  return ?
}

func Compare() {
  BDB := &BDatabase{}
  CDB := &CDatabase{}

  if TotalTime(BDB) < TotalTime(CDB) {
    fmt.Println("BํšŒ์‚ฌ ์ œํ’ˆ์ด ๋” ๋น ๋ฆ„")
  } else {
    fmt.Println("CํšŒ์‚ฌ ์ œํ’ˆ์ด ๋” ๋น ๋ฆ„")
  }
}

// ๋•ํƒ€์ดํ•‘์„ ์ง€์›ํ•˜์ง€ ์•Š๋Š”๋‹ค๋ฉด
//    B ์ธํ„ฐํŽ˜์ด์Šค ์ •์˜, C ์ธํ„ฐํŽ˜์ด์Šค ์ •์˜ํ•˜์—ฌ
//    ๊ณ ๊ฐ์—๊ฒŒ ์•Œ๋ ค์ค˜์•ผํ•˜๋Š” ๋ถˆํŽธํ•จ
//    ๋˜ํ•œ, C์—๊ฒŒ B์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ง€์›ํ•˜๋„๋ก ์š”์ฒญ ํ•ด์•ผํ•จ
//  ๋•ํƒ€์ดํ•‘์„ ํ™œ์šฉํ•˜๋ฉด, ์ธํ„ฐํŽ˜์ด์Šค ์ง€์›์—ฌ๋ถ€๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ชฝ์—์„œ ํŒ๋‹จ
  1. ํฌํ•จ๋œ ์ธํ„ฐํŽ˜์ด์Šค
package main

type Reader interface {
  Read()(n int, err error)
  Close() error
}

type Writer interface {
  Write()(n int, err error)
  Close() error
}

type ReadWriter interface {
  Reader
  Writer
}
// 1 Read() Write() Close() ํฌํ•จ ํƒ€์ž…
//    => Reader, Writer, ReadWriter ๋ชจ๋‘ ์‚ฌ์šฉ๊ฐ€๋Šฅ
// 2 Read() Close() ํฌํ•จ ํƒ€์ž…
//    => Reader๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
// 3 Write Close() ํฌํ•จ ํƒ€์ž…
//    => Writer๋งŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
// 4 Read() Write() ํฌํ•จ ํƒ€์ž…
//    => Close()์—†๊ธฐ ๋–„๋ฌธ์— 3๊ฐœ interface ๋ชจ๋‘ ์‚ฌ์šฉ ๋ถˆ๊ฐ€๋Šฅ
  1. ๋นˆ ์ธํ„ฐํŽ˜์ด์Šค

  2. interface{}๋Š” ๋ฉ”์„œ๋„๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š์€ ๋นˆ ์ธํ„ฐํŽ˜์ด์Šค

  3. ๊ฐ€์ง€๊ณ  ์žˆ์–ด์•ผํ•  ๋ฉ”์„œ๋“œ๊ฐ€ ํ•˜๋‚˜๋„ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๋ชจ๋“  ํƒ€์ž…์ด ๋นˆ์ธํ„ฐํŽ˜์ด์Šค๋กœ ์“ฐ์ผ ์ˆ˜ ์žˆ์Œ
  4. ์–ด๋–ค๊ฐ’์ด๋“  ๋ฐ›์„ ์ˆ˜ ์žˆ๋Š” ํ•จ์ˆ˜, ๋ฉ”์†Œ๋“œ, ๋ณ€์ˆซ๊ฐ’์„ ๋งŒ๋“ค ๋–„ ์‚ฌ์šฉ

  5. ๋นˆ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ์ธ์ˆ˜๋กœ ๋ฐ›๊ธฐ

package main
import "fmt"


type Student struct{
  Age int
}

func PrintVal(v interface{}) {

  switch t:= v.(type) {
    case int:
      fmt.Printf("v is int %d\n", int(t))
    case float64:
      fmt.Printf("v is float64 %f\n", float64(t))
    case string:
      fmt.Printf("v is string %s\n", string(t))
    case []int:
      fmt.Printf("v is []int Slice %T:%v", t, t)
    case [5]int:
      fmt.Printf("v is [5]int Array %T:%v", t, t)
    default:
      fmt.Printf("Not supported type %T:%v\n", t, t)
  }
}

func main() {
  PrintVal(10)
  PrintVal(3.14)
  PrintVal("Hello")
  PrintVal(Student{15})

  arr := [5]int{1,2,3,4,5}
  slice := arr[2:]
  PrintVal(arr)
  PrintVal(slice)
}
  1. ์ธํ„ฐํŽ˜์ด์Šค ๊ธฐ๋ณธ ๊ฐ’ nil
package main

type Attacker interface {
  Attack()
}

func main() {
  var att Attacker // ์ธํ„ฐํŽ˜์ด์Šค ๊ธฐ๋ณธ๊ฐ’์€ nil์ž…๋‹ˆ๋‹ค.
  att.Attack()     // att๊ฐ€ nil์ด๊ธฐ ๋•Œ๋ฌธ์— ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
}
  • ์ธํ„ฐํŽ˜์ด์Šค ๋ณ€ํ™˜ ํ•˜๊ธฐ

  • ๊ตฌ์ฒดํ™”๋œ ๋‹ค๋ฅธ ํƒ€์ž…์œผ๋กœ ํƒ€์ž… ๋ณ€ํ™˜

  • ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๋ณธ๋ž˜์˜ ๊ตฌ์ฒดํ™”๋œ ํƒ€์ž…์œผ๋กœ ๋ณต์›ํ•  ๋•Œ
  • ์ธํ„ฐํŽ˜์ด์Šค ๋ณ€์ˆ˜ a ๋ฅผ ConcreteType ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ, ConcreteType ๋ณ€์ˆ˜ t ์ƒ์„ฑํ•˜์—ฌ ๋Œ€์ž…
var a Interface
t := a.(ConcreteType)
package main
import "fmt"

type Stringer interface {
  String() string
}

// Student ๊ตฌ์กฐ์ฒด๊ฐ€  Stringer ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„
//    String()๋ฉ”์†Œ๋“œ ์ •์˜๋˜์–ด ์žˆ๊ธฐ ๋•Œ๋ฌธ
type Student struct {
  Age int
}

func (s *Student) String() string {
  return fmt.Sprintf("ํ•™์ƒ ๋‚˜์ด๋Š”: %d\n", s.Age)
}

// ์ธํ„ฐํŽ˜์ด์Šค -> ๊ตฌํ˜„์ฒด ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜
// Stringer -> *Student
func PrintAge(stringer Stringer) {
  // ํŠน์ • ๊ตฌํ˜„์ฒด ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜
  // PrintAge์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์–ด๋–ค ํƒ€์ž…์ด ๋“ค์–ด์˜จ์ง€ ์•Œ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— 
  s := stringer.(*Student)
  fmt.Printf("Age: %d\n", s.Age)
  // fmt.Println(stringer.String())
}

func main() {
  s := &Student{100}
  PrintAge(s)
}
  • ์ปดํŒŒ์ผ ์—๋Ÿฌ
  • ์ธํ„ฐํŽ˜์ด์Šค ๋ณ€์ˆ˜๋ฅผ ๊ตฌ์ฒดํ™”๋œ ํƒ€์ž…์œผ๋กœ ๋ณ€ํ™˜ ํ•˜๋ ค๋ฉด
  • ํ•ด๋‹น ํƒ€์ž…์ด ์ธํ„ฐํŽ˜์ด์Šค ๋ฉ”์„œ๋“œ ์ง‘ํ•ฉ์„ ํฌํ•จํ•˜๊ณ  ์žˆ์–ด์•ผํ•จ, ๊ทธ๋ ‡์ง€ ์•Š์€ ๊ฒฝ์šฐ ์ปดํŒŒ์ผ ์—๋Ÿฌ
type Stringer interface {
  String() string
}

type Student struct {
}

func main() {
  var Stringer stringer
  stringer.(*Student) // ์ปดํŒŒ์ผ ์—๋Ÿฌ
}

  • ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ
  • go build๋Š” ๋˜์ง€๋งŒ, ์‹คํ–‰ ์ค‘ ์—๋Ÿฌ ๋ฐœ์ƒ
package main
import "fmt"

type Stringer interface {
  String() string
}

type Student struct {
}

func (s *Student) String() string {
  return "Student"
}

type Actor struct {
}

func (a *Actor) String() string {
  return "Actor"
}

func ConvertType(stringer Stringer) {
  student := stringer.(*Student)
  fmt.Println(student)
}

func main() {
  actor := &Actor{}

  // ๋Ÿฐํƒ€์ž„ ์—๋Ÿฌ:
  // Actor -> Student ํƒ€์ž… ์—๋Ÿฌ!!!
  //  panic: interface conversion: main.Stringer is *main.Actor, not *main.Student
  ConvertType(actor)
}
  1. ๋‹ค๋ฅธ ์ธํ„ฐํŽ˜์ด์Šค๋กœ ๋ณ€ํ™˜
  2. ๊ตฌ์ฒดํ™”๋œ ํƒ€์ž… ๋ฟ์•„๋‹ˆ๋ผ, ๋‹ค๋ฅธ ์ธํ„ฐํŽ˜์ด์Šค๋กœ๋„ ๋ณ€ํ™˜ ๊ฐ€๋Šฅ
  3. AInterface -> ConcreteType <- BInterface
var AInterface = ConcreteType{}
b := a.(BInterface)
package main
import "fmt"

type Reader interface {
  Read()
}

type Closer interface {
  Close()
}

type File struct {
}

func (*f File) Read() {
}

func ReadFile(reader Reader) {
  c := reader.(Closer)
  // ERROR! ๋ฐœ์ƒ!
  // reader ์ธํ„ฐํŽ˜์ด์Šค๋ณ€์ˆ˜๋Š” Fileํƒ€์ž…์„ ๊ฐ€๋ฆฌํ‚ค๊ณ  ์žˆ๊ณ ,
  // File ํƒ€์ž…์€ Close() ๋ฉ”์„œ๋“œ๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์—๋Ÿฌ๋ฐœ์ƒ
  c.Close()
}

func main() {
  file := &File{}

  // ERROR!
  ReadFile(file)
}
  • ํƒ€์ž… ๋ณ€ํ™˜ ์„ฑ๊ณต ์—ฌ๋ถ€ ๋ฐ˜ํ™˜
package main
import (
  "fmt"
  "os"
)

type Reader interface {
  Read()
}

type Closer interface {
  Close()
}

type File struct {
}

func (f *File) Read() {
}

func ReadFile(reader Reader {
  if c, ok := reader.(Closer); ok {
    c.Close()
  }
}

func main() {
  f := &File{}
  ReadFile(f)
}

21.ํ•จ์ˆ˜๊ณ ๊ธ‰ํŽธ

  • ๋งค๊ฐœ๋ณ€์ˆ˜ ... ํ‚ค์›Œ๋“œ ์‚ฌ์šฉ
package main
import "fmt"

func sum(nums ...int) int {
  sum := 0

  fmt.Printf("nums ํƒ€์ž…: %T\n", nums)
  for _,v := range nums {
    sum += v
  }
  return sum
}

func PrintT(args ...interface{}) {
  for _,arg := range args {
    switch f := arg.(type) {
      case int:
        val := arg.(int)
        fmt.Println(val, int(f))
        fmt.Println(val == int(f))
        // Print๋กœ์ง
      case float64:
        val := arg.(float64)
        fmt.Println(val == float64(f))
      case string:
        val := arg.(string)
        fmt.Println(val == string(f))
    }
  }
}

func main() {
  fmt.Println(sum(1,2,3,4,5))
  fmt.Println(sum(10,20))
  fmt.Println(sum())

  PrintT(1,3.14, "abc")

}

  • defer ์ง€์—ฐ ์‹คํ–‰
  • ํ•จ์ˆ˜๊ฐ€ ์ข…๋ฃŒ๋˜๊ธฐ ์ง์ „์— ์‹คํ–‰ํ•ด์•ผํ•˜๋Š” ์ฝ”๋“œ์— ์‚ฌ์šฉ
    • ํŒŒ์ผ์ด๋‚˜ ์†Œ์บฃ ํ•ธ๋“ค ์ฒ˜๋Ÿผ OS ๋‚ด๋ถ€ ์ž์›์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ
    • ๋‚ด๋ถ€ ์ž์› ์ด๊ธฐ ๋–„๋ฌธ์—, ๋ฐ˜๋“œ์‹œ ์“ฐ๊ณ ๋‚˜์„œ OS์— ๋˜๋Œ๋ ค ์ค˜์•ผํ•จ.
    • ๋‚ด๋ถ€ ์ž์›/๋ฉ”๋ชจ๋ฆฌ ๊ณ ๊ฐˆ์„ ๋ง‰๊ธฐ ์œ„ํ•จ.
package main

import (
    "fmt"
    "os"
)

// 1->2->3->4
// ํŒŒ์ผ์— Hello World๋ฅผ ์”๋‹ˆ๋‹ค.
// ํŒŒ์ผ์„ ๋‹ซ์•˜์Šต๋‹ˆ๋‹ค.
// ๋ฐ˜๋“œ์‹œ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.
func main() {
    f, err := os.Create("test.txt") // ํŒŒ์ผ ์ƒ์„ฑ
    if err != nil {
        fmt.Println("Failed to create a file")
        return
    }
    defer fmt.Println("๋ฐ˜๋“œ์‹œ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค.") // 4
    defer f.Close() // 3
    defer fmt.Println("ํŒŒ์ผ์„ ๋‹ซ์•˜์Šต๋‹ˆ๋‹ค.") // 2

    fmt.Println("ํŒŒ์ผ์— Hello World๋ฅผ ์”๋‹ˆ๋‹ค.") //1
    fmt.Fprintln(f, "Hello, World!") // ํŒŒ์ผ์— ํ…์ŠคํŠธ๋ฅผ ์”๋‹ˆ๋‹ค.
}
  • ํ•จ์ˆ˜ํƒ€์ž… ๋ณ€์ˆ˜
  • ํ•จ์ˆ˜ ์‹œ์ž‘์ง€์ ์„ ๊ฐ€๋ฆฌํ‚ค๋Š” ํ”„๋กœ๊ทธ๋žจ ์นด์šดํ„ฐ ์žˆ์Œ e.g. 1๋ฒˆ๋ผ์ธ, 2๋ฒˆ๋ผ์ธ...
  • ํ•จ์ˆ˜ ์‹œ์ž‘์ง€์ ์€ ์ˆซ์ž๋กœ ํ‘œํ˜„๋˜๋ฉฐ, ๊ฐ€๋ฆฌํ‚ค๋Š” ๊ฐ’์„ ํ•จ์ˆ˜ํฌ์ธํ„ฐ๋ผ๊ณ  ํ•จ
  • ํ•จ์ˆ˜ ํƒ€์ž…์€ ํ•จ์ˆ˜์ •์˜๋กœ ํ‘œํ˜„ e.g. func (int, int) int
  • ํ•จ์ˆ˜ alias ์ง€์ •๊ฐ€๋Šฅ
    • type opFunc func (int, int) int
    • type CollectorOption func(*Collector)
  • ํ•จ์ˆ˜๋ฆฌํ„ฐ๋Ÿด (lambda; ์ต๋ช…ํ•จ์ˆ˜)
    • ํ•จ์ˆ˜๋ฆฌํ„ฐ๋Ÿด ์™ธ๋ถ€๋ณ€์ˆ˜๋ฅผ ์ž๋™์œผ๋กœ ๋ฆฌํ„ฐ๋Ÿด ํ•จ์ˆ˜ ๋‚ด๋ถ€์ƒํƒœ๋กœ ๊ฐ€์ ธ์™€ ์ €์žฅ ('capture') : ๊ฐ’๋ณต์‚ฌ๊ฐ€ ์•„๋‹Œ ์ฐธ์กฐํ˜•ํƒœ๋กœ ๊ฐ€์ ธ์˜ด
package main

import "fmt"

func add(a, b int) int {
  return a + b
}

func mul(a, b int) int {
  return a * b
}

type opFunc func(int, int) int

func getOperator(op string) opFunc {
  if op == "+" {
    return add
  } else if op == "*" {
    return mul
  } else {
    return nil
  }
}

func main() {
  // var opFnc func(int,int) int = getOperator("*")
  opFnc := getOperator("*")

  res := opFnc(2, 9)
  fmt.Println("2*9 = ", res)
}

  • ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด
  • ๋‹ค๋ฅธ ์–ธ์–ด ์—์„œ๋Š” lambda; ์ต๋ช… ํ•จ์ˆ˜
package main
import "fmt"

func getOperator(operator string) func(int, int) int {
  if operator == "*" {
    return func(a, b int) int {
      return a * b
    }
  } else if operator == "+" {
    return func(a, b int) int {
      return a + b
    }
  } else {
    return nil
  }
}

func main() {
  fn := getOperator("*")
  a, b := 3,4
  fmt.Printf("%d x %d = %d\n", a,b, fn(a,b))
}
  • ํ•จ์ˆ˜๋ฆฌํ„ฐ๋Ÿด ๋‚ด๋ถ€ ์ƒํƒœ
  • ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ ์‚ฌ์šฉ ๋˜๋Š” ์™ธ๋ถ€ ๋ณ€์ˆ˜๋Š” ์ž๋™์œผ๋กœ ํ•จ์ˆ˜ ๋‚ด๋ถ€ ์ƒํƒœ๋กœ ์ €์žฅ ๋จ
package main
import "fmt"
func main() {
  i := 0

  // ํ•จ์ˆ˜
  f := func() {
    i += 10
  }

  i++
  f()
  fmt.Println(i)
}
  • ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด ๋‚ด๋ถ€์ƒํƒœ ์ฃผ์˜์ 
  • ์บก์ณ: ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด ์™ธ๋ถ€ ๋ณ€์ˆ˜๋ฅผ ๋‚ด๋ถ€๋กœ ๊ฐ€์ ธ์˜ด
  • ๋‹ค๋งŒ ์บก์ณ๋Š” ๋ณต์‚ฌ๊ฐ€ ์•„๋‹Œ ์ฐธ์กฐ ํ˜•ํƒœ๋กœ ๊ฐ€์ ธ์˜ด
package main
import "fmt"

func CaptureLoop() {
  // make slice of functions with length 3
  f := make([]func(), 3)
  fmt.Println("CaptureLoop")

  // i ๊ฐ€ ๋ณต์‚ฌ๋˜๋Š”๊ฒƒ์ด ์•„๋‹Œ ์ฐธ์กฐ๋˜๊ธฐ ๋–„๋ฌธ์—
  for i :=0; i< len(f); i++ {
    f[i] = func() {
      // ์บก์ณํ•  ๋–„ ์บก์ณํ•˜๋Š” ์ˆœ๊ฐ„์˜ i๊ฐ’(1,2,3)์ด
      // ๋ณต์ œ ๋˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ๋ณ€์ˆ˜๊ฐ€ ์ฐธ์กฐ๋กœ ์บก์ณ๋˜๋ฏ€๋กœ
      // i๊ฐ€ ์ตœ์ข…์ ์œผ๋กœ 3์ด ๋˜์—ˆ์„๋–„
      // i๋ฅผ ์ฐธ์กฐํ•˜๋Š” f[0], f[1], f[2]๋Š” ๋ชจ๋‘ i=3๋ฅผ ์ฐธ์กฐํ•˜๊ฒŒ ๋จ
      fmt.Println(i)
    }
  }

  for i :=0; i< len(f); i++ {
    f[i]()
  }
}


func CaptureLoop2() {
  // make slice of functions with length 3
  f := make([]func(), 3)
  fmt.Println("CaptureLoop2")

  // i ๊ฐ€ ๋ณต์‚ฌ๋˜๋Š”๊ฒƒ์ด ์•„๋‹Œ ์ฐธ์กฐ๋˜๊ธฐ ๋–„๋ฌธ์—
  for i :=0; i< len(f); i++ {
    v := i
    f[i] = func() {
      // ์บก์ณํ•  ๋–„ ์บก์ณํ•˜๋Š” ์ˆœ๊ฐ„์˜ i๊ฐ’(1,2,3)์ด
      // ๋ณต์ œ ๋˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ, ๋ณ€์ˆ˜๊ฐ€ ์ฐธ์กฐ๋กœ ์บก์ณ๋˜๋ฏ€๋กœ
      // i๊ฐ€ ์ตœ์ข…์ ์œผ๋กœ 3์ด ๋˜์—ˆ์„๋–„
      // i๋ฅผ ์ฐธ์กฐํ•˜๋Š” f[0], f[1], f[2]๋Š” ๋ชจ๋‘ i=3๋ฅผ ์ฐธ์กฐํ•˜๊ฒŒ ๋จ
      fmt.Println(v)
    }
  }

  for i :=0; i< len(f); i++ {
    f[i]()
  }
}

func main() {
  // 3 3 3
  CaptureLoop()
  // 1 2 3
  CaptureLoop2()
}
  • ํŒŒ์ผ ํ•ธ๋“ค์„ ๋‚ด๋ถ€ ์ƒํƒœ๋กœ ์‚ฌ์šฉํ•˜๋Š” ์˜ˆ
package main
import (
  "os"
  "fmt"
)

type Writer func(string)

func writeHello(writer Writer) {
  writer("Hello World")
}

func main() {
  f, err := os.Create("test.txt")
  if err != nil {
    fmt.Println("Failed to create a file")
    return
  }

  defer f.Close()

  writeHello(func (msg string) {
    // ํ•จ์ˆ˜๋ฆฌํ„ฐ๋Ÿด ์™ธ๋ถ€ ๋ณ€์ˆ˜ f์‚ฌ์šฉ
    fmt.Fprintf(f, msg)
  })
}

22.์ž๋ฃŒ๊ตฌ์กฐ

  • ๋ฆฌ์ŠคํŠธ
  • ๋ฆฌ์ŠคํŠธ ๊ตฌํ˜„ํ•˜๋Š” ๊ตฌ์กฐ์ฒด ์ฝ”๋“œ
type Element struct {
  Value interface{}
  Next *Element
  Prev *Element
}
  • golang standard library container
  • https://pkg.go.dev/container
  • container/heap
  • container/list
  • container/ring

  • Slices (golang's idiomatic datatype) vs. container/list standard library

  • Slices are generally more efficient due to their contiguous memory layout and dynamic resizing strategy.
  • (Doubly) Linked lists (from container/list) have additional overhead due to memory allocation and pointer traversal.
  • Use slices for most scenarios unless you specifically need a doubly-linked list.

  • ๋ฆฌ์ŠคํŠธ container/list

  • ์Šคํƒ ๋‹ค๋“œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
// Element
func (e *Element) Next() *Element
func (e *Element) Prev() *Element

// List
// Package list implements a doubly linked list.
func New() *List
func (l *List) Back() *Element
func (l *List) Front() *Element
func (l *List) Init() *List
func (l *List) InsertAfter(v any, mark *Element) *Element
func (l *List) InsertBefore(v any, mark *Element) *Element
func (l *List) Len() int
func (l *List) MoveAfter(e, mark *Element)
func (l *List) MoveBefore(e, mark *Element)
func (l *List) MoveToBack(e *Element)
func (l *List) MoveToFront(e *Element)
func (l *List) PushBack(v any) *Element
func (l *List) PushBackList(other *List)
func (l *List) PushFront(v any) *Element
func (l *List) PushFrontList(other *List)
func (l *List) Remove(e *Element) any
  • ๋ฆฌ์ŠคํŠธ ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•
package main
import (
  "container/list"
  "fmt"
)

func main() {
  // PushBack, PushFront returns Pointer to newly insertedElement
  // 1 4
  //    1 <- e1
  //    4 <- e4
  v := list.New()
  e4 := v.PushBack(4)
  e1 := v.PushFront(1)

  v.InsertBefore(3, e4)
  v.InsertAfter(2, e1)

  for e := v.Front(); e != nil; e= e.Next() {
    fmt.Print(e.Value, " ")
  }
  fmt.Println()

  // ์—ญ์ˆœ
  for e := v.Back(); e != nil; e = e.Prev() {
    fmt.Print(e.Value, " ")
  }
}
  • ๋ฐฐ์—ด,์Šฌ๋ผ์ด์Šค vs. ๋ฆฌ์ŠคํŠธ
  • ์š”์†Œ์‚ฝ์ž… : O(N) vs. O(1)
  • ์š”์†Œ์‚ญ์ œ : O(N) vs. O(N)
  • ์š”์†Œ์ ‘๊ทผ : O(1) vs. O(N)

    • ๋ฐฐ์—ด์‹œ์ž‘์ฃผ์†Œ + (์ธ๋ฑ์Šค*ํƒ€์ž…ํฌ๊ธฐ)
  • ํ

  • Not a standard library: use container/list to custom implement
  • First In First Out
package main

import (
  "fmt"
  "container/list"
)

// list.List : Doubly Linked List!
//    .PushBack(n)
//    .Front()
//        front = .Front()
//    .Remove(front)
type Queue struct {
  v *list.List
}

func (q *Queue) Push(val interface{}) {
  q.v.PushBack(val)
}

func (q *Queue) Pop() interface{} {
  front := q.v.Front()
  if front != nil {
    return q.v.Remove(front)
  }
  return nil
}

func NewQueue() *Queue {
  return &Queue{ list.New()}
}

func main() {
  queue := NewQueue()

  for i :=1; i<=4; i++ {
    queue.Push(i)
  }

  v := queue.Pop()
  for v != nil {
    fmt.Print(v, " ")
    v = queue.Pop()
  }
  fmt.Println()
}
  • ์Šคํƒ
  • Not a standard library: use container/list to custom implement
  • First In Last Out
package main

import (
  "fmt"
  "container/list"
)

type Stack struct {
  v *list.List
}

func (s *Stack) Push(val interface{}) {
  s.v.PushBack(val)
}

func (s *Stack) Pop() interface{} {
  back := s.v.Back()
  if back !=nil {
    return s.v.Remove(back)
  }
  return nil
}

func NewStack() *Stack {
  return &Stack{ list.New() }
}

func main() {
  stack := NewStack()
  for i:=1; i<=4; i++ {
    stack.Push(i)
  }

  val := stack.Pop()
  for val != nil {
    fmt.Print(val , " ")
    val = stack.Pop()
  }
}
  1. ๋ง container/ring
  2. ์Šคํƒ ๋‹ค๋“œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
func New(n int) *Ring
func (r *Ring) Do(f func(any))
func (r *Ring) Len() int
func (r *Ring) Link(s *Ring) *Ring
func (r *Ring) Move(n int) *Ring
func (r *Ring) Next() *Ring
func (r *Ring) Prev() *Ring
func (r *Ring) Unlink(n int) *Ring
package main

import (
  "fmt"
  "container/ring"
)

func main() {
  r := ring.New(5)
  n := r.Len()

  for i:=0; i< n; i++ {
    r.Value = 'a' + i
    r = r.Next()
  }

  for i :=0; i<n; i++ {
    fmt.Printf("%c", r.Value)
    r = r.Next()
  }
  fmt.Println()

  for i :=0; i<n; i++ {
    fmt.Printf("%c", r.Value)
    r = r.Prev()
  }
}
  • ๋งต
  • ๊ฐ™์€์ž…๋ ฅ์ด ๋“ค์–ด์˜ค๋ฉด ๊ฐ™์€๊ฐ’์ด ๋‚˜์˜ด
  • ์ž…๋ ฅ๋ฒ”์œ„๋Š” ๋ฌดํ•œ, ๊ฒฐ๊ณผ๋Š” ํŠน์ •๋ฒ”์œ„
package main
import "fmt"

func main() {
  m := make(map[string]string)
  m["์ดํ•˜๋‚˜"] = "A100"
  m["์†กํ•˜๋‚˜"] = "A101"
  m["๋ฐ•ํ•˜๋‚˜"] = "A102"
  m["์ตœํ•˜๋‚˜"] = "A103"

  s := "์†กํ•˜๋‚˜"
  fmt.Print(s, ": ")
  fmt.Println(m[s])
}
  • ์š”์†Œ ์‚ญ์ œ์™€ ์—†๋Š” ์š”์†Œ ํ™•์ธ
package main
import "fmt"
func main() {
  m := make(map[int]int)
  m[1] = 0
  m[2] = 1
  m[3] = 2

  // delete(๋งต, key๊ฐ’)
  delete(m, 3)
  delete(m, 4) // ์—†๋Š”์š”์†Œ ์‚ญ์ œ์‹œ๋„

  v, ok := m[3]
  fmt.Println(v, ok) // ์‚ญ์ œ๋œ ์š”์†Œ๊ฐ’ ์ถœ๋ ฅ

  v, ok = m[1]
  fmt.Println(v, ok) // ์กด์žฌํ•˜๋Š” ์š”์†Œ๊ฐ’ ์ถœ๋ ฅ


  // ๋งต ์ˆœํ™˜ํ•˜๊ธฐ
  for k, v := range m {
    fmt.Printf("Key: %d, Value: %d\n", k, v)
  }
}
  • ๋ฐฐ์—ด,์Šฌ๋ผ์ด์Šค vs. ๋ฆฌ์ŠคํŠธ vs. ๋งต
  • ์‚ฝ์ž… : O(N) vs. O(1) vs. O(1)
  • ์‚ญ์ œ : O(N) vs. O(N) vs. O(1)
  • ์ ‘๊ทผ : O(1) vs. O(N) vs. O(1) ํ‚ค๋กœ ์ ‘๊ทผ

    • ๋ฐฐ์—ด์‹œ์ž‘์ฃผ์†Œ + (์ธ๋ฑ์Šค*ํƒ€์ž…ํฌ๊ธฐ)
  • ๋งต์ด ์ถ”๊ฐ€,์‚ญ์ œ,์ฝ๊ธฐ์—์„œ ์†๋„๊ฐ€ ๋น ๋ฅธ์ด์œ : ํ•ด์‰ฌํ•จ์ˆ˜

` ๋ฐฐ์—ด, ์Šฌ๋ผ์ด์Šค ๋ฆฌ์ŠคํŠธ ๋งต
์ถ”๊ฐ€ O(N) O(1) O(1)
์‚ญ์ œ O(N) O(1) O(1)
์ฝ๊ธฐ O(1)-์ธ๋ฑ์Šค๋กœ ์ ‘๊ทผ O(N)-์ธ๋ฑ์Šค๋กœ ์ ‘๊ทผ O(1)-ํ‚ค๋กœ ์ ‘๊ทผ
  • ๋งต์˜ ์›๋ฆฌ - ํ•ด์‹œํ•จ์ˆ˜๋Š” ๋‹ค์Œ 3๊ฐ€์ง€ ํŠน์ง•์„ ๋งŒ์กฑ ํ•ด์•ผํ•œ๋‹ค
  • ๊ฐ™์€ ์ž…๋ ฅ -> ๊ฐ™์€ ๊ฒฐ๊ณผ
  • ๋‹ค๋ฅธ ์ž…๋ ฅ -> (๋˜๋„๋ก) ๋‹ค๋ฅธ ๊ฒฐ๊ณผ (in general different result)
  • ์ž…๋ ฅ๊ฐ’์˜ ๋ฒ”์œ„๋Š” ๋ฌดํ•œ๋Œ€, ๊ฒฐ๊ณผ๋Š” ํŠน์ • ๋ฒ”์œ„๋ฅผ ๊ฐ–๋Š”๋‹ค.

    • e.g. f(x) = sign(x): ์‚ผ๊ฐํ•จ์ˆ˜๋Š” ๊ณ„์‚ฐ์ด ๋ณต์žกํ•ด์„œ, ์ฃผ๋กœ Mod๋ฅผ ์”€
    • e.g. f(x) = Mod(x, 10) does x % 10 operation
    • ๋‚˜๋จธ์ง€ ์—ฐ์‚ฐ์€ ๊ณ„์‚ฐ์ด ๋น ๋ฅด๊ณ , ๊ฒฐ๊ณผ๊ฐ’์˜ ๋ฒ”์œ„์™€ ๊ฐ„๊ฒฉ ์กฐ์ ˆ ์‰ฌ์›€
    • ๋ฐฐ์—ด๋กœ ๊ตฌํ˜„์‹œ ๊ฐ™์€ ํ•ด์‹œ๊ฐ’(์ธ๋ฑ์Šค)์— 1๊ฐœ์˜ ๊ฐ’๋งŒ ๋งตํ•‘
    • m[hash(23)] m[hash(33)] ํ•ด์‹œ์ถฉ๋Œ
    • ์ด ๋ฌธ์ œ๋Š” ๋ฐฐ์—ด์•ˆ์— ๋ฆฌ์ŠคํŠธ ์ €์žฅํ•˜์—ฌ ํ•ด๊ฒฐ๊ฐ€๋Šฅ
    • ํ•ด์‹œํ•จ์ˆ˜๋Š” ์š”์†Œ๊ฐœ์ˆ˜์™€ ์ƒ๊ด€์—†์ด ๊ณ ์ •๋œ ์‹œ๊ฐ„์„ ๊ฐ–๋Š” ํ•จ์ˆ˜์ด๋ฏ€๋กœ ๋งต์ด ์ฝ๊ธฐ,์“ฐ๊ธฐ์—์„œ O(1)์˜ ์‹œ๊ฐ„
  • ํ•ด์‹œ๋กœ ๋งต ๋งŒ๋“ค๊ธฐ

  • ํ•ด์‹œ์ถฉ๋Œ์€ ์ธ๋ฑ์Šค๋งˆ๋‹ค ๋ฆฌ์ŠคํŠธ์— ์ถฉ๋Œ๋˜๋Š” ๊ฐ’๋“ค ์ „๋ถ€ ์ €์žฅํ•˜์—ฌ ํ•ด๊ฒฐํ•œ๋‹ค
  • ํ•ด์‹œ ํ•จ์ˆ˜๋Š” ์•”ํ˜ธํ™”, ๋น„ํŠธ์ฝ”์ธ ๋“ฑ์—์„œ ๊ด‘๋ฒ”์œ„ํ•˜๊ฒŒ ์“ฐ์ž„
package main
import "fmt"

const M = 10

// returns in range of 0,1,...9
func hash(d int) int {
  return d % M
}

func main() {
  m := [M]int{}
  m[hash(23)] = 10
  m[hash(33)] = 10 // ๋ฎ์–ด ์”Œ์›Œ์ง (ํ•ด์‹œ์ถฉ๋Œ)
  m[hash(259)] = 50

  // Prints:
  // m[hash(23)] = 10
  // m[hash(33)] = 10
  // m[hash(259)] = 50
  fmt.Printf("m[hash(23)] = %d\n", m[hash(23)])
  fmt.Printf("m[hash(33)] = %d\n", m[hash(33)])
  fmt.Printf("m[hash(259)] = %d\n", m[hash(259)])
}

23.์—๋Ÿฌ ํ•ธ๋“ค๋ง

  • ์—๋Ÿฌ ๋ฐ˜ํ™˜
package main

import (
  "bufio"
  "fmt"
  "os"
)

const filename string =  "data.txt"

// ํŒŒ์ผ์—์„œ ํ•œ์ค„ ์ฝ๊ธฐ
func ReadFile(filename string) (string, error) {
  file, err := os.Open(filename)
  if err != nil {
    return "", err
  }
  defer file.Close()

  rd := bufio.NewReader(file)
  line, _ := rd.ReadString('\n')
  return line, nil
}

func WriteFile(filename, line string) error {
  file, err := os.Create(filename)
  if err != nil {
    return err
  }
  defer file.Close()
  _, err = fmt.Fprintln(file, line)
  return err
}

func main() {
  line, err := ReadFile(filename)
  if err != nil {
    err = WriteFile(filename, "This is WriteFile")
    if err !=nil {
      fmt.Println("ํŒŒ์ผ ์ƒ์„ฑ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค.", err)
      return
    }
    line, err = ReadFile(filename)
    if err !=nil {
      fmt.Println("ํŒŒ์ผ ์ฝ๊ธฐ์— ์‹คํŒจ ํ–ˆ์Šต๋‹ˆ๋‹ค.", err)
      return
    }
  }

  fmt.Println("ํŒŒ์ผ๋‚ด์šฉ: ", line)
}
  • ์‚ฌ์šฉ์ž ์—๋Ÿฌ ๋ฐ˜ํ™˜
  • fmt.Errorf() ์‚ฌ์šฉ ๋˜๋Š”
  • func New(text string) error ์—๋Ÿฌ ์ƒ์„ฑํ•˜์—ฌ ๋ฐ˜ํ™˜ ๊ฐ€๋Šฅ
import errors
errors.New("์—๋Ÿฌ ๋ฉ”์‹œ์ง€")
package main

import (
  "fmt"
  "math"
)

func Sqrt(f float64) (float64, error) {
  if f <0 {
    return 0, fmt.Errorf("์ œ๊ณฑ๊ทผ์€ ์–‘์ˆ˜์—ฌ์•ผ ํ•ฉ๋‹ˆ๋‹ค. f:%g", f)
  }
  return math.Sqrt(f), nil
}

func main() {
  sqrt, err := Sqrt(-2)
  if err != nil {
    fmt.Printf("์—๋Ÿฌ: %v\n", err)
    return
  }
  fmt.Printf("Sqrt(-2)=%v\n", sqrt)
}
  • ์—๋Ÿฌ ํƒ€์ž…
type error interface {
  Error() string
}
package main

import "fmt"

// Error() ๋ฉ”์†Œ๋“œ ์žˆ๋Š” error ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„
type PasswordError struct {
  Len int
  RequireLen int
}

func (err PasswordError) Error() string {
  return "์•”ํ˜ธ ๊ธธ์ด๊ฐ€ ์งง์Šต๋‹ˆ๋‹ค."
}

func RegisterAccount(name, password string) error {
  if len(password) < 8 {
    return PasswordError{len(password), 8}
  }
  return nil
}

func main() {
  err := RegisterAccount("myID", "myPw")
  if err != nil {
    if errInfo, ok := err.(PasswordError); ok {
      fmt.Printf("%v Len: %d RequiredLen: %d\n", errInfo, errInfo.Len, errInfo.RequireLen)
    }
  } else {
    fmt.Println("ํšŒ์› ๊ฐ€์ž…๋์Šต๋‹ˆ๋‹ค.")
  }
}
  • ์—๋Ÿฌ ๋žฉํ•‘
  • ์—๋Ÿฌ๋ฅผ ๊ฐ์‹ธ์„œ ์ƒˆ๋กœ์šด ์—๋Ÿฌ ๋งŒ๋“ค๊ธฐ
  • ์˜ˆ๋ฅผ๋“ค์–ด, ํŒŒ์ผ์„ ์ฝ์„๋–„ ์—๋Ÿฌ ๋ฟ ์•„๋‹ˆ๋ผ, ๋ช‡๋ฒˆ์งธ ์ค„์—์„œ ์—๋Ÿฌ๋ฐœ์ƒํ–ˆ๋Š”์ง€ ๋“ฑ ์ •๋ณด ๋‹ด์„ ์—๋Ÿฌ ํ•„์š”
// As finds the first error in err's chain that matches target,
// and if one is found, sets target to that error value
// and returns true. Otherwise, it returns false.
//    type any = interface{}
func As(err error, target any) bool
func As(err error, target interface{}) bool

// Reader is the interface that wraps the basic Read method.
// Read reads up to len(p) bytes into p.
// It returns the number of bytes read (0 <= n <= len(p)) and any error encountered. 
type Reader interface {
  Read(p []byte) (n int, err error)
}

// NewReader returns a new Reader reading from s.
// It is similar to bytes.NewBufferString but more efficient and read-only.
func NewReader(s string) *Reader

// NewScanner returns a new Scanner to read from r.
// The split function defaults to ScanLines.
func NewScanner(r io.Reader) *Scanner


// Split sets the split function for the Scanner.
// The default split function is ScanLines.
func (s *Scanner) Split(split SplitFunc)

// ScanWords is a split function for a Scanner that returns
// each space-separated word of text, with surrounding spaces deleted.
// It will never return an empty string.
// The definition of space is set by unicode.IsSpace.
func ScanWords(data []byte, atEOF bool) (advance int, token []byte, err error)
//ch23/ex23.4/ex23.4.go
package main

import (
  "bufio"
  "errors"
  "fmt"
  "strconv"
  "strings"
)

func MultipleFromString(str string) (int, error) {
  scanner := bufio.NewScanner(strings.NewReader(str)) // โถ ์Šค์บ๋„ˆ ์ƒ์„ฑ
  scanner.Split(bufio.ScanWords)                      // โท ํ•œ ๋‹จ์–ด์”ฉ ๋Š์–ด์ฝ๊ธฐ

  pos := 0
  a, n, err := readNextInt(scanner)
  if err != nil {
    return 0, fmt.Errorf("Failed to readNextInt(), pos:%d err:%w", pos, err) // โž ์—๋Ÿฌ ๊ฐ์‹ธ๊ธฐ
  }

  pos += n + 1
  b, n, err := readNextInt(scanner)
  if err != nil {
    return 0, fmt.Errorf("Failed to readNextInt(), pos:%d err:%w", pos, err)
  }
  return a * b, nil
}

// ๋‹ค์Œ ๋‹จ์–ด๋ฅผ ์ฝ์–ด์„œ ์ˆซ์ž๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
// ๋ณ€ํ™˜๋œ ์ˆซ์ž, ์ฝ์€ ๊ธ€์ž์ˆ˜, ์—๋Ÿฌ๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
func readNextInt(scanner *bufio.Scanner) (int, int, error) {
  if !scanner.Scan() { // โธ ๋‹จ์–ด ์ฝ๊ธฐ
    return 0, 0, fmt.Errorf("Failed to scan")
  }
  word := scanner.Text()
  number, err := strconv.Atoi(word) // โน ๋ฌธ์ž์—ด์„ ์ˆซ์ž๋กœ ๋ณ€ํ™˜
  if err != nil {
    return 0, 0, fmt.Errorf("Failed to convert word to int, word:%s err:%w", word, err) // โžŽ ์—๋Ÿฌ ๊ฐ์‹ธ๊ธฐ
  }
  return number, len(word), nil
}

func readEq(eq string) {
  rst, err := MultipleFromString(eq)
  if err == nil {
    fmt.Println(rst)
  } else {
    fmt.Println(err)
    var numError *strconv.NumError
    if errors.As(err, &numError) { // โž ๊ฐ์‹ธ์ง„ ์—๋Ÿฌ๊ฐ€ NumError์ธ์ง€ ํ™•์ธ
      fmt.Println("NumberError:", numError)
    }
  }
}
func main() {
  readEq("123 3")
  readEq("123 abc")
}
  • ํŒจ๋‹‰
  • ๋ฌธ์ œ ๋ฐœ์ƒ ์‹œ์ ์— ์ฆ‰์‹œ ์ข…๋ฃŒ
  • ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฉ”์‹œ์ง€ ํ‘œ์‹œ ํ›„ ํ˜ธ์ถœ ์ˆœ์„œ ๋‚˜ํƒ€๋‚ด๋Š” ์ฝœ์Šคํƒ ํ‘œ์‹œ (์—๋Ÿฌ ๋ฐœ์ƒ๊ฒฝ๋กœ ํ™•์ธ ๊ฐ€๋Šฅ)

  • ํŒจ๋‹‰ ์ƒ์„ฑ

  • func panic(interface{})
// panic(42)
// panic("Error message")
// panic(fmt.Errorf("Error object"))
// panic(SomeType{SomeData})
package main
import "fmt"

func divide(a,b int) {
  if b==0 {
    panic("b๊ฐ€ 0์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค")
  }
  fmt.Printf("%d /%d= %d\n",a,b, a/b )
}

func main() {
  divide(3,6)
  divide(6,3)
  divide(3,0)
}
  • ํŒจ๋‹‰ ์ „ํŒŒ ๊ทธ๋ฆฌ๊ณ  ๋ณต๊ตฌ
  • ํŒจ๋‹‰ ๋ฐœ์ƒ ํ›„ ์ข…๋ฃŒ ๋Œ€์‹  ๋ณต๊ตฌ
  • ํ•จ์ˆ˜ํ˜ธ์ถœ ์ˆœ์„œ : main-> f->g-> h()
  • ํŒจ๋‹‰์ „ํŒŒ ์ˆœ์„œ(h() ํŒจ๋‹‰๋ฐœ์ƒ): g->f->main()
  • main()์—์„œ ๋ณต๊ตฌ ๋˜์ง€ ์•Š์œผ๋ฉด ํ”„๋กœ๊ทธ๋žจ ์ข…๋ฃŒ๋จ
  • recover()๋กœ ๋ณต๊ตฌํ•˜์—ฌ ๊ณ„์† ์ง„ํ–‰ ๊ฐ€๋Šฅ
  • recover() ํ˜ธ์ถœ๋œ ์‹œ์ ์— panic ์ „ํŒŒ ์ค‘์ด๋ฉด ํŒจ๋‹‰๊ฐ์ฒด ๋ฐ˜ํ™˜, ์•„๋‹ˆ๋ฉด nil ๋ฐ˜ํ™˜
//ch23/ex23.6/ex23.6.go
package main

import "fmt"

func f() {
  fmt.Println("f() ํ•จ์ˆ˜ ์‹œ์ž‘")
  defer func() { // โน ํŒจ๋‹‰ ๋ณต๊ตฌ
    if r := recover(); r != nil {
      fmt.Println("panic ๋ณต๊ตฌ -", r)
    }
  }()

  g() // โถ g() -> h() ์ˆœ์„œ๋กœ ํ˜ธ์ถœ
  fmt.Println("f() ํ•จ์ˆ˜ ๋")
}

func g() {
  fmt.Printf("9 / 3 = %d\n", h(9, 3))
  fmt.Printf("9 / 0 = %d\n", h(9, 0)) // โท h() ํ•จ์ˆ˜ ํ˜ธ์ถœ - ํŒจ๋‹‰
}

func h(a, b int) int {
  if b == 0 {
    panic("์ œ์ˆ˜๋Š” 0์ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.") // โธ ํŒจ๋‹‰ ๋ฐœ์ƒ!!
  }
  return a / b
}

func main() {
  f()
  fmt.Println("ํ”„๋กœ๊ทธ๋žจ์ด ๊ณ„์† ์‹คํ–‰๋จ") // โžŽ ํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰ ์ง€์†๋จ
}
  • recover() ๊ฒฐ๊ณผ
  • func recover() interface{}
if r, ok := recover().(net.Error); ok {
  fmt.Println("r is net.Error type")
}

24.๊ณ ๋ฃจํ‹ด๊ณผ ๋™์‹œ์„ฑ ํ”„๋กœ๊ทธ๋ž˜๋ฐ

25.์ฑ„๋„๊ณผ ์ปจํ…์ŠคํŠธ

26.๋‹จ์–ด๊ฒ€์ƒ‰ ํ”„๋กœ๊ทธ๋žจ ๋งŒ๋“ค๊ธฐ

  • ์™€์ผ๋“œ์นด๋“œ
    • : 0๊ฐœ์ด์ƒ ๋ฌธ์ž
  • ? : 1๊ฐœ ๋ฌธ์ž
  • os.Args []string ๋ณ€์ˆ˜์™€ ์‹คํ–‰ ์ธ์ˆ˜
  • Args๋Š” os ํŒจํ‚ค์ง€์˜ ์ „์—ญ๋ณ€์ˆ˜๋กœ ๊ฐ ์‹คํ–‰์ธ์ˆ˜๊ฐ€ []string ์Šฌ๋ผ์ด์Šค์— ๋‹ด๊น€
  • ์ฒซ๋ฒˆ์งธ ํ•ญ๋ชฉ์œผ๋กœ ์‹คํ–‰ ๋ช…๋ น์ด ๋“ค์–ด๊ฐ
  • ๋‘๋ฒˆ์จฐ ๋ถ€ํ„ฐ ์ž…๋ ฅํ•œ ์ธ์ˆ˜๋“ค์ด ์ฐจ๋ก€๋กœ ๋“ค์–ด๊ฐ

    • find word filepath
    • os.Args[0] : find
    • os.Args[1] : word
    • os.Args[2] : filepath
  • ํŒŒ์ผ ํ•ธ๋“ค๋ง

// ํŒŒ์ผ์—ด๊ธฐ
func Open(name string) (*File, error)

// ํŒŒ์ผ ๋ชฉ๋ก๊ฐ€์ ธ์˜ค๊ธฐ (slice)
func Glob(pattern string) (matches []string, err error)
filepaths, err := filepath.Glob("*.txt")

// ํŒŒ์ผ ๋‚ด์šฉ ํ•œ์ค„ ์”ฉ ์ฝ๊ธฐ
func NewScanner(r io.Reader) *Scanner

type Scanner
  func (s *Scanner) Scan() bool
  func (s *Scanner) Text() string
  • ๋‹จ์–ด ํฌํ•จ์—ฌ๋ถ€ ๊ฒ€์‚ฌ
  • func Contains(s, substr string) bool

  • ์‹คํ–‰ ์ธ์ˆ˜ ์ฝ๊ณ  ํŒŒ์ผ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ

  • ๋‹จ์–ดํ”„๋กœ๊ทธ๋žจ ์‹คํ–‰ํŒŒ์ผ ๋งŒ๋“ค๊ธฐ :
  • cd 26-search-word-project/ex26.1
  • touch ex26.1.go
  • go mod init 26-search-word-project/ex26.1
  • go mod tidy
  • go build
  • ex26.1 word ex*
  • ์ฐพ์œผ๋ ค๋Š” ๋‹จ์–ด : word
  • ์ฐพ์œผ๋ ค๋Š” ํŒŒ์ผ ๋ฆฌ์ŠคํŠธ :
  • ex26.1.exe
  • ex26.1.go
package main

import (
  "fmt"
  "os"
  "path/filepath"
)

func main() {
  if len(os.Args) < 3 {
    fmt.Println("2๊ฐœ ์ด์ƒ์˜ ์‹คํ–‰ ์ธ์ˆ˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ex) ex26.1 word filepath")
    return
  }

  // ์‹คํ–‰์ธ์ˆ˜ ๊ฐ€์ ธ์˜ค๊ธฐ
  //  ์ฐพ์œผ๋ ค๋Š” ๋‹จ์–ด
  word := os.Args[1]
  //  ๊ฒ€์ƒ‰ํ•  ํŒŒ์ผ๋ฆฌ์ŠคํŠธ (์Šฌ๋ผ์ด์Šค)
  files := os.Args[2:]
  fmt.Println("- ์ฐพ์œผ๋ ค๋Š” ๋‹จ์–ด: ", word)
  PrintAllFiles(files)
}

// ํŒŒ์ผ๋ฆฌ์ŠคํŠธ ๋ฐ ์—๋Ÿฌ ๋ฐ˜ํ™˜
func GetFileList(path string) ([]string, error) {
  return filepath.Glob(path)
}

// ์ฐพ์€ ํŒŒ์ผ๋ฆฌ์ŠคํŠธ ์ถœ๋ ฅ
func PrintAllFiles(files []string) {
  fmt.Println("- ์ฐพ์œผ๋ ค๋Š” ํŒŒ์ผ ๋ฆฌ์ŠคํŠธ")
  for _, path := range files {
    filelist, err := GetFileList(path)
    if err != nil {
      fmt.Println("ํŒŒ์ผ๊ฒฝ๋กœ๊ฐ€ ์ž˜๋ชป๋˜์—ˆ์Šต๋‹ˆ๋‹ค. err:", err, "path:", path)
      return
    }

    for _, name := range filelist {
      fmt.Println(name)
    }
  }
}
  • ํŒŒ์ผ์„ ์—ด์–ด์„œ ๋ผ์ธ ์ฝ๊ธฐ
  • ํŒŒ์ผ์„ ์—ด๊ณ  bufio ํŒจํ‚ค์ง€์˜ NewScanner()๋กœ ์Šค์บ๋„ˆ๋ฅผ ๋งŒ๋“ค์–ด, ํŒŒ์ผ๋‚ด์šฉ์„ ํ•œ๋‹จ์–ด์”ฉ ์ฝ๊ธฐ
package main

import (
  "bufio"
  "fmt"
  "os"
)

func main() {
  PrintFile("hamlet.txt")
}

// ํŒŒ์ผ์„ ์ฝ์–ด์„œ ์ถœ๋ ฅ
func PrintFile(filename string) {
  file, err := os.Open(filename) // ํŒŒ์ผ ์—ด๊ธฐ
  if err != nil {
    fmt.Println("ํŒŒ์ผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.", filename)
    return
  }

  defer file.Close() // ํ•จ์ˆ˜ ์ข…๋ฃŒ์ „ ํŒŒ์ผ ๋‹ซ๊ธฐ

  scanner := bufio.NewScanner(file) // ์Šค์บ๋„ˆ๋ฅผ ์ƒ์„ฑํ•ด์„œ ํ•œ์ค„ ์”ฉ ์ฝ๊ธฐ
  for scanner.Scan() {
    fmt.Println(scanner.Text())
  }
}
  • ํŒŒ์ผ ๊ฒ€์ƒ‰ ํ”„๋กœ๊ทธ๋žจ ์™„์„ฑ ํ•˜๊ธฐ
//ch26/ex26.3/ex26.3.go

/**
cd 26-search-word-project/ex26.3
go mod init 26-search-word-project/ex26.3
go mod tidy
go build
./ex26.3 word *.txt
*/

package main

import (
  "bufio"
  "fmt"
  "os"
  "path/filepath"
  "strings"
)

// ์ฐพ์€ ๋ผ์ธ ์ •๋ณด
type LineInfo struct { // โถ ์ฐพ์€ ๊ฒฐ๊ณผ ์ •๋ณด
  lineNo int
  line   string
}

// ํŒŒ์ผ ๋‚ด ๋ผ์ธ ์ •๋ณด
type FindInfo struct {
  filename string
  lines    []LineInfo
}

func main() {
  if len(os.Args) < 3 {
    fmt.Println("2๊ฐœ ์ด์ƒ์˜ ์‹คํ–‰ ์ธ์ˆ˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ex) ex26.3 word filepath")
    return
  }

  word := os.Args[1] // โท ์ฐพ์œผ๋ ค๋Š” ๋‹จ์–ด
  files := os.Args[2:]
  findInfos := []FindInfo{}
  for _, path := range files {
    // โธ ํŒŒ์ผ ์ฐพ๊ธฐ
    findInfos = append(findInfos, FindWordInAllFiles(word, path)...)
  }

  for _, findInfo := range findInfos {
    fmt.Println(findInfo.filename)
    fmt.Println("--------------------------------")
    for _, lineInfo := range findInfo.lines {
      fmt.Println("\t", lineInfo.lineNo, "\t", lineInfo.line)
    }
    fmt.Println("--------------------------------")
    fmt.Println()
  }
}

func GetFileList(path string) ([]string, error) {
  return filepath.Glob(path)
}

func FindWordInAllFiles(word, path string) []FindInfo {
  findInfos := []FindInfo{}

  filelist, err := GetFileList(path) // โถ ํŒŒ์ผ ๋ฆฌ์ŠคํŠธ ๊ฐ€์ ธ์˜ค๊ธฐ
  if err != nil {
    fmt.Println("ํŒŒ์ผ ๊ฒฝ๋กœ๊ฐ€ ์ž˜๋ชป๋˜์—ˆ์Šต๋‹ˆ๋‹ค. err:", err, "path:", path)
    return findInfos
  }

  for _, filename := range filelist { // โท ๊ฐ ํŒŒ์ผ๋ณ„๋กœ ๊ฒ€์ƒ‰
    findInfos = append(findInfos, FindWordInFile(word, filename))
  }
  return findInfos
}

func FindWordInFile(word, filename string) FindInfo {
  findInfo := FindInfo{filename, []LineInfo{}}
  file, err := os.Open(filename)
  if err != nil {
    fmt.Println("ํŒŒ์ผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ", filename)
    return findInfo
  }
  defer file.Close()

  lineNo := 1
  scanner := bufio.NewScanner(file) // โธ ์Šค์บ๋„ˆ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
  for scanner.Scan() {
    line := scanner.Text()
    if strings.Contains(line, word) { // โน ํ•œ ์ค„์”ฉ ์ฝ์œผ๋ฉด ๋‹จ์–ด ํฌํ•จ ์—ฌ๋ถ€ ๊ฒ€์ƒ‰
      findInfo.lines = append(findInfo.lines, LineInfo{lineNo, line})
    }
    lineNo++
  }
  return findInfo
}
  • ๊ฐœ์„ ํ•˜๊ธฐ (๊ณ ๋ฃจํ‹ด, ์ฑ„๋„)
  • ํŒŒ์ผ๊ฐœ์ˆ˜๊ฐ€ ๋Š˜์–ด๋‚˜๋„ ๋นจ๋ฆฌ์กฐํšŒ ๋˜๋„๋ก
  • ํŒŒ์ผ๋ณ„๋กœ ์ž‘์—…์„ ํ• ๋‹นํ•˜๊ณ  ์ž‘์—…์ด ์™„๋ฃŒ๋˜๋ฉด, ์ฑ„๋„์„ ์ด์šฉํ•˜์—ฌ ๊ฒฐ๊ณผ ์ˆ˜์ง‘
package main

import (
  "fmt"
  "os"
  "path/filepath"
  "bufio"
  "strings"
)

type LineInfo struct {
  lineNo int
  line string
}

type FindInfo struct {
  filename string
  lines []LineInfo
}

/**
cd 26-search-word-project/ex26.4
go mod init 26-search-word-project/ex26.4
go mod tidy
go build
./ex26.4 word *.txt
*/



func GetFileList(path string) ([]string, error) {
  return filepath.Glob(path)
}

func FindWordInAllFiles(word, path string) []FindInfo {
  findInfos := []FindInfo{}

  filelist, err := GetFileList(path)
  if err != nil {
    fmt.Println("ํŒŒ์ผ๊ฒฝ๋กœ๊ฐ€ ์ž˜๋ชป๋˜์—ˆ์Šต๋‹ˆ๋‹ค. err:", err, "path:", path)
    return findInfos
  }

  ch := make(chan FindInfo)
  cnt := len(filelist)
  recvCnt := 0

  for _,filename := range filelist {
    go FindWordInFile(word, filename, ch)
  }

  for findInfo := range ch {
    findInfos = append(findInfos, findInfo)
    recvCnt++
    if recvCnt == cnt {
      // all received
      break
    }
  }
  return findInfos
}

func FindWordInFile(word, filename string, ch chan FindInfo) {
  findInfo := FindInfo{filename, []LineInfo{}}

  file, err := os.Open(filename)
  if err != nil {
    fmt.Println("ํŒŒ์ผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ", filename)
    ch <- findInfo
    return
  }
  defer file.Close()

  scanner := bufio.NewScanner(file)

  lineNo := 1
  for scanner.Scan() {

    line := scanner.Text()
    if strings.Contains(line, word) {
      findInfo.lines = append(findInfo.lines, LineInfo{lineNo, line})
    }
    lineNo++
  }

  ch <- findInfo
}


func main() {
  if len(os.Args) < 3 {
    fmt.Println("2๊ฐœ ์ด์ƒ์˜ ์‹คํ–‰ ์ธ์ˆ˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ex) ex26.1 word filepath")
    return
  }

  findInfos := []FindInfo{}
  word := os.Args[1]
  files := os.Args[2:]

  for _, path := range files {
    findInfos = append(findInfos, FindWordInAllFiles(word, path)...)
  }

  for _, findInfo := range findInfos {
    fmt.Println(findInfo.filename)
    fmt.Println("=====")
    for _, lineInfo := range findInfo.lines {
      fmt.Println(lineInfo.lineNo, "\t", lineInfo.line)
    }
    fmt.Println("=====")
  }
}

27.๊ฐ์ฒด์ง€ํ–ฅ์›์น™ SOLID

  • ๋‹จ์ผ ์ฑ…์ž…์›์น™
  • ๊ฐœ๋ฐฉ-ํ์‡„ ์›์น™
  • ๋ผ์Šค์ฝ”ํ”„ ์น˜ํ™˜ ์›์น™
  • ์ธํ„ฐํŽ˜์ด์Šค ๋ถ„๋ฆฌ ์›์น™
  • ์˜์กด ๊ด€๊ณ„ ์—ญ์ „ ์›์น™

28.ํ…Œ์ŠคํŠธ์™€ ๋ฒค์น˜๋งˆํฌ

  • _test.go ์ž‘์„ฑ
  • testing ํŒจํ‚ค์ง€ ์ž„ํฌํŠธ
  • func TestXxxx(t *testing.T) ํ˜•ํƒœ์ด์–ด์•ผ ํ•จ
  • ex28.1.go
  • ex28_1_test.go
# gcc ๊ด€๋ จ ์—๋Ÿฌ
sudo apt install build-essential
go mod init ex28.1
go mod tidy

# ํ…Œ์ŠคํŠธ ์ „๋ถ€ ์‹คํ–‰
go test

# ์ผ๋ถ€ ํ…Œ์ŠคํŠธ๋งŒ ์‹คํ–‰
go test -run ํ…Œ์ŠคํŠธ๋ช…
go test -run TestSquare1

// ex28.1.go
package main

import "fmt"

func square(x int) int {
  return x*x
}

func main() {
  fmt.Printf("9 * 9 = %d\n", square(9))
}
  • ํ…Œ์ŠคํŠธ ๋•๋Š” ์™ธ๋ถ€ ํŒจํ‚ค์ง€
import "github.com/stretchr/testify/assert"

// func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool
// func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool
// func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool
// func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool
// func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bool
//    NotNilf asserts that the specified object is not nil.
//    assert.NotNilf(t, err, "error message %s", "formatted")
// ex28_1_test.go
package main

import (
  "testing"
  "github.com/stretchr/testify/assert"
)

func TestSquare1(t *testing.T) {
  rst := square(9)
  if rst != 81 {
    t.Errorf("square(9) should return 81, but returned %d\n", rst)
  }
}

func TestSquare2(t *testing.T) {
  rst := square(3)
  if rst != 9 {
    t.Errorf("square(3) should return 9, but returned %d\n", rst)
  }
}

func TestSquare3(t *testing.T) {
  // func New(t TestingT) *Assertions
  assert := assert.New(t)

  // ํ…Œ์ŠคํŠธ ํ•จ์ˆ˜ ํ˜ธ์ถœ
  // func (a *Assertions)Equal(expected, actual interface{}, msgAndArgs ...interface{}) bool
  assert.Equal(81, square(9),"9^2 = 81 ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์™€์•ผ ํ•จ")
  assert.Equal(9, square(3),"3^2 = 9 ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์™€์•ผ ํ•จ")

  // ๋˜๋Š” assert.New(t) ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ 
  // assert.Equal(t, 49, square(7),"7^2 = 49 ๊ฒฐ๊ณผ๊ฐ€ ๋‚˜์™€์•ผ ํ•จ")
}
  • mock(ํ…Œ์ŠคํŠธ์šฉ ๋ชฉ์—…)๊ณผ suite(ํ…Œ์ŠคํŠธ ์‹œ์ž‘๊ณผ ์ข…๋ฃŒ) ํŒจํ‚ค์ง€ ์ œ๊ณต
  • mock ํŒจํ‚ค์ง€: ๋ชจ๋“ˆํ–‰๋™์„ ๊ฐ€์žฅํ•˜๋Š” mockup ๊ฐ์ฒด ์ œ๊ณต
    • ์˜จ๋ผ์ธ ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ ์‹œ ํ•˜์œ„์˜์—ญ์ธ ๋„คํŠธ์›Œํฌ ๊ธฐ๋Šฅ๊นŒ์ง€ ํ…Œ์ŠคํŠธ ํž˜๋“ค๋•Œ, ๋„คํŠธ์›Œํฌ ๊ฐ์ฒด ๊ฐ€์žฅํ•œ ๋ชฉ์—… ๊ฐ์ฒด ๋งŒ๋“ค๋•Œ ์œ ์šฉ
  • suite ํŒจํ‚ค์ง€: ํ…Œ์ŠคํŠธ ์ค€๋น„ ์ž‘์—…์ด๋‚˜ ์ข…๋ฃŒ ํ›„ ๋’ค์ฒ˜๋ฆฌ ์ž‘์—…

    • ์‹œ์ž‘์ „ ์ž„์‹œํŒŒ์ผ ์ƒ์„ฑ
    • ์ข…๋ฃŒ ํ›„ ์ƒ์„ฑํ•œ ์ž„์‹œํŒŒ์ผ ์‚ญ์ œ
  • ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ - TDD

  • ๋ธ”๋ž™๋ฐ•์Šค ํ…Œ์ŠคํŠธ : ์ œํ’ˆ๋‚ด๋ถ€ ์˜คํ”ˆํ•˜์ง€ ์•Š์€ ์ƒํƒœ๋กœ ์‚ฌ์šฉ์ž ์ž…์žฅ์—์„œ ํ…Œ์ŠคํŠธ (QA ๋‹ด๋‹น)
  • ํ™”์ดํŠธ๋ฐ•์Šค ํ…Œ์ŠคํŠธ : ๋‚ด๋ถ€์ฝ”๋“œ ๊ฒ€์ฆ - unit test

    • ์ฝ”๋“œ์ž‘์„ฑ -> [ํ…Œ์ŠคํŠธ-์ฝ”๋“œ์ˆ˜์ •] -> ์™„์„ฑ
  • ๋ฒค์น˜๋งˆํฌ

  • ์ฝ”๋“œ ์„ฑ๋Šฅ ๊ฒ€์‚ฌ : testing ํŒจํ‚ค์ง€์—์„œ ์ œ๊ณต
  • _test.go ์ž‘์„ฑ
  • testing ํŒจํ‚ค์ง€ ์ž„ํฌํŠธ
  • func BenchmarkXxxx(b *testing.B) ํ˜•ํƒœ ์—ฌ์•ผ ํ•จ
  • go test -bench .
# 47035 vs 9.346 nanosecond
go test -bench .
  goos: linux
  goarch: amd64
  pkg: ex28.2
  cpu: Intel(R) Core(TM) i7-6700HQ CPU @ 2.60GHz
  BenchmarkFibonacci1-8              24924             47035 ns/op
  BenchmarkFibonacci2-8           125435312                9.346 ns/op
  PASS
  ok      ex28.2  3.810s
// ex28.2.go
package main

import "fmt"

func fibonacci1(n int) int {
  if n < 0 {
    return 0
  }
  if n < 2 {
    return n
  }
  return fibonacci1(n-1) + fibonacci1(n-2) // โถ ์žฌ๊ท€ ํ˜ธ์ถœ
}

func fibonacci2(n int) int {
  if n < 0 {
    return 0
  }
  if n < 2 {
    return n
  }
  one := 1
  two := 0
  rst := 0
  for i := 2; i <= n; i++ { // โท ๋ฐ˜๋ณต๋ฌธ
    rst = one + two
    two = one
    one = rst
  }
  return rst
}

func main() {
  fmt.Println(fibonacci1(7))
  fmt.Println(fibonacci2(7))
}
// ex28_2_test.go
package main

import (
  "testing"
  "github.com/stretchr/testify/assert"
)


func TestFibonacci1(t *testing.T) {
  assert := assert.New(t)
  assert.Equal(0, fibonacci1(-1), "fibonacci1(-1) should be 0")
  assert.Equal(0, fibonacci1(0), "fibonacci1(0) should be 0")
  assert.Equal(1, fibonacci1(1), "fibonacci1(1) should be 1")
  assert.Equal(2, fibonacci1(3), "fibonacci1(2) should be 2")
  assert.Equal(233, fibonacci1(13), "fibonacci1(13) should be 233")
}

func TestFibonacci2(t *testing.T) {
  assert := assert.New(t)
  assert.Equal(0, fibonacci2(-1), "fibonacci1(-1) should be 0")
  assert.Equal(0, fibonacci2(0), "fibonacci1(0) should be 0")
  assert.Equal(1, fibonacci2(1), "fibonacci1(1) should be 1")
  assert.Equal(2, fibonacci2(3), "fibonacci1(2) should be 2")
  assert.Equal(233, fibonacci2(13), "fibonacci1(13) should be 233")
}

func BenchmarkFibonacci1(b *testing.B) {
  // b.N๋งŒํผ ๋ฐ˜๋ณต
  for i := 0; i < b.N; i++ {
    fibonacci1(20)
  }
}

func BenchmarkFibonacci2(b *testing.B) {
  // b.N๋งŒํผ ๋ฐ˜๋ณต
  for i := 0; i < b.N; i++ {
    fibonacci2(20)
  }
}

29.Go์–ธ์–ด๋กœ ๋งŒ๋“œ๋Š” ์›น์„œ๋ฒ„

  • HTTP๋Š” ์ธํ„ฐ๋„ท์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฃผ๊ณ ๋ฐ›๋Š” ํ†ต์‹  ํ”„๋กœํ† ์ฝœ
  • ๋น„์—ฐ๊ฒฐ์„ฑ ํŠน์ง• : ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„๊ฐ€ ๊ณ„์† ์—ฐ๊ฒฐ ๋˜์–ด ์žˆ์ง€ ์•Š๊ณ , ์‘๋‹ต ํ›„ ์—ฐ๊ฒฐ์„ ๋Š์–ด๋ฒ„๋ฆผ
  • ๊ทธ๋ž˜์„œ ์ด์ „ ์ƒํƒœ ์ •๋ณด๋‚˜ ํ˜„์žฌ ํ†ต์‹  ์ƒํƒœ๊ฐ€ ๋‚จ์•„ ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ์ž์› ๋‚ญ๋น„๋ฅผ ์ค„์ผ ์ˆ˜ ์žˆ์ง€๋งŒ, ํด๋ผ์ด์–ธํŠธ์˜ ์ƒํƒœ๋ฅผ ์œ ์ง€ ํ•  ์ˆ˜ ์—†๋Š” ๋‹จ์ 
  • cookie, session ์‚ฌ์šฉํ•˜๋ฉด ์ƒํƒœ ์ •๋ณด ์œ ์ง€ ํ•  ์ˆ˜ ์žˆ์Œ
    • cookie: 'ํด๋ผ์ด์–ธํŠธ'์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅ/๊ด€๋ฆฌํ•˜์—ฌ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๋Š” ๊ธฐ์ˆ 
    • session: '์„œ๋ฒ„'์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅ/๊ด€๋ฆฌํ•˜์—ฌ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๋Š” ๊ธฐ์ˆ 
  • HTTPS๋Š” HTTP์˜ ์•”ํ˜ธํ™” ํ†ต์‹ ๋ฒ„์ „. ํ†ต์‹ ๋‚ด์šฉ์„ ์•”ํ˜ธํ™” ํ•˜๋ฏ€๋กœ ๋” ์•ˆ์ „
  • Go ์–ธ์–ด๋ฅผ ์ด์šฉํ•ด ๋งŒ๋“  ์›น์„œ๋ฒ„๋Š” ๋›ฐ์–ด๋‚œ ์„ฑ๋Šฅ์„ ์ž๋ž‘ํ•จ

  • HTTP ์›น์„œ๋ฒ„ ๋งŒ๋“ค๊ธฐ

  • net/http ํŒจํ‚ค์ง€๋กœ ์›น์„œ๋ฒ„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Œ

  • ํ•ธ๋“ค๋Ÿฌ ๋“ฑ๋ก : HTTP ์š”์ฒญ URL ์ˆ˜์‹ ๋์„๋•Œ ๊ทธ๊ฑธ ์ฒ˜๋ฆฌํ•˜๋Š” ํ•จ์ˆ˜

  • HandleFunc() ํ•จ์ˆ˜ ๋“ฑ๋ก: URL ํ•ด๋‹น ๊ฒฝ๋กœ ํ˜ธ์ถœ ์‹œ ํ•จ์ˆ˜ ํ˜ธ์ถœ
  • http.Handler ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„ํ•œ ๊ฐ์ฒด ๋“ฑ๋ก. ์ด ๊ฐ์ฒด์˜ ์ธํ„ฐํŽ˜์ด์Šค์ธ ServeHTTP() ํ˜ธ์ถœ
func IndexPathHandler(w http.ResponseWriter, r *http.Request) { ...  }
// "/" ์š”์ฒญ ์‹œ IndexPathHandler ํ˜ธ์ถœ
http.HandleFunc("/", IndexPathHandler)
  • http ํŒจํ‚ค์ง€์˜ Request ๊ตฌ์กฐ์ฒด
type Request struct {
  // GET, POST, PUT, DELETE
  Method string

  UL *url.URL

  Proto string
  ProtoMajor int
  ProtoMinor int
  Header header

  Body io.ReadCloser
}
  • ์›น์„œ๋ฒ„ ์‹œ์ž‘
  • ๊ฐ ๊ฒฝ๋กœ์— ๋Œ€ํ•œ ํ•ธ๋“ค๋Ÿฌ ๋“ฑ๋ก์„ ๋งˆ์น˜๊ณ , ์›น์„œ๋ฒ„ ์‹œ์ž‘
// addr: HTTP ์š”์ฒญ ์ˆ˜์‹  ์ฃผ์†Œ (ํฌํŠธํฌํ•จ)
// handler: nil ์ด๋ฉด ๋””ํดํŠธ ํ•ธ๋“ค๋Ÿฌ
//    ํŒจํ‚ค์ง€ ํ•จ์ˆ˜์ธ hattp.HandleFunc()๋กœ ํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜ ๋“ฑ๋ก ์‹œ ์ธ์ž์— nil ์ „๋‹ฌ
//    ๋˜๋Š” ์ƒˆ๋กœ์šด ํ•ธ๋“ค๋Ÿฌ ์ธ์Šคํ„ด์Šค๋ฅผ ๋‘๋ฒˆ์จฐ ์ธ์ˆ˜๋กœ ์ „๋‹ฌ ๊ฐ€๋Šฅ (ServeMux ์ธ์Šคํ„ด์Šค ์ด์šฉ)
func ListenAndServe(addr string, handler Handler) error
package main

import (
  "fmt"
  "net/http"
)

func main() {
  // httpํŒจํ‚ค์ง€ ํ•จ์ˆ˜์ธ HandleFunc๋กœ ํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜ ๋“ฑ๋กํ•  ๋•Œ๋Š”
  // ListenAndServe ๋‘๋ฒˆ์งธ ์ธ์ˆ˜์— nil ์ „๋‹ฌ ํ•ด์•ผํ•จ!
  // ๋‘๋ฒˆ์งธ ์ธ์ˆ˜ nil ์ „๋‹ฌ ์‹œ, DefaultServeMux ์‚ฌ์šฉ-> http.HandleFunc() ํ˜ธ์ถœํ•˜์—ฌ ๋“ฑ๋ก๋œ ํ•ธ๋“ค๋Ÿฌ ์‚ฌ์šฉ
  // "/" ๊ฒฝ๋กœ๋กœ HTTP ์š”์ฒญ์„ ๋ฐ›์œผ๋ฉด ํ•จ์ˆ˜ ๋ฆฌํ„ฐ๋Ÿด func(w,r){...} ์‹คํ–‰!
  http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
      // ์ถœ๋ ฅ ์ŠคํŠธ๋ฆผ์— ๊ฐ’์„ ์“ฐ๋Š” ํ•จ์ˆ˜
      // ์ง€์ •ํ•œ ์ถœ๋ ฅ ์ŠคํŠธ๋ฆผ w์— "Hello World" ์ถœ๋ ฅ
      // http.ResponseWriter์— ๊ฐ’์„ ์“ฐ๋ฉด, HTTP ์‘๋‹ต์œผ๋กœ ์ „์†ก ๋จ
      fmt.Fprint(w, "Hello World")
  })

  // ์›น์„œ๋ฒ„ ์‹คํ–‰
  http.ListenAndServe(":3000", nil)
}
  • HTTP ๋™์ž‘์›๋ฆฌ
  • https://goldenrabbit.co.kr:3000 ํ˜ธ์ถœ
  • ์›น ๋ธŒ๋ผ์šฐ์ €๋Š” DNS(domain name system)์— ๋„๋ฉ”์ธ์— ํ•ด๋‹นํ•˜๋Š” IP ์ฃผ์†Œ๋ฅผ ์š”์ฒญ
  • ์›น๋ธŒ๋ผ์šฐ์ € goldenrabbit.co.kr->DNS
  • DNS ip์ฃผ์†Œ -> ์›น๋ธŒ๋ผ์šฐ์ €
  • IP์ฃผ์†Œ:๋ชฉ์ ์ง€(์ปดํ“จํ„ฐ), ํฌํŠธ: ํ•ด๋‹น ์ปดํ“จํ„ฐ ๋‚ด ๋ฐ์ดํ„ฐ ์ˆ˜์‹  ๊ฐ€๋Šฅํ•œ ์ฐฝ๊ตฌ (0~65535)
  • ์›น์„œ๋ฒ„๋ž€? ํŠน์ • ํฌํŠธ์—์„œ ๋Œ€๊ธฐํ•˜๋ฉฐ ์‚ฌ์šฉ์ž์˜ HTTP์š”์ฒญ์— HTTP์‘๋‹ต์„ ์ „์†กํ•˜๋Š” ์„œ๋ฒ„ (์‘๋‹ต์€ ์ผ๋ฐ˜์ ์œผ๋กœ HTMl ๋ฌธ์„œ)

  • HTTP ์ฟผ๋ฆฌ์ธ์ˆ˜ ์‚ฌ์šฉํ•˜๊ธฐ

  • HTTP ์š”์ฒญ์— ํฌํ•จ๋œ ์ฟผ๋ฆฌ ์ธ์ˆ˜๋ฅผ ์ฝ๊ณ  ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • http://localhost?id=1&name=abcd
package main

import (
  "fmt"
  "net/http"
  "strconv"
)

func barHandler(w http.ResponseWriter, r *http.Request) {
  values := r.URL.Query() // ์ฟผ๋ฆฌ์ธ์ˆ˜ ๊ฐ€์ ธ์˜ค๊ธฐ
  name := values.Get("name") // ํŠน์ • ํ‚ค ๊ฐ’์ด ์žˆ๋Š”์ง€ ํ™•์ธ
  if name == "" {
    name = "World"
  }

  id, _ := strconv.Atoi(values.Get("id")) // id๊ฐ’์„ ๊ฐ€์ ธ์™€์„œ intํ˜• ํƒ€์ž… ๋ณ€ํ™˜
  fmt.Fprintf(w, "Hello %s! id: %d", name, id)
}


// http://localhost:3000/bar?name=Lalalala&id=123
func main() {
  http.HandleFunc("/bar", barHandler)
  http.ListenAndServe(":3000", nil)
}
  • ServeMux ์ธ์Šคํ„ด์Šค ์ด์šฉํ•˜๊ธฐ
  • ListenAndServe ๋‘๋ฒˆ์งธ ์ธ์ˆ˜ nil -> DefaultServeMux๊ฐ€ http.HandleFunc() ํŒจํ‚ค์ง€ํ•จ์ˆ˜ ํ˜ธ์ถœ. ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ์–ด๋ ค์›€
  • ServeMux ์ธ์Šคํ„ด์Šค ์ƒ์„ฑํ•˜์—ฌ ์‚ฌ์šฉ
    • http.HandleFunc()๋Š” DefaultServeMux์— ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋กํ•˜๋Š” ๋ฐ˜๋ฉด
    • mux.HandleFunc()๋Š” ์ƒ์„ฑํ•œ ServeMux ์ธ์Šคํ„ด์Šค์— ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋“ฑ๋ก ํ•จ
    • Mux (multiplexer ์•ฝ์ž): ์—ฌ๋Ÿฌ ์ž…๋ ฅ ์ค‘ ํ•˜๋‚˜๋ฅผ ์„ ํƒํ•ด์„œ ๋ฐ˜ํ™˜ ํ•˜๋Š” ๋””์ง€ํ„ธ ์žฅ์น˜
package main
import (
  "fmt"
  "net/http"
)

func main() {
  mux := http.NewServeMux()
  mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request){
      fmt.Fprint(w, "Hello World")
  })
  mux.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request){
      fmt.Fprint(w, "Hello Bar")
  })

  http.ListenAndServe(":3000", mux)
}
  • ํŒŒ์ผ์„œ๋ฒ„
  • ์ด๋ฏธ์ง€ ๋ฐ์ดํ„ฐ๋ฅผ ์ง์ ‘ ๊ฐ€์ ธ์˜ค์ง€ ์•Š๊ณ  src์— ๊ฒฝ๋กœ๋งŒ ๋‹ด๊ณ  ์žˆ์Œ
  • ์›น๋ธŒ๋ผ์šฐ์ €๋Š” ๋‹ค์‹œ ํ•„์š”ํ•œ ์ด๋ฏธ์ง€ ๋ฐ์ดํ„ฐ๋ฅผ HTTP ์š”์ฒญ์„ ํ†ตํ•ด ๊ฐ€์ ธ์˜ด
  • ์ด๋ฏธ์ง€ ์š”์ฒญ์„ ๋ฐ›์€ ์›น์„œ๋ฒ„๋Š” ์ด๋ฏธ์ง€ ๊ฒฝ๋กœ์— ํ•ด๋‹นํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ ํ•˜์—ฌ ์ด๋ฏธ์ง€ ํ‘œ์‹œ
  • ๋Œ€์‹ ์— '/static' ํด๋”์˜ ํŒŒ์ผ ์ œ๊ณตํ•˜๋Š” ํŒŒ์ผ ์„œ๋ฒ„ ์ƒ์„ฑ
<!-- ch29/ex29.4/test.html -->
<html>
<body>
<img src="https://golang.org/lib/godoc/images/footer-gopher.jpg"/>
<h1>์ด๊ฒƒ์€ Gopher ์ด๋ฏธ์ง€์ž…๋‹ˆ๋‹ค.</h1>
</body>
</html>
package main
import "net/http"

// http://localhost:3000/gopher.jpg
func main() {
  http.Handle("/", http.FileServer(http.Dir("static")))
  http.ListenAndServe(":3000", nil)
}
  • ํŠน์ • ๊ฒฝ๋กœ์— ์žˆ๋Š” ํŒŒ์ผ ์ฝ์–ด์˜ค๊ธฐ
  • http://localhost:3000/static/gopher.jpg ํ˜ธ์ถœ
package main
import "net/http"

func main() {
  // http://localhost:3000/gopher.jpg
  // http.Handle("/", http.FileServer(http.Dir("static")))

  // http.StripPrefix๋กœ URL์—์„œ /static/์„ ์ œ๊ฑฐ ํ•ด์คŒ
  // http://localhost:3000/static/gopher.jpg ์ถœ๋ ฅ
  http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))
  http.ListenAndServe(":3000", nil)
}
<!-- ch29/ex29.4/test.html -->
<html>
<body>
<img src="http://localhost:3000/static/gopher.jpg"/>
<h1>์ด๊ฒƒ์€ Gopher ์ด๋ฏธ์ง€์ž…๋‹ˆ๋‹ค.</h1>
</body>
</html>
  • ์›น์„œ๋ฒ„ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ๋งŒ๋“ค๊ธฐ
// ex29.5/ex29.5.go
package main

import (
  "fmt"
  "net/http"
)

func MakeWebHandler() http.Handler {
  mux := http.NewServeMux()
  mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request){
      fmt.Fprint(w, "Hello World")
  })
  mux.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request){
      fmt.Fprint(w, "Hello Bar")
  })
  return mux
}

func main() {
  http.ListenAndServe(":3000", MakeWebHandler())
}
// ex29.5/ex29_5_test.go
package main

import (
  "io"
  "net/http"
  "net/http/httptest"
  "testing"
  "github.com/stretchr/testify/assert"
)


func TestIndexHandler(t *testing.T) {
  assert := assert.New(t)

  res := httptest.NewRecorder()
  req := httptest.NewRequest("GET", "/", nil) // '/' ๊ฒฝ๋กœ ํ…Œ์ŠคํŠธ

  mux := MakeWebHandler()
  mux.ServeHTTP(res, req)

  assert.Equal(http.StatusOK, res.Code) // Code ํ™•์ธ
  data, _ := io.ReadAll(res.Body)    // ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด์„œ ํ™•์ธ
  assert.Equal("Hello World", string(data))
}

func TestBarHandler(t *testing.T) {
  assert := assert.New(t)

  res := httptest.NewRecorder()
  req := httptest.NewRequest("GET", "/bar", nil) // '/bar' ๊ฒฝ๋กœ ํ…Œ์ŠคํŠธ

  mux := MakeWebHandler()
  mux.ServeHTTP(res, req)

  assert.Equal(http.StatusOK,res.Code)
  data, _ := io.ReadAll(res.Body)
  assert.Equal("Hello Bar", string(data))
}
  • JSON ๋ฐ์ดํ„ฐ ์ „์†ก
{
  "Name":"abc",
  "Age": 16,
  "Score": 87
}
// ex29.6/ex29.6.go
package main

import (
  "fmt"
  "net/http"
  "encoding/json"
)

type Student struct {
  Name string
  Age int
  Score int
}

func StudentHandler(w http.ResponseWriter, r *http.Request) {
  student := Student{"Abc", 18, 87}
  data, _ := json.Marshal(student)
  w.Header().Add("content-type", "application/json")
  w.WriteHeader(http.StatusOK)
  fmt.Fprint(w, string(data))
}

func MakeWebHandler() http.Handler {
  mux := http.NewServeMux()
  mux.HandleFunc("/student", StudentHandler)
  mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request){
      fmt.Fprint(w, "Hello World")
  })
  return mux
}

func main() {
  http.ListenAndServe(":3000", MakeWebHandler())
}
// ex29.6/ex29_6_test.go
package main

import (
  "io"
  "net/http/httptest"
  "net/http"
  "testing"
  "encoding/json"
  "github.com/stretchr/testify/assert"
)

func TestIndexHandler(t *testing.T) {
  assert := assert.New(t)

  res := httptest.NewRecorder()
  req := httptest.NewRequest("GET", "/", nil) // '/' ๊ฒฝ๋กœ ํ…Œ์ŠคํŠธ

  mux := MakeWebHandler()
  mux.ServeHTTP(res, req)

  assert.Equal(http.StatusOK, res.Code)
  data, _ := io.ReadAll(res.Body)
  assert.Equal("Hello World", string(data))
}

func TestJsonHandler(t *testing.T) {
  assert := assert.New(t)

  res := httptest.NewRecorder()
  req := httptest.NewRequest("GET", "/student", nil) // '/student' ๊ฒฝ๋กœ ํ…Œ์ŠคํŠธ

  mux := MakeWebHandler()
  mux.ServeHTTP(res, req)

  assert.Equal(http.StatusOK, res.Code)
  student := &Student{}

  // res.Body ํŒŒ์‹ฑ -> Student ํƒ€์ž…
  err := json.NewDecoder(res.Body).Decode(student)
  assert.Nil(err) // ๊ฒฐ๊ณผ ํ™•์ธ
  assert.Equal("Abc", student.Name)
  assert.Equal(18, student.Age)
  assert.Equal(87, student.Score)
}
  • HTTPS ์›น์„œ๋ฒ„ ๋งŒ๋“ค๊ธฐ
  • HTTP๋Š” ๋ชจ๋“  ์š”์ฒญ์ด ํ‰๋ฌธ(์ผ๋ฐ˜ ๋ฌธ์ž์—ด)
  • HTTPS๋Š” ์š”์ฒญ๊ณผ ์‘๋‹ต์„ ๊ณต๊ฐœํ‚ค ์•”ํ˜ธํ™” ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•ด์„œ ์•”ํ˜ธํ™”ํ•œ ํ”„๋กœํ† ์ฝœ

    • ํŒจํ‚ท์ด ์•”ํ˜ธํ™” ๋˜๊ธฐ ๋•Œ๋ฌธ์— ์Šค๋‹ˆํ•‘ ํ•ด๋„ ๋‚ด์šฉ ์•Œ ์ˆ˜ ์—†์Œ
  • ๊ณต๊ฐœํ‚ค ์•”ํ˜ธํ™” ๋ฐฉ์‹

  • ๊ณต๊ฐœํ‚ค, ๋น„๋ฐ€ํ‚ค ๋‘๊ฐ€์ง€ ํ‚ค ์ƒ์„ฑํ•˜์—ฌ ํด๋ผ์ด์–ธํŠธ์— ๊ณต๊ฐœํ‚ค, ์„œ๋ฒ„์— ๋น„๋ฐ€ํ‚ค ๋น„๊ณต๊ฐœ ์ƒํƒœ๋กœ ๋ณด๊ด€
  • ํด๋ผ์ด์–ธํŠธ๊ฐ€ HTTPS ์š”์ฒญ ๋ณด๋‚ผ ๋•Œ ๊ณต๊ฐœํ‚ค๋กœ ์•”ํ˜ธํ™”, ์„œ๋ฒ„๊ฐ€ ๋น„๋ฐ€ํ‚ค๋กœ ๋ณตํ˜ธํ™”: ๋น„๋Œ€์นญ ์•”ํ˜ธํ™” ๋ฐฉ์‹

    • ์ฐธ๊ณ : Khan Academy Lecture, "์นธ ์•„์นด๋ฐ๋ฏธ ๊ณต๊ฐœํ‚ค"
  • ์ธ์ฆ์„œ์™€ ๋น„๋ฐ€ํ‚ค ์ƒ์„ฑ

  • ์ธ์ฆ์„œ๋Š” ์ธ์ฆ๊ธฐ๊ด€์—์„œ ๋ฐœ๊ธ‰ํ•ด์•ผ ํ•˜์ง€๋งŒ ๊ฐœ์ธ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์…€ํ”„์ธ์ฆ
  • openssl๋กœ ์ธ์ฆ์„œ ๋ฐœ๊ธ‰
# rsh:2048 ๋ฐฉ์‹์œผ๋กœ ํ‚ค localhost.key ์™€ ์ธ์ฆํŒŒ์ผ localhost.csr ์ƒ์„ฑ
# localhost.key๋Š” ์ ˆ๋Œ€ ์™ธ๋ถ€์— ๊ณต๊ฐœ๋˜์ง€ ์•Š๋„๋ก ๋ณ„๋„ ์ €์žฅ์†Œ์— ์ €์žฅ (์›น์„œ๋ฒ„ ๋กœ์ปฌ ํŒŒ์ผ ์‹œ์Šคํ…œ ์ €์žฅ ๊ถŒ์žฅ X, ์ผ์ •์ฃผ๊ธฐ ๊ต์ฒด ๊ถŒ์žฅ)
openssl req -new -newkey rsa:2048 -nodes -keyout localhost.key -out localhost.csr

# .csr ์ธ์ฆํŒŒ์ผ์„ ๊ธฐ๊ด€์— ์ œ์ถœํ•ด์„œ .crt ์ธ์ฆ์„œ ์ƒ์„ฑ
# x509 ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ 1๋…„์งœ๋ฆฌ ์ธ์ฆ์„œ ๋ฐœ๊ธ‰
# ๊ฐœ์ธ์ •๋ณด๋ฅผ ์ˆ˜์ง‘ํ•˜๊ณ  ์™ธ๋ถ€๋กœ ๊ณต๊ฐœ๋˜๋Š” ์‚ฌ์ดํŠธ๋Š” ๋ฐ˜๋“œ์‹œ ์ธ์ฆ๊ธฐ๊ด€์„ ํ†ตํ•ด ์ธ์ฆ๋ฐ›์€ ์ธ์ฆ์„œ ์‚ฌ์šฉํ•˜๋„๋ก ๋ฒ•์  ๊ฐ•์ œ๋˜์–ด์žˆ์Œ
openssl x509 -req -days 365 -in localhost.csr -signkey localhost.key -out localhost.crt
package main
import (
  "fmt"
  "log"
  "net/http"
)

func main() {
  http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
      fmt.Fprint(w, "Hello World")
  })

  err := http.ListenAndServeTLS( ":3000", "localhost.crt", "localhost.key", nil)
  if err != nil {
    log.Fatal(err)
  }
}

30.Restful API ์„œ๋ฒ„ ๋งŒ๋“ค๊ธฐ

  1. gorilla/mux ๊ฐ™์€ RESTful API ์›น์„œ๋ฒ„ ์ œ์ž‘ ๋„์™€์ฃผ๋Š” ํŒจํ‚ค์ง€ ์„ค์น˜
  2. RESTful API์— ๋งž์ถฐ์„œ Web handler ํ•จ์ˆ˜ ์ƒ์„ฑ
  3. RESTful API๋ฅผ ํ…Œ์ŠคํŠธํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ƒ์„ฑ
  4. ์›น ๋ธŒ๋ผ์šฐ์ €๋กœ ๋ฐ์ดํ„ฐ ์กฐํšŒ

  5. HTTP ๋ฉ”์„œ๋“œ: GET, POST, PUT, PATCH, DELETE

  6. GET /students ์ „์ฒด ํ•™์ƒ ๋ฐ์ดํ„ฐ ๋ฐ˜ํ™˜
  7. GET /students/id ์•„์ด๋””์— ํ•ด๋‹นํ•˜๋Š” ํ•™์ƒ ๋ฐ์ดํ„ฐ ๋ฐ˜ํ™˜
  8. POST /students ์ƒˆ๋กœ์šด ํ•™์ƒ ๋“ฑ๋ก
  9. PUT /students/id ์•„์ด๋”” ํ•ด๋‹น ํ•™์ƒ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ
  10. DELETE /students/id ์•„์ด๋”” ํ•ด๋‹น ํ•™์ƒ ๋ฐ์ดํ„ฐ ์‚ญ์ œ

  11. RESTful API ํŠน์ง•

  12. ์ž๊ธฐ ํ‘œํ˜„์ ์ธ URL
  13. ๋ฉ”์„œ๋“œ๋กœ ํ–‰์œ„ ํ‘œํ˜„ : URL๊ณผ ๋ฉ”์„œ๋“œ ์กฐํ•ฉ์œผ๋กœ ๋ฐ์ดํ„ฐ ์กฐ์ž‘ ์ •์˜
  14. ์„œ๋ฒ„/ํด๋ผ์ด์–ธํŠธ ๊ตฌ์กฐ
    • ๋ฐ์ดํ„ฐ ์ œ๊ณต์ž / ๋ฐ์ดํ„ฐ ์‚ฌ์šฉ์ž
  15. ๋ฌด์ƒํƒœ statless: ์„œ๋ฒ„๋Š” ํด๋ผ์ด์–ธํŠธ ์ƒํƒœ ์œ ์ง€ X
  16. ์บ์‹œ ์ฒ˜๋ฆฌ cacheable: ๋” ์‰ฝ๊ฒŒ ์บ์‰ฌ์ •์ฑ… ์ ์šฉํ•˜์—ฌ ์„ฑ๋Šฅ ๊ฐœ์„  ๊ฐ€๋Šฅ

  17. gorilla/mux ํŒจํ‚ค์ง€ ์„ค์น˜

go get -u github.com/gorilla/mux
  • GET "/students" ํ•™์ƒ ๋ฆฌ์ŠคํŠธ ์กฐํšŒ
  • GET "/student/{id:[0-9]+}" ํ•™์ƒ ์กฐํšŒ
  • POST "/students" ํ•™์ƒ ๋“ฑ๋ก
  • DELETE "/student/{id:[0-9]+}" ํ•™์ƒ ์‚ญ์ œ
// ex30.1/ex30.1.go
package main

import (
  "encoding/json"
  "net/http"
  "sort"
  "strconv"

  "github.com/gorilla/mux"
)

type Student struct {
  Id int
  Name string
  Age int
  Score int
}

/**
  Student ๋ฆฌ์ŠคํŠธ๋ฅผ Id ๊ธฐ์ค€์ •๋ ฌํ•˜๊ธฐ ์œ„ํ•œ, Id ์ •๋ ฌ ์ธํ„ฐํŽ˜์ด์Šค ์ •์˜
*/
type Students []Student

func (s Students) Len() int {
  return len(s)
}

func (s Students) Swap(i, j int) {
  s[i], s[j] = s[j], s[i]
}

func (s Students) Less(i, j int) bool {
  return s[i].Id < s[j].Id
}

var students map[int]Student
var lastId int

func GetStudentListHandler(w http.ResponseWriter, r *http.Request) {
  list := make(Students, 0) // ํ•™์ƒ ๋ชฉ๋ก์„ Id๋กœ ์ •๋ ฌ
  for _, student := range students {
    list = append(list, student)
  }
  sort.Sort(list)

  w.WriteHeader(http.StatusOK)
  w.Header().Set("Content-Type", "application/json")

  json.NewEncoder(w).Encode(list) // JSON ํฌ๋งท์œผ๋กœ ๋ณ€๊ฒฝ
}

func GetStudentHandler(w http.ResponseWriter, r *http.Request) {
  vars := mux.Vars(r)
  id, _ := strconv.Atoi(vars["id"])
  student, ok := students[id]

  if !ok {
    w.WriteHeader(http.StatusNotFound)
    return
  }
  w.WriteHeader(http.StatusOK)
  w.Header().Set("Content-Type", "application/json")
  json.NewEncoder(w).Encode(student)
}

func PostStudentHandler(w http.ResponseWriter, r *http.Request) {
  var student Student
  err := json.NewDecoder(r.Body).Decode(&student) // JSON ๋ฐ์ดํ„ฐ ๋ณ€ํ™˜
  if err != nil {
    w.WriteHeader(http.StatusBadRequest)
    return
  }

  lastId++  // id๋ฅผ ์ฆ๊ฐ€์‹œํ‚จ ํ›„ ์•ฑ์— ๋“ฑ๋ก
  student.Id = lastId

  students[lastId] = student
  w.WriteHeader(http.StatusCreated)
}

func DeleteStudentHandler(w http.ResponseWriter, r *http.Request) {
  vars := mux.Vars(r)
  id, _ := strconv.Atoi(vars["id"])
  _, ok := students[id]
  if !ok {
    w.WriteHeader(http.StatusNotFound) // id ํ•ด๋‹น ํ•™์ƒ ์—†์œผ๋ฉด ์—๋Ÿฌ
    return
  }
  delete(students, id)
  w.WriteHeader(http.StatusOK) // ์‚ญ์ œ ์„ฑ๊ณต ์‹œ, StatusOK ๋ฐ˜ํ™˜
}

func MakeWebHandler() http.Handler {
  mux := mux.NewRouter() // gorilla/mux ์ƒ์„ฑ

  /** ์ƒˆ๋กœ์šด ํ•ธ๋“ค๋Ÿฌ ๋“ฑ๋ก */
  mux.HandleFunc("/students", GetStudentListHandler).Methods("GET") // ํ•™์ƒ ๋ฆฌ์ŠคํŠธ ์กฐํšŒ
  mux.HandleFunc("/student/{id:[0-9]+}", GetStudentHandler).Methods("GET") // ํ•™์ƒ ํ•œ๋ช… ์กฐํšŒ
  mux.HandleFunc("/students", PostStudentHandler).Methods("POST") // ํ•™์ƒ ๋“ฑ๋ก
  mux.HandleFunc("/student/{id:[0-9]+}", DeleteStudentHandler).Methods("DELETE") // ํ•™์ƒ ์‚ญ์ œ

  students = make(map[int]Student)
  students[1] = Student{1, "zzz", 10, 77}
  students[2] = Student{2, "aaa", 20, 88}
  students[3] = Student{3, "ccc", 30, 99}
  students[4] = Student{4, "bbb", 40, 50}
  lastId = 4

  return mux
}

func main() {
  http.ListenAndServe(":3000", MakeWebHandler())
}
// ex30.1/ex30_1_test.go
package main

import (
  "encoding/json"
  "net/http"
  "net/http/httptest"
  "testing"
  "strings"

  "github.com/stretchr/testify/assert"
)

func TestJsonHandler(t *testing.T) {
  assert := assert.New(t)

  res := httptest.NewRecorder()
  req := httptest.NewRequest("GET", "/students", nil) // '/students' ๊ฒฝ๋กœ ํ…Œ์ŠคํŠธ

  mux := MakeWebHandler()
  mux.ServeHTTP(res, req)

  assert.Equal(http.StatusOK, res.Code)
  var list []Student
  err := json.NewDecoder(res.Body).Decode(&list)
  assert.Nil(err)
  assert.Equal(4, len(list))
  assert.Equal("zzz", list[0].Name)
  assert.Equal("aaa", list[1].Name)
}

func TestJsonHandler2(t *testing.T) { 
  assert := assert.New(t)

  var student Student
  mux := MakeWebHandler()
  res := httptest.NewRecorder()
  req := httptest.NewRequest("GET", "/student/1", nil) // id=1 ํ•™์ƒ ์กฐํšŒ

  mux.ServeHTTP(res, req)
  assert.Equal(http.StatusOK, res.Code)
  err := json.NewDecoder(res.Body).Decode(&student)
  assert.Nil(err)
  assert.Equal("zzz", student.Name)


  res = httptest.NewRecorder()
  req = httptest.NewRequest("GET", "/student/2", nil) // id=2 ํ•™์ƒ ์กฐํšŒ
  mux.ServeHTTP(res, req)
  assert.Equal(http.StatusOK, res.Code)
  err = json.NewDecoder(res.Body).Decode(&student)
  assert.Nil(err)
  assert.Equal("aaa", student.Name)
}

func TestJsonHandler3(t *testing.T) { 
  assert := assert.New(t)

  var student Student
  mux := MakeWebHandler()
  res := httptest.NewRecorder()
  req := httptest.NewRequest("POST", "/students",
    // ์ƒˆ๋กœ์šด ํ•™์ƒ ๋ฐ์ดํ„ฐ ๋“ฑ๋ก
    strings.NewReader(`{"Id":0, "Name": "nnnn", "Age": 15, "Score": 78}`),
  )

  mux.ServeHTTP(res, req)
  assert.Equal(http.StatusCreated, res.Code)

  res = httptest.NewRecorder()
  req = httptest.NewRequest("GET", "/student/5", nil)
  // ์ถ”๊ฐ€๋œ ํ•™์ƒ ๋ฐ์ดํ„ฐ
  mux.ServeHTTP(res, req)
  assert.Equal(http.StatusOK, res.Code)
  err := json.NewDecoder(res.Body).Decode(&student)
  assert.Nil(err)
  assert.Equal("nnnn", student.Name)
}

func TestJsonHandler4(t *testing.T) { 
  assert := assert.New(t)

  mux := MakeWebHandler()
  res := httptest.NewRecorder()
  req := httptest.NewRequest("DELETE", "/student/1", nil)

  // DELETE ์š”์ฒญ
  mux.ServeHTTP(res, req)
  assert.Equal(http.StatusOK, res.Code)

  res = httptest.NewRecorder()
  req = httptest.NewRequest("GET", "/students", nil)
  mux.ServeHTTP(res, req)

  assert.Equal(http.StatusOK, res.Code)
  var list []Student
  err := json.NewDecoder(res.Body).Decode(&list)
  assert.Nil(err)
  assert.Equal(3, len(list))
  assert.Equal("aaa", list[0].Name)
}

31.TODO๋ฆฌ์ŠคํŠธ ์›น์‚ฌ์ดํŠธ ๋งŒ๋“ค๊ธฐ

  • ํ”„๋ก ํŠธ์—”๋“œ(HTML, Javascript) <-> ๋ฐฑ์—”๋“œ(Go ์›น ์„œ๋ฒ„)
  • gorilla/mux ์ด์™ธ์— urfave/negroni ์™€ unrolled/render ํŒจํ‚ค์ง€ ์‚ฌ์šฉ

  • RESTful API์— ๋งž์ถฐ ์„œ๋น„์Šค ์ •์˜

  • Todo ๊ตฌ์กฐ์ฒด ์ •์˜
  • RESTful API์— ๋งž์ถฐ ๊ฐ ํ•ธ๋“ค๋Ÿฌ ์ •์˜
  • HTML ์ž‘์„ฑ
  • Javascript ์ž‘์„ฑ
  • ์›น๋ธŒ๋ผ์šฐ์ € ๋™์ž‘ ํ™•์ธ

  • urfave/negroni ์„ค์น˜

  • ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ์›น ํ•ธ๋“ค๋Ÿฌ๋ฅผ ์ œ๊ณตํ•˜๋Š” ํŒจํ‚ค์ง€
    • ๋กœ๊ทธ๊ธฐ๋Šฅ
    • panic ๋ณต๊ตฌ ๊ธฐ๋Šฅ
    • ํŒŒ์ผ ์„œ๋ฒ„ ๊ธฐ๋Šฅ
go get github.com/urfave/negroni
mux := MakeWebHandler() // ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ํ•ธ๋“ค๋Ÿฌ
n := negroni.Classic()  // negroni ๊ธฐ๋ณธ ํ•ธ๋“ค๋Ÿฌ
n.UseHandler(mux) // ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๊ฐ์Œˆ

err := http.ListenAndServe(":3000", n) // negroni ๊ธฐ๋ณธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ๋™์ž‘ํ•จ
  • unrolled/render ์„ค์น˜
  • ์›น์„œ๋ฒ„ ์‘๋‹ต์„ ๊ตฌํ˜„ํ•˜๋Š”๋ฐ ์œ ์šฉํ•œ ํŒจํ‚ค์ง€.
  • ์„œ๋ฒ„์‘๋‹ต์œผ๋กœ HTML, JSON, TEXT ๊ฐ™์€ ํฌ๋งท ๊ฐ„๋‹จํžˆ ์‚ฌ์šฉ๊ฐ€๋Šฅ
go get github.com/unrolled/render
// JSON. ํฌ๋งท์œผ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์‘๋‹ต
r := render.New()
r.JSON(w, http.StatusOK, map[string]string{"hello": "json"})
  • ์›น ์„œ๋ฒ„ ๋งŒ๋“ค๊ธฐ
  • GET /todos ๋ฆฌ์ŠคํŠธ์กฐํšŒ
  • POST /todos ๋“ฑ๋ก
  • PUT /todos/id ์—…๋ฐ์ดํŠธ
  • DELETE /todos/id ์‚ญ์ œ
cd 30-restful/ex31.1
go mod init ex31.1
go mod tidy
go build
./ex31.1
  2022/07/27 16:02:13 Started App
// ex31.1/ex31.1.go
package main

import (
  "encoding/json"
  "log"
  "net/http"
  "sort"
  "strconv"

  "github.com/gorilla/mux"
  "github.com/unrolled/render"
  "github.com/urfave/negroni"
)


var rd *render.Render

// ํ• ์ผ ์ •๋ณด ๋‹ด๋Š” Todo ๊ตฌ์กฐ์ฒด
type Todo struct {
  ID int `json:"id,omitempty"`    // JSON ํฌ๋งท์œผ๋กœ ๋ณ€ํ™˜ ์˜ต์…˜
  Name string `json:"name"`
  Completed bool `json:"completed,omitempty"`
}

var todoMap map[int]Todo
var lastID int = 0

type Todos []Todo

func (t Todos) Len() int {
  return len(t)
}

func (t Todos) Swap(i, j int) {
  t[i], t[j] = t[j], t[i]
}

func (t Todos) Less(i, j int) bool {
  return t[i].ID < t[j].ID
}

func GetTodoListHandler(w http.ResponseWriter, r *http.Request) {
  list := make(Todos, 0)
  for _, todo := range todoMap {
    list = append(list, todo)
  }
  sort.Sort(list)  // ID ๊ธฐ์ค€ ์ •๋ ฌ
  rd.JSON(w, http.StatusOK, list)  // JSON ํฌ๋งท์œผ๋กœ ๋ฐ˜ํ™˜

  // w.WriteHeader(http.StatusOK)
  // w.Header().Set("Content-Type", "application/json")
  // json.NewEncoder(w).Encode(list) // JSON ํฌ๋งท์œผ๋กœ ๋ณ€๊ฒฝ
}

func PostTodoHandler(w http.ResponseWriter, r *http.Request) {
  var todo Todo
  err := json.NewDecoder(r.Body).Decode(&todo)
  if err != nil {
    log.Fatal(err)
    w.WriteHeader(http.StatusBadRequest)
    return
  }
  lastID++
  todo.ID = lastID
  todoMap[lastID] = todo
  rd.JSON(w, http.StatusCreated, todo)
}

type Success struct {
  Success bool `json:"Success"`
}

func RemoveTodoHandler(w http.ResponseWriter, r *http.Request) {
  vars := mux.Vars(r)
  id, _ := strconv.Atoi(vars["id"])
  if _, ok := todoMap[id]; ok {
    delete(todoMap, id)
    rd.JSON(w, http.StatusOK, Success{true})
  } else {
    rd.JSON(w, http.StatusNotFound, Success{false})
  }
}

func UpdateTodoHandler(w http.ResponseWriter, r *http.Request) {
  var newTodo Todo
  err := json.NewDecoder(r.Body).Decode(&newTodo)
  if err != nil {
    log.Fatal(err)
    w.WriteHeader(http.StatusBadRequest)
    return
  }

  vars := mux.Vars(r)
  id, _ := strconv.Atoi(vars["id"])
  if todo, ok := todoMap[id]; ok {
    todo.Name = newTodo.Name
    todo.Completed = newTodo.Completed
    rd.JSON(w, http.StatusOK, Success{true})
  } else {
    rd.JSON(w, http.StatusBadRequest, Success{false})
  }
}

func MakeWebHandler() http.Handler {
  todoMap = make(map[int]Todo)

  mux := mux.NewRouter() // gorilla/mux ๊ฐ์ฒด ์ƒ์„ฑ

  mux.Handle("/", http.FileServer(http.Dir("public")))
  mux.HandleFunc("/todos", GetTodoListHandler).Methods("GET")
  mux.HandleFunc("/todos", PostTodoHandler).Methods("POST")
  mux.HandleFunc("/todos/{id:[0-9]+}", RemoveTodoHandler).Methods("DELETE")
  mux.HandleFunc("/todos/{id:[0-9]+}", UpdateTodoHandler).Methods("PUT")
  return mux
}

func main() {
  rd = render.New()
  m := MakeWebHandler()
  n := negroni.Classic()
  n.UseHandler(m)

  log.Println("Started App")
  err := http.ListenAndServe(":3000", n)
  if err != nil {
    panic(err)
  }
}
  • ํ”„๋ก ํŠธ์—”๋“œ ๋งŒ๋“ค๊ธฐ
  • ex31.1/public/index.html
  • ex31.1/public/todo.js
  • ex31.1/public/todo.css

  • 3ํ‹ฐ์–ด ์›น

  • ํ”„๋ก ํŠธ์—”๋“œ-๋ฐฑ์—”๋“œ-๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค

  • ์›น ๋ฐฐํฌ ๋ฐฉ๋ฒ•

  • ๋กœ์ปฌ์— ๋„์šฐ๋Š” ์„œ๋ฒ„ ํ•œ๊ณ„
    1. ๋„๋ฉ”์ธ์ด ์—†์Œ (e.g. https://13.125.141.84 https://mytodolist.com)
    2. ๊ณ ์ •๋œ ๊ณต๊ฐœ IP๊ฐ€ ์—†์Œ
    3. IPv4๋Š” ๊ณ ์ •๋œ๊ฒƒ์ด ์•„๋‹Œ ๊ณต์œ ๊ธฐ๋‚˜ ISP์—์„œ ์ปดํ“จํ„ฐ์— ์ž„์‹œํ• ๋‹นํ•œ ์ฃผ์†Œ์ด๊ณ , ๊ณต๊ฐœ๋œ ์ฃผ์†Œ๋„ ์•„๋‹˜
    4. ๊ณต์œ ๊ธฐ ๋„คํŠธ์›Œํฌ ์•ˆ์ชฝ์—์„œ๋งŒ ์œ ํšจํ•จ
  • ๊ฐœ์ธ PC๋Š” ๊ณ ์ • IP๊ฐ€ ์—†์Œ
  • ๊ณ ์ •๋œ ๊ณต๊ฐœ IP์ฃผ์†Œ๋ฅผ ์–ป์œผ๋ ค๋ฉด ๊ณต๊ฐœ IP ๋Œ€์—ญ์„ ์ด๋ฏธ ํš๋“ํ•œ ์ธํ„ฐ๋„ท ํ˜ธ์ŠคํŒ…์—…์ฒด๋กœ๋ถ€ํ„ฐ ํ˜ธ์ŠคํŒ… ๋ฐ›์•„์•ผํ•จ
  • IaaS, PaaS: aws,gcloud,azure (์œ ๋ฃŒ)
  • Heroku(๋ฌด๋ฃŒ) Paas: ๊ฐ„๋‹จํ•œ ๋ช…๋ น์–ด๋กœ ์›น์„œ๋ฒ„ ๋ฐฐํฌ ๊ฐ€๋Šฅ

  • ํด๋ผ์šฐ๋“œ ์„œ๋น„์Šค ์œ ํ˜•

  • IaaS : infrastructure as a service
  • PaaS : ๋จธ์‹ ์„ฑ๋Šฅ ์„ ํƒ ๋ฐ ์„œ๋ฒ„ ์‹คํ–‰๊ฐ„ํŽธํ™”. ์„œ๋ฒ„ ์„ธ๋ถ€์กฐ์ • ์–ด๋ ค์›€
  • SaaS : e.g. ๊ตฌ๊ธ€๋“œ๋ผ์ด๋ธŒ, ๊ตฌ๊ธ€๋ฌธ์„œ๋„๊ตฌ

  • ํ—ค๋กœ์ฟ ๋กœ ๋ฐฐํฌํ•˜๊ธฐ

  • heroku.com ๊ฐ€์ž…
  • CLI ์„ค์น˜
    • https://devcenter.heroku.com/articles/heroku-cli
    • curl https://cli-assets.heroku.com/install.sh | sh
    • heroku version
# ๋ชจ๋“ˆ ์ƒ์„ฑ
mkdir todo && cd $_
touch ex31.1.go
mkdir public
go mod init todo
go build

git init
heroku login
heroku create
  • ์›น์„œ๋ฒ„ ์ˆ˜์ •
  • ํ—ค๋กœ์ฟ ์—์„œ ์›น์„œ๋ฒ„ ๋ฐฐํฌํ•˜๋ ค๋ฉด port๋“ฑ ์ผ๋ถ€ ์ˆ˜์ • ํ•„์š”
  • Procfile ์ƒ์„ฑ ๋ฐ ๋‚ด์šฉ ์ž…๋ ฅ
func main() {
  rd = render.New()
  m := MakeWebHandler()
  n := negroni.Classic()
  n.UseHandler(m)

  log.Println("Started App")
  port := os.Getenv("PORT") // ํ—ค๋กœ์ฟ ์„œ๋ฒ„์—์„œ ํ™˜๊ฒฝ๋ณ€์ˆ˜ PORT ๊ฐ€์ ธ์˜ค๊ธฐ
  // err := http.ListenAndServe(":3000", n)
  err := http.ListenAndServe(":" + port, n) // port๋ฅผ ์ด์šฉํ•ด์„œ ์›น ์„œ๋ฒ„ ์‹คํ–‰
  if err != nil {
    panic(err)
  }
}
cd todo
cat > Procfile
  web: bin/todo
  • ๊นƒ ์ปค๋ฐ‹
# .gitignore
cat > .gitignore
  *.exe

git add .
git status
git commit -m 'first commit of todo'
git push heroku master

heroku open
  • go build ํฌ๋กœ์Šค ํ”Œ๋žซํผ
GOOS=windows GOARCH=amd64 go build
  • Linux ํ™˜๊ฒฝ๋ฐฐํฌ
# ๋กœ์ปฌ
zip -r public.zip public/
scp public.zip root@{ip}:/root
scp demo root@{ip}:/root

# ์›๊ฒฉ
mkdir goapp && cd $_
unzip public.zip -d goapp
cp /root/demo /goapp
./demo &

# http://{ip}:3000/
# ์„œ๋ฒ„ 

golang http server

Q) what's the best practice for golang to store aws credentials 
A) When it comes to storing AWS credentials in a Go application,
the best practice is to use the AWS SDK for Go's built-in credential chain.
The credential chain allows you to specify multiple ways to provide AWS credentials,
including environment variables, shared credential files, and IAM roles.
The SDK will automatically search for and use the first set of valid credentials it finds in the chain.

Here's an example of using the credential chain in a Go application:
package main

import (
    "fmt"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/s3"
)

func main() {
    // Create a new session using the default configuration
    sess := session.Must(session.NewSessionWithOptions(session.Options{
        SharedConfigState: session.SharedConfigEnable,
    }))

    // Create a new S3 client using the session
    svc := s3.New(sess)

    // List all buckets in the account
    result, err := svc.ListBuckets(nil)
    if err != nil {
        panic(err)
    }

    // Print the name of each bucket
    for _, bucket := range result.Buckets {
        fmt.Println(*bucket.Name)
    }
}
In this example, we create a new session using the session.NewSessionWithOptions function
and specifying SharedConfigEnable as the SharedConfigState option.
This tells the SDK to use the default configuration file and environment variables to find AWS credentials.

If you need to use a specific set of credentials or a different configuration file,
you can specify those options in the session.NewSessionWithOptions function. For example:
  • This would create a session with the specified profile ("my-profile") and region ("us-west-2").
sess := session.Must(session.NewSessionWithOptions(session.Options{
    Config: aws.Config{
        Region: aws.String("us-west-2"),
    },
    Profile: "my-profile",
}))

Stack vs. Heap

  • https://medium.com/@denniswon/a-practical-guide-to-memory-allocation-in-go-3e7aca6b449a
  • Variables whose addresses are taken become candidates for allocation on the heap.
  • Memory allocation in Go follows escape analysis, where variables local to a function are typically allocated on the stack. However, if the compiler cannot prove that a variable is not referenced after the function returns, it allocates the variable on the heap to avoid dangling pointer errors.

  • Variables in Go can be allocated either on the stack or the heap:

  • Stack: Local variables (like function arguments or temporary values) are usually allocated on the stack. This memory is fast to allocate and deallocate.
  • Heap: Dynamic memory (like slices, maps, and large data structures) is allocated on the heap. This memory is managed by the garbage collector and is slower to allocate and deallocate.

  • Both stdout and stderr use memory from the stack or heap, depending on how they are accessed.

Userspace vs. Kernal space

https://stackoverflow.com/questions/1739799/doesnt-the-fact-that-go-and-java-use-user-space-thread-mean-that-you-cant-real

In Go, the term userspace refers to the part of a program that runs in user mode, as opposed to kernel mode.

  1. Kernel Mode vs. User Mode:
  2. When a computer runs an operating system, it operates in two primary modes:

    • Kernel Mode: In this mode, the operating system's kernel (core) has direct access to hardware resources (CPU, memory, devices). It can execute privileged instructions and manage system resources.
    • User Mode: In this mode, regular application code (user programs) runs. User-mode processes don't have direct access to hardware; they rely on the kernel for system calls (e.g., file I/O, networking, process management).
  3. Userspace in Go:

  4. In the context of Go programming, userspace specifically refers to the part of your Go program that runs in user mode.
  5. When you write Go code, most of it runs in userspace. This includes your application logic, data processing, and interactions with libraries.
  6. Userspace code interacts with the Go runtime, which manages memory, goroutines, garbage collection, and other runtime features.

  7. Examples:

  8. When you create a Go program that reads a file, processes data, and sends an HTTP request, all those actions happen in userspace.
  9. However, if your Go program needs to perform low-level tasks (e.g., interacting directly with hardware, managing memory pages), it might need to make system calls to the kernel (enter kernel mode).

  10. WireGuard-Go Example:

  11. An interesting example related to userspace is WireGuard-Go.
  12. WireGuard is a modern VPN protocol, and WireGuard-Go is an implementation of WireGuard in Go.
  13. When you run WireGuard-Go, it creates a virtual network interface (like wg0) entirely in userspace.
  14. Instead of relying on a kernel module (which is faster but requires administrative privileges), WireGuard-Go manages the VPN interface purely in userspace.
  15. You can use it by running: $ wireguard-go wg0 This command creates the interface and forks into the background.

  16. Pros and Cons of Userspace:

  17. Advantages:
    • Portability: Userspace code is more portable across different platforms (Linux, macOS, Windows, etc.).
    • Flexibility: Userspace programs can be written in any language (including Go) without requiring kernel-specific code.
  18. Disadvantages:
    • Performance: Userspace operations are generally slower than kernel-mode operations due to the overhead of system calls.
    • Privileges: Some tasks (e.g., managing network interfaces) require kernel-level access, which userspace programs lack.

In summary, userspace in Go refers to the part of your program that runs outside the kernel, handling application logic and interactions with libraries. WireGuard-Go is an excellent example of userspace networking! ๐Ÿš€ยนโต

(1) WireGuard/wireguard-go - GitHub. https://github.com/WireGuard/wireguard-go. (2) GitHub - google/netstack: IPv4 and IPv6 userland network stack. https://github.com/google/netstack. (3) examples module - github.com/cilium/ebpf/examples - Go Packages. https://pkg.go.dev/github.com/cilium/ebpf/examples. (4) Golang 1.18 workspaces ๅฐ้ฒœ - ๆŽ˜้‡‘. https://juejin.cn/post/7087207256126652429. (5) Get familiar with workspaces - The Go Programming Language. https://go.dev/blog/get-familiar-with-workspaces. (6) undefined. https://git.zx2c4.com/wireguard-go.