본문으로 건너뛰기

실전 고수 팁 — 모듈과 패키지 관리

모노레포 vs 멀티모듈

모노레포 (단일 저장소, 다중 모듈)

company/
├── go.work ← Go Workspaces (Go 1.18+)
├── backend/
│ ├── go.mod (module github.com/company/backend)
│ └── main.go
├── shared/
│ ├── go.mod (module github.com/company/shared)
│ └── types.go
└── tools/
├── go.mod (module github.com/company/tools)
└── generate.go

Go Workspaces(go.work)로 여러 모듈을 로컬에서 함께 개발:

// go.work
go 1.21

use (
./backend
./shared
./tools
)

replace github.com/company/shared => ./shared
# workspace 초기화
go work init ./backend ./shared ./tools

# workspace에 모듈 추가
go work use ./newservice

# workspace 동기화
go work sync

어떤 구조를 선택할까?

모노레포 장점:
- 코드 재사용 쉬움
- 의존성 일관성 유지
- 원자적 커밋 가능

멀티모듈 (폴리레포) 장점:
- 독립적 배포 주기
- 팀 간 명확한 경계
- 간단한 접근 권한 관리

권장:
- 소규모 팀 → 모노레포
- 마이크로서비스 → 멀티모듈
- 공유 라이브러리 있을 때 → go.work 활용

주요 서드파티 라이브러리 생태계

웹 프레임워크

// Gin — 가장 인기 있는 웹 프레임워크
// go get github.com/gin-gonic/gin
import "github.com/gin-gonic/gin"

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

// Echo — 고성능 웹 프레임워크
// go get github.com/labstack/echo/v4
import "github.com/labstack/echo/v4"

// Chi — 경량 라우터 (표준 라이브러리 호환)
// go get github.com/go-chi/chi/v5
import "github.com/go-chi/chi/v5"

데이터베이스

// GORM — Go ORM
// go get gorm.io/gorm
// go get gorm.io/driver/postgres

// sqlc — SQL → Go 코드 생성
// go install github.com/sqlc-dev/sqlc/cmd/sqlc@latest

// sqlx — database/sql 확장
// go get github.com/jmoiron/sqlx

// migrate — DB 마이그레이션
// go get -tags 'postgres' github.com/golang-migrate/migrate/v4

설정 관리

// Viper — 설정 파일, 환경변수, 플래그 통합
// go get github.com/spf13/viper
import "github.com/spf13/viper"

func initConfig() {
viper.SetConfigName("config") // 파일명 (확장자 제외)
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
viper.AddConfigPath("$HOME/.config")

viper.AutomaticEnv() // 환경변수 자동 바인딩
viper.SetEnvPrefix("APP")

if err := viper.ReadInConfig(); err != nil {
// 파일 없으면 환경변수만 사용
}

host := viper.GetString("database.host")
port := viper.GetInt("database.port")
_, _ = host, port
}

로깅

// zap — 고성능 구조화 로깅 (Uber 개발)
// go get go.uber.org/zap
import "go.uber.org/zap"

logger, _ := zap.NewProduction()
defer logger.Sync()

logger.Info("서버 시작",
zap.String("host", "localhost"),
zap.Int("port", 8080),
)

// logrus — 구조화 로깅
// go get github.com/sirupsen/logrus

// slog — Go 1.21 표준 라이브러리 (외부 의존성 불필요)
import "log/slog"

slog.Info("서버 시작", "host", "localhost", "port", 8080)

테스팅

// testify — assert, mock, suite
// go get github.com/stretchr/testify
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestAddUser(t *testing.T) {
user := addUser("Alice")
assert.NotNil(t, user)
assert.Equal(t, "Alice", user.Name)
require.NoError(t, user.Validate()) // 실패 시 즉시 중단
}

// gomock — 인터페이스 모킹
// go install github.com/golang/mock/mockgen@latest

// testcontainers-go — 실제 DB 테스트
// go get github.com/testcontainers/testcontainers-go

의존성 취약점 스캔

# govulncheck — Go 공식 취약점 스캐너
go install golang.org/x/vuln/cmd/govulncheck@latest
govulncheck ./...

# Nancy — 의존성 취약점 체크
go install github.com/sonatype-nexus-community/nancy@latest
go list -json -deps ./... | nancy sleuth

효율적인 go mod 관리

# 현재 의존성 트리 확인
go mod graph | head -20

# 특정 패키지를 사용하는 의존성 찾기
go mod why github.com/pkg/errors

# 사용 가능한 버전 확인
go list -m -versions github.com/gin-gonic/gin

# 모든 의존성의 최신 버전 확인
go list -m -u all

# 의존성 캐시 정리
go clean -modcache

빌드 태그 활용

빌드 조건에 따라 다른 코드를 컴파일합니다.

//go:build linux && amd64

package platform

const OSName = "Linux AMD64"
//go:build !production

package config

// 개발 환경 전용 설정
var DebugMode = true
var LogLevel = "debug"
# 빌드 태그 지정
go build -tags production ./...
go test -tags integration ./...

Makefile로 워크플로 자동화

.PHONY: build test lint clean tidy

# 빌드
build:
go build -ldflags="-w -s" -o bin/server ./cmd/server

# 테스트 (race detector 포함)
test:
go test -race -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html

# 린터
lint:
golangci-lint run ./...

# 의존성 정리
tidy:
go mod tidy
go mod verify

# 취약점 검사
security:
govulncheck ./...

# 모두 실행
all: tidy lint test build

패키지 퍼블리싱 체크리스트

자신의 Go 패키지를 공개할 때 확인할 사항입니다.

□ 패키지명이 명확하고 목적을 반영하는가?
□ 공개 API에 GoDoc 주석이 있는가?
□ README.md에 사용 예제가 있는가?
□ go.mod의 모듈 경로가 올바른가?
□ LICENSE 파일이 있는가?
□ 테스트 커버리지가 충분한가? (70%+ 권장)
□ 예시 코드가 Example_xxx 형식으로 작성됐는가?
□ CHANGELOG.md가 있는가?
□ CI/CD가 설정됐는가? (GitHub Actions 등)
□ pkg.go.dev에서 문서가 올바르게 렌더링되는가?

표준 라이브러리 우선 원칙

// ✅ 표준 라이브러리로 충분한 경우
import (
"encoding/json" // JSON 처리
"net/http" // HTTP 서버/클라이언트
"log/slog" // 구조화 로깅 (Go 1.21+)
"testing" // 기본 테스트
"sync" // 동시성 기본 도구
)

// 외부 라이브러리를 추가하기 전 질문:
// 1. 표준 라이브러리로 해결 가능한가?
// 2. 해당 라이브러리가 활발히 유지보수되는가?
// 3. 의존성 추가로 인한 보안/라이선스 이슈는 없는가?
// 4. 빌드 시간과 바이너리 크기 증가는 감수할 수 있는가?

핵심 정리

  • Go Workspaces: 모노레포에서 여러 모듈을 로컬 개발할 때 활용
  • 표준 라이브러리 우선: 외부 의존성은 꼭 필요할 때만 추가
  • govulncheck: 의존성 취약점 정기 스캔으로 보안 관리
  • go mod why: 의존성 도입 이유 추적
  • 빌드 태그: 환경별 코드 분기 (dev/prod/test)
  • Makefile: 반복적인 빌드/테스트 워크플로 자동화