Menu

Golang const: 타입 있는 상수와 타입 없는 상수

Go에서 const가 동작하는 방식: 상수 선언, 타입 있는 상수와 타입 없는 상수의 차이, 임의 정밀도의 상수 표현식, 그리고 Go에 상수 슬라이스, 맵, 구조체가 없는 이유를 다룹니다.

이 페이지에는 실행 가능한 에디터가 있습니다 - 편집하고 실행하면 결과를 바로 볼 수 있습니다.

상수는 컴파일 타임에 고정되는 값입니다. const로 선언하면 Go는 값을 바꾸려는 모든 시도를 거부합니다.

상수는 패키지 수준이나 함수 안에서 선언할 수 있고, 한 줄에 하나씩 쓰거나 const ( ... ) 블록으로 묶을 수 있습니다. MBGB는 다른 상수로부터 계산되며, 컴파일러가 빌드할 때 이 산술을 한 번 수행합니다.

MaxRetries = 5처럼 상수에 대입하면 컴파일 오류입니다:

./main.go:8:2: cannot assign to MaxRetries (neither addressable nor a map index expression)

상수가 될 수 있는 것

값의 종류는 세 가지뿐입니다. 불리언, 숫자(정수, 실수, 복소수, 룬), 문자열입니다. 값은 컴파일러가 계산할 수 있어야 합니다. 즉 리터럴, 다른 상수, 그것들의 산술, 그리고 상수 문자열의 len 같은 몇몇 내장 함수입니다.

코드를 실행하거나 메모리를 할당해야 하는 것은 모두 안 됩니다:

const Colors = []string{"red", "green"} // slice
const Started = time.Now()              // function call
./main.go:9:16: []string{…} (value of type []string) is not constant
./main.go:10:13: time.Now() (value of struct type time.Time) is not constant

Go에 상수 슬라이스나 맵이 없는 이유

슬라이스, 맵, 그리고 그것을 담은 구조체는 런타임에 존재하는 메모리에 대한 참조이므로, 컴파일러가 고정된 값으로 프로그램에 새겨 넣을 수 없습니다. 게다가 Go에는 변수용 readonlyfinal 수식어가 없습니다. 흔히 쓰는 우회책은 다음과 같습니다:

복사는 호출할 때마다 할당이 한 번 일어나지만, 설정 정도 크기의 데이터라면 괜찮습니다. 키로 조회하는 경우라면 함수 안의 switch도 상수처럼 쓸 수 있는 또 다른 방법입니다: func statusText(code int) string { switch code { ... } }.

타입 없는 상수

타입 없이 선언한 상수는 타입이 없습니다(untyped). 종류(정수, 실수, 룬, 문자열, bool)는 있지만 사용되기 전까지 특정 Go 타입이 없고, 문맥에 맞춰 적응합니다:

Ratio는 변환 없이 int, float64, uint8, float32로 동작합니다. time.Sleep(2 * time.Second)가 컴파일되는 이유도 이것입니다. 2는 타입이 없어서 time.Duration이 됩니다.

x := Ratio처럼 문맥이 타입을 요구하지 않으면 상수는 기본 타입을 갖습니다:

타입 없는 상수의 종류기본 타입
정수 (42)int
부동소수점 (4.2)float64
룬 ('a')rune (int32)
복소수 (2i)complex128
문자열string
불리언bool

정확한 산술

타입 없는 숫자 상수는 정확합니다. 컴파일러는 정수를 최소 256비트로 표현하므로 중간 값이 어떤 Go 타입보다도 훨씬 클 수 있습니다:

Huge 자체는 정수로 출력할 수 없습니다. fmt.Println에 넘기면 int로 변환되는데 컴파일러가 이를 잡아내기 때문입니다(실수는 그 크기를 담을 수 있으므로 float64(Huge)는 동작합니다):

cannot use Huge (untyped int constant 1267650600228229401496703205376) as int value in argument to fmt.Println (overflows)

같은 검사가 var b byte = 300 같은 작은 실수도 컴파일 타임에 막아 줍니다:

