[Go] constant
Updated:
개요
상수(constant)는 컴파일 타임에 값이 결정되는 불변 식별자입니다.
주요 특징:
const키워드로 선언- 선언과 동시에 초기화 필수
:=단축 선언 불가- 컴파일 타임 상수 표현식만 허용
- 타입 있는 상수와 타입 없는 상수 지원
iota를 통한 자동 증가 열거형
기본 상수 선언
1. 단일 상수
package main
import "fmt"
func main() {
// 타입 명시
const pi float64 = 3.14159
const maxRetries int = 5
// 타입 추론
const name = "Go" // string으로 추론
const version = 1.22 // float64로 추론
const enabled = true // bool로 추론
fmt.Println(pi, maxRetries)
fmt.Println(name, version, enabled)
// ❌ 재할당 불가
// pi = 3.14 // 컴파일 에러: cannot assign to pi
// ❌ := 사용 불가
// const value := 10 // 컴파일 에러
}
2. 상수 그룹
const (
StatusOK = 200
StatusCreated = 201
StatusAccepted = 202
)
// 여러 줄 선언
const (
host = "localhost"
port = 8080
timeout = 30
)
// 혼합 타입
const (
appName = "MyApp"
appVersion = 1.0
debug = true
)
3. 타입 있는 상수 vs 타입 없는 상수
func typedVsUntyped() {
// 타입 있는 상수
const typedInt int = 100
const typedFloat float64 = 3.14
// 타입 없는 상수 (더 유연함)
const untypedInt = 100
const untypedFloat = 3.14
// 타입 있는 상수는 정확한 타입 필요
var i int = typedInt
// var f float64 = typedInt // 컴파일 에러
// 타입 없는 상수는 자동 변환
var j int = untypedInt
var k float64 = untypedInt // OK: 자동 변환
var l float32 = untypedFloat // OK: 자동 변환
fmt.Println(i, j, k, l)
}
iota를 사용한 열거형
1. 기본 iota
const (
Sunday = iota // 0
Monday // 1
Tuesday // 2
Wednesday // 3
Thursday // 4
Friday // 5
Saturday // 6
)
func main() {
fmt.Println(Sunday, Monday, Saturday)
// 출력: 0 1 6
}
2. iota 시작 값 조정
const (
January = iota + 1 // 1
February // 2
March // 3
April // 4
May // 5
June // 6
July // 7
August // 8
September // 9
October // 10
November // 11
December // 12
)
3. iota 건너뛰기
const (
_ = iota // 0 무시
KB = 1 << (10 * iota) // 1 << 10 = 1024
MB // 1 << 20 = 1048576
GB // 1 << 30 = 1073741824
TB // 1 << 40 = 1099511627776
)
const (
Active = iota + 1 // 1
_ // 2 건너뛰기
Inactive // 3
Deleted // 4
)
4. 비트 플래그
const (
FlagNone = 0
FlagRead = 1 << iota // 1 << 1 = 2
FlagWrite // 1 << 2 = 4
FlagExecute // 1 << 3 = 8
)
func main() {
// 비트 연산으로 플래그 조합
permissions := FlagRead | FlagWrite
fmt.Printf("Permissions: %b\n", permissions) // 110 (binary)
// 플래그 확인
hasRead := permissions&FlagRead != 0
hasExecute := permissions&FlagExecute != 0
fmt.Println("Read:", hasRead) // true
fmt.Println("Execute:", hasExecute) // false
}
5. 복잡한 iota 표현식
const (
B = 1 << (10 * iota) // 1
KiB // 1024
MiB // 1048576
GiB // 1073741824
TiB // 1099511627776
)
const (
_ = iota // 0
First = iota * 10 + 1 // 11
Second // 21
Third // 31
)
const (
Hundred = iota * 100 // 0
TwoH // 100
ThreeH // 200
FourH // 300
)
6. 여러 상수에 같은 iota 적용
const (
a, b = iota, iota * 10 // 0, 0
c, d // 1, 10
e, f // 2, 20
)
func main() {
fmt.Println(a, b, c, d, e, f)
// 출력: 0 0 1 10 2 20
}
상수 표현식
const (
// 산술 연산
sum = 10 + 20 // 30
diff = 100 - 50 // 50
prod = 5 * 6 // 30
quot = 100 / 4 // 25
// 비트 연산
bitAnd = 0b1100 & 0b1010 // 0b1000 = 8
bitOr = 0b1100 | 0b1010 // 0b1110 = 14
bitXor = 0b1100 ^ 0b1010 // 0b0110 = 6
// 시프트 연산
leftShift = 1 << 3 // 8
rightShift = 16 >> 2 // 4
// 문자열 연산
greeting = "Hello, " + "World!"
// 불리언 연산
result = true && false // false
// 복합 표현식
complex = (10 + 20) * 3 / 2 // 45
)
func main() {
fmt.Println(sum, bitAnd, greeting, complex)
}
타입 있는 상수의 활용
type Status int
const (
StatusPending Status = iota
StatusRunning
StatusCompleted
StatusFailed
)
func (s Status) String() string {
return [...]string{"Pending", "Running", "Completed", "Failed"}[s]
}
type FileMode uint32
const (
ModeDir FileMode = 1 << iota // 1
ModeAppend // 2
ModeExclusive // 4
ModeTemporary // 8
ModeSymlink // 16
)
func main() {
status := StatusRunning
fmt.Println(status) // Running
mode := ModeDir | ModeTemporary
fmt.Printf("Mode: %b\n", mode) // 1001
}
숫자 상수의 정밀도
func constantPrecision() {
// 상수는 임의 정밀도 지원
const huge = 1e1000
const tiny = 1e-1000
// 변수에 할당 시 타입 제한 적용
// var x = huge // 오버플로우 에러
// 표현식에서는 높은 정밀도 유지
const result = huge / huge // 1
fmt.Println(result)
// 부동소수점 상수
const pi = 3.14159265358979323846264338327950288419716939937510
fmt.Printf("%.50f\n", pi)
// 정수 상수
const maxInt64 = 9223372036854775807
const beyondInt64 = maxInt64 * 2
// 타입 없는 상수는 컨텍스트에 맞게 변환
var i int = 100.0 // OK
var f float64 = 100 // OK
}
실전 활용 패턴
1. HTTP 상태 코드
const (
// 2xx Success
StatusOK = 200
StatusCreated = 201
StatusAccepted = 202
StatusNoContent = 204
// 3xx Redirection
StatusMovedPermanently = 301
StatusFound = 302
StatusNotModified = 304
// 4xx Client Error
StatusBadRequest = 400
StatusUnauthorized = 401
StatusForbidden = 403
StatusNotFound = 404
StatusMethodNotAllowed = 405
// 5xx Server Error
StatusInternalServerError = 500
StatusNotImplemented = 501
StatusBadGateway = 502
StatusServiceUnavailable = 503
)
func handleResponse(statusCode int) {
switch statusCode {
case StatusOK:
fmt.Println("Success")
case StatusNotFound:
fmt.Println("Not Found")
case StatusInternalServerError:
fmt.Println("Server Error")
default:
fmt.Println("Unknown Status")
}
}
2. 애플리케이션 설정
const (
AppName = "MyApplication"
AppVersion = "1.0.0"
DefaultPort = 8080
DefaultTimeout = 30
MaxRetries = 3
// 환경별 설정
DevEnvironment = "development"
ProdEnvironment = "production"
)
type Config struct {
Name string
Version string
Port int
}
func NewConfig() *Config {
return &Config{
Name: AppName,
Version: AppVersion,
Port: DefaultPort,
}
}
3. 에러 메시지
const (
ErrInvalidInput = "invalid input provided"
ErrNotFound = "resource not found"
ErrUnauthorized = "unauthorized access"
ErrInternalServer = "internal server error"
ErrTimeout = "operation timeout"
)
func validateUser(id int) error {
if id <= 0 {
return errors.New(ErrInvalidInput)
}
// 검증 로직...
return nil
}
4. 파일 권한 (Unix 스타일)
const (
// 소유자 권한
OwnerRead = 0400
OwnerWrite = 0200
OwnerExecute = 0100
// 그룹 권한
GroupRead = 0040
GroupWrite = 0020
GroupExecute = 0010
// 기타 사용자 권한
OtherRead = 0004
OtherWrite = 0002
OtherExecute = 0001
// 일반적인 조합
PermReadWrite = OwnerRead | OwnerWrite | GroupRead | OtherRead // 0644
PermExecutable = PermReadWrite | OwnerExecute // 0744
PermFullAccess = 0777
)
func createFile(path string, perm int) {
fmt.Printf("Creating file %s with permission %o\n", path, perm)
}
func main() {
createFile("data.txt", PermReadWrite)
createFile("script.sh", PermExecutable)
}
5. 로그 레벨
type LogLevel int
const (
LogLevelDebug LogLevel = iota
LogLevelInfo
LogLevelWarn
LogLevelError
LogLevelFatal
)
func (l LogLevel) String() string {
return [...]string{"DEBUG", "INFO", "WARN", "ERROR", "FATAL"}[l]
}
type Logger struct {
level LogLevel
}
func (l *Logger) log(level LogLevel, message string) {
if level >= l.level {
fmt.Printf("[%s] %s\n", level, message)
}
}
func (l *Logger) Debug(msg string) { l.log(LogLevelDebug, msg) }
func (l *Logger) Info(msg string) { l.log(LogLevelInfo, msg) }
func (l *Logger) Warn(msg string) { l.log(LogLevelWarn, msg) }
func (l *Logger) Error(msg string) { l.log(LogLevelError, msg) }
func main() {
logger := &Logger{level: LogLevelInfo}
logger.Debug("This won't print")
logger.Info("Application started")
logger.Warn("High memory usage")
logger.Error("Failed to connect")
}
6. 상태 머신
type State int
const (
StateIdle State = iota
StateInitializing
StateRunning
StatePaused
StateStopped
StateError
)
type Machine struct {
state State
}
func (m *Machine) Start() error {
if m.state != StateIdle {
return fmt.Errorf("cannot start from state %d", m.state)
}
m.state = StateRunning
return nil
}
func (m *Machine) Pause() error {
if m.state != StateRunning {
return fmt.Errorf("cannot pause from state %d", m.state)
}
m.state = StatePaused
return nil
}
func (m *Machine) Stop() {
m.state = StateStopped
}
7. 비트 마스크 패턴
type Permission uint8
const (
PermNone Permission = 0
PermRead Permission = 1 << iota
PermWrite
PermDelete
PermShare
PermAdmin
)
func (p Permission) Has(perm Permission) bool {
return p&perm != 0
}
func (p Permission) Add(perm Permission) Permission {
return p | perm
}
func (p Permission) Remove(perm Permission) Permission {
return p &^ perm
}
func main() {
// 권한 조합
userPerm := PermRead | PermWrite
adminPerm := PermRead | PermWrite | PermDelete | PermAdmin
fmt.Println("User has Read:", userPerm.Has(PermRead)) // true
fmt.Println("User has Admin:", userPerm.Has(PermAdmin)) // false
// 권한 추가
userPerm = userPerm.Add(PermShare)
fmt.Println("User has Share:", userPerm.Has(PermShare)) // true
// 권한 제거
userPerm = userPerm.Remove(PermWrite)
fmt.Println("User has Write:", userPerm.Has(PermWrite)) // false
}
변수 vs 상수 비교
func varVsConst() {
// 변수: 런타임 값, 변경 가능
var variable = time.Now().Unix()
variable = 123
// 상수: 컴파일 타임 값, 변경 불가
const constant = 100
// constant = 200 // 컴파일 에러
// ❌ 런타임 함수 호출 결과는 상수 불가
// const now = time.Now() // 컴파일 에러
// ❌ 변수 값으로 상수 초기화 불가
// const x = variable // 컴파일 에러
// ✅ 상수 표현식은 가능
const x = 10 + 20
const y = x * 2
fmt.Println(variable, constant, x, y)
}
일반적인 실수와 주의사항
1. 상수 재할당 시도
func reassignmentError() {
const pi = 3.14159
// ❌ 상수는 재할당 불가
// pi = 3.14 // 컴파일 에러: cannot assign to pi
// ✅ 새로운 상수 선언은 가능 (섀도잉)
{
const pi = 3.14 // 다른 스코프의 새 상수
fmt.Println(pi) // 3.14
}
fmt.Println(pi) // 3.14159
}
2. 런타임 값으로 초기화
func runtimeValueError() {
// ❌ 컴파일 에러: 런타임 함수 호출 불가
// const now = time.Now()
// const random = rand.Intn(100)
// const length = len([]int{1, 2, 3})
// ✅ 변수 사용
var now = time.Now()
var random = rand.Intn(100)
// ✅ 리터럴 길이는 상수 가능
const arrayLen = len([3]int{1, 2, 3})
const strLen = len("hello")
}
3. 타입 있는 상수의 제약
func typedConstError() {
const typedInt int = 100
const typedFloat float64 = 3.14
// ❌ 타입이 다르면 에러
// var f float64 = typedInt // 컴파일 에러
// ✅ 명시적 변환 필요
var f float64 = float64(typedInt)
// ✅ 타입 없는 상수는 자동 변환
const untypedInt = 100
var g float64 = untypedInt // OK
fmt.Println(f, g)
}
4. iota 재설정
const (
A = iota // 0
B // 1
C // 2
)
// 새 const 블록에서 iota는 0부터 시작
const (
D = iota // 0 (재설정됨)
E // 1
F // 2
)
func main() {
fmt.Println(A, B, C) // 0 1 2
fmt.Println(D, E, F) // 0 1 2
}
5. iota 표현식 재사용
const (
X = iota * 2 // 0
Y // 2 (이전 표현식 재사용)
Z // 4
)
const (
P = iota + 10 // 10
Q = iota * 2 // 2 (새 표현식)
R // 4 (Q의 표현식 재사용)
)
func main() {
fmt.Println(X, Y, Z) // 0 2 4
fmt.Println(P, Q, R) // 10 2 4
}
6. 상수 오버플로우
func overflowExample() {
// 상수 표현식은 임의 정밀도 지원
const huge = 1 << 100
// ❌ 변수 할당 시 타입 제한
// var x int = huge // 오버플로우 에러
// ✅ 적절한 타입 사용 또는 표현식에서만 사용
const y = huge / huge // 1
fmt.Println(y)
}
모범 사례
1. 명명 규칙
// ✅ 상수는 PascalCase 또는 UPPER_SNAKE_CASE
const MaxConnections = 100
const DEFAULT_TIMEOUT = 30
// ✅ 열거형은 타입 접두사
type Status int
const (
StatusPending Status = iota
StatusActive
StatusInactive
)
// ✅ 플래그는 명확한 이름
const (
FlagReadOnly = 1 << iota
FlagHidden
FlagSystem
)
2. 상수 그룹화
// ✅ 관련된 상수는 함께 그룹화
const (
// Database settings
DBHost = "localhost"
DBPort = 5432
DBName = "myapp"
DBUser = "admin"
// Cache settings
CacheHost = "localhost"
CachePort = 6379
CacheTTL = 3600
)
3. 문서화
// HTTPMethod represents HTTP request methods
type HTTPMethod string
const (
// GET retrieves a resource
MethodGet HTTPMethod = "GET"
// POST creates a resource
MethodPost HTTPMethod = "POST"
// PUT updates a resource
MethodPut HTTPMethod = "PUT"
// DELETE removes a resource
MethodDelete HTTPMethod = "DELETE"
)
4. 타입 안전성
// ✅ 타입 정의로 안전성 확보
type Color int
const (
Red Color = iota
Green
Blue
)
func SetColor(c Color) {
// Color 타입만 받음
}
func main() {
SetColor(Red) // OK
// SetColor(1) // 컴파일 에러 (strict mode에서)
}
정리
- 상수는
const키워드로 선언하며 컴파일 타임에 값 결정 - 선언 시 초기화 필수, 재할당 불가
- 타입 있는 상수와 타입 없는 상수 (더 유연함)
iota로 자동 증가 열거형 생성- 비트 플래그, 상태 코드, 권한 등에 활용
- 상수 표현식은 임의 정밀도 지원
- 런타임 함수 호출 결과는 상수 불가
- 각
const블록마다iota는 0부터 재시작 - 명확한 명명과 그룹화로 가독성 향상
- 타입 정의와 결합하여 타입 안전성 확보