Golang Wire
Basics
Defining Providers
Wire의 기본 동작은 provider입니다. provider는 value를 생성하는 go code입니다.
package foobarbaz
type Foo struct {
	X int
}
fucn ProvideFoo() Foo {
	return Foo{X: 42}
}Provider는 파라미터를 통해 의존성을 표시할 수 있습니다.
package foobarbaz
// ...
type Bar struct {
	X int
}
func ProvideBar(foo Foo) Bar { // 인자로 Foo 타입을 받습니다.
	return Bar{X: -foo.X}
}Provider는 error를 반환할 수 있습니다.
package foobarbaz
import (
	"context"
	"errors"
)
// ...
type Baz struct {
	X int
}
func ProvideBaz(ctx context.Context, bar Bar) (Baz, error) {
	if bar.X == 0 {
		return Baz{}, errors.New("cannot provide baz when bar is zero")
	}
	return Baz{X: bar.X}, nil
}Provider는 provider set으로 그룹지울 수 있습니다. 여러 provider가 같이 사용될 때 유용합니다. provider를 새로운 set에 추가하려면 wire.NewSet을 사용합니다.
package foobarbaz
import (
	// ...
	"github.com/google/wire"
)
// ...
var SuperSet = wire.NewSet(ProvideFoo, ProvideBar, ProvideBaz)기존 set에 새로운 provider를 추가할 수 있습니다.
var MegaSet = wire.NewSet(SuperSet, pkg.OtherSet)Injectors
Injector는 provider를 의존성 순서에 따라 호출합니다.
Injector는 내부에서 wire.Build를 호출하는 함수입니다. 반환 값을 타입만 일치하면 문제가 되지 않습니다.
// +build wireinject
package main
import (
	"context"
	"github.com/google/wire"
	"example.com/foobarbaz"
)
func initializeBaz(ctx context.Context) (foobarbaz.Baz, error) {
	wire.Build(foobarbaz.MegaSet)
	return foobarbaz.Baz{}, nil
}injector가 아닌 코드는 생성된 파일에 복사됩니다.
이제 wire명령어를 통해 wire_gen.go파일을 생성합니다.
// Code generated by Wire. DO NOT EDIT.
//go:generate go run github.com/google/wire/cmd/wire
//+build !wireinject
package main
import (
    "example.com/foobarbaz"
)
func initializeBaz(ctx context.Context) (foobarbaz.Baz, error) {
    foo := foobarbaz.ProvideFoo()
    bar := foobarbaz.ProvideBar(foo)
    baz, err := foobarbaz.ProvideBaz(ctx, bar)
    if err != nil {
        return foobarbaz.Baz{}, err
    }
    return baz, nil
}Advanced Features
Biding Interfaces
type Fooer interface {
	Foo() string
}
type MyFooer string
func (b *MyFooer) Foo() string {
	return string(*b)
}
func provideMyFooer() *MyFooer {
	b := new(MyFooer)
	*b = "Hello, World!"
	return b
}
type Bar string
func provideBar(f Fooer) string {
	return f.Foo()
}
var Set = wire.NewSet(
	provideMyFooer,
	wire.Bind(new(Fooer), new(*MyFooer)),
	provideBar)예제에서 provideMyFooer는 *MyFooer 타입을 반환합니다. provideBar는 인자로 Fooer 타입을 받습니다. 여기서 타입이 일치하지 않는 문제가 발생합니다. 이를 해결하기 위해서
- provideMyFooer의 반환 타입을 Fooer로 변경합니다. (Go best practice는 concrete type을 반환하는 것이기 때문에 이 방법은 좋지 않습니다.)
- wire.Bind를 사용해 두 타입을 연결해줍니다. 첫 번째 인자는 provider가 반환하기를 기대하는 타입, 두 번째 인자는 provider가 실제로 반환하는 타입입니다.
Struct Providers
wire.Struct를 사용해서 struct 타입의 필드에 필요한 타입을 주입할 수 있습니다.
type Foo int
type Bar int
func ProvideFoo() Foo { /* ... */ }
func ProvideBar() Bar { /* ... */ }
type FooBar struct {
	MyFoo Foo
	MyBar Bar
}
var Set = wire.NewSet(
	ProvideFoo,
	ProvideBar,
	wire.Struct(new(FooBar), "MyFoo", "MyBar"))wire.Struct의 첫 번째 인자는 원하는 struct의 포인터 입니다. 이어지는 인자는 주입할 struct의 필드 이름입니다. "*"를 사용하면 모든 필드를 주입할 수 있습니다. wire.Struct(new(FooBar), "*")