cannot use 300 (untyped int constant) as byte value in variable declaration (overflows)

타입 있는 상수

상수에 타입을 주면 어디서나 그 타입의 값처럼 동작하며, 타입을 섞는 것을 거부하는 Go의 규칙도 그대로 적용됩니다:

const Limit int = 10

var f float64 = 2
fmt.Println(Limit * f)
invalid operation: Limit * f (mismatched types int and float64)

const Limit = 10(타입 없음)이라면 Limit * f는 컴파일되고 20이 됩니다. 그러니 타입이 의미를 가질 때가 아니면 상수에 타입을 붙이지 마세요. 자신만의 이름 있는 타입으로 상수를 선언할 때는 타입이 의미를 가지며, Go는 이렇게 열거형을 만듭니다:

type Weekday int

const (
	Sunday Weekday = iota
	Monday
	Tuesday
)

여기서는 타입이 핵심입니다. Weekday를 받는 함수는 무엇을 원하는지 스스로 설명합니다. iota와 그 위에 만든 열거형 패턴은 열거형과 iota 페이지에서 다룹니다.

const와 var

constvar
변경 가능불가가능
값이 정해지는 시점컴파일 타임런타임
허용 타입bool, 숫자, 문자열모든 타입
런타임 메모리 사용없음있음
주소 얻기 (&x)불가가능
사용하지 않으면 오류아님지역 변수는 오류

정말로 고정된 값에는 const를 쓰세요. 한도, 크기, 이름, 프로토콜 코드, 형식 문자열 같은 것들입니다. 컴파일 타임 오버플로 검사와 정확한 산술이 공짜로 따라옵니다.

주의할 점

타입 있는 상수는 적응하지 않습니다. const Timeout int = 5는 변환 없이 time.Duration이 필요한 자리에 넘길 수 없습니다. 타입 없는 const Timeout = 5는 곱할 수 있습니다: Timeout * time.Second.

정수 상수끼리는 정수 나눗셈이 됩니다. const Half = 1 / 2는 두 피연산자가 모두 타입 없는 정수이므로 0입니다. 0.5를 얻으려면 1.0 / 2라고 쓰세요.

주소가 없습니다. &MaxRetries는 컴파일되지 않습니다. API가 *int를 원한다면 먼저 상수를 변수에 복사하세요.

자주 묻는 질문

Go에서 상수는 어떻게 선언하나요?

const를 씁니다: const MaxRetries = 3, 또는 타입을 명시해서 const Timeout time.Duration = 5 * time.Second. 여러 개는 블록으로 묶습니다: const ( A = 1; B = 2 ). 값은 컴파일 타임에 계산할 수 있어야 합니다.

Go에서 const 슬라이스, 맵, 배열을 만들 수 있나요?

없습니다. 상수는 불리언, 숫자(룬 포함), 문자열만 될 수 있습니다. const Colors = []string{"red"}is not constant로 실패합니다. 패키지 수준 var를 쓰고, 호출하는 쪽이 수정하면 안 된다면 새 복사본을 반환하는 함수를 공개하세요.

Go에서 타입 없는 상수란 무엇인가요?

const Pi = 3.14159처럼 타입 없이 선언된 상수입니다. 사용되기 전까지 고정된 타입이 없으므로 같은 상수가 float32, float64, 값이 허용한다면 int로도 동작합니다. 타입 없는 상수는 정확하기도 합니다. 컴파일러가 최소 256비트의 정밀도를 유지하므로, 결과가 범위 안에 들어가는 표현식에서만 쓴다면 const Big = 1 << 100도 문법상 맞습니다.

Go에서 const와 var의 차이는 무엇인가요?

const는 컴파일 타임에 고정되어 절대 바뀌지 않습니다. 런타임에 메모리를 차지하지 않고 bool, 숫자, 문자열만 담을 수 있습니다. var는 다시 대입할 수 있는 메모리 위치이며, time.Now()처럼 런타임에 계산된 값을 포함해 어떤 타입이든 담을 수 있습니다.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기