μ˜ˆμ‹œλ₯Ό ν†΅ν•œ Go 객체 생성 μ΄ν•΄ν•˜κΈ°

2024. 1. 25. 09:07ㆍBackend/🩡 Go

 

λͺ©ν‘œ

  1. Go의 struct 이해
  2. struct의 method 이해
  3. method 와 function 의 차이 이해

ꡬ쑰체(Struct ) λž€?

Go μ–Έμ–΄μ˜ structλŠ” 데이터λ₯Ό λ¬ΆλŠ” 데 μ‚¬μš©λ˜λŠ” μžλ£Œν˜• μž…λ‹ˆλ‹€. structλŠ” λ‹€μ–‘ν•œ 데이터 νƒ€μž…μ˜ ν•„λ“œλ₯Ό λ¬Άμ–΄ ν•˜λ‚˜μ˜ λ ˆμ½”λ“œλ‘œ λ§Œλ“€ 수 μžˆμŠ΅λ‹ˆλ‹€. 이λ₯Ό 톡해 κ΄€λ ¨ μžˆλŠ” 데이터λ₯Ό λ…Όλ¦¬μ μœΌλ‘œ κ·Έλ£Ήν™”ν•˜κ³  ꡬ쑰화할 수 μžˆμŠ΅λ‹ˆλ‹€.

ꡬ쑰체 μ •μ˜

ꡬ쑰체(struct)λŠ” λ‹€μŒκ³Ό 같이 μ •μ˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

type νƒ€μž…λͺ… struct {
  ν•„λ“œλͺ… νƒ€μž…
  ...
  ν•„λ“œλͺ… νƒ€μž…
}

μ΄ˆκΈ°ν™”

μ•„λž˜ μ˜ˆμ‹œμ½”λ“œλŠ” Account ꡬ쑰체λ₯Ό Owner(κ³„μ’Œμ£Ό)와 Balance(μž”μ•‘) ν•„λ“œμ™€ ν•¨κ»˜ μ •μ˜ν–ˆμŠ΅λ‹ˆλ‹€. ꡬ쑰체둜 λ³€μˆ˜λ₯Ό 생성할 λ•Œ ν•„λ“œ μˆœμ„œλ‘œ μ΄ˆκΈ°κ°’μ„ λŒ€μž…ν•˜κ±°λ‚˜, ν•„λ“œλͺ…을 μ§€μ •ν•˜μ—¬ μ΄ˆκΈ°ν™” ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

package banking

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

// ν•„λ“œ μˆœμ„œλŒ€λ‘œ μ΄ˆκΈ°ν™”
var account Account = Account{"Hoo", 1000000}

// ν•„λ“œλͺ…κ³Ό ν•¨κ»˜ μ΄ˆκΈ°ν™”
var account Account = Account{ Owner: "Hoo", Balance: 1000000}

ꡬ쑰체도 ν•¨μˆ˜μ™€ λ§ˆμ°¬κ°€μ§€λ‘œ ν•„λ“œλͺ…을 μ‚¬μš©ν•˜μ—¬ 외뢀에 λ…ΈμΆœμ‹œν‚¬ ν•„λ“œ(Public)와 λ…ΈμΆœ μ‹œν‚€μ§€ μ•Šμ„ ν•„λ“œ(Private)λ₯Ό μ„€μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€. ν•„λ“œλͺ…이 λŒ€λ¬Έμžλ‘œ μ‹œμž‘ν•˜λ©΄ μ™ΈλΆ€μ—μ„œ μ‚¬μš©μ΄ κ°€λŠ₯ν•˜μ§€λ§Œ, μ†Œλ¬Έμžλ‘œ μ‹œμž‘ν•˜λŠ” κ²½μš°μ—λŠ” μ™ΈλΆ€μ—μ„œ μ‚¬μš©μ΄ λΆˆκ°€λŠ₯ν•©λ‹ˆλ‹€.

ν˜„μž¬ Account와 λ‚΄λΆ€ ν•„λ“œ (Owner, Balance) λŠ” λͺ¨λ‘ λŒ€λ¬Έμž(Capital letter)둜 선언돼 μžˆλŠ”λ°, Golang μ—μ„œλŠ” public ν•œ μƒνƒœλ₯Ό μ˜λ―Έν•©λ‹ˆλ‹€. 즉 μ™ΈλΆ€ package μ—μ„œ ν•΄λ‹Ή ν•„λ“œμ™€ ꡬ쑰체에 μ ‘κ·Όν•  수 μžˆμŠ΅λ‹ˆλ‹€.

package main


func main() {
	account := banking.Account{Owner: "hoo", Balance: 1000000}
	fmt.Println(account) // 좜λ ₯ : {hoo 100000}
}

