[Go] 기본 자료형
개요
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 - 문자열 불변성: 문자열은 변경 불가, 새로운 문자열 생성 필요
- 제로값 활용: 제로값이 유효한 초기값인지 확인
- 오버플로우 주의: 정수 연산 시 범위 초과 주의