λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
ν”„λ‘œκ·Έλž˜λ° μ–Έμ–΄/Go & Rust

[Go] Mordern Go κΈ°λ³Έ2(ν•¨μˆ˜, μΈν„°νŽ˜μ΄μŠ€, λ©”μ„œλ“œ, μ—λŸ¬)

by μ„œμ•„λž‘πŸ˜ 2025. 5. 21.

 

ν•¨μˆ˜

βœ” μ—¬λŸ¬κ°œ λ°˜ν™˜κ°’

func divmod(a, b int) (int, int) {
    return a / b, a % b
}

q, r := divmod(10, 3)  // q=3, r=1


βœ”  이름 μžˆλŠ” λ°˜ν™˜κ°’

func sum(a, b int) (result int) {
    result = a + b
    return // 이름 있으면 return μ‹œ λ³€μˆ˜ μƒλž΅ κ°€λŠ₯
}


βœ”  ν•¨μˆ˜λ„ 일급객체

func operate(a, b int, f func(int, int) int) int {
    return f(a, b)
}

sum := func(x, y int) int { return x + y } // λžŒλ‹€
result := operate(3, 4, sum)  // 7


βœ”  λžŒλ‹€(읡λͺ…ν•¨μˆ˜)

result := func(x int) int {
    return x * 2
}(5)  // 10


βœ”  panic & recover

func safeDivide(a, b int) {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("μ—λŸ¬ λ°œμƒ:", r)
        }
    }()
    fmt.Println(a / b)  // b=0일 경우 panic
}


βœ”  Defer: ν•¨μˆ˜ μ’…λ£Œμ‹œ 싀행됨

func writeLog(w io.Writer, message string) {
    fmt.Fprintf(w, "[Log] %s\\n", message)
}

func main() {
    f, _ := os.Create("app.log")
    defer f.Close()

    writeLog(f, "File log")        // νŒŒμΌμ— 좜λ ₯
    writeLog(os.Stdout, "Console") // μ½˜μ†”μ— 좜λ ₯
}


βœ”  κ°€λ³€ 인수 ν•¨μˆ˜

func printArgs(args ...string) {
    fmt.Printf("νƒ€μž…: %T\\n", args) // []string
    for _, arg := range args {
        fmt.Println(arg)
    }
}


βœ”  슬라이슀λ₯Ό unpacking ν˜•νƒœλ‘œ κ°€λ³€ 인수 전달

words := []string{"hello", "world"}
printArgs(words...) // 슬라이슀 → κ°€λ³€ 인수둜 전달 (words... λ°˜λ“œμ‹œ ν•„μš”)


βœ”  κ³ μ • 인수 + κ°€λ³€ 인수 μ‘°ν•© κ°€λŠ₯

func log(level string, msgs ...string) {
    for _, msg := range msgs {
        fmt.Printf("[%s] %s\\n", level, msg)
    }
}

log("INFO", "started", "processing") // [INFO] started \\n [INFO] processing

⚠️ μ£Όμ˜μ‚¬ν•­

ν•­λͺ© μ„€λͺ…
μΈμžκ°€ 0개일 μˆ˜λ„ 있음 len(nums)λ₯Ό 체크해야 μ•ˆμ „
νƒ€μž…μ΄ κ³ μ •λ˜μ–΄μ•Ό 함 ...interface{}λ₯Ό μ“°λ©΄ λͺ¨λ“  νƒ€μž…μ„ 받을 수 있음
μ„±λŠ₯ 이슈 큰 λ°°μ—΄μ΄λ‚˜ λΉˆλ²ˆν•œ ν˜ΈμΆœμ—μ„œλŠ” 슬라이슀 생성 λΉ„μš© κ³ λ €

πŸ”§ λͺ¨λ“  νƒ€μž… 수용 예 (interface{})

func logAnything(args ...interface{}) {
    for _, a := range args {
        fmt.Printf("Value: %v, Type: %T\\n", a, a)
    }
}

logAnything(123, "hello", true, 3.14)

 

βœ… μ£Όμš” νŠΉμ§•

