
이 글은 Must Have Tucker의 Go 언어 프로그래밍 책을 참고하여 작성하였으며, 개인적인 학습 내용을 정리한 글입니다.
구조체 란?
구조체는 여러 필드를 묶어서 사용하는 타입 입니다.
구조체를 통해서 연관된 여러 데이터를 하나의 이름으로 묶을 수 있습니다.
예를 들어 이름, 나이 등을 묶어서 학생 구조체 데이터를 만들어 처리할 수 있습니다.
바로 선언하는 방법에 대해 알아보겠습니다.
구조체 선언 방법
type 타입명 struct {
필드명 타입
필드명 타입
...
}
위와 같은 방식으로 구조체를 선언을 합니다.
아래와 같은 예시를 들어보자면,
type User struct {
Name string
Age int
Email string
}
User 라는 새로운 사용자 정의 타입을 만든다고 정의를 하고,
해당 구조체(User) 안에는 이 구조체 안에 속한 필드들을 적어줍니다.
각 필드는 필드명과 타입을 적어줍니다.
그래서 정리하면, User라는 구조체안에, string 타입의 Name, Email 필드와 int 타입의 Age 필드가 존재합니다.
그리고 변수와 마찬가지로 구조체도 첫 번째 글자가 대문자이면 패키지 외부로 공개되는 타입 입니다.
이렇게 구조체를 사용하면 관련 데이터를 묶어서 하나의 변수로 다룰 수 있습니다.
이제 예시 코드를 사용해 실제 코드에서 구조체를 어떻게 사용하는 지 알아보겠습니다.
package main
import "fmt"
type House struct { // ❶ House 구조체를 정의합니다.
Address string
Size int
Price float64
Type string
}
func main() {
var house House // ❷ House 구조체 변수를 선언합니다.
house.Address = "서울시 강동구 ..." // ❸ 각 필드값을 초기화합니다.
house.Size = 28
house.Price = 9.8
house.Type = "아파트"
fmt.Println("주소:", house.Address) // ❹ 필드값을 출력합니다.
fmt.Printf("크기 %d평\n", house.Size)
fmt.Printf("가격: %.2f억원\n", house.Price) // ➎ 소수점 2자리까지 출력합니다.
fmt.Println("타입:", house.Type)
}
---
주소: 서울시 강동구 ...
크기 28평
가격: 9.80억원
타입: 아파트
❶ House 구조체를 정의를 해줍니다.
❷ House 구조체를 사용하기 위해 변수로 선언을 해줍니다. house 변수에 구조체 House를 선언합니다.
❸ 여기서 부터 각 구조체 안에 있던 필드 값을 설정해줍니다.
- Address: "서울시 강동구 ..."
- Size: 28
- Price: 9.8
- Type: "아파트"
❹ 앞서 ❸번 과정에서 설정한 필드 값들을 출력해줍니다.
이렇게 예시를 통해 구조체를 어떤식으로 사용하는 지 알아보았습니다.
이제 구조체 변수 초기화 하는 방법에 대해 좀 더 자세히 알아보겠습니다.
구조체 변수 초기화
구조체 변수를 선언하고 각 필드를 초기화 하는 방법은 초깃값 생략, 모든 필드 초기화, 일부 필드 초기화 방법들이 있습니다.
앞서 예제에서 사용했던 House 구조체를 이어서 사용해보겠습니다.
초깃값 생략
초깃값을 생략하면 모든 필드가 기본값으로 초기화됩니다.
var house House
이렇게 하면 각 필드는 아래와 같이 기본 값으로 초기화가 됩니다.
- Address: ""
- Size: 0
- Price: 0.0
- Type: ""
모든 필드 초기화
모든 필드 값을 중괄호 사이에 넣어서 초기화 합니다. 모든 필드가 순서대로 초기화됩니다.
var house House = House{ "서울시 강동구", 28, 9.80, "아파트" }
- Address: "서울시 강동구"
- Size: 28
- Price: 9.80
- Type: "아파트"
이렇게 중괄호에 있는 순서대로 필드 값이 초기화 됩니다.
그리고 아래 예시와 같이 여러 줄에 걸쳐서 초기화 할 수도 있습니다.
var house House = House{
"서울시 강동구",
28,
9.80,
"아파트", // 여러 줄로 초기화 할 때는 제일 마지막 값 뒤에 꼭 쉼표를 달아주어야 합니다.
}
일부 필드 초기화
말 그대로 일부 필드 값만 초기화를 합니다.
이 때 주의 사항으로는 '필드명:필드값' 형식으로 초기화 해야 합니다.
그리고 초기화되지 않은 나머지 변수에는 기본값이 할당됩니다.
var house House = House{ Size: 28, Type: "아파트" }
- Address: ""
- Size: 28
- Price: 0.0
- Type: "아파트"
구조체를 포함하는 구조체
이게 무슨 말이냐면 구조체의 필드안에 다른 구조체를 포함할 수 있다는 뜻 입니다.
구조체를 포함하는 방식은 두가지가 있는데,
내장타입 처럼 포함하는 방식
type User struct {
Name string
ID string
Age int
}
type VIPUser struct {
UserInfo User
VIPLevel int
Price int
}
이렇게 VIPUser 라는 구조체에 UserInfo 필드를 User 타입으로 선언해서 사용하는 방식이 내장타입으로 포함하는 방식 입니다.
해당 방식으로 실제 사용을 하는 예시를 보면,
package main
import "fmt"
type User struct { // 일반 고객용 구조체
Name string
ID string
Age int
}
type VIPUser struct { // VIP 고객용 구조체
UserInfo User
VIPLevel int
Price int
}
func main() {
user := User{"송하나", "hana", 23}
vip := VIPUser{
User{"화랑", "hwarang", 40},
3,
250, // 여러 줄로 초기화할 때는 제일 마지막 값 뒤에 꼭 쉼표를 달아주세요.
} // ❶ User를 포함한 VIPUser 구조체 변수를 초기화합니다.
fmt.Printf("유저: %s ID: %s 나이 %d\n", user.Name, user.ID, user.Age)
fmt.Printf("VIP 유저: %s ID: %s 나이 %d VIP 레벨: %d VIP 가격: %d만원\n",
vip.UserInfo.Name, // ❷ UserInfo 안의 Name
vip.UserInfo.ID, // ❸ UserInfo 안의 ID
vip.UserInfo.Age,
vip.VIPLevel, // ❹ VIPUser의 VIPLevel
vip.Price, // ➎ 마지막에 쉼표
)
}
---
유저: 송하나 ID: hana 나이 23
VIP 유저: 화랑 ID: hwarang 나이 40 VIP 레벨: 3 VIP 가격: 250만원
❷ 앞서 VIPUser 구조체를 초기화한 vip 변수의 각 필드 값을 선언하는 건데, VIPUser 구조체 안에 UserInfo 라는 필드 값으로 User 구조체가 선언이 되었으므로 vip.UserInfo.Name 이렇게 해야지 필드명에 접근할 수 있습니다.
포함된 필드 방식
앞선 방법으로 구조체 안의 구조체 필드에 접근하려면 vip.UserInfo.Name 과 같이 두 단계를 걸쳐 접근해야 합니다.
이런 방법 보다 좀 더 간단하게 접근할 수 있는 방법이 있습니다.
바로 이전 예제를 사용하여 알아보겠습니다.
package main
import "fmt"
type User struct { // 일반 고객용 구조체
Name string
ID string
Age int
}
type VIPUser struct { // VIP 고객용 구조체
User // ❶ 필드명 생략
VIPLevel int
Price int
}
func main() {
user := User{"송하나", "hana", 23}
vip := VIPUser{
User{"화랑", "hwarang", 40},
3,
250,
}
fmt.Printf("유저: %s ID: %s 나이 %d\n", user.Name, user.ID, user.Age)
fmt.Printf("VIP 유저: %s ID: %s 나이 %d VIP 레벨: %d VIP 가격: %d만원\n",
vip.Name, // ❷ . 하나로 접근할 수 있습니다.
vip.ID,
vip.Age,
vip.VIPLevel,
vip.Price, // 여러 줄로 초기화할 때는 제일 마지막 값 뒤에 꼭 쉼표를 달아주세요.
)
}
앞선 예제와 다른 부분은 ❶번 에서 User 구조체를 사용하기 위한 필드명을 생략을 하였고, ❷번에서 vip변수 뒤에 바로 . 을 찍어 바로 User 구조체의 필드를 사용할 수 있도록 하였습니다.
이렇게 구조체 안에 포함된 다른 구조체의 필드명을 생략하는 경우 '포함된 필드' 라고 표현 합니다.
구조체 크기
구조체 변수가 선언되면 컴퓨터는 구조체 필드를 모두 담을 수 있는 메모리 공간을 할당 합니다.
구조체 크기를 구하는 방법에 대해 알아보겠습니다.
type User struct { // User 구조체 선언
Age int
Score float64
}
var user User // User 구조체의 user 변수 선언
이렇게 User 구조체의 변수인 user를 선언해주면 컴퓨터는 Age, Score 필드를 연속되게 담을 수 있는 메모리 공간을 찾아 할당 합니다.
- int 타입 Age는 8바이트
- float64 타입 Score는 8바이트
따라서, user 변수에는 총 16바이트 크기가 필요합니다.
필드 배치 순서에 따른 구조체 크기 변화
구조체 크기에 대해 더 알아보겠습니다.
package main
import (
"fmt"
"unsafe"
)
type User struct {
Age int32 // ❶ 4바이트
Score float64 // 8바이트
}
func main() {
user := User{23, 77.2}
fmt.Println(unsafe.Sizeof(user)) // 해당 변수의 메모리 공간 크기를 반환
}
---
16
지금 보면 int32의 크기는 4바이트고,float64의 크기는 8바이트 이므로,
결과 값이 12 바이트가 나와야 하는데, 16바이트가 나왔습니다.
왜 이런지 알아보겠습니다.
메모리 정렬
메모리 정렬이란 컴퓨터가 데이터에 효과적으로 접근하고자 메모리를 일정 크기 간격으로 정렬하는 것을 말합니다.
레지스터는 실제 연산에 사용되는 데이터가 저장되는 곳 입니다.
레지스터 크기가 4바이트인 컴퓨터를 32비트 컴퓨터
레지스터 크기가 8바이트인 컴퓨터를 64비트 컴퓨터
라고 합니다.
현재 대부분의 PC, 서버는 64비트 컴퓨터 이고,
데이터가 레지스터 크기와 똑같은 크기로 정렬되어 있으면 효율적으로 데이터를 읽어올 수 있습니다.
이러다 보니, 처음부터 프로그램 언어에서 데이터를 만들 때, 8의 배수인 메모리 주소에 데이터를 할당 합니다.
예를 들어,
type User struct {
Age int32 // 4바이트
Score float64 // 8바이트
}
var user User
위 구조체를 예시로 User 구조체 변수 user의 시작 주소가 240번지라면
Age 필드의 시작주소 역시 240번지가 됩니다.
그런데, Age는 4바이트 공간을 차지하기 때문에 바로 붙어서 Score 필드를 할당하면 Score 필드의 시작주소가 244번지가 되어 8의 배수가 아니라 성능 손해를 봅니다.
따라서 프로그램 언어에서는 User 구조체를 할당할 때, Age와 Score 사이에 4바이트 크기 만큼 띄워서 할당을 합니다.

