Skip to content
Published on

모던 Go 2026 — Go 1.24 / Echo / Gin / Fiber / Huma / Encore / sqlc / Templ / Bubble Tea 심층 가이드

Authors

프롤로그 — 왜 다시 Go인가, 2026년에

2009년에 공개된 Go가 2026년 5월이면 만 16살이다. 십 년이 넘는 시간 동안 "지루한 언어"라는 정체성을 무너뜨리지 않은 게 가장 큰 강점이었다. 자바스크립트 진영이 한 해에 빌드 도구를 세 번 갈아엎는 동안, Rust가 borrow checker를 정교하게 다듬는 동안, Python이 타이핑 시스템을 매년 다시 그리는 동안, Go는 "1.0의 호환성 약속"을 깨지 않은 채 작은 개선만 쌓아 왔다. 그러나 2026년 봄에 Go를 다시 보면, 그 작은 개선이 모여서 만든 풍경이 만만치 않다.

세 가지 변곡점이 있다.

첫째, 언어 자체의 성숙이다. 2024년 2월의 Go 1.22가 for-loop 변수 캡처 함정을 끝냈고, 2024년 8월의 Go 1.23이 range-over-func을 정식 기능으로 들였고, 2025년 2월의 Go 1.24가 Generics를 "실전에서 굳건히 쓸 만한 도구"로 만들었다. 동시에 tool 지시문이 추가돼 tools.go 트릭을 끝냈고, weak.Pointer가 캐시 구현을 바꿨다.

둘째, 생태계의 분화다. 한때 "표준 net/http만 쓰면 된다"는 합의가 있었지만, 지금은 Gin이 시장 점유 1위를 유지한 채 Echo·Fiber·Chi·Huma가 각자의 자리를 차지했다. ORM도 GORM 일변도가 아니라 sqlc(쿼리 우선)·ent(스키마 우선)·GORM(러닝 커브)·Bun(쿼리 빌더)이 네 갈래로 갈렸다.

셋째, 새로운 슈퍼파워다. Templ이 등장해 htmx와 함께 "JS 없이 풀스택"이라는 길을 열었고, Encore는 "Go 친화 백엔드 플랫폼"이라는 카테고리를 새로 만들었다. Charmbracelet의 Bubble Tea·Lipgloss·Glamour가 터미널 UI 르네상스를 견인했고, Wails는 Electron 대신 Go + 웹뷰로 데스크탑 앱을 쓰는 길을 굳혔다.

이 글은 이 세 흐름을 한 번에 정리한다. Go 1.22~1.24가 무엇을 끝냈는지, 웹·데이터베이스·풀스택·CLI·데스크탑 각 카테고리의 현재 챔피언이 누구인지, 한국과 일본의 대형 기업들이 실제로 어떻게 쓰는지, 그리고 2026년에 새 프로젝트에서 Go를 골라야 하는 경우와 골라서는 안 되는 경우.

이 글의 숫자·버전은 2026년 5월 16일 기준이다. Go의 변화 속도는 느리지만, 생태계 라이브러리는 빠르다. 구조적 결정 프레임은 6개월 뒤에도 유효하도록 적었다.


1장 · Go 1.22 (2024.2) — for-loop 함정의 종말과 range over int

오랫동안 Go를 처음 배운 사람이 가장 자주 빠지는 함정이 for i := range slice { go func() { use(i) }() } 패턴이었다. 모든 고루틴이 마지막 i 값을 캡처하는 그 함정. 2024년 2월의 Go 1.22가 이 동작을 바꿨다. 이제 매 반복마다 새로운 변수가 만들어진다. 호환성 약속을 깰 수 없는 Go팀이 이걸 통과시키기까지 꼬박 십 년이 걸렸다. go.modgo 1.22 선언이 새 동작을 활성화한다 — 즉, 옛 모듈은 옛 동작을 유지한다.

같은 버전에서 **range over int**도 들어왔다. for i := range 10for i := 0; i < 10; i++와 같은 의미가 된다. 사소해 보이지만 테스트 코드와 벤치마크에서 가독성을 눈에 띄게 올린다.

// Go 1.22 이전 — 매 반복마다 같은 i를 캡처
for i := range items {
    wg.Add(1)
    go func() {
        defer wg.Done()
        process(items[i]) // 위험: i는 모든 고루틴이 공유
    }()
}

// Go 1.22 이상 — 각 반복마다 새 i
for i := range items {
    wg.Add(1)
    go func() {
        defer wg.Done()
        process(items[i]) // 안전
    }()
}