ν•­λͺ© μ„€λͺ…
닀쀑 λ°˜ν™˜ (T1, T2) ν˜•νƒœλ‘œ μ—¬λŸ¬ κ°’ λ°˜ν™˜ κ°€λŠ₯
일급 ν•¨μˆ˜ λ³€μˆ˜μ— μ €μž₯, 인자/λ¦¬ν„΄κ°’μœΌλ‘œ μ‚¬μš© κ°€λŠ₯
ν΄λ‘œμ € λ‚΄λΆ€ μƒνƒœ μœ μ§€ κ°€λŠ₯
κ°€λ³€ 인자 ...T λ¬Έλ²•μœΌλ‘œ 처리
defer μ’…λ£Œ 직전 μ‹€ν–‰, λ¦¬μ†ŒμŠ€ 정리에 ν•„μˆ˜
panic/recover μ˜ˆμ™Έ 볡ꡬ 처리

 

 

λ©”μ„œλ“œ

βœ”  λ¦¬μ‹œλ²„

func (d Dog) info() int {
	return d.width * d.height
}
  • d Dog 뢀뢄이 λ¦¬μ‹œλ²„ → info λ©”μ„œλ“œλŠ” Dog νƒ€μž…μ— 속함
  • dλŠ” ꡬ쑰체 λ³€μˆ˜λ‘œμ¨ ν•΄λ‹Ή λ©”μ„œλ“œμ—μ„œ λ§€κ°œλ³€μˆ˜μ²˜λŸΌ μ‚¬μš©λ¨
  • λ¦¬μ‹œλ²„λ‘œλŠ” λͺ¨λ“  둜컬 νƒ€μž…λ“€μ΄ κ°€λŠ₯함 → 둜컬 νƒ€μž…μ€ ν•΄λ‹Ή νŒ¨ν‚€μ§€ μ•ˆμ—μ„œ type ν‚€μ›Œλ“œλ‘œ μ„ μ–Έλœ νƒ€μž…λ“€
  • λ©”μ„œλ“œ μ •μ˜λŠ” 같은 νŒ¨ν‚€μ§€ λ‚΄ 어디에도 μœ„μΉ˜ν•  수 μžˆμ§€λ§Œ ꡬ쑰체가 μ„ μ–Έλœ 파일 내에 λ©”μ„œλ“œλ„ μœ„μΉ˜ν•˜λŠ” 것이 일반적
  • 일반 ν•¨μˆ˜λŠ” 어디에도 μ†ν•˜μ§€ μ•Šμ§€λ§Œ λ©”μ„œλ“œλŠ” λ¦¬μ‹œλ²„μ— 속함

 

βœ”  포인터 λ©”μ„œλ“œ vs κ°’ νƒ€μž… λ©”μ„œλ“œ

package main

import "fmt"

type account struct {
	balance int
	credit float64
	name string
}

func (a *account) withdrawPointer(amount int) {
	a.balance -= amount
}

func (a account) withdrawValue(amount int) {
	a.balance -= amount
}

func (a account) withdrawReturnValue(amount int) account {
	a.balance -= amount
	return a
}

func main() {
	mainA := account{balance: 100}
	fmt.Println(mainA.balance)
	
	mainA.withdrawPointer(10)
	fmt.Println(mainA.balance)

	mainA.withdrawValue(10)
	fmt.Println(mainA.balance)

	mainA = mainA.withdrawReturnValue(10)
	fmt.Println(mainA.balance)
}	

/*
100   μ΄ˆκΈ°κ°’
90    포인터 λ©”μ„œλ“œ
90    κ°’ λ©”μ„œλ“œ
80    κ°’ λ°˜ν™˜ λ©”μ„œλ“œ
*/


βœ” 
μΈν„°νŽ˜μ΄μŠ€μ™€ ν•¨κ»˜ μ“°κΈ°

type Shape interface {
    Area() float64
}

type Circle struct {
    Radius float64
}

func (c Circle) Area() float64 {
    return 3.14 * c.Radius * c.Radius
}


βœ” 
λ©”μ„œλ“œ 체이닝(포인터 λ¦¬μ‹œλ²„λ₯Ό λ¦¬ν„΄ν•΄μ„œ 체이닝)

type Builder struct {
    data string
}

func (b *Builder) Add(s string) *Builder {
    b.data += s
    return b
}

func (b *Builder) Result() string {
    return b.data
}

res := (&Builder{}).Add("Hello, ").Add("Go!").Result()  // "Hello, Go!"

 

βœ”  데이터 은닉

type account struct {
    balance int
}

func NewAccount() *account {
    return &account{balance: 0}
}

func (a *account) Deposit(amount int) {
    if amount > 0 {
        a.balance += amount
    }
}

func (a *account) Balance() int {
    return a.balance
}

// μ™ΈλΆ€μ—μ„œ balance μ ‘κ·Ό λΆˆκ°€


βœ”  λ©”μ„œλ“œμ—μ„œ μΈν„°νŽ˜μ΄μŠ€ νƒ€μž… 인자(λ‹€ν˜•μ„±)

type Logger interface {
    Log(msg string)
}

type ConsoleLogger struct{}

func (c ConsoleLogger) Log(msg string) {
    fmt.Println("[LOG]", msg)
}

func process(l Logger) {
    l.Log("Starting process")
}

 

βœ… μ£Όμš” νŠΉμ§•

ꡬ뢄 μ„€λͺ…
λ¦¬μ‹œλ²„(receiver) λ©”μ„œλ“œλ₯Ό μ–΄λ–€ νƒ€μž…μ— 뢙일지 μ •μ˜
포인터 λ¦¬μ‹œλ²„ μƒνƒœ λ³€κ²½ ν•„μš” μ‹œ μ‚¬μš©
κ°’ λ¦¬μ‹œλ²„ 읽기 μ „μš© 처리 μ‹œ μ‚¬μš©
체이닝 포인터 λ¦¬ν„΄ν•˜λ©΄ κ°€λŠ₯
μΈν„°νŽ˜μ΄μŠ€μ™€ μ‚¬μš© λ‹€ν˜•μ„± κ΅¬ν˜„ κ°€λŠ₯
μž„λ² λ”© 상속 μœ μ‚¬ κΈ°λŠ₯

 

 

μΈν„°νŽ˜μ΄μŠ€

βœ”  κΈ°λ³Έ
- λ©”μ„œλ“œ κ΅¬ν˜„μ„ ν¬ν•¨ν•œ κ΅¬μ²΄ν™”λœ 객체가 μ•„λ‹Œ μΆ”μƒν™”λœ 객체둜 μƒν˜Έμž‘μš©ν•  수 있음
- μžλ°”μ˜ μΈν„°νŽ˜μ΄μŠ€ κ°œλ…κ³Ό κ°™μŒ → λ©”μ„œλ“œ 기반 μΈν„°νŽ˜μ΄μŠ€
- μΈν„°νŽ˜μ΄μŠ€λŠ” λ‹€ν˜•μ„±μ„ κ΅¬ν˜„ν•˜κΈ° μœ„ν•œ 도ꡬ

type Logger interface {
    Log(msg string)
}

type FileLogger struct{}
func (f FileLogger) Log(msg string) {
    fmt.Println("[File]", msg)
}

type ConsoleLogger struct{}
func (c ConsoleLogger) Log(msg string) {
    fmt.Println("[Console]", msg)
}

func Process(logger Logger) {
    logger.Log("처리 μ‹œμž‘")
    // ...
    logger.Log("처리 μ’…λ£Œ")
}

func main() {
    fileLogger := FileLogger{}
    consoleLogger := ConsoleLogger{}

    Process(fileLogger)
    Process(consoleLogger)
}


βœ”  μ˜μ‘΄μ„± μ£Όμž…(DI)
μ„œλΉ„μŠ€ κ³„μΈ΅μ—μ„œ μ˜μ‘΄μ„± 뢄리

// μΈν„°νŽ˜μ΄μŠ€ μ •μ˜
type UserRepository interface {
    FindByID(id int) (*User, error)
}

// μ‹€μ œ κ΅¬ν˜„μ²΄
type MySQLUserRepository struct {
    db *sql.DB
}

func (r *MySQLUserRepository) FindByID(id int) (*User, error) {
    // DBμ—μ„œ μ‚¬μš©μž 쑰회
    ...
}

// μ„œλΉ„μŠ€ 계측
type UserService struct {
    repo UserRepository // μΈν„°νŽ˜μ΄μŠ€μ— 의쑴
}

func (s *UserService) GetUser(id int) (*User, error) {
    return s.repo.FindByID(id)
}

// UserRepository에 λŒ€ν•œ Mock을 λ§Œλ“€μ–΄ λ‹¨μœ„ ν…ŒμŠ€νŠΈλ₯Ό μ‰½κ²Œ μž‘μ„±ν•  수 있음
// MySQL 외에 Redis, Memory 기반 λ“± λ‹€μ–‘ν•œ κ΅¬ν˜„μ²΄λ‘œ ꡐ체 κ°€λŠ₯


βœ”  빈 μΈν„°νŽ˜μ΄μŠ€ ν™œμš©
빈 μΈν„°νŽ˜μ΄μŠ€λŠ” ν…œν”Œλ¦Ώ or μ œλ„€λ¦­ 처럼 μ–΄λ–€ νƒ€μž…μ΄λ“  받아쀄 수 μžˆλŠ” ν˜•νƒœ

// JSON 예제
var result map[string]interface{}
err := json.Unmarshal([]byte(jsonStr), &result)

fmt.Println(result["price"]) // float64, string λ“± λ‹€μ–‘ν•œ νƒ€μž…μΌ 수 있음

// μ»¨ν…Œμ΄λ„ˆ 자료ꡬ쑰 κ΅¬ν˜„
type Stack struct {
    items []interface{}
}

func (s *Stack) Push(v interface{}) {
    s.items = append(s.items, v)
}

func (s *Stack) Pop() interface{} {
    if len(s.items) == 0 {
        return nil
    }
    last := s.items[len(s.items)-1]
    s.items = s.items[:len(s.items)-1]
    return last
}
  • μ£Όμ˜μ‚¬ν•­
    • νƒ€μž… μ•ˆμ •μ„± μ—†μŒ(잘λͺ»λœ νƒ€μž… → νŒ¨λ‹‰ λ°œμƒ κ°€λŠ₯)
    • λ‚¨μš© μœ„ν—˜(λͺ¨λ“  κ±Έ interface{}둜 μ²˜λ¦¬ν•˜λ©΄ μœ μ§€λ³΄μˆ˜ μ•…ν™”)
  • λŒ€μ•ˆ: μ œλ„€λ¦­(Go 1.18 이후)
type Stack[T any] struct {
    items []T
}

func (s *Stack[T]) Push(v T) {
    s.items = append(s.items, v)
}

 

βœ”  any

  • Go 1.18λΆ€ν„° λ„μž…λœ μ œλ„€λ¦­ 지원 νƒ€μž…
  • type any = interface{}
var x any

x = 123
fmt.Println(x)

x = "hello"
fmt.Println(x)

 

βœ”  νƒ€μž… μŠ€μœ„μΉ˜

var x any = "hello"

switch v := x.(type) {
case int:
    fmt.Println("μ •μˆ˜:", v)
case string:
    fmt.Println("λ¬Έμžμ—΄:", v)
default:
    fmt.Println("μ•Œ 수 μ—†λŠ” νƒ€μž…")
}

 

βœ”  특히 json μ§λ ¬ν™”μ‹œ 많이 μ‚¬μš©

var data map[string]any
err := json.Unmarshal([]byte(jsonStr), &data)

 

βœ”  덕 타이핑
덕 타이핑(Duck Typing)은 “μ–΄λ–€ νƒ€μž…μ΄ νŠΉμ • λ©”μ„œλ“œλ₯Ό κ°€μ§€κ³  μžˆλ‹€λ©΄, κ·Έ νƒ€μž…μ€ ν•΄λ‹Ή μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•œ κ²ƒμœΌλ‘œ κ°„μ£Όν•œλ‹€”λŠ” κ°œλ…. Goμ—μ„œλŠ” λͺ…μ‹œμ μœΌλ‘œ implements 같은 ν‚€μ›Œλ“œλ₯Ό μ“°μ§€ μ•Šκ³ λ„ μ•”λ¬΅μ μœΌλ‘œ μΈν„°νŽ˜μ΄μŠ€λ₯Ό 만쑱

package main

import "fmt"

// μΈν„°νŽ˜μ΄μŠ€ μ •μ˜
type Walker interface {
    Walk()
}

// νƒ€μž… μ •μ˜
type Dog struct{}

func (d Dog) Walk() {
    fmt.Println("Dog walks")
}

type Cat struct{}

func (c Cat) Walk() {
    fmt.Println("Cat walks")
}

// μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜λŠ” ν•¨μˆ˜
func StartWalking(w Walker) {
    w.Walk()
}

func main() {
    dog := Dog{}
    cat := Cat{}

    StartWalking(dog) // DogλŠ” λͺ…μ‹œμ μœΌλ‘œ Walkerλ₯Ό κ΅¬ν˜„ν•˜μ§€ μ•Šμ•˜μ§€λ§Œ Walk() λ©”μ„œλ“œκ°€ μžˆμœΌλ―€λ‘œ μ‚¬μš© κ°€λŠ₯
    StartWalking(cat) // Cat도 λ§ˆμ°¬κ°€μ§€
}

 

 

μ—λŸ¬ 핸듀링

βœ”  κΈ°λ³Έ Go ν•¨μˆ˜λŠ” μ—λŸ¬κ°€ λ°œμƒν•  κ°€λŠ₯성이 있으면 보톡 λ‹€μŒκ³Ό 같이 error νƒ€μž…μ„ ν•¨κ»˜ 리턴

func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("division by zero")
    }
    return a / b, nil
}

result, err := divide(10, 2)
if err != nil {
    fmt.Println("Error:", err)
    return
}
fmt.Println("Result:", result)


βœ”  Error() λ©”μ„œλ“œλ₯Ό κ΅¬ν˜„ν•˜λ©΄ μ–΄λ–€ νƒ€μž…μ΄λ“  error둜 μ‚¬μš©ν•  수 있음

type error interface {
    Error() string
}


βœ”  fmt.Errorf: μ—λŸ¬ 포맷 생성

err := fmt.Errorf("파일 μ—΄κΈ° μ‹€νŒ¨: %v", err)

βœ”  μ‚¬μš©μž μ •μ˜ μ—λŸ¬ νƒ€μž…

type MyError struct {
    Code int
    Msg  string
}

func (e MyError) Error() string {
    return fmt.Sprintf("Error %d: %s", e.Code, e.Msg)
}

return MyError{Code: 404, Msg: "Not Found"} // 싀무 μ½”λ“œ 기반(자주 μ‚¬μš©λ¨)


βœ”  λ‘œκ·Έμ™€ μ—λŸ¬ 뢄리 μ—λŸ¬λ₯Ό λ¦¬ν„΄ν•˜λŠ” ν•¨μˆ˜λŠ” 둜그λ₯Ό 찍지 μ•Šκ³  ν˜ΈμΆœμžκ°€ νŒλ‹¨ν•˜λ„λ‘ μœ„μž„: 호좜 μΈ‘μ—μ„œ log.Println(err) λ˜λŠ” fmt.Println(err) 처리

func readFile(path string) ([]byte, error) {
    data, err := os.ReadFile(path)
    if err != nil {
        return nil, fmt.Errorf("readFile: %w", err)
    }
    return data, nil
}

 

βœ… μ£Όμš” νŠΉμ§•

ν•­λͺ© μ„€λͺ…
error μΈν„°νŽ˜μ΄μŠ€ Error() string λ©”μ„œλ“œλ§Œ κ΅¬ν˜„
fmt.Errorf λ©”μ‹œμ§€ 포함 μ—λŸ¬ 생성 (%w둜 감싸기 κ°€λŠ₯)
errors.Is/As μ—λŸ¬ 비ꡐ 및 λ‹€μš΄μΊμŠ€νŒ…
panic/recover 치λͺ…적 상황 λŒ€λΉ„ (일반 λ‘œμ§μ—μ„  μ§€μ–‘)
μ‚¬μš©μž μ •μ˜ μ—λŸ¬ νƒ€μž… 싀무 μ½”λ“œμ—μ„œ μƒμ„Έν•œ μ—λŸ¬ 전달 κ°€λŠ₯
sentinel errors κ³ μ •λœ μ—λŸ¬ κ°’μœΌλ‘œ μƒνƒœ ꡬ뢄
early return νŒ¨ν„΄ μ½”λ“œ 가독성 확보에 μ€‘μš”

λŒ“κΈ€