구조체 포인터
Coddy GO 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 107개 중 20번째.
구조체(struct)를 다룰 때, 큰 데이터 구조를 복사하는 것을 피하고 원본 구조체를 수정하기 위해 포인터를 자주 사용하게 됩니다. 구조체에 대한 포인터는 해당 구조체 인스턴스의 메모리 주소를 보유합니다.
기존 구조체에 & 연산자를 사용하거나, 구조체 리터럴과 함께 주소 연산자를 직접 사용하여 구조체 포인터를 생성할 수 있습니다.
type Person struct {
Name string
Age int
}
p := Person{Name: "Alice", Age: 30}
ptr := &p // 기존 구조체에 대한 포인터
// 또는 직접 생성
ptr2 := &Person{Name: "Bob", Age: 25}Go는 포인터를 통해 구조체 필드에 접근할 때 편리한 단축 방식을 제공합니다. (*ptr).Name이라고 쓰는 대신, 간단히 ptr.Name이라고 쓸 수 있습니다. Go가 자동으로 포인터를 역참조해 줍니다.
ptr := &Person{Name: "Alice", Age: 30}
// 둘 다 동일하게 작동합니다
fmt.Println((*ptr).Name) // 명시적 역참조
fmt.Println(ptr.Name) // 자동 역참조이 자동 역참조는 필드를 수정할 때도 적용됩니다. 포인터를 통한 변경사항은 원본 구조체에 영향을 미칩니다.
p := Person{Name: "Alice", Age: 30}
ptr := &p
ptr.Age = 31 // 원본을 수정함
fmt.Println(p.Age) // 31구조체 포인터는 Go의 OOP 패턴에서 필수적입니다. 이는 메서드가 리시버 상태를 수정할 수 있게 하고, 대규모 구조체를 함수에 효율적으로 전달할 수 있도록 하며, 트리나 리스트와 같은 연결된 데이터 구조를 구축하기 위한 기초를 형성합니다.
챌린지
쉬움구조체 포인터가 원본 데이터를 어떻게 효율적으로 수정할 수 있는지 보여주는 은행 계좌 시스템을 구축해 보겠습니다. 포인터 연산을 통해 업데이트할 수 있는 계좌를 만들게 됩니다.
코드는 두 개의 파일로 구성됩니다:
account.go:Owner(string),Balance(float64),AccountNumber(string) 필드를 가진BankAccount구조체를 정의합니다. 소유자 이름과 계좌 번호를 매개변수로 받아 시작 잔액이 0인 새로운BankAccount에 대한 포인터를 반환하는NewAccount함수를 만듭니다. 또한*BankAccount포인터와 금액을 매개변수로 받아 포인터를 통해 해당 금액을 계좌 잔액에 추가하는Deposit함수를 만듭니다.main.go: 입력으로부터 계좌 정보를 읽고, 생성자 함수(포인터를 반환함)를 사용하여 계좌를 생성하고, 포인터를 사용하여 입금을 진행한 후, 잔액이 수정되었는지 확인하기 위해 계좌 상세 정보를 표시합니다.
다음과 같은 입력이 제공됩니다:
- Line 1: 소유자 이름
- Line 2: 계좌 번호
- Line 3: 입금 금액 (float)
출력 형식:
Account: [AccountNumber]
Owner: [Owner]
Balance: [Balance]예를 들어, John Smith, ACC-12345, 500.50이 주어지면 출력은 다음과 같아야 합니다:
Account: ACC-12345
Owner: John Smith
Balance: 500.50NewAccount 함수는 & 연산자를 사용하여 *BankAccount를 반환해야 하며, Deposit 함수는 Go의 자동 역참조(예: (*account).Balance 대신 account.Balance)를 사용하여 포인터를 통해 직접 잔액을 수정할 수 있음을 기억하세요.
치트 시트
구조체 포인터(pointer to a struct)는 구조체 인스턴스의 메모리 주소를 보유합니다. & 연산자를 사용하여 구조체 포인터를 생성합니다:
type Person struct {
Name string
Age int
}
p := Person{Name: "Alice", Age: 30}
ptr := &p // 기존 구조체에 대한 포인터
// 또는 직접 생성
ptr2 := &Person{Name: "Bob", Age: 25}Go는 포인터를 통해 구조체 필드에 접근할 때 자동 역참조(automatic dereferencing)를 제공합니다. (*ptr).Name 대신 ptr.Name이라고 쓸 수 있습니다:
ptr := &Person{Name: "Alice", Age: 30}
// 둘 다 동일하게 작동합니다
fmt.Println((*ptr).Name) // 명시적 역참조
fmt.Println(ptr.Name) // 자동 역참조포인터를 통한 변경은 원본 구조체에 영향을 미칩니다:
p := Person{Name: "Alice", Age: 30}
ptr := &p
ptr.Age = 31 // 원본을 수정함
fmt.Println(p.Age) // 31구조체 포인터는 메서드가 수신자(receiver) 상태를 수정할 수 있게 하고, 큰 구조체를 함수에 효율적으로 전달할 수 있게 하며, 연결된 데이터 구조(linked data structures)의 기초를 형성합니다.
직접 해보기
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// 소유자 이름 읽기
ownerName, _ := reader.ReadString('\n')
ownerName = strings.TrimSpace(ownerName)
// 계좌 번호 읽기
accountNumber, _ := reader.ReadString('\n')
accountNumber = strings.TrimSpace(accountNumber)
// 입금액 읽기
amountStr, _ := reader.ReadString('\n')
amountStr = strings.TrimSpace(amountStr)
amount, _ := strconv.ParseFloat(amountStr, 64)
// TODO: NewAccount 함수를 사용하여 새 계좌 생성 (포인터 반환)
// TODO: 계좌 포인터를 사용하여 Deposit 함수로 입금 수행
// TODO: 다음의 필수 형식으로 계좌 상세 정보 출력:
// 계좌: [AccountNumber]
// 소유자: [Owner]
// 잔액: [Balance]
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
8에러 처리와 OOP
error 인터페이스사용자 정의 에러 타입에러 래핑 (fmt.Errorf)센티넬 에러errors.Is()와 errors.As()Panic, Defer, Recover요약 - 파일 파서