Go 언어의 개요와 시작에 필요한 정보를 알아봅니다.

학습 자료

웹 프레임워크

일반적으로 Go로 웹 애플리케이션을 작성할 때는 프레임워크를 사용하지 않는 것이 권장됩니다.

프레임워크 목록

  • Revel
  • Beego
  • Gin
  • Buffalo - 웹 개발용 프레임워크

라이브러리

  • Gin - 자동 재컴파일, 라이브 빌드 서버 도구
  • Buffalo - 웹 개발용 프레임워크

의존성 관리

  • dep - Go 의존성 관리 도구

커뮤니티

Go의 장점

변수 스왑이 간편

x, y := 0, 1
x, y = y, x
fmt.Println(x, y)

다중 반환값 지원

함수에서 여러 값을 반환할 수 있습니다.

코딩 규칙

함수 이름

  • 대문자로 시작: 외부에서 호출 가능 (exported)
  • 소문자로 시작: 패키지 내부용 (unexported)

개발 팁

코드 포맷팅

커밋 전에 go fmt 실행을 권장합니다.

GoLand IDE 기능

  • 여러 프로젝트 Attach: Open Project 시 프로젝트 여러 개를 attach 가능
  • SQL 실행: SQL -> Show Intention Actions -> Run query in console
  • 데이터베이스 연결 기능

Go의 기본 문법

변수 선언

Go는 정적 타입 언어이면서도 타입 추론을 지원합니다.

// 명시적 타입 선언
var name string = "Go"
var age int = 15

// 타입 추론 (함수 내부에서만 사용 가능)
name := "Go"
age := 15

// 여러 변수 동시 선언
var (
    x int    = 10
    y string = "hello"
)

구조체 (Struct)

Go에는 클래스가 없지만 구조체와 메서드를 조합하여 객체지향적인 코드를 작성할 수 있습니다.

type Person struct {
    Name string
    Age  int
}

func (p Person) Greet() string {
    return fmt.Sprintf("Hello, I'm %s", p.Name)
}

func main() {
    p := Person{Name: "Kim", Age: 30}
    fmt.Println(p.Greet())
}

인터페이스

Go의 인터페이스는 암시적으로 구현됩니다. implements 키워드 없이 메서드만 구현하면 자동으로 인터페이스를 만족합니다.

type Speaker interface {
    Speak() string
}

type Dog struct{}

func (d Dog) Speak() string {
    return "Woof!"
}
// Dog은 자동으로 Speaker 인터페이스를 구현

에러 처리

Go는 예외(exception) 대신 반환값으로 에러를 처리합니다. 이 방식은 에러 처리를 명시적으로 만들어 코드의 안정성을 높입니다.

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, 0)
if err != nil {
    log.Fatal(err)
}

Go 모듈 시스템

Go 1.11부터 도입된 Go Modules는 현재 공식 의존성 관리 시스템입니다. 기존 depGOPATH 방식을 대체합니다.

# 새 모듈 초기화
go mod init github.com/username/project

# 의존성 추가 (import 후 자동으로 추가됨)
go mod tidy

# 의존성 다운로드
go mod download

go.mod 파일이 프로젝트 루트에 생성되어 의존성 정보를 관리합니다. go.sum 파일은 각 의존성의 체크섬을 기록하여 무결성을 보장합니다.


동시성 (Goroutine과 Channel)

Go의 가장 큰 강점 중 하나는 내장된 동시성 지원입니다.

// Goroutine: go 키워드로 간단하게 생성
go func() {
    fmt.Println("별도 고루틴에서 실행")
}()

// Channel: 고루틴 간 안전한 데이터 통신
ch := make(chan string)
go func() {
    ch <- "Hello from goroutine"
}()
msg := <-ch
fmt.Println(msg)

