Kim Seogyu
Backend

Wire를 활용한 Go 의존성 주입(DI) 구현

Google Wire로 컴파일 타임 DI를 구성하는 실전 패턴을 정리합니다.

Published 2025년 12월 30일2 min read255 words

Wire를 활용한 Go 의존성 주입(DI) 구현

Wire를 쓰는 이유

Wire는 리플렉션 기반 컨테이너가 아니라 코드 생성 기반 DI입니다.

장점:

  • 컴파일 타임 타입 검증
  • 런타임 오버헤드 없음
  • 생성 코드가 명시적이라 디버깅이 쉬움

단점:

  • 초기 설정과 학습 비용
  • 그래프가 크면 생성 코드 추적이 번거로울 수 있음

기본 구조

func NewConfig() *Config { ... }
func NewDB(cfg *Config) (*sql.DB, error) { ... }
func NewRepo(db *sql.DB) *Repo { ... }
func NewService(r *Repo) *Service { ... }
//go:build wireinject
func InitializeService() (*Service, error) {
	wire.Build(
		NewConfig,
		NewDB,
		NewRepo,
		NewService,
	)
	return nil, nil
}

wire 실행 후 wire_gen.go가 생성됩니다.

Provider Set으로 모듈화

var InfraSet = wire.NewSet(NewConfig, NewDB, NewLogger)
var RepoSet = wire.NewSet(NewRepo)
var ServiceSet = wire.NewSet(NewService)

조립 루트:

func InitializeApp() (*App, error) {
	wire.Build(InfraSet, RepoSet, ServiceSet, NewApp)
	return nil, nil
}

인터페이스 바인딩

type UserRepo interface {
	FindByID(ctx context.Context, id string) (*User, error)
}

type userRepo struct { ... }

var RepoSet = wire.NewSet(
	NewUserRepo,
	wire.Bind(new(UserRepo), new(*userRepo)),
)

운영에서 자주 겪는 문제

  • 빌드 태그(wireinject) 누락으로 생성 파일 충돌
  • Provider 함수가 숨은 사이드이펙트를 가져 테스트 격리 깨짐
  • 생성 코드 커밋 정책이 팀마다 달라 CI 불일치

추천 규칙

  • DI 조립 코드는 internal/app 또는 cmd/*/wire.go에 집중
  • Provider는 생성 책임만, 환경 검증/실행 로직 분리
  • CI에서 wire 재실행 후 diff 검사

요약

Wire의 가치는 "자동 주입"이 아니라 명시적인 의존성 그래프를 안전하게 유지하는 데 있습니다. 규칙만 잘 잡으면 규모가 커질수록 수동 DI보다 유지보수가 쉬워집니다.

Share

Related Articles

Comments

이 블로그는 제가 알고 있는 것들을 잊지 않기 위해 기록하는 공간입니다.
직접 작성한 글도 있고, AI의 도움을 받아 정리한 글도 있습니다.
정확하지 않은 내용이 있을 수 있으니 참고용으로 봐주세요.

© 2026 Seogyu Kim