이렇게 메모리 정렬을 위해 필드 사이에 공간을 띄우는 것을 메모리 패딩 이라고 합니다.
그래서 구조체를 생성할 때, 필드와 타입을 막 정의하는 것 보다 메모리 크기를 생각하여 정렬 하는 것이 좋습니다.
이게 무슨 뜻인지 예시를 통해 알아보겠습니다.
- 메모리 패딩을 고려하지 않은 필드 배치
package main
import (
"fmt"
"unsafe"
)
type User struct {
A int8 // 1바이트
B int // 8바이트
C int8 // 1바이트
D int // 8바이트
E int8 // 1바이트
}
func main() {
user := User{1, 2, 3, 4, 5}
fmt.Println(unsafe.Sizeof(user))
}
---
40
User 구조체의 필드들의 타입을 생각치 않고 막 정렬을 해버리면 메모리 패딩에 의해
아래 이미지 처럼 실제로 사용하지 않지만 메모리 양을 더 잡아버리는 경우가 생깁니다.

따라서 최대한 메모리 패딩으로 불필요한 메모리를 잡지 않도록 필드들의 타입을 생각하여 구조체를 정의해보겠습니다.
- 메모리 패딩을 고려한 필드 배치
package main
import (
"fmt"
"unsafe"
)
type User struct {
A int8 // 1바이트
C int8 // 1바이트
E int8 // 1바이트
B int // 8바이트
D int // 8바이트
}
func main() {
user := User{1, 2, 3, 4, 5}
fmt.Println(unsafe.Sizeof(user))
}
---
24
이렇게 1바이트 크기를 가진 필드들을 붙여놓고 그 뒤에 8바이트 크기를 가진 필드를 붙이면
동일한 구조체이지만, 메모리 크기가 40 -> 24로 줄어든 것을 확인할 수 있습니다.

이렇게 구조체에 대해 알아보았습니다.
이상 입니다.
감사합니다.
'개발 > Go' 카테고리의 다른 글
| [Go 언어 학습기 #8] Go 언어(Golang) 의 슬라이스 (0) | 2025.12.23 |
|---|---|
| [Go 언어 학습기 #7] Go 언어(Golang) 의 패키지 (0) | 2025.12.22 |
| [Go 언어 학습기 #5] Go 언어(Golang) 의 배열 (0) | 2025.12.17 |
| [Go 언어 학습기 #4] Go 언어(Golang) 의 함수 (0) | 2025.12.02 |
| [Go 언어 학습기 #3] Go 언어(Golang) 의 변수가 갖는 속성과 사용법 알아보기 (0) | 2025.11.30 |