sayu.day
Backend

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

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

발행 2025년 12월 30일2254

같은 주제에서 이어 읽기

Enterprise Go 시리즈 #1: 프로젝트 설계와 구조화

Backend 안에서 이어지는 글

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보다 유지보수가 쉬워집니다.

다음 읽기

이 생각이 이어지는 방향

Backend 더 보기
공유

읽은 뒤의 대화

읽은 뒤의 생각을 이어갑니다

질문, 반론, 조용한 후속 메모를 이 글 아래에 남길 수 있습니다.

sayu.day는 생각과 작업의 흔적을 천천히 정리하는 개인 출판물입니다.
직접 겪고 검토한 내용, 다시 읽을 만한 아이디어, 작업하며 남긴 메모를 모읍니다.
시간이 지난 글은 현재의 판단과 다를 수 있어 업데이트 맥락을 함께 남깁니다.

© 2026 sayu.day