Golang의 private interface를 활용한 의존성 주입

Golang의 private interface를 활용한 의존성 주입

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

  • 특정 메서드가 외부패키지인 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)
  }