// range over int (Go 1.22+)
for i := range 10 {
    fmt.Println(i) // 0..9
}

net/httpServeMux도 1.22에서 메서드별·경로 패턴 매칭을 정식 지원하기 시작했다. mux.HandleFunc("GET /users/{id}", handler)로 chi·gin 없이도 기본 라우팅이 가능해졌다는 뜻이다. 단순 API라면 외부 의존성을 안 쓰고 출발할 여지가 생겼다.


2장 · Go 1.23 (2024.8) — range over func, 이터레이터의 정식 입성

2024년 8월의 Go 1.23의 시그니처 변화는 range over func이다. 어떤 함수가 func(yield func(T) bool) 형태라면 그 함수 자체를 for v := range f처럼 돌릴 수 있다. 컬렉션 라이브러리의 API가 통일된다는 뜻이다.

// Go 1.23+ 이터레이터 패턴
func Filter[T any](s []T, pred func(T) bool) func(yield func(T) bool) {
    return func(yield func(T) bool) {
        for _, v := range s {
            if !pred(v) {
                continue
            }
            if !yield(v) {
                return
            }
        }
    }
}

// 사용
for v := range Filter(nums, func(n int) bool { return n%2 == 0 }) {
    fmt.Println(v)
}

표준 라이브러리에 iter, slices, maps 패키지가 채워졌다. slices.All·maps.Keys·maps.Values가 이제 모두 이터레이터를 돌려준다. Rust의 Iterator 트레잇이나 자바스크립트의 generator처럼, 컬렉션 처리의 표준 어휘가 생긴 셈이다.

structs 패키지의 HostLayout 타입은 cgo와 syscall 코드에서 구조체 레이아웃을 명시적으로 보장한다. 시스템 프로그래밍에서 자주 부딪히는 패딩 이슈를 정리하는 도구다.

호환성 약속 때문에 신규 기능은 모두 옵트인이다. go.modgo 1.23을 적은 모듈만 새 동작을 쓴다. 이게 Go의 가장 큰 자산이다 — 16년 전 코드도 여전히 빌드된다.


3장 · Go 1.24 (2025.2) — Generics 성숙, Tool deps, Weak pointers, omitzero

Go 1.24가 2025년 2월에 나왔다. Generics를 처음 도입한 1.18 이후 가장 의미 있는 릴리즈로 평가된다. 핵심 변경은 네 가지다.

(1) Generics 타입 별칭. 1.24부터 타입 별칭에 타입 매개변수를 붙일 수 있다. type Set[T comparable] = map[T]struct{}처럼. 1.23 이전에는 generic alias를 못 만들어 코드 곳곳에 보일러플레이트가 쌓였다.

(2) Tool 지시문. go.modtool 지시문이 추가됐다. 그동안 tools.go 파일에 _ 임포트를 적어 빌드 도구를 잡아두던 트릭을 끝낸다.

// Go 1.24 — go.mod
module example.com/myapp

go 1.24

require (
    github.com/spf13/cobra v1.10.0
)

tool (
    github.com/golangci/golangci-lint/v2/cmd/golangci-lint
    github.com/sqlc-dev/sqlc/cmd/sqlc
)

이제 go tool golangci-lint run이 작동하고, go install을 별도로 하지 않아도 된다. CI 파이프라인이 깨끗해진다.

(3) Weak pointers (weak.Pointer). 캐시 구현에 큰 영향. 큰 값을 캐시하고 싶은데 메모리 압박이 오면 GC가 회수하도록 두고 싶을 때 쓴다. 자바의 WeakReference나 Rust의 Weak<T>와 비슷한 개념.

import "weak"

type Cache struct {
    mu sync.Mutex
    m  map[string]weak.Pointer[Bitmap]
}

func (c *Cache) Get(key string) *Bitmap {
    c.mu.Lock()
    defer c.mu.Unlock()
    if wp, ok := c.m[key]; ok {
        if b := wp.Value(); b != nil {
            return b
        }
    }
    b := load(key)
    c.m[key] = weak.Make(b)
    return b
}

(4) omitzero JSON 태그. 오랫동안 골치 아팠던 문제 — omitempty는 zero value를 비어 있다고 본다. 즉 int0, boolfalse, time.Time의 zero가 모두 직렬화에서 빠진다. 이게 항상 원하는 동작은 아니다. 1.24의 omitzero는 정확히 zero일 때만 뺀다. 더 정밀하다.

