[Go] 기본 자료형
Updated:
개요
Go는 정적 타입 언어로, 컴파일 시점에 모든 변수의 타입이 결정됩니다. Go의 기본 자료형은 크게 숫자형, 문자열, 불린, 함수로 구분됩니다.
숫자형 (Numeric Types)
정수형 (Integer)
부호 있는 정수 (Signed Integer)
| 타입 | 크기 | 범위 | 설명 |
|---|---|---|---|
int8 |
1 byte | -128 ~ 127 | 8비트 정수 |
int16 |
2 bytes | -32,768 ~ 32,767 | 16비트 정수 |
int32 |
4 bytes | -2,147,483,648 ~ 2,147,483,647 | 32비트 정수 |
int64 |
8 bytes | -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 | 64비트 정수 |
int |
플랫폼 의존 | 32bit 시스템: int32, 64bit 시스템: int64 | 기본 정수형 |
부호 없는 정수 (Unsigned Integer)
| 타입 | 크기 | 범위 | 설명 |
|---|---|---|---|
uint8 |
1 byte | 0 ~ 255 | 8비트 부호 없는 정수 |
uint16 |
2 bytes | 0 ~ 65,535 | 16비트 부호 없는 정수 |
uint32 |
4 bytes | 0 ~ 4,294,967,295 | 32비트 부호 없는 정수 |
uint64 |
8 bytes | 0 ~ 18,446,744,073,709,551,615 | 64비트 부호 없는 정수 |
uint |
플랫폼 의존 | 32bit 시스템: uint32, 64bit 시스템: uint64 | 부호 없는 기본 정수형 |
uintptr |
플랫폼 의존 | 포인터 크기와 동일 | 포인터 값 저장용 |
별칭 타입
| 타입 | 실제 타입 | 용도 |
|---|---|---|
byte |
uint8 |
바이트 데이터 (ASCII, 바이너리) |
rune |
int32 |
유니코드 코드 포인트 (문자) |
정수형 예제
package main
import "fmt"
func main() {
// 기본 정수 타입
var a int = 10
var b int8 = 127
var c uint = 100
fmt.Printf("int: %d, int8: %d, uint: %d\n", a, b, c)
// 타입 추론
x := 42 // int로 추론
fmt.Printf("x의 타입: %T, 값: %d\n", x, x)
// 16진수, 8진수, 2진수 표현
hex := 0xFF // 16진수 (255)
oct := 0o77 // 8진수 (63)
bin := 0b1010 // 2진수 (10)
fmt.Printf("hex: %d, oct: %d, bin: %d\n", hex, oct, bin)
}
부동소수점형 (Floating Point)
| 타입 | 크기 | 정밀도 | 설명 |
|---|---|---|---|
float32 |
4 bytes | 약 6-7 자리 | 단정밀도 부동소수점 |
float64 |
8 bytes | 약 15-16 자리 | 배정밀도 부동소수점 (기본) |
package main
import "fmt"
func main() {
var f32 float32 = 3.14159
var f64 float64 = 3.141592653589793
fmt.Printf("float32: %.5f\n", f32)
fmt.Printf("float64: %.15f\n", f64)
// 타입 추론 (float64가 기본)
pi := 3.14
fmt.Printf("pi의 타입: %T\n", pi) // float64
// 과학적 표기법
scientific := 1.23e-4 // 0.000123
fmt.Printf("scientific: %f\n", scientific)
}
복소수형 (Complex)
| 타입 | 크기 | 구성 | 설명 |
|---|---|---|---|
complex64 |
8 bytes | float32 실수부 + float32 허수부 | 단정밀도 복소수 |
complex128 |
16 bytes | float64 실수부 + float64 허수부 | 배정밀도 복소수 (기본) |
package main
import "fmt"
func main() {
var c1 complex64 = 1 + 2i
var c2 complex128 = complex(3, 4) // 3 + 4i
fmt.Printf("c1: %v\n", c1)
fmt.Printf("c2: %v\n", c2)
// 실수부와 허수부 추출
fmt.Printf("실수부: %f, 허수부: %f\n", real(c2), imag(c2))
// 연산
sum := c1 + complex64(c2)
fmt.Printf("합: %v\n", sum)
}
문자열형 (String)
string
- 불변(immutable) 타입
- UTF-8 인코딩 바이트 시퀀스
- 큰따옴표(
")로 정의 - 백틱(
`)으로 raw string 정의 (이스케이프 무시)
package main
import "fmt"
func main() {
// 기본 문자열
str1 := "Hello, 世界"
fmt.Println(str1)
// Raw string (이스케이프 시퀀스 무시)
str2 := `첫 번째 줄
두 번째 줄
세 번째 줄`
fmt.Println(str2)
// 문자열 연산
concatenated := "Hello" + " " + "World"
fmt.Println(concatenated)
// 길이 (바이트 수)
fmt.Printf("바이트 길이: %d\n", len(str1)) // 13 (영문 6 + 한자 6 + 쉼표/공백 2)
// 룬(문자) 개수
fmt.Printf("문자 개수: %d\n", len([]rune(str1))) // 9
// 인덱싱 (바이트 단위)
fmt.Printf("첫 바이트: %c\n", str1[0]) // H
}
문자열 vs 바이트 슬라이스
package main
import "fmt"
func main() {
str := "Hello"
// 문자열 → 바이트 슬라이스
bytes := []byte(str)
fmt.Printf("바이트: %v\n", bytes) // [72 101 108 108 111]
// 바이트 슬라이스 → 문자열
newStr := string(bytes)
fmt.Printf("문자열: %s\n", newStr) // Hello
}
rune (문자)
int32의 별칭- 유니코드 코드 포인트 표현
- 작은따옴표(
')로 정의 - 단일 유니코드 문자 저장
package main
import "fmt"
func main() {
var r1 rune = 'A'
var r2 rune = '가'
var r3 rune = '世'
fmt.Printf("r1: %c (코드: %d)\n", r1, r1) // A (코드: 65)
fmt.Printf("r2: %c (코드: %d)\n", r2, r2) // 가 (코드: 44032)
fmt.Printf("r3: %c (코드: %d)\n", r3, r3) // 世 (코드: 19990)
// 문자열 순회 (룬 단위)
str := "Hello, 世界"
for i, r := range str {
fmt.Printf("인덱스 %d: %c (코드: %d)\n", i, r, r)
}
}
byte
uint8의 별칭- ASCII 문자 또는 바이너리 데이터 표현
- 1바이트 데이터
package main
import "fmt"
func main() {
var b1 byte = 'A' // ASCII 65
var b2 byte = 72 // 'H'
fmt.Printf("b1: %c (%d)\n", b1, b1) // A (65)
fmt.Printf("b2: %c (%d)\n", b2, b2) // H (72)
// 바이트 슬라이스
data := []byte{72, 101, 108, 108, 111}
fmt.Printf("문자열: %s\n", string(data)) // Hello
}
불린형 (Boolean)
| 타입 | 크기 | 값 | 설명 |
|---|---|---|---|
bool |
1 byte | true 또는 false |
논리값 |
package main
import "fmt"
func main() {
var isTrue bool = true
var isFalse bool = false
fmt.Printf("isTrue: %t, isFalse: %t\n", isTrue, isFalse)
// 비교 연산 결과
result := 10 > 5
fmt.Printf("10 > 5: %t\n", result)
// 논리 연산
and := true && false // false
or := true || false // true
not := !true // false
fmt.Printf("AND: %t, OR: %t, NOT: %t\n", and, or, not)
}
주의: Go에서는 0이나 빈 문자열이 자동으로 false로 변환되지 않습니다.
// ❌ 컴파일 에러
if 1 { // non-bool 1 used as if condition
fmt.Println("Error")
}
// ✅ 명시적 비교 필요
if 1 != 0 {
fmt.Println("OK")
}
함수형 (Function)
함수도 Go의 일급 객체(first-class citizen)로 타입입니다.
package main
import "fmt"
func main() {
// 함수를 변수에 할당
var add func(int, int) int = func(a, b int) int {
return a + b
}
result := add(3, 5)
fmt.Printf("3 + 5 = %d\n", result)
// 함수를 매개변수로 전달
calculate := func(a, b int, op func(int, int) int) int {
return op(a, b)
}
multiply := func(a, b int) int { return a * b }
fmt.Printf("4 * 5 = %d\n", calculate(4, 5, multiply))
}
제로값 (Zero Value)
Go는 선언만 하고 초기화하지 않은 변수에 제로값(zero value)을 자동으로 할당합니다.
| 타입 | 제로값 |
|---|---|
정수형 (int, int8, …) |
0 |
부동소수점 (float32, float64) |
0.0 |
복소수 (complex64, complex128) |
0+0i |
bool |
false |
string |
"" (빈 문자열) |
| 포인터, 함수, 인터페이스, 슬라이스, 채널, 맵 | nil |
package main
import "fmt"
func main() {
var i int
var f float64
var b bool
var s string
fmt.Printf("int: %d\n", i) // 0
fmt.Printf("float64: %f\n", f) // 0.000000
fmt.Printf("bool: %t\n", b) // false
fmt.Printf("string: '%s'\n", s) // ''
}
타입 추론과 리터럴
타입 추론
:= 연산자를 사용하면 Go가 자동으로 타입을 추론합니다.
package main
import "fmt"
func main() {
// 타입 추론
a := 42 // int
b := 3.14 // float64
c := "hello" // string
d := true // bool
e := 'A' // rune (int32)
fmt.Printf("%T, %T, %T, %T, %T\n", a, b, c, d, e)
// int, float64, string, bool, int32
}
리터럴 접미사
특정 타입을 명시하려면 타입 변환이 필요합니다.
package main
import "fmt"
func main() {
// float32로 명시
var f32 float32 = 3.14
// 타입 변환 필요
x := float32(3.14)
fmt.Printf("%T, %T\n", f32, x) // float32, float32
}
실전 예제
package main
import "fmt"
func main() {
// 1. 정수형
var age int = 25
var maxUsers uint = 1000
fmt.Printf("나이: %d, 최대 사용자: %d\n", age, maxUsers)
// 2. 부동소수점
var price float64 = 19.99
var discount float32 = 0.15
finalPrice := price * (1 - float64(discount))
fmt.Printf("최종 가격: $%.2f\n", finalPrice)
// 3. 문자열
name := "홍길동"
greeting := fmt.Sprintf("안녕하세요, %s님!", name)
fmt.Println(greeting)
// 4. 룬과 문자열 처리
text := "Hello, 世界"
runeCount := len([]rune(text))
byteCount := len(text)
fmt.Printf("문자 수: %d, 바이트 수: %d\n", runeCount, byteCount)
// 5. 불린
isActive := true
isPremium := false
if isActive && !isPremium {
fmt.Println("무료 회원이 활성화됨")
}
// 6. 복소수 (과학/공학 계산)
impedance := complex(3, 4) // 3 + 4i
magnitude := real(impedance)*real(impedance) + imag(impedance)*imag(impedance)
fmt.Printf("임피던스 크기의 제곱: %.0f\n", magnitude)
}
타입 변환
Go는 명시적 타입 변환만 허용합니다 (암시적 변환 없음).
package main
import "fmt"
func main() {
var i int = 42
var f float64 = float64(i) // int → float64
var u uint = uint(f) // float64 → uint
fmt.Printf("int: %d, float64: %f, uint: %d\n", i, f, u)
// ❌ 암시적 변환 불가
// var x float64 = i // cannot use i (type int) as type float64
// ✅ 명시적 변환 필요
var x float64 = float64(i)
fmt.Printf("x: %f\n", x)
}
타입 확인
%T 포맷 지정자나 reflect 패키지로 타입을 확인할 수 있습니다.
package main
import (
"fmt"
"reflect"
)
func main() {
var x = 42
// %T 사용
fmt.Printf("타입: %T\n", x) // int
// reflect 패키지 사용
fmt.Println(reflect.TypeOf(x)) // int
}
베스트 프랙티스
- 기본 타입 사용: 특별한 이유가 없다면
int,float64사용 - 명시적 변환: 타입 변환은 항상 명시적으로
- rune vs byte: 유니코드 문자는
rune, ASCII/바이너리는byte - 문자열 불변성: 문자열은 변경 불가, 새로운 문자열 생성 필요
- 제로값 활용: 제로값이 유효한 초기값인지 확인
- 오버플로우 주의: 정수 연산 시 범위 초과 주의