『Go in Action』의 내용을 기반으로 학습 및 정리했습니다.
타입
사용자 정의 타입(Struct)
struct는 여러 필드를 하나의 타입으로 묶어 표현하는 합성 타입(composite type)이다.
type user struct {
name string
email string
number int
}
type 키워드 뒤에 새로운 타입의 이름을 정의하고 struct 키워드로 구조체를 선언한다.
제로값 초기화
var 키워드로 구조체 변수를 선언하면 모든 필드는 각 타입의 제로값(zero value)으로 초기화된다.
var bill user
실제로는 다음과 같은 상태가 된다.
user{
name: "",
email: "",
number: 0,
}
구조체 리터럴
구조체를 생성하면서 원하는 값으로 초기화하려면 구조체 리터럴(struct literal)을 사용할 수 있다.
필드명을 명시하는 방법:
alice := user{
name: "Alice",
email: "alice@gmail.com",
number: 1,
}
필드명을 생략하는 방법:
alice := user{
"Alice",
"alice@gmail.com",
1,
}
필드명을 생략하면 선언 순서에 의존하므로 일반적으로는 첫 번째 방식을 권장한다.
강한 타입 시스템
Go는 강한 타입 시스템(Strong Typing)을 가진 언어이다.
기반 타입(base type)이 같더라도 서로 다른 타입으로 정의되었다면 암시적 형변환을 허용하지 않는다.
type Duration int64
func main() {
var dur Duration
dur = int64(1000) // 컴파일 에러
}
Duration의 기반 타입은 int64이지만, Go는 두 타입을 서로 다른 타입으로 취급한다.
따라서 명시적으로 변환해야 한다.
dur = Duration(1000)
다만 상수는 예외가 존재한다.
dur = 1000
1000은 타입이 정해지지 않은 상수(untyped constant)이므로 컴파일러가 필요한 타입으로 해석할 수 있다.
이러한 특성 덕분에 의도하지 않은 형변환으로 발생할 수 있는 버그를 컴파일 시점에 발견할 수 있다.
메서드
Go의 메서드는 특정 타입에 연결된 함수이다.
수신자(Receiver)
func (u user) notify() {
fmt.Printf("이름: %s, 이메일: %s\n", u.name, u.email)
}
(u user) 부분을 수신자(receiver)라고 한다.
수신자를 통해 해당 타입에 메서드를 연결할 수 있다.
bill := user{
name: "Bill",
email: "bill@gmail.com",
}
bill.notify()
값 수신자(Value Receiver)
func (u user) notify() {
fmt.Println(u.name)
}
값 수신자는 메서드 호출 시 구조체의 복사본을 전달받는다.
따라서 메서드 내부에서 값을 변경해도 원본에는 영향을 주지 않는다.
func (u user) changeName() {
u.name = "Alice"
}
bill.changeName()
fmt.Println(bill.name)
// Bill
포인터 수신자(Pointer Receiver)
func (u *user) changeName() {
u.name = "Alice"
}
포인터 수신자는 원본 데이터를 직접 참조한다.
bill.changeName()
fmt.Println(bill.name)
// Alice
구조체의 상태를 변경해야 하는 메서드는 일반적으로 포인터 수신자를 사용한다.
타입 임베딩(Type Embedding)
타입 임베딩은 기존 타입을 새로운 구조체 내부에 포함시키는 기능이다.
type person struct {
name string
}
type employee struct {
person
company string
}
person이 내부 타입(inner type), employee가 외부 타입(outer type)이다.
e := employee{
person: person{name: "Tom"},
company: "Google",
}
fmt.Println(e.name)
외부 타입은 내부 타입의 필드와 메서드에 직접 접근할 수 있다.
타입 임베딩은 객체지향 언어의 상속(inheritance)과 비슷한 효과를 제공하지만, Go는 상속 대신 조합(composition)을 권장한다.
외부 노출 식별자와 비노출 식별자
Go는 식별자의 첫 글자가 대문자인지 여부로 접근 가능 범위를 결정한다.
외부 노출 식별자(Exported Identifier)
type User struct {
Name string
}
대문자로 시작하는 식별자는 다른 패키지에서도 접근할 수 있다.
비노출 식별자(Unexported Identifier)
type user struct {
name string
}
소문자로 시작하는 식별자는 같은 패키지 내부에서만 사용할 수 있다.
Go에는 public, private 키워드가 존재하지 않으며, 대소문자 규칙만으로 접근 범위를 제어한다.
인터페이스 구현과 method set
주의할 점은 포인터 수신자 메서드를 가진 타입은 “포인터 타입만” 인터페이스를 구현한다는 것이다.
func (m *defaultMatcher) Search(feed *Feed, searchTerm string) {}
var dm defaultMatcher
var matcher Matcher = dm
// 컴파일 에러 cannot use dm as Matcher
반면 아래 코드는 정상 동작한다.
var matcher Matcher = &dm
'Programming > Go' 카테고리의 다른 글
| Go의 동시성 패턴 (0) | 2026.06.14 |
|---|---|
| Go 런타임 스케줄러와 동시성 모델 (1) | 2026.06.07 |
| Package와 Go의 데이터 컬렉션 (0) | 2026.05.31 |
| Go의 특징과 핵심 살펴보기 (0) | 2026.05.24 |