type Event struct {
    Name     string    `json:"name"`
    Count    int       `json:"count,omitempty"`    // 0이면 빠짐
    Active   bool      `json:"active,omitempty"`   // false면 빠짐
    StartAt  time.Time `json:"start_at,omitzero"` // Zero time일 때만 빠짐
}

이외에도 runtime/trace가 더 가벼워졌고, FIPS 140-3 인증 암호화 모듈이 들어왔고, crypto/mlkem(post-quantum KEM)이 표준 라이브러리에 합류했다. PQC가 Go 표준 라이브러리에 들어왔다는 게 인상적이다.

1.24는 "1.18의 Generics를 진짜로 쓸 만하게 만든 버전"이다. 1.18~1.20은 사용성 이슈가 많아 라이브러리 저자만 썼다. 1.24부터는 일반 애플리케이션 코드에서도 generics를 부담 없이 쓴다.


4장 · 웹 프레임워크 — Gin / Echo / Fiber / Chi / Huma

2026년 봄, Go 웹 프레임워크 시장은 "Gin 우세 + 4파전" 구도다. 표는 대략적인 인상이지 절대 점유율은 아니다.

프레임워크별점철학강점
Gin~84k빠르고 익숙함가장 많은 예제·블로그·미들웨어
Echo~32k표준에 가까움깨끗한 API, 잘 정리된 문서
Fiber~36kExpress 흉내fasthttp 기반, JS 출신에 친숙
Chi~19knet/http에 얇은 래퍼표준 호환, 작고 단단함
Huma~3.5kOpenAPI 우선스키마에서 핸들러 생성

Gin — 시장에서 가장 많이 쓰이는 프레임워크. 이유는 단순하다. 처음 검색했을 때 가장 많은 답이 나오고, 가장 많은 미들웨어가 있다. 단점은 net/http 패턴에서 살짝 벗어나 자체 gin.Context를 쓴다는 것. 2026년에 새 프로젝트를 시작한다면, 팀에 Go 익숙한 사람이 많지 않을 때 Gin이 가장 안전한 선택이다.

// Gin
r := gin.Default()
r.GET("/users/:id", func(c *gin.Context) {
    id := c.Param("id")
    c.JSON(200, gin.H{"id": id})
})
r.Run(":8080")

Echo — Gin과 거의 동급의 인지도. API가 약간 더 깨끗하다는 평이 많다. 미들웨어 작성이 직관적이고, 문서가 잘 정리돼 있다. WebSocket·HTTP/2·gRPC 지원도 자연스럽다.

// Echo
e := echo.New()
e.GET("/users/:id", func(c echo.Context) error {
    id := c.Param("id")
    return c.JSON(200, map[string]string{"id": id})
})
e.Logger.Fatal(e.Start(":8080"))

Fiber — Express.js를 베낀 API. fasthttp 기반이라 벤치마크에서 빠르다. 그러나 fasthttp는 net/http가 아니라서 일부 표준 미들웨어를 못 쓰고, HTTP/2 지원이 약하다. JS 출신 개발자가 Go로 옮길 때 친숙해서 인기.

// Fiber
app := fiber.New()
app.Get("/users/:id", func(c *fiber.Ctx) error {
    return c.JSON(fiber.Map{"id": c.Params("id")})
})
app.Listen(":8080")

Chi — net/http에 얇게 얹은 라우터. gorilla/mux 시대의 영적 후계자. 작고, 표준 호환이고, 미들웨어가 가벼우면서도 강력하다. 모놀리스보다 마이크로서비스에 잘 어울린다. Go 1.22 ServeMux의 등장 이후에도 여전히 매력적인 이유는 풍부한 미들웨어와 sub-router 패턴.

// Chi
r := chi.NewRouter()
r.Use(middleware.Logger)
r.Get("/users/{id}", func(w http.ResponseWriter, r *http.Request) {
    id := chi.URLParam(r, "id")
    w.Write([]byte(id))
})
http.ListenAndServe(":8080", r)

Huma — OpenAPI를 1급 시민으로 다루는 프레임워크. 핸들러 함수의 타입과 태그를 보고 OpenAPI 스키마를 자동 생성한다. Chi·Gin·Echo·Fiber 위에 얹어서 쓸 수 있다는 게 특이점. API 문서를 자동 생성하면서 검증·직렬화·라우팅까지 끝낸다.

// Huma
type GetUserInput struct {
    ID string `path:"id" doc:"User ID"`
}
type GetUserOutput struct {
    Body struct {
        ID   string `json:"id"`
        Name string `json:"name"`
    }
}

