Go 3. 기본 문법 활용

jiffydev·2021년 1월 1일
0

메소드

go에서는 객체지향 프로그래밍을 조금 다른 방식으로 지원한다.
다른 언어들이 클래스 안에 constructor와 메소드가 함께 존재하는 것과는 다르게 go에서는 struct가 필드만을 가지고 메소드는 별도로 분리되어 정의한다.

그렇기 때문에 메소드의 모양도 다른 언어에서는 함수와 같은 반면, go에서는 receiver를 메소드명 앞에 붙여 이 메소드가 어떤 struct를 위한 메소드인지를 표시해준다.

// accounts/accounts.go

package accounts

import (
	"fmt"
)

type Account struct {
	owner   string
	balance int
}

// NewAccount creates new account
func NewAccount(owner string) *Account {
	account := Account{owner: owner, balance: 0}
	return &account
}

// Deposit x amount on your account
func (a *Account) Deposit(amount int) {
	a.balance += amount
}

// Balance of account
func (a Account) Balance() int {
	return a.balance
}
// main.go
package main

import (
	"fmt"
	"strings"
    
	"/accounts"
)

func main() {
	account := accounts.NewAccount("nico")
	account.Deposit(100)
    fmt.Println(account.Balance(), account.Owner())
	fmt.Println(account)
}

>> 100 nico
>> &{nico 100}

위 예제는 은행 계좌를 생성하는 메소드, 입금 메소드, 잔고확인 메소드로 구성되어 있다. accounts.go는 컴파일하지 않고 main으로 export하기 때문에, 각 메소드 및 struct는 main에서 작성하던 함수와는 다른점이 몇가지 존재한다.

우선 앞에서도 설명했던 것처럼 export 하기 위해서는 이름이 대문자로 시작해야 하므로 모두 대문자로 시작한다.
메소드에서 다른 점은 메소드 위에 주석을 달아야 하는데, 주석은 반드시 메소드명(대소문자 구분)으로 시작해야 하고 앞에 설명한 receiver는 (t TypeName)과 같이 타입의 이름 첫 글자의 소문자로 보통 지정한다.

또한 receiver에는 value receiver - pointer receiver가 존재하는데, 이는 이전에 나왔던 값을 복사하냐 주소값을 바라보게 하냐의 차이이다.
예를 들어 Deposit 메소드라면 계좌에 돈을 넣어야 하는 것이므로 필드값이 변경(=입금)되면 호출자의 데이터도 그에 맞추어 변해야 한다. 따라서 주소값(포인터)을 바라보게 하여 기존 필드값에 추가해야 한다.
반면 Balance 메소드는 필드값이 변경되지 않기 때문에 그대로 현재 필드값을 그대로 복사해서 사용해도 무방하다. 따라서 포인터를 사용하지 않는다.

에러 처리

위의 예제 코드에 출금하는 메소드를 추가해보자.

// accounts.go

...

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

// main.go

func main() {
	account := accounts.NewAccount("nico")
	account.Deposit(100)
    account.Withdraw(1000)
}

위를 컴파일하면 -900이 나오는데 잔액은 마이너스통장이 아닌 이상 0이하로 내려갈 수 없다. 따라서 잔액이 출금액보다 적을 때는 실행되지 않도록 처리해주어야 한다.
그런데 go에서는 다른 언어와 다르게 예외처리 구문(try..except / 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")
	}
	a.balance -= amount
	return nil
}

메소드에서 error를 리턴하도록 초기화 한 다음, 잔액이 출금액보다 적다면 errors.New()를 통해 에러를 리턴해 준다. 출금이 가능하다면 계좌에서 출금액을 빼 주고 nil을 리턴한다.
nil에 대해서는 이 블로그에 잘 정리되어 있다.
에러를 정의하는 방법으로는 위 예제처럼 바로 errors.New()를 통해 생성할 수도 있고 미리 변수에 저장시켜 놓고 변수를 리턴하는 방법도 있다.

profile
잘 & 열심히 살고싶은 개발자

0개의 댓글