Table of contents
var와 :=
Go에서 변수를 선언하는 방법은 두 가지다. var 키워드를 쓰거나, 짧은 선언 연산자 :=를 쓰거나.
package main
import "fmt"
func main() {
var name string = "Go"
age := 15 // 2009년에 태어났으니까
fmt.Println(name, age)
}
var는 어디서든 쓸 수 있다. 함수 바깥에서도, 안에서도. 반면 :=는 함수 안에서만 쓸 수 있는 단축 문법이다. 타입을 적지 않아도 오른쪽 값에서 알아서 추론해준다.
그렇다면 언제 뭘 쓸까? 함수 안에서 바로 값을 할당할 때는 :=가 훨씬 깔끔하다. var는 패키지 레벨 변수나, 초기값 없이 선언만 해둬야 할 때 쓴다.
package main
import "fmt"
var globalMsg = "패키지 레벨 변수" // := 사용 불가
func main() {
var count int // 초기값 없이 선언 — 0으로 초기화됨
name := "gopher" // 함수 안에서는 이게 간결
fmt.Println(globalMsg, count, name)
}
여러 변수를 한꺼번에 선언하는 것도 가능하다.
var (
host = "localhost"
port = 8080
debug = false
)
이 블록 선언은 패키지 레벨에서 관련 변수를 묶어둘 때 유용하다. 설정값이나 상수 그룹을 정리하기 좋은 패턴이다.
상수: const
변하지 않는 값에는 const를 쓴다. 컴파일 타임에 결정되는 값이어야 하므로, 함수 호출 결과 같은 건 넣을 수 없다.
package main
import "fmt"
const pi = 3.14159
const greeting = "Hello, Go!"
const (
statusOK = 200
statusNotFound = 404
)
func main() {
fmt.Println(pi, greeting)
fmt.Println(statusOK, statusNotFound)
}
Go의 상수에는 재밌는 특징이 하나 있다. 타입이 없는 상수(untyped constant)라는 개념이다.
package main
import "fmt"
const x = 100 // 타입이 없다 — int도 아니고 float64도 아닌, 그냥 '숫자'
func main() {
var i int = x // int로 쓰임
var f float64 = x // float64로도 쓰임
fmt.Println(i, f) // 100 100
}
타입 없는 상수는 사용되는 맥락에 맞춰 타입이 결정된다. 덕분에 같은 상수 100을 int에도, float64에도 넣을 수 있다. 이건 Go만의 독특한 설계로, 상수를 유연하게 쓸 수 있게 해준다.
iota도 알아두면 좋다. 열거형 비슷한 걸 만들 때 쓰는 Go만의 도구다.
package main
import "fmt"
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
func main() {
fmt.Println(Wednesday) // 3
}
iota는 const 블록 안에서 0부터 시작해 한 줄마다 1씩 증가한다. Java의 enum이나 C의 #define 연속 선언과 비슷한 역할이지만, 훨씬 간결하다.
기본 타입
Go의 타입 시스템은 간결하다. 필요한 것만 있고, 군더더기가 없다.
| 분류 | 타입 | 설명 |
|---|---|---|
| 정수 | int, int8, int16, int32, int64 | 플랫폼에 따라 int는 32비트 또는 64비트 |
| 부호 없는 정수 | uint, uint8, uint16, uint32, uint64 | 양수만 |
| 실수 | float32, float64 | 소수점이 필요하면 float64가 기본 |
| 복소수 | complex64, complex128 | 과학 계산용 |
| 문자열 | string | UTF-8, 불변 |
| 불리언 | bool | true / false |
| 바이트 | byte | uint8의 별칭 |
| 룬 | rune | int32의 별칭, 유니코드 코드 포인트 |
대부분의 경우 int, float64, string, bool 네 가지면 충분하다. 메모리를 아껴야 하는 임베디드 환경이 아니라면 비트 수를 일일이 지정할 일은 드물다.
package main
import "fmt"
func main() {
var age int = 25
var height float64 = 175.5
var name string = "gopher"
var active bool = true
fmt.Println(age, height, name, active)
}
byte와 rune은 문자를 다룰 때 구분이 중요해진다. byte는 ASCII 한 글자, rune은 유니코드 한 글자를 나타낸다. 한글처럼 멀티바이트 문자를 다룰 때는 rune을 써야 한다.
package main
import "fmt"
func main() {
s := "Go는 재밌다"
fmt.Println(len(s)) // 15 — 바이트 수
fmt.Println(len([]rune(s))) // 7 — 문자 수
}
len()에 문자열을 넣으면 바이트 수를 반환한다. 한글은 한 글자가 3바이트이기 때문에 숫자가 다르게 나온다. 실제 문자 수를 알고 싶으면 []rune으로 변환한 뒤 길이를 재면 된다.
타입 변환
Go는 암시적 타입 변환을 허용하지 않는다. 이건 Go의 철학이 담긴 부분이다. C나 Java에서는 int를 float64에 넣으면 알아서 변환해주지만, Go는 명시적으로 변환을 요구한다.
package main
import "fmt"
func main() {
i := 42
f := float64(i) // int → float64
u := uint(f) // float64 → uint
fmt.Println(i, f, u) // 42 42 42
}
이 방식이 처음엔 귀찮게 느껴질 수 있다. 하지만 암시적 변환이 만들어내는 미묘한 버그를 생각하면 오히려 안전한 선택이다. 특히 정수와 실수를 섞어 쓸 때, 의도치 않은 정밀도 손실을 막아준다.
문자열과 숫자 사이의 변환은 strconv 패키지를 쓴다.
package main
import (
"fmt"
"strconv"
)
func main() {
// 숫자 → 문자열
s := strconv.Itoa(42)
fmt.Println(s) // "42"
// 문자열 → 숫자
n, err := strconv.Atoi("123")
if err != nil {
fmt.Println("변환 실패:", err)
return
}
fmt.Println(n) // 123
}
Atoi가 값과 에러를 함께 반환하는 게 보일 것이다. Go에서는 실패할 수 있는 연산이 에러를 두 번째 반환값으로 돌려주는 패턴이 일상적이다. 이건 4편 에러 처리에서 자세히 다룬다.
Zero Value
Go에서 변수를 선언만 하고 초기값을 주지 않으면, 타입에 맞는 제로 값(zero value)이 자동으로 들어간다.
package main
import "fmt"
func main() {
var i int // 0
var f float64 // 0.0
var b bool // false
var s string // "" (빈 문자열)
fmt.Println(i, f, b, s)
}
| 타입 | Zero Value |
|---|---|
| 정수 / 실수 | 0 |
| bool | false |
| string | "" |
| 포인터, 슬라이스, 맵, 채널, 함수, 인터페이스 | nil |
이건 단순한 편의 기능이 아니라 Go의 설계 원칙이다. “초기화되지 않은 변수”라는 개념 자체를 없앤 것이다. C에서 초기화 안 된 변수가 쓰레기 값을 가지고 있어서 디버깅에 고생한 경험이 있다면, 이 결정이 얼마나 고마운지 체감할 수 있다.
실무에서도 이 특성을 적극 활용한다. 예를 들어 int 카운터는 선언만 해두면 0부터 시작하고, bool 플래그는 false가 기본이다. 별도의 초기화 코드가 필요 없으니 코드가 깔끔해진다.
다음 편에서는 Go의 조건문과 반복문을 다룬다. if에 초기화문을 넣는 독특한 문법과, Go에 while이 없는 이유를 살펴보자.
-> 2편: 조건문과 반복문
Loading comments...