api := huma.New("My API", "1.0.0")
huma.Get(api, "/users/{id}", func(ctx context.Context, in *GetUserInput) (*GetUserOutput, error) {
    out := &GetUserOutput{}
    out.Body.ID = in.ID
    out.Body.Name = "Alice"
    return out, nil
})

무엇을 골라야 하나 — 신규 팀에 Go 익숙한 사람이 적다면 Gin. 깨끗함을 원하면 Echo. JS 출신이 많다면 Fiber(단 표준 미들웨어 제약을 알고). 표준에 가깝게 가고 싶으면 Chi. API 스키마 우선이면 Huma.


5장 · 데이터베이스 — sqlc / ent / GORM / Bun

데이터베이스 도구 선택은 Go 팀의 두 번째로 큰 논쟁이다. 네 가지 길이 있다.

sqlc — 쿼리에서 코드 생성 SQL을 직접 쓴다. 그러면 sqlc가 그 SQL을 읽고 타입 안전한 Go 함수를 자동 생성한다. ORM이 아니다. "SQL 우선" 접근법.

-- queries/users.sql
-- name: GetUser :one
SELECT id, name, email FROM users WHERE id = $1;

-- name: ListUsers :many
SELECT id, name, email FROM users ORDER BY id;

이걸로 sqlc가 다음을 생성한다.

// 생성된 코드
type GetUserRow struct {
    ID    int64
    Name  string
    Email string
}

func (q *Queries) GetUser(ctx context.Context, id int64) (GetUserRow, error) {
    row := q.db.QueryRowContext(ctx, getUser, id)
    var i GetUserRow
    err := row.Scan(&i.ID, &i.Name, &i.Email)
    return i, err
}

SQL을 그대로 보존하고, 타입 안전성은 codegen으로 확보. 마이그레이션이 SQL 그대로라 DBA가 읽기 편하다. 단점은 동적 쿼리가 까다롭다는 것 — 조건이 많이 붙는 검색 화면에 약하다.

ent — 스키마 우선 ORM (Meta) Facebook(현 Meta)이 만든 그래프 기반 ORM. Go 코드로 스키마를 선언하면 ent가 마이그레이션·쿼리 빌더·관계 로딩 코드를 생성한다.

// schema/user.go
func (User) Fields() []ent.Field {
    return []ent.Field{
        field.String("name"),
        field.String("email").Unique(),
    }
}

func (User) Edges() []ent.Edge {
    return []ent.Edge{
        edge.To("posts", Post.Type),
    }
}

// 사용
user, err := client.User.
    Create().
    SetName("Alice").
    SetEmail("alice@example.com").
    Save(ctx)

posts, err := user.QueryPosts().All(ctx)

생성된 API가 매우 타입 안전. 복잡한 관계가 많은 도메인에 강하다. 학습곡선이 sqlc보다 가파르다.

GORM — 가장 많이 쓰이는 ORM Rails의 ActiveRecord 영향을 받은 동적 ORM. 가장 많은 예제와 튜토리얼이 있다. 그러나 성능 오버헤드와 매직이 단점.

type User struct {
    gorm.Model
    Name  string
    Email string `gorm:"unique"`
}

db.AutoMigrate(&User{})
db.Create(&User{Name: "Alice", Email: "alice@example.com"})
var user User
db.First(&user, 1)

빠른 프로토타이핑에는 좋다. 큰 트래픽이 도는 프로덕션에서는 sqlc·ent로 옮기는 팀이 많다.

Bun — 쿼리 빌더 go-pg의 후속작. SQL과 가까운 쿼리 빌더. ORM이 아니라 빌더라 명시성이 높다.

err := db.NewSelect().
    Model(&users).
    Where("active = ?", true).
    OrderExpr("id ASC").
    Limit(10).
    Scan(ctx)

무엇을 골라야 하나 — 팀이 SQL에 능하고 통제력을 원하면 sqlc. 그래프형 도메인(소셜·SaaS 다중 테넌트)이면 ent. 빠른 프로토타입은 GORM. SQL에 가깝되 빌더 패턴을 선호하면 Bun. 2026년 트렌드는 sqlc와 ent가 GORM의 자리를 잠식하는 흐름이다.


6장 · Encore — Go 친화 백엔드 플랫폼

Encore는 2024~2025년 사이에 카테고리를 새로 정의한 도구다. "Go용 백엔드 프레임워크 + 클라우드 배포 + 인프라 코드 자동 생성"을 한 번에 한다. 코드를 보면 Encore가 어떤 도구인지 직관적이다.

//encore:service
type Service struct {
    db *sqldb.Database
}