function 을 μ΄μš©ν•œ ꡬ쑰체 μ •μ˜

Account ꡬ쑰체λ₯Ό ν™œμš©ν•΄ κ°„λ‹¨νžˆ object λ₯Ό 생성할 수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ μœ„ μ˜ˆμ‹œμ˜ ν•„λ“œλŠ” λͺ¨λ‘ public ν•˜κ²Œ μ •μ˜λΌ μžˆμ–΄, 이미 μ •μ˜λœ ꡬ쑰체의 값을 λˆ„κ΅¬λ‚˜ μ‰½κ²Œ μˆ˜μ •ν•  수 μžˆλŠ” 문제점이 μžˆμŠ΅λ‹ˆλ‹€.

func main() {
	account := banking.Account{Owner: "hoo", Balance: 1000000}
	account.Balance = 100 // λˆ„κ΅¬λ‚˜ μ‰½κ²Œ 값을 μˆ˜μ •ν•  수 μžˆλŠ” 문제 쑴재
	fmt.Println(account) // 좜λ ₯ : 0
}

ν•„λ“œλ₯Ό public 으둜 μ—΄μ–΄λ’€κΈ° λ•Œλ¬Έμ— λˆ„κ΅¬λ‚˜ Balance 에 μ ‘κ·Όν•˜μ—¬ 값을 μˆ˜μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€. λ©€μ©‘ν•˜λ˜ μž”κ³ κ°€ κ°‘μžκΈ° 0으둜 λ³€κ²½λ˜λŠ” λΆˆμƒμ‚¬κ°€ λ°œμƒν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

ν•„λ“œλ₯Ό μ†Œλ¬Έμžλ‘œ μ„ μ–Έν•˜λ©΄ private 으둜 λ³€κ²½ν•  수 μžˆμŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ private ν•˜κ²Œ λ³€κ²½λœ ν•„λ“œλŠ” μ™ΈλΆ€ νŒ¨ν‚€μ§€μ—μ„œ μ ‘κ·Όν•  수 μ—†κΈ° λ•Œλ¬Έμ— object λ₯Ό 생성 ν•˜λ €λ©΄ λ‹€λ₯Έ 방법이 ν•„μš”ν•©λ‹ˆλ‹€. Golang 은 λ³„λ„μ˜ μƒμ„±μž κ°œλ…μ΄ μ—†κΈ° λ•Œλ¬Έμ— function을 ν™œμš©ν•΄ objectλ₯Ό λ§Œλ“€μ–΄μ•Ό ν•©λ‹ˆλ‹€.

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

// NewAccount creates Account
func NewAccount(owner string) *Account {
	account := Account{owner: owner, balance: 0}
	return &account // μƒˆλ‘œ λ§Œλ“€μ–΄μ§„ object λ₯Ό 리턴
}

NewAccount ν•¨μˆ˜λ₯Ό 톡해 Account λ₯Ό 생성할 수 있게 λμŠ΅λ‹ˆλ‹€. μœ„ μ˜ˆμ‹œ μ½”λ“œμ—μ„œλŠ” owner ν•„λ“œλ§Œ μ£Όμž…ν•˜μ—¬ 값을 μ„€μ •ν•˜κ³  balanace λŠ” 감좔어 μ™ΈλΆ€μ—μ„œ λ³€κ²½ν•  수 없도둝 κ°•μ œν–ˆμŠ΅λ‹ˆλ‹€.

func main() {
	account := banking.NewAccount("hoo")
	fmt.Println(account) // 좜λ ₯: &{hoo 0}
}

Method λ₯Ό μ΄μš©ν•œ ꡬ쑰체 - ν•„λ“œ κ°’ μ •μ˜

은행 κ³„μ’Œ(account) 객체λ₯Ό μƒμ„±ν–ˆμ§€λ§Œ owner 만 μ •μ˜λ˜κ³  balance λŠ” μˆ˜μ •μ΄ λΆˆκ°€λŠ₯ν•œ μƒνƒœμž…λ‹ˆλ‹€. κΈ°μ‘΄ μ½”λ“œμ—μ„œλŠ” Balance ν•„λ“œκ°€ λŒ€λ¬Έμžλ‘œ μ •μ˜λ˜μ–΄(public) κ°’ ν• λ‹Ήκ³Ό μˆ˜μ •μ΄ κ°€λŠ₯ν–ˆμ§€λ§Œ μˆ˜μ •λœ μ½”λ“œλŠ” μ†Œλ¬Έμžλ‘œ balance λ₯Ό μ •μ˜ν•˜μ—¬ 직접 값을 ν• λ‹Ήν•˜κ±°λ‚˜ μˆ˜μ •ν•  수 μ—†λŠ” μƒνƒœμž…λ‹ˆλ‹€.

