GO

[Go]Bank 프로젝트 따라하기

[Go]Bank 프로젝트

private struct 생성

구조체(struct, 자바에서의 클래스)를 만들 때 export 하기 위해서는 구조체 이름과 내부 변수명을 대문자로 적어 public하게 만들어야 한다. 하지만 여기서 구조체를 private 하게 만들기 위해서는 다르 언어에서 처럼 생성자를 만들어줘야 하는데 Golang에는 생성자를 만들 수 없다. 그래서 Golang에서는 흔히 function으로 private한 구조체를 생성한다.

public 구조체

구조체 이름과 내부 변수를 대문자로 적어 public 구조체를 생성하여 다른 파일로 export/import 할 수 있게 만들 수 있다.

package main

import (
  "github.com/saichoi/learngo/accounts"
)

func main() {
  account := accounts.Account{Owner: "choi"}
  account.Owner = "dahye"
}
package accounts

// Account struct
type Account struct {
  Owner   string
  balance int
}

private 구조체

생성자가 없는 Golang에서는 func을 사용하여 private 구조체를 만들 수 있다.

package main

import (
  "fmt"

  "github.com/saichoi/learngo/accounts"
)

func main() {
  // accounts.go 파일에 생성해둔 private한 struct를 사용
  account := accounts.NewAccount("choi")
  fmt.Println(account)
}
package accounts

// Account struct
type Account struct {
  owner   string
  balance int
}

// NewAccount creates Account
// *, & : pointer를 사용하면 메모리를 적게 차지 하기 때문에 실행 속도가 빨라진다.
func NewAccount(owner string) *Account { // *Account : 위에 선언한 Account를 그대로 사용하기 위함
  account := Account{owner: owner, balance: 0}
  return &account // &account : 새로 생성하지 않고 복사본을 그대로 return한다.
}
터미널 실행

터미널에서 파일을 실행하면 &가 붙어 있는 것을 확인할 수 있다. 새로 생성하지 않고 기존에 존재하던 데이터를 그대로 반환했다는 것을 알 수 있다.

private 구조체 값 변경

private 구조체는 일반적인 방법으로는 값을 변경할 수 없다. 이를 변경하기 위해서 method가 필요하다. Golang에서의 method는 구조체 내부에 선언하는 것이 아니라 func에 reciver을 추가하는 것으로 생성할 수 있다. receiver의 이름은 구조체의 앞글자(소문자)를 따온다.

method 생성

// method 형식
func (리시버명 리시버타입) 메소드명(인자명 인자타입) {

}
package main

import (
  "fmt"

  "github.com/saichoi/learngo/accounts"
)

func main() {
  account := accounts.NewAccount("choi")
  account.Deposit(10)
  fmt.Println(account.Balance())
}
package accounts

import "fmt"

// Account struct
type Account struct {
  owner   string
  balance int
}

// NewAccount creates Account
// *, & : pointer를 사용하면 메모리를 적게 차지 하기 때문에 실행 속도가 빨라진다.
func NewAccount(owner string) *Account { // *Account : 위에 선언한 Account를 그대로 사용하기 위함
  account := Account{owner: owner, balance: 0}
  return &account // &account : 새로 생성하지 않고 기존에 존재하던 데이터를 그대로 return한다.
}

// Deposit x amount on your account
func (a Account) Deposit(amount int) {
  fmt.Println("Gonna deposit", amount)
  a.balance += amount
}

// Balance of your account
func (a Account) Balance() int {
  return a.balance
}
// Deposit x amount on your account
func (a *Account) Deposit(amount int) {
  fmt.Println("Gonna deposit", amount)
  a.balance += amount
}

// Balance of your account
func (a Account) Balance() int {
  return a.balance
}

Deposit(예금) 메소드에서 사용하여는 Account는 기존의 데이터를 활용하여 연산해야 하기 때문에 pointer(*)를 붙여 Go에게 복사본을 만들지 않도록 알려준다.

method 예외처리

위와 같은 방법으로 Withdraw(인출) 메소드를 만들 수 있다.

// Withdraw x amount from your account
func (a *Account) Withdraw(amount int) {
  a.balance -= amount
}

하지만 인출의 경우 잔액이 부족한 경우 실행이 되서는 안된다.

package main

import (
  "fmt"

  "github.com/saichoi/learngo/accounts"
)

func main() {
  // accounts.go 파일에 생성해둔 private한 struct를 사용
  account := accounts.NewAccount("choi")
  account.Deposit(10)
  fmt.Println(account.Balance())
  account.Withdraw(20)
  fmt.Println(account.Balance())
}

이러한 문제를 해결하기 위해서는 예외처리를 해줘야하는데 Golang에서는 try ~ catch와 같은 문법이 존재하지 않기 때문에 직접 에러 처리를 해줘야 한다.

// Withdraw x amount from your account (인출)
func (a *Account) Withdraw(amount int) error {
  if a.balance < amount {
    return errors.New("Can't withdraw you are poor")
  }
  a.balance -= amount
  return nil
}
func main() {
  account := accounts.NewAccount("choi")
  account.Deposit(10)
  fmt.Println(account.Balance())
  err := account.Withdraw(20)
  if err != nil {
    fmt.Println(err)
  }
  fmt.Println(account.Balance())
}

코드의 재활용을 위해서 에러를 변수로 선언해주는 것이 더 효율적이다.

var erroNoMoney = errors.New("Can't withdraw")

...

// Withdraw x amount from your account (인출)
func (a *Account) Withdraw(amount int) error {
  if a.balance < amount {
    return erroNoMoney
  }
  a.balance -= amount
  return nil
}

method 내부적 호출

Go의 구조체는 내부적으로 자동 method를 호출 기능을 가지고 있다.

// ChangeOwner of the account
func (a *Account) ChangeOwner(newOwner string) {
  a.owner = newOwner
}

// Owner of the account
func (a Account) Owner() string {
  return a.owner
}

func (a Account) String() string {
  return fmt.Sprint(a.Owner(), "'s account.\nHas: ", a.Balance())
}
package main

import (
  "fmt"

  "github.com/saichoi/learngo/accounts"
)

func main() {

  account := accounts.NewAccount("choi")
  account.Deposit(10)
  fmt.Println(account)
}

 

최신글