func initService() (*Service, error) {
    db := sqldb.NewDatabase("users", sqldb.DatabaseConfig{
        Migrations: "./migrations",
    })
    return &Service{db: db}, nil
}

//encore:api public method=GET path=/users/:id
func (s *Service) GetUser(ctx context.Context, id string) (*User, error) {
    var u User
    err := s.db.QueryRow(ctx, "SELECT id, name FROM users WHERE id = $1", id).
        Scan(&u.ID, &u.Name)
    if err != nil {
        return nil, err
    }
    return &u, nil
}

//encore:api 주석 한 줄로 API 엔드포인트가 정의된다. Encore CLI는 이 주석을 정적으로 파싱해 OpenAPI 스키마, 클라이언트 SDK, 트레이싱 설정, IAM 규칙, 마이그레이션을 자동 생성한다. encore run이면 로컬에서 Postgres와 함께 도는 환경이 자동으로 뜬다. encore deploy면 GCP/AWS에 자기 인프라가 배포된다.

장점은 명백하다 — 백엔드 셋업 시간이 거의 0. 마이크로서비스 간 호출이 타입 안전한 RPC. 트레이싱·로깅·모니터링이 디폴트.

단점도 명백하다 — Encore는 자기 런타임을 강제한다. 일반 Go 프로젝트와 호환은 되지만, Encore 밖으로 빠져나가려면 일이 많다. 그리고 클라우드 lock-in이 어느 정도는 있다.

Encore는 "Go용 Vercel"에 가깝다. 작은 팀·스타트업에 매력적이다. 큰 기업이 자체 K8s에 박혀 있다면 부담이다.


7장 · Templ + htmx — JS 없이 풀스택

2025년 봄, Go 커뮤니티에서 가장 뜨거운 조합이 Templ + htmx다. 의도는 "JS 빌드 도구 없이도 인터랙티브 웹앱"이다.

Templ은 type-safe HTML 템플릿 엔진. .templ 파일을 쓰고 templ generate로 Go 코드를 만든다. 정적 분석이 가능하고, 컴포넌트가 함수다.

// hello.templ
package main

templ Hello(name string) {
    <div class="greeting">
        <h1>Hello, { name }!</h1>
        <p>It is { time.Now().Format("2006-01-02") }</p>
    </div>
}

// 사용
func handler(w http.ResponseWriter, r *http.Request) {
    Hello("Alice").Render(r.Context(), w)
}

html/template보다 안전하고 빠르고 IDE 지원이 좋다.

htmx는 HTML 속성으로 AJAX·WebSocket·SSE를 다루는 라이브러리. JS 13KB. 핵심 아이디어: 모든 인터랙션을 HTML fragment 교환으로 표현한다.

<button hx-post="/like/123" hx-swap="outerHTML">Like</button>

서버는 /like/123에 POST 요청을 받고, 새 HTML 조각을 돌려준다. htmx가 그걸 DOM에 끼워 넣는다. SPA의 복잡성 없이 부분 갱신을 한다.

Go + Templ + htmx 스택의 의미는 명확하다 — React를 모르는 백엔드 엔지니어가 풀스택을 짤 수 있다. Webpack도, Vite도, NPM도 없이. 단점은 매우 복잡한 인터랙션(실시간 협업 에디터, 3D 렌더링)에는 부족하다는 것.

// Echo + Templ + htmx
e := echo.New()
e.GET("/", func(c echo.Context) error {
    return Hello("World").Render(c.Request().Context(), c.Response())
})
e.POST("/like/:id", func(c echo.Context) error {
    id := c.Param("id")
    return LikeButton(id, true).Render(c.Request().Context(), c.Response())
})

2026년에 사이드 프로젝트나 사내 어드민을 만든다면 진지하게 검토할 만한 조합이다. Hugo 슬랙·SaaS 어드민·블로그 빌더가 이 스택으로 빠르게 만들어진다.


8장 · Charmbracelet — Bubble Tea / Lipgloss / Glamour의 TUI 르네상스

Charmbracelet은 2020년대 중반 터미널 UI 르네상스를 견인한 회사다. 그들의 라이브러리 셋이 사실상 Go TUI의 표준이 됐다.

Bubble Tea — Elm 아키텍처를 기반으로 한 TUI 프레임워크. Model, Update, View 세 함수로 구성된다.

type model struct {
    counter int
}

func (m model) Init() tea.Cmd {
    return nil
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
    switch msg := msg.(type) {
    case tea.KeyMsg:
        switch msg.String() {
        case "+":
            m.counter++
        case "-":
            m.counter--
        case "q":
            return m, tea.Quit
        }
    }
    return m, nil
}