Golang 은 struct ν•˜μœ„μ— method λ₯Ό μ •μ˜ν•˜μ—¬ private ν•„λ“œμ˜ 값을 μˆ˜μ •ν•  수 μžˆλ„λ‘ ν•©λ‹ˆλ‹€. Java μ—μ„œ private ν•„λ“œμ— 직접 접근이 λΆˆκ°€λŠ₯ν•œ λŒ€μ‹  setter λ‚˜ λ³„λ„μ˜ method λ₯Ό λ§Œλ“€μ–΄ 값을 μˆ˜μ •ν•˜λŠ” 것과 λΉ„μŠ·ν•œ κ°œλ…μ΄λΌ 생각할 수 μžˆμŠ΅λ‹ˆλ‹€.

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

// Deposit `x` amount on your account
// between the func and name(Deposit) `a` is called as Receiver
// pointer receiver -> don't make a copy of object, just use the object.
func (a *Account) Deposit(amount int) {
  a.balance += amount
}

Deposit method λŠ” balance 에 ν• λ‹Ήν•  값을 amount λΌλŠ” 인자둜 λ°›μŠ΅λ‹ˆλ‹€.

func μ˜ˆμ•½μ–΄μ™€ Deposit λ©”μ„œλ“œλͺ… 사이에 μœ„μΉ˜ν•œ (a *Account) λŠ” receiver 둜 λ©”μ„œλ“œκ°€ μ–΄λ–€ νƒ€μž…μ˜ 값에 λ™μž‘ν•˜λŠ”μ§€ λͺ…μ‹œν•˜λŠ” λΆ€λΆ„μž…λ‹ˆλ‹€. ReceiverλŠ” ν•¨μˆ˜κ°€ μ†ν•œ νƒ€μž…μ„ μ§€μ •ν•˜λŠ”λ° μ‚¬μš©λ˜λ©° λ©”μ„œλ“œλ₯Ό μ •μ˜ν•˜λŠ”λ° ν•„μš”ν•œ μš”μ†Œμž…λ‹ˆλ‹€.

Receiver λŠ” 2가지 μ’…λ₯˜λ‘œ λΆ„λ₯˜λ©λ‹ˆλ‹€.

κ°’(Value) Receiver:

  • κ°’(receiver)λŠ” ν•΄λ‹Ή νƒ€μž…μ˜ 볡사본을 λ°›μŠ΅λ‹ˆλ‹€.
  • λ©”μ„œλ“œ λ‚΄μ—μ„œ receiver λ₯Ό λ³€κ²½ν•˜λ”λΌλ„ ν˜ΈμΆœμžμ—κ²ŒλŠ” 영ν–₯을 λ―ΈμΉ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
  • 주둜 값을 λ³€κ²½ν•˜μ§€ μ•ŠλŠ” 읽기 μ „μš© λ©”μ„œλ“œμ— μ‚¬μš©λ©λ‹ˆλ‹€.

포인터(Pointer) Receiver:

  • 포인터(receiver)λŠ” ν•΄λ‹Ή νƒ€μž…μ˜ 포인터λ₯Ό λ°›μŠ΅λ‹ˆλ‹€.
  • λ©”μ„œλ“œ λ‚΄μ—μ„œ receiver λ₯Ό λ³€κ²½ν•˜λ©΄ ν˜ΈμΆœμžμ—κ²Œλ„ 변경이 λ°˜μ˜λ©λ‹ˆλ‹€.
  • 주둜 값을 λ³€κ²½ν•˜λŠ” λ©”μ„œλ“œμ— μ‚¬μš©λ©λ‹ˆλ‹€. λ©”μ„œλ“œκ°€ 호좜될 λ•Œ ꡬ쑰체의 볡사λ₯Ό ν”Όν•  λ•Œ μœ μš©ν•©λ‹ˆλ‹€.

Account 의 method μ •μ˜

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

// Withdraw your account, return error when balance is lower than amount
func (a *Account) Withdraw(amount int) error {
  if a.balance < amount {
   return errors.New("cant withdraw you are poor")
  }
  a.balance -= amount
  return nil
}

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

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

이제 balance ν•„λ“œκ°€ private ν•˜λ”λΌλ„ struct 에 μ •μ˜λœ method λ₯Ό 톡해 balance 값을 μ‘°μ ˆν•  수 μžˆμŠ΅λ‹ˆλ‹€. 특히 Withdraw λ©”μ„œλ“œμ—λŠ” μž”μ•‘(balance) 보닀 amount κ°€ 높을 땐 μž”μ•‘ λΆ€μ‘±μ΄λΌλŠ” μ˜ˆμ™Έμ²˜λ¦¬λ„ κ°€λŠ₯ν•˜λ„λ‘ λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ„ μΆ”κ°€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