injector의 반환 타입에 따라 FooBar 또는 *FooBar를 반환할 수 있습니다.
// wire.go
func initializeFooBar() FooBar {
        wire.Build(Set)
        return FooBar{}
}
func initializeFooBarPointer() *FooBar {
        wire.Build(Set)
        return &FooBar{}
}
// wire_gen.go
func initializeFooBar() FooBar {
        foo := ProvideFoo()
        bar := ProvideBar()
        fooBar := FooBar{
                MyFoo: foo,
                MyBar: bar,
        }
        return fooBar
}
func initializeFooBarPointer() *FooBar {
        foo := ProvideFoo()
        bar := ProvideBar()
        fooBar := &FooBar{
                MyFoo: foo,
                MyBar: bar,
        }
        return fooBar
}Struct가 sync.Mutex 필드를 가지고 있을 때 처럼 의존성 주입을 원하지 않을 경우도 있습니다. 이럴 경우 필드에 wire:"-" 태그를 적용해서 무시하도록 할 수 있습니다.
type Foo struct {
	mu sync.Mutex `wire:"-"`
	Bar bar
}Binding Values
어떤 경우에는 특정한 값을 주입하고 싶을 때도 있습니다.
type Foo struct {
	X int
}
func injectFoo() Foo {
	wire.Build(wire.Value(Foo{X: 42}))
	return Foo{}
}interface에 대해선느 InterfaceValue를 사용합니다.
func injectReader() io.Reader {
	wire.Build(wire.InterfaceValue(new(io.Reader), os.Stdin))
	return nil
}Use Fields of a Struct as Providers
종종 사용자가 원하는 provider가 Struct의 특정 필드인 경우가 있습니다. 이럴 경우 wire.FieldsOf를 사용해 특정 필드를 사용할 수 있습니다.
type Foo struct {
	S string
	N int
	F float64
}
func getS(foo Foo) string {
	// Foo.S 필드를 위한 Provider를 만드는 방법.
	// 좋지 않은 방법입니다. 대신 wire.FieldsOf를 사용하세요.
	return foo.S
}
func provideFoo() Foo {
	return Foo{ S: "Hello, World", N: 1, F: 3.14 }
}
func injectedMessageBad() string {
	wire.Build(
		provideFoo,
		getS)
	return ""
}
func injectedMessage() string {
	wire.Build(
		provideFoo,
		wire.FieldsOf(new(Foo), "S"))
	return ""
}Cleanup functions
만약 provider가 cleanup이 필요한 리소스를 생성한다면, cleanup 함수를 반환할 수 있습니다. Injector는 모든 cleanup 함수를 모은 cleanup함수를 반환하거나, 에러 상황에는 cleanup함수를 호출합니다.
func provideFile(log Logger, path Path) (*os.File, func(), error) {
	f, err := os.Open(string(path))
	if err != nil {
		return nil, nil, err
	}
	cleanup := func() {
		if err := f.Close(); err != nil {
			log.Log(err)
		}
	}
	return f, cleanup, nil
}// wire.go
func ProvideFoo() (Foo, func(), error){
        cleanup := func() {
                fmt.Println("ProvideFoo() cleanup")
        }
        return Foo(1), cleanup, nil
}
func ProvideBar() (Bar, func(), error){
        cleanup := func() {
                fmt.Println("ProvideBar() cleanup")
        }
        return Bar(2), cleanup, nil
}
var Set = wire.NewSet(
        ProvideFoo,
        ProvideBar,
        wire.Struct(new(FooBar), "MyFoo", "MyBar"))
func initializeFooBar() (FooBar, func(), error) {
        wire.Build(Set)
        return FooBar{}, func() {}, nil
}
// wire_gen.go
func initializeFooBar() (FooBar, func(), error) {
        foo, cleanup, err := ProvideFoo()
        if err != nil {
                return FooBar{}, nil, err
        }
        bar, cleanup2, err := ProvideBar()
        if err != nil {
                cleanup()
                return FooBar{}, nil, err
        }
        fooBar := FooBar{
                MyFoo: foo,
                MyBar: bar,
        }
        return fooBar, func() {
                cleanup2()
                cleanup()
        }, nil
}생성된 injector를 보면 provider에서 반환한 cleanup 함수를 모두 호출하는 cleanup 함수를 반환하는 것을 확인할 수 있습니다.
Author And Source
이 문제에 관하여(Golang Wire), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@jeonghyeon/Golang-Wire저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
                                
                                
                                
                                
                                
                                우수한 개발자 콘텐츠 발견에 전념
                                (Collection and Share based on the CC Protocol.)