func (m model) View() string {
    return fmt.Sprintf("Counter: %d\n[+/-] adjust, [q] quit\n", m.counter)
}

func main() {
    tea.NewProgram(model{}).Run()
}

상태 변화가 명확하고, 테스트 가능하고, 컴포넌트 합성이 자연스럽다. GitHub CLI(gh)의 일부 인터랙티브 화면, glow(마크다운 뷰어), lazygit 류의 도구가 모두 Bubble Tea 영향권에 있다.

Lipgloss — CSS 영감을 받은 스타일 라이브러리. 박스 모델, 패딩, 보더, 색상.

style := lipgloss.NewStyle().
    Bold(true).
    Foreground(lipgloss.Color("#FAFAFA")).
    Background(lipgloss.Color("#7D56F4")).
    Padding(1, 2).
    Border(lipgloss.RoundedBorder())

fmt.Println(style.Render("Hello, World!"))

Glamour — 터미널에서 마크다운을 예쁘게 렌더링하는 라이브러리. AI CLI 도구들이 모델 응답을 보여줄 때 거의 표준으로 쓴다.

Wish — SSH 서버를 Go로 만드는 라이브러리. ssh chess.example.com으로 접속하면 체스를 둘 수 있는 식의 데모가 가능하다.

Charmbracelet 생태계는 "터미널이 다시 즐겁다"는 정서를 만들었다. AI CLI 시대에 이 생태계의 가치가 더 올라갔다.


9장 · Cobra — CLI 표준

Go CLI를 만들 때 사실상 표준이 Cobra다. Kubernetes·Hugo·Helm·GitHub CLI 등이 모두 Cobra 기반. 명령·서브명령·플래그·자동완성을 일관된 방식으로 처리한다.

var rootCmd = &cobra.Command{
    Use:   "myapp",
    Short: "An example CLI",
}

var serveCmd = &cobra.Command{
    Use:   "serve",
    Short: "Run the server",
    Run: func(cmd *cobra.Command, args []string) {
        port, _ := cmd.Flags().GetInt("port")
        fmt.Printf("Serving on :%d\n", port)
    },
}

func init() {
    serveCmd.Flags().IntP("port", "p", 8080, "Port to listen on")
    rootCmd.AddCommand(serveCmd)
}

func main() {
    rootCmd.Execute()
}

자매 라이브러리 Viper는 설정 관리(env, JSON, YAML, TOML, etcd, Consul) — Cobra와 함께 자주 쓰인다.

대안으로 urfave/cli가 있다. API가 조금 다르고 더 간결하지만, Cobra만큼 광범위하진 않다. 2026년 신규 CLI는 거의 100% Cobra로 시작한다.


10장 · Wails — Go + 웹뷰 데스크탑

Electron의 무게가 부담스러운 사람에게 Wails는 매력적인 대안이다. Go가 백엔드를 잡고, 웹뷰(macOS의 WebKit, Windows의 WebView2, Linux의 WebKitGTK)가 프론트엔드를 그린다. 결과 바이너리는 Electron보다 훨씬 작다 — 10MB대.

// app.go
type App struct {
    ctx context.Context
}

func (a *App) Greet(name string) string {
    return "Hello " + name
}

func main() {
    wails.Run(&options.App{
        Title:  "MyApp",
        Width:  1024,
        Height: 768,
        Bind:   []interface{}{&App{}},
    })
}

프론트엔드는 React·Vue·Svelte 무엇이든 쓴다. Go 함수를 JS에서 호출 가능. 작은 데스크탑 도구·Markdown 에디터·로컬 데이터베이스 GUI·미니멀 IDE 같은 용도에 잘 어울린다.

단점은 OS별 웹뷰 차이. Safari·Chromium·Edge의 미묘한 렌더링 차이를 신경 써야 한다. 그래도 Electron의 100MB+ Chromium을 안 묶어도 된다는 게 큰 이득.

대안으로 Fyne(순수 Go GUI)이 있다. 웹뷰 없이 직접 그린다. 모바일도 일부 지원. 그러나 디자인 자유도가 낮다.


11장 · golangci-lint v2 (2024) — 설정 재작성

Go 프로젝트의 기본 린터인 golangci-lint가 2024년 v2를 출시했다. 가장 큰 변화는 설정 파일의 재작성.

v1은 십 년간 누적된 옵션이 어지럽게 쌓여 있었다. v2는 구조를 깔끔하게 재정의했다.

