Search

[Go 기초] [#9] 구조체와 메서드

요약
구조체와 메서드의 사용법
썸네일
https://noticon-static.tammolo.com/dgggcrkxq/image/upload/v1566913552/noticon/xjarxsfmmcouhih40val.png
작성일자
2024/05/08 11:26
수정일자
2024/05/18 08:01
스택
Go
카테고리
기초
태그
구조체
메서드
메소드
structure
method

구조체

구조체는 Go 언어의 핵심 요소 중 하나로 서로 관련 있는 데이터를 묶어서 관리할 수 있게 해주는 도구입니다.
Go 언어의 구조체는 C 언어의 구조체와 매우 유사하여 간결하지만, 객체 지향 프로그래밍 언어의 클래스와 비슷한 역할을 수행할 정도로 강력합니다.
이번 강의에서는 구조체의 정의 방법부터 활용 방법까지 자세히 알아보겠습니다.

선언

구조체를 선언하는 방법은 간단합니다. 다음과 같은 형식을 따릅니다.
type 구조체명 struct { 필드1 타입1 필드2 타입2 // ... }
Go
복사
예를 들어, 학생을 나타내는 구조체를 선언하고 싶다면 다음과 같이 선언할 수 있습니다.
package main import "fmt" type Student struct { Name string Age int Grade string } func main() { var s Student s.Name = "John Doe" s.Age = 20 s.Grade = "Sophomore" fmt.Println(s) } // 실행 결과 : // {John Doe 20 Sophomore}
Go
복사

Convention

함수 강의에서 잠깐 언급했던 것 처럼, 구조체에도 Go 언어 만의 명명 규칙이 적용됩니다.
대문자로 시작하는 전역 변수, 구조체의 멤버 변수, 함수는 외부 패키지에서 접근하거나 호출할 수 있습니다. (Java Public 선언과 유사한 효과)
반면, 소문자로 시작하는 전역 변수, 구조체의 멤버 변수, 함수는 외부 패키지에서 접근하거나 호출할 수 없습니다. (Java Private 선언과 유사한 효과)
대문자로 시작하는 멤버는 외부 패키지에서도 접근할 수 있는 공개 멤버로 선언되며, 반대로 소문자로 시작하는 멤버는 외부 패키지에서는 접근할 수 없는 비공개 멤버로 선언됩니다.
예시는 person 패키지에서 Person 구조체를 선언하고 main 패키지에서 person 패키지를 import 하여 Person 구조체를 사용하는 예시입니다.
// person.go 파일 package person // Person 구조체 정의 type Person struct { Name string // 대문자로 시작: 공개 멤버 age int // 소문자로 시작: 비공개 멤버 } // NewPerson 함수는 Person 구조체의 인스턴스를 생성하고 반환합니다. func NewPerson(name string, age int) Person { return Person{Name: name, age: age} } // GetAge는 Person 구조체의 비공개 멤버 age를 반환하는 메서드입니다. func (p Person) GetAge() int { return p.age }
Go
복사
// main.go 파일 package main import ( "fmt" "path/to/your/project/person" // 실제 프로젝트 경로로 수정해야 합니다. ) func main() { p := person.NewPerson("Alice", 30) fmt.Println(p.Name) // Name은 공개 멤버라서 접근 가능 fmt.Println(p.GetAge()) // GetAge 메서드를 통해 age에 접근 가능 // fmt.Println(p.age) // age는 비공개 멤버라서 직접 접근 불가, 주석 해제 시 컴파일 에러 발생 } // 실행 결과 : // Alice // 30
Go
복사

임베딩

Go 언어의 임베딩 기능을 이용하면, 한 구조체의 필드로 다른 구조체를 사용할 수 있습니다. 이를 통해 상속과 비슷한 효과를 낼 수 있습니다.
package main import "fmt" type Person struct { Name string Age int } type Student struct { Person Grade string } func main() { var s Student s.Name = "John Doe" s.Age = 20 s.Grade = "Sophomore" fmt.Println(s) } // 실행 결과 : // {{John Doe 20} Sophomore}
Go
복사
위의 예시에서 Student 구조체는 Person 구조체를 임베딩하고 있습니다. 이렇게 되면 Student 구조체는 Person의 모든 필드를 포함하게 됩니다.

메서드

메서드는 특정 타입에 연결된 함수입니다. Go 언어에서는 구조체에 메서드를 정의하여 해당 구조체의 인스턴스에서 해당 메서드를 호출할 수 있습니다.
메서드는 값 타입포인터 타입 의 두 가지 유형의 리시버를 가질 수 있습니다.
값 타입 리시버는 메서드가 호출될 때 값이 복사되지만, 포인터 타입 리시버는 값의 주소가 전달됩니다.
따라서, 값 타입 리시버는 메서드 내에서 리시버의 값 자체를 수정할 수 없지만, 포인터 타입 리시버를 사용하면 메서드 내에서 리시버의 값 자체를 수정할 수 있습니다.

값 타입 리시버 선언

값 타입 리시버는 메서드가 호출될 때 값이 복사됩니다.
따라서, 값 타입 리시버를 사용한다면 원본 구조체의 멤버 변수 값을 변경할 수 없음에 유의해야 합니다.
package main import "fmt" type Rectangle struct { Width float64 Height float64 } // Area 메서드는 Rectangle의 넓이를 계산하여 반환합니다. func (r Rectangle) Area() float64 { return r.Width * r.Height } // TryResize 메소드는 Rectangle의 너비와 높이를 변경하려고 시도합니다. // 하지만 값 타입 리시버를 사용했기 때문에 원본 구조체의 멤버 값을 변경할 수 없습니다. func (r Rectangle) TryResize(w, h float64) { r.Width = w r.Height = h } func main() { r := Rectangle{Width: 5, Height: 10} fmt.Println("Original Rectangle: ", r) fmt.Println("Area of Rectangle: ", r.Area()) r.TryResize(15, 20) fmt.Println("After TryResize: ", r) fmt.Println("Area of Rectangle after TryResize: ", r.Area()) } // 실행 결과 : // Original Rectangle: {5 10} // Area of Rectangle: 50 // After TryResize: {5 10} // Area of Rectangle after TryResize: 50
Go
복사
위의 코드에서 Rectangle 구조체에 Area 라는 메서드를 정의했습니다. Area 메서드는 Rectangle 인스턴스의 넓이를 계산하여 반환합니다.
TryResize 메서드에서 구조체의 값을 변경하려고 시도하지만 main 함수에서 출력한 원본 구조체의 내용은 변경되지 않는 것을 확인할 수 있습니다.

포인터 타입 리시버 선언

포인터 타입 리시버는 값의 주소가 전달됩니다.
따라서 포인터 타입 리시버를 사용하여 메서드를 만든다면, 원본 구조체의 멤버 변수에 접근할 수 있습니다.
package main import "fmt" type Rectangle struct { Width float64 Height float64 } // Scale 메서드는 Rectangle의 넓이와 높이를 주어진 비율로 확장합니다. func (r *Rectangle) Scale(factor float64) { r.Width = r.Width * factor r.Height = r.Height * factor } func main() { r := Rectangle{Width: 5, Height: 10} fmt.Println("Original Rectangle: ", r) r.Scale(2) fmt.Println("Rectangle after Scale: ", r) } // 실행 결과 : // Original Rectangle: {5 10} // Rectangle after Scale: {10 20}
Go
복사
위의 코드에서 Rectangle 구조체에 Scale이라는 메서드를 정의했습니다. 이 메서드는 Rectangle 인스턴스의 넓이와 높이를 주어진 비율로 확장합니다. 이 메서드에서는 포인터 타입 리시버를 사용하여 Rectangle 인스턴스의 값 자체를 변경하고 있습니다.
이러한 메서드의 특성에 의해서 Go 언어는 타 언어의 객체 지향 패러다임과는 다르게 Setter, Getter 패턴의 사용을 권장하지 않습니다. Private 멤버에 대한 접근이 아니라면 Setter, Getter를 사용하기 보다는 그냥 구조체에 직접 접근하여 값을 읽고 쓰면 됩니다.
강의 목록

참조