golang private method를 이용해 의존성 주입을 쉽고 간편하게 하는 방법


구조체 초기화 시점에 의존성이 주입되는 경우

  • 특정 메서드가 외부패키지인 billingService에 의존적인 구조
    • Bill 구조체는 선언 시점에 billingService를 주입받아야 하기 때문에, billingService에 의존적인 구조를 갖게됨
      type Bill struct {
          price int
          productName string 
          billingService billing.Service
      }
      
      func NewBill(price, productName string, billingService billing.Service) Bill {
          return Bill {
              price: price,
              productName: productName,
              billingService: billingService
          }
      }
      
      func (b Bill) Billing() (err error) {
          return b.billingService.Billing(b.price, b.productName)
      }

구조체가 초기화 된 후에도 필요에 따라 각기 다른 구현의 의존성을 주입받을 수 있는 구조

  • private interface를 활용해 외부패키지와 상관 없이 동작하도록 함. billingService는 외부에서 주입할 수 있다.
  type Bill struct {
      price int
      productName string
  }

  func NewBill(price, productName string) Bill {
      return Bill {
          price: price,
          productName: productName
      }
  }

  type billerFromOutside interface {
      Billing(price int, productName string) error
  }

  func (b Bill) Billing(biller billerFromOutside) (err error) {
      return biller.Billing(b.price, b.productName)
  }

  • 아래의 구조에 따르면 Bill 구조체를 만들때는 billingService와 전혀 상관이 없다.
    • billerFromOutside를 구현하고있는 구조체만 Billing 메서드의 인자로 넣어주면 된다.
    • 이렇게 하면 Bill 구조체를 만드는 시점이 아닌, Billing 메서드를 호출하는 시점에 외부에서 의존성을 주입해줄 수 있다.
      type IamportBiller struct {
       accessToken string
       secretToken string
      }
      
      func NewIamportBiller(accessToken, secretToken string) IamportBiller {
       return IamportBiller {
           accessToken: accessToken,
           secretToken: secretToken,
       }
      }
      
      func (b IamportBiller) Billing(price int, productName string) (err error) {
       // iamport에 결제하는 로직....
       return err
      }
      
      type NicepayBiller struct {
       accessToken string
       secretToken string
      }
      
      func NewNicepayBiller(accessToken, secretToken string) NicepayBiller {
       return NicepayBiller {
           accessToken: accessToken,
           secretToken: secretToken,
       }
      }
      
      func (b NicepayBiller) Billing(price int, productName string) (err error) {
       // nicepay 결제하는 로직
       return err
      }
      
      func main() {
       // iamport Biller
       iBiller := NewIamportbiller("abc", "xyz")
      
       // nicepay Biller
       nBiller := NewNicepayBiller("abc", "xyz")
      
       price := 500
       productName := "휴지"
       productBill := Bill{price, productName}
      
       // bill with iamport
       productBill.Billing(iBiller)
      
       // bill with nicepay
       productBill.Billing(nBiller)
      }