# .golangci.yml — v2
version: "2"

run:
  timeout: 5m
  go: "1.24"

linters:
  default: standard
  enable:
    - errcheck
    - govet
    - staticcheck
    - revive
    - gosec
    - bodyclose
    - errorlint
  disable:
    - lll

linters-settings:
  revive:
    rules:
      - name: exported
      - name: unused-parameter

formatters:
  enable:
    - gofumpt
    - goimports

default: standard로 베스트프랙티스 묶음을 한 번에 활성화한다. 포매터(gofumpt·goimports)가 린터에서 분리됐다 — 별도 카테고리. v1에서 v2로 마이그레이션은 golangci-lint migrate 명령으로 거의 자동.

CI에서는 다음 패턴이 표준이다.

# .github/workflows/lint.yml
name: lint
on: [push, pull_request]
jobs:
  golangci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - uses: actions/setup-go@v6
        with:
          go-version: "1.24"
      - uses: golangci/golangci-lint-action@v7
        with:
          version: v2.0

Go 1.24의 tool 지시문과 결합하면 go tool golangci-lint run만으로 끝난다. 별도 설치 불필요.


12장 · Pulumi Go / gRPC-Go / Wasmtime-Go — 그 외 중요한 도구들

Pulumi Go — IaC를 진짜 프로그래밍 언어로 쓰는 도구. Terraform이 HCL이라는 자체 DSL을 쓴 반면, Pulumi는 TypeScript·Python·Go·C#로 쓴다. Go 사용자에게 매력적인 건 타입 안전성과 표준 도구 체인.

// main.go
func main() {
    pulumi.Run(func(ctx *pulumi.Context) error {
        bucket, err := s3.NewBucket(ctx, "my-bucket", &s3.BucketArgs{
            Acl: pulumi.String("private"),
        })
        if err != nil {
            return err
        }
        ctx.Export("bucketName", bucket.ID())
        return nil
    })
}

pulumi up이면 AWS에 버킷이 생긴다. Terraform보다 학습곡선이 가파르지만, 큰 인프라를 관리할 때 추상화의 힘이 빛난다.

gRPC-Go — Google의 RPC 프레임워크. proto3 정의에서 Go 코드를 생성. 마이크로서비스 간 호출의 표준. HTTP/2 위에 다중화·스트리밍·메타데이터.

// user.proto
service UserService {
    rpc GetUser(GetUserRequest) returns (GetUserResponse);
    rpc StreamEvents(EventRequest) returns (stream Event);
}

protoc로 Go 코드를 만들고, 서버·클라이언트를 채워 넣는다. Connect-Go(Buf 사가 만든)가 gRPC에 HTTP 호환성을 더한 대안으로 떠올랐다. 브라우저에서도 그대로 호출 가능.

Wasmtime-Go — Bytecode Alliance의 WebAssembly 런타임을 Go에서 호출. 사용자 코드를 격리해 돌리는 플러그인 시스템이 필요할 때 유용. Envoy·Linkerd 같은 프록시에서 WASM 필터로 쓰는 패턴도 늘었다.

import "github.com/bytecodealliance/wasmtime-go/v22"

engine := wasmtime.NewEngine()
module, _ := wasmtime.NewModuleFromFile(engine, "hello.wasm")
store := wasmtime.NewStore(engine)
instance, _ := wasmtime.NewInstance(store, module, nil)
run := instance.GetExport(store, "run").Func()
result, _ := run.Call(store)
fmt.Println(result)

또 하나 짚고 갈 도구가 NATS Go 클라이언트다. NATS는 Go로 쓰인 가벼운 메시징. Kafka보다 가볍고 운영이 쉽다. 마이크로서비스 간 pub/sub·request/reply에 매력적.


13장 · 한국 / 일본 — 토스 / 카카오 / 메르카리 / CyberAgent / LINE Yahoo

토스 (Toss) 한국에서 Go를 가장 적극적으로 쓰는 핀테크. 마이크로서비스 시대로 옮기면서 일부 서비스를 Go로 작성했다. 기술 블로그(toss.tech)에 Go 관련 글이 꾸준히 올라온다 — gRPC 기반 내부 통신, K8s 상의 Go 워크로드, 결제 처리의 latency 최적화. Kotlin·JVM이 여전히 메인이지만, "초저지연이 필요한 핵심 결제 경로"에 Go가 박힌 패턴.