package main

import (
  "fmt"
  "log"
  "nomad-go/chapter2/banking"
)

func main() {
  account := banking.NewAccount("hoo")
  fmt.Println("Account : ", account) // Account :  &{hoo 0}
  account.Deposit(2000000)
  fmt.Println(account.Balance()) // 2000000
  err := account.Withdraw(1000000)
  if err != nil {
	log.Fatalln(err)
  }
  fmt.Println(account.Balance(), account.Owner()) // 1000000 hoo
  fmt.Println(account) // &{hoo 1000000}
}

method 와 function의 차이

μœ„ μ˜ˆμ œμ—μ„œ NewAccount λ₯Ό μ΄μš©ν•΄ Account 객체λ₯Ό 생성할 땐 function이라 ν‘œν˜„ν–ˆκ³ , κ³„μ’Œ(balance)의 값을 μˆ˜μ •ν•  λ•Œ μ •μ˜ν•œ Deposit 은 method 라 ν‘œν˜„ν–ˆμŠ΅λ‹ˆλ‹€. 이 λ‘˜μ€ μ•„λž˜μ™€ 같은 차이점을 κ°€μ§‘λ‹ˆλ‹€.

ν•¨μˆ˜ (function)

  • ν•¨μˆ˜λŠ” νŠΉμ • κ΅¬μ‘°μ²΄λ‚˜ νƒ€μž…μ— μ’…μ†λ˜μ§€ μ•ŠλŠ” 독립적인 κΈ°λŠ₯을 μˆ˜ν–‰ν•©λ‹ˆλ‹€.
  • ν•¨μˆ˜λŠ” νŒ¨ν‚€μ§€ λ ˆλ²¨μ—μ„œ μ •μ˜λ˜λ©° νŠΉμ • νƒ€μž…κ³Ό λ¬΄κ΄€ν•˜κ²Œ μ‚¬μš©λ  수 μžˆμŠ΅λ‹ˆλ‹€.

λ©”μ„œλ“œ (Method)

  • λ©”μ„œλ“œλŠ” νŠΉμ • νƒ€μž…μ— μ’…μ†λ˜μ–΄ κ·Έ νƒ€μž…μ˜ μΈμŠ€ν„΄μŠ€μ— λŒ€ν•΄ λ™μž‘ν•©λ‹ˆλ‹€.
  • λ©”μ„œλ“œλŠ” ν•΄λ‹Ή νƒ€μž…μ— μ—°κ²°λœ ν•¨μˆ˜λ‘œμ„œ, νŠΉλ³„ν•œ μˆ˜μ‹ μž(receiver)λ₯Ό 가지고 μžˆμŠ΅λ‹ˆλ‹€.

정리

  • Go의 struct λ₯Ό 톡해 데이터λ₯Ό κ΄€λ¦¬ν•˜κ³  μ΄ˆκΈ°ν™”ν•˜λŠ” 방법을 μ•Œ 수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.
  • λ©”μ„œλ“œλ₯Ό μ΄μš©ν•˜μ—¬ ꡬ쑰체의 λ™μž‘μ„ μ •μ˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€. λ©”μ„œλ“œλŠ” νŠΉμ • νƒ€μž…μ— μ’…μ†λ˜μ–΄ ν•΄λ‹Ή νƒ€μž…μ˜ μΈμŠ€ν„΄μŠ€μ— λŒ€ν•΄ λ™μž‘ν•˜λ©°, κ°’(receiver) λ˜λŠ” 포인터(receiver)둜 μ •μ˜ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
  • λ§ˆμ§€λ§‰μœΌλ‘œ, ν•¨μˆ˜μ™€ λ©”μ„œλ“œμ˜ 차이λ₯Ό κ°„λ‹¨νžˆ μ •λ¦¬ν–ˆμŠ΅λ‹ˆλ‹€. ν•¨μˆ˜λŠ” νŠΉμ • ꡬ쑰체에 μ’…μ†λ˜μ§€ μ•ŠλŠ” 독립적인 κΈ°λŠ₯을 μˆ˜ν–‰ν•˜λ©°, λ©”μ„œλ“œλŠ” νŠΉμ • νƒ€μž…μ— μ’…μ†λ˜μ–΄ ν•΄λ‹Ή νƒ€μž…μ˜ μΈμŠ€ν„΄μŠ€μ— λŒ€ν•΄ λ™μž‘ν•˜λŠ” ν•¨μˆ˜λ‘œ 이해할 수 μžˆμŠ΅λ‹ˆλ‹€.