Goroutine은 OS 스레드보다 훨씬 가볍습니다(약 2KB의 스택). 수천 개의 Goroutine을 동시에 실행해도 메모리 부담이 적어 높은 동시성이 필요한 서버 프로그램에 적합합니다.

Select 문

여러 Channel에서 동시에 대기할 때 select를 사용합니다. 어떤 Channel이 먼저 준비되든 해당 case가 실행됩니다.

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        time.Sleep(1 * time.Second)
        ch1 <- "one"
    }()

    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "two"
    }()

    // 먼저 도착하는 메시지를 처리
    select {
    case msg := <-ch1:
        fmt.Println("Received from ch1:", msg)
    case msg := <-ch2:
        fmt.Println("Received from ch2:", msg)
    case <-time.After(3 * time.Second):
        fmt.Println("Timeout!")
    }
}

WaitGroup

여러 Goroutine이 모두 완료될 때까지 대기할 때 sync.WaitGroup을 사용합니다.

func main() {
    var wg sync.WaitGroup

    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            fmt.Printf("Worker %d done\n", id)
        }(i)
    }

    wg.Wait() // 모든 Goroutine이 완료될 때까지 대기
    fmt.Println("All workers done")
}

테스트 작성

Go는 테스트를 위한 내장 패키지 testing을 제공합니다. 파일명이 _test.go로 끝나면 테스트 파일로 인식됩니다.

// math.go
package math

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

// math_test.go
package math

import "testing"

func TestAdd(t *testing.T) {
    result := Add(2, 3)
    if result != 5 {
        t.Errorf("Add(2, 3) = %d; want 5", result)
    }
}

// 테이블 기반 테스트 (Table-Driven Test)
func TestAddTableDriven(t *testing.T) {
    tests := []struct {
        a, b, expected int
    }{
        {1, 2, 3},
        {0, 0, 0},
        {-1, 1, 0},
        {100, 200, 300},
    }

    for _, tt := range tests {
        result := Add(tt.a, tt.b)
        if result != tt.expected {
            t.Errorf("Add(%d, %d) = %d; want %d",
                tt.a, tt.b, result, tt.expected)
        }
    }
}
# 테스트 실행
go test ./...

# 커버리지 확인
go test -cover ./...

# 벤치마크 실행
go test -bench=. ./...

웹 서버 작성 (표준 라이브러리)

Go의 표준 라이브러리만으로도 충분히 실용적인 웹 서버를 만들 수 있습니다.

package main

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

type Response struct {
    Message string `json:"message"`
    Status  int    `json:"status"`
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
    resp := Response{Message: "OK", Status: 200}
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(resp)
}

func main() {
    http.HandleFunc("/health", healthHandler)

    log.Println("Server starting on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

Gin 프레임워크 예시

더 풍부한 기능이 필요하다면 Gin 프레임워크를 사용합니다:

package main

import (
    "net/http"
    "github.com/gin-gonic/gin"
)

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

    r.GET("/health", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{
            "message": "OK",
        })
    })

    r.GET("/users/:id", func(c *gin.Context) {
        id := c.Param("id")
        c.JSON(http.StatusOK, gin.H{
            "id": id,
        })
    })

    r.Run(":8080")
}

Go vs 다른 언어 비교

항목 Go Python Java Rust
타입 시스템 정적 동적 정적 정적
컴파일 속도 매우 빠름 인터프리터 느림 느림
실행 속도 빠름 느림 빠름 매우 빠름
동시성 Goroutine (내장) asyncio Thread/Virtual Thread async/await
학습 곡선 낮음 매우 낮음 중간 높음
메모리 관리 GC GC GC 소유권 시스템
바이너리 크기 단일 바이너리 런타임 필요 JVM 필요 단일 바이너리

Go는 특히 마이크로서비스, CLI 도구, 네트워크 프로그래밍, DevOps 도구 분야에서 강점을 보입니다. Docker, Kubernetes, Terraform 등 유명한 인프라 도구들이 Go로 작성되었습니다.