카카오 (Kakao) 카카오와 카카오엔터프라이즈에서 Go 채용 공고가 꾸준히 나온다. 사내 클라우드 컴포넌트, CI/CD 도구, 운영 자동화에 Go가 많이 쓰인다. K8s 운영 경험과 Go가 자주 묶여서 채용된다.

메르카리 (Mercari) 일본에서 가장 유명한 Go 회사. 마이크로서비스 아키텍처가 거의 Go로 짜였다. 메르카리 엔지니어링 블로그에 Go 관련 글이 압도적으로 많다 — gRPC, K8s, 분산 트레이싱, 성능 튜닝. 사내에 "Go 위원회" 같은 조직이 있어 표준을 잡는다고 알려졌다.

CyberAgent (サイバーエージェント) 일본의 광고·게임·미디어 대기업. ABEMA·AmebaTV·게임 백엔드에서 Go 사용. CyberAgent의 자회사 AbemaTV는 라이브 방송 인프라에 Go를 광범위하게 쓴다. Go Conference 도쿄의 단골 스폰서.

LINE Yahoo (LINEヤフー) LINE과 Yahoo Japan의 합병으로 만들어진 거대 회사. LINE 시절부터 Go를 많이 썼고, Yahoo Japan은 한때 Java 중심이었으나 Go도 늘렸다. 합병 후 인프라 통합 과정에서 Go의 역할이 더 커졌다는 보고. 사내 메시징·검색·광고 백엔드에 Go 컴포넌트가 들어 있다.

동아시아의 큰 빅테크에서 공통된 패턴 — Go는 "마이크로서비스 + K8s + gRPC" 묶음의 디폴트 언어다. Java/Kotlin과 공존하되, "초저지연 백엔드"와 "데브옵스·플랫폼 도구"에서 자리를 굳혔다.


14장 · 누가 Go를 골라야 하나 — 마이크로서비스 / CLI / 데브옵스 / 시스템

긴 글의 끝에 짧은 의사결정 프레임을 둔다.

Go가 맞는 경우.

  1. 마이크로서비스 백엔드 — 빠른 빌드, 작은 바이너리, 명료한 동시성. gRPC와 결합하면 표준 아키텍처.
  2. CLI / 데브옵스 도구 — 정적 바이너리 하나로 배포 끝. Kubernetes·Terraform·Docker·Hugo·Cobra가 모두 Go인 게 우연이 아니다.
  3. 네트워크 프록시 / 인프라 서비스 — Caddy·Traefik·Envoy(C++) 자리. 동시성·메모리 효율.
  4. 데이터 파이프라인의 핵심 컴포넌트 — Vector·Bento·NATS. Rust만큼 빠르진 않지만 충분히 빠르고 배우기 쉽다.
  5. JS 없는 풀스택 사내 도구 — Templ + htmx 스택.
  6. 터미널 도구 — Bubble Tea 생태계.

Go가 안 맞는 경우.

  1. 데이터 사이언스 / ML 모델 학습 — Python·Julia가 압도적. Go는 ML inference 서빙에 일부 쓰일 뿐.
  2. 시스템 프로그래밍의 가장 낮은 층 — 커널·임베디드·실시간. Rust·C가 더 어울린다. GC 때문.
  3. 복잡한 도메인 모델 — Algebraic Data Type, 패턴 매칭, sum type이 절실하면 Rust·Scala·Haskell·OCaml.
  4. 단순 스크립트 — 컴파일·빌드 사이클이 거추장스럽다. Python·Bash·우즈크립트가 빠르다.
  5. 고성능 게임 엔진 — GC pause가 부담.

언어 선택 결정 트리.

  • "마이크로서비스 백엔드?" → Go
  • "데브옵스 도구를 한 바이너리로?" → Go
  • "타입 안전 ORM이 필요한 SaaS 백엔드?" → Go (ent) 또는 TypeScript (Drizzle)
  • "JS 없는 풀스택 사내 도구?" → Go + Templ + htmx
  • "AI agent harness?" → Go가 좋은 선택이지만 Python·TypeScript도 활발
  • "실시간 게임 서버?" → Rust·C++ 우선, Go는 차선
  • "수치 계산?" → Python·Julia·Rust

Go의 16살은 "지루함이 검증됐다"는 의미다. 다른 언어가 새 문법을 매년 들이는 동안 Go는 작은 개선만 더한 결과, 2026년에는 가장 안정적이고 가장 예측 가능한 선택지가 됐다. 1.24의 Generics 성숙이 마지막 큰 변경일 거라는 관측이 많다. 그 다음은 진짜로 "거대한 표준 라이브러리와 안정된 도구 체인"의 시대다.


15장 · 참고 / References