[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) }
