Go는 정적 타입 언어입니다. 변수의 타입은 컴파일 과정에서 결정되며, 한 번 결정된 변수 타입은 실행 중에 다른 타입으로 바뀌지 않습니다.
:=로 변수를 선언하더라도 타입이 없는 것이 아닙니다. 오른쪽 값을 기준으로 컴파일러가 타입을 추론합니다. 또한 :=는 함수 내부에서만 사용할 수 있고, 함수 밖의 package scope에서는 var 또는 const를 사용해야 합니다.
이 글에서는 다음 내용을 예제로 확인합니다.
- literal, variable, constant, zero value의 차이
var와:=를 이용한 변수 선언- package scope와 function scope
- 여러 변수 선언과
:=재선언 규칙 - 서로 다른 수치 타입의 명시적 변환
- 변수 shadowing
- 의미가 분명한 변수명 작성 방법
예제 준비와 실행
디렉터리 구조는 다음과 같이 구성합니다.
sources/
└── hello-values/
└── main.gosources 디렉터리에서 실행합니다.
cd sources
go run ./hello-valuesgo run은 지정한 main 패키지를 컴파일한 뒤 실행합니다. 예제의 실행 시작점은 package main에 선언된 main 함수입니다.
전체 예제 소스
01-hello-values/main.go 파일을 다음과 같이 작성합니다.
package main
import "fmt"
// package scope 변수
// 함수 밖에서는 :=를 사용할 수 없으므로 var로 선언합니다.
var applicationName = "shopping-app"
// 값을 지정하지 않은 int 변수에는 zero value인 0이 들어갑니다.
var totalProcessedOrderCount int
// 상수는 실행 중에 값을 변경할 수 없습니다.
const maxLoginRetryCount = 5
func main() {
fmt.Println("=== literal ===")
// 코드에 직접 작성한 값이 literal입니다.
fmt.Println("PRODUCT-1001") // 문자열 literal
fmt.Println(3) // 정수 literal
fmt.Println(4.5) // 부동소수점 literal
fmt.Println(true) // boolean literal
fmt.Println("\n=== zero value ===")
// 타입만 선언하고 값을 지정하지 않았습니다.
var productCount int
var totalAmount int64
var averageRating float64
var paymentCompleted bool
var customerName string
var managerID *string
fmt.Printf("productCount: %d\n", productCount)
fmt.Printf("totalAmount: %d\n", totalAmount)
fmt.Printf("averageRating: %.1f\n", averageRating)
fmt.Printf("paymentCompleted: %t\n", paymentCompleted)
fmt.Printf("customerName: %q\n", customerName)
fmt.Printf("managerID: %v\n", managerID)
fmt.Println("\n=== variable declarations ===")
// 타입과 초기값을 모두 명시합니다.
var productID string = "PRODUCT-1001"
// 타입을 생략하면 오른쪽 값으로 타입을 추론합니다.
var productName = "무선 키보드"
// :=는 함수 내부에서 사용할 수 있습니다.
productPrice := 35000
quantity := 2
isAvailable := true
fmt.Printf("productID: %s, type: %T\n", productID, productID)
fmt.Printf("productName: %s, type: %T\n", productName, productName)
fmt.Printf("productPrice: %d, type: %T\n", productPrice, productPrice)
fmt.Printf("quantity: %d, type: %T\n", quantity, quantity)
fmt.Printf("isAvailable: %t, type: %T\n", isAvailable, isAvailable)
fmt.Println("\n=== multiple declaration ===")
// 여러 변수를 한 번에 선언할 수 있습니다.
customerID, customerEmail := "CUSTOMER-1001", "customer@example.com"
fmt.Printf("customerID: %s\n", customerID)
fmt.Printf("customerEmail: %s\n", customerEmail)
fmt.Println("\n=== short declaration and reassignment ===")
orderStatus := "CREATED"
// 기존 변수에 새 값을 넣을 때는 =를 사용합니다.
orderStatus = "PAID"
fmt.Printf("orderStatus: %s\n", orderStatus)
// :=의 왼쪽에 같은 scope의 새 변수가 하나 이상 있으면
// 기존 변수와 새 변수를 함께 선언할 수 있습니다.
orderStatus, paidAt := "COMPLETED", "2026-07-10T15:30:00+09:00"
fmt.Printf("orderStatus: %s\n", orderStatus)
fmt.Printf("paidAt: %s\n", paidAt)
fmt.Println("\n=== constant ===")
fmt.Printf("maxLoginRetryCount: %d\n", maxLoginRetryCount)
fmt.Println("\n=== package scope ===")
fmt.Printf("applicationName: %s\n", applicationName)
fmt.Printf("totalProcessedOrderCount: %d\n", totalProcessedOrderCount)
fmt.Println("\n=== explicit numeric conversion ===")
var productAmount int = 35000
var shippingFee int64 = 3000
// int와 int64는 서로 다른 타입이므로 다음 코드는 컴파일되지 않습니다.
//
// finalAmount := productAmount + shippingFee
finalAmount := int64(productAmount) + shippingFee
fmt.Printf("productAmount: %d, type: %T\n", productAmount, productAmount)
fmt.Printf("shippingFee: %d, type: %T\n", shippingFee, shippingFee)
fmt.Printf("finalAmount: %d, type: %T\n", finalAmount, finalAmount)
fmt.Println("\n=== integer discount calculation ===")
var paymentAmount int64 = 50000
var discountPercent int64 = 10
discountAmount := paymentAmount * discountPercent / 100
finalPaymentAmount := paymentAmount - discountAmount
fmt.Printf("paymentAmount: %d\n", paymentAmount)
fmt.Printf("discountAmount: %d\n", discountAmount)
fmt.Printf("finalPaymentAmount: %d\n", finalPaymentAmount)
fmt.Println("\n=== variable shadowing ===")
currentPageTitle := "상품 목록"
if true {
// 바깥 변수와 이름은 같지만 새로운 블록 변수입니다.
currentPageTitle := "상품 상세"
fmt.Printf("inside block: %s\n", currentPageTitle)
}
fmt.Printf("outside block: %s\n", currentPageTitle)
}예상 결과는 다음과 비슷합니다.
=== literal ===
PRODUCT-1001
3
4.5
true
=== zero value ===
productCount: 0
totalAmount: 0
averageRating: 0.0
paymentCompleted: false
customerName: ""
managerID: <nil>
=== variable declarations ===
productID: PRODUCT-1001, type: string
productName: 무선 키보드, type: string
productPrice: 35000, type: int
quantity: 2, type: int
isAvailable: true, type: bool
=== multiple declaration ===
customerID: CUSTOMER-1001
customerEmail: customer@example.com
=== short declaration and reassignment ===
orderStatus: PAID
orderStatus: COMPLETED
paidAt: 2026-07-10T15:30:00+09:00
=== constant ===
maxLoginRetryCount: 5
=== package scope ===
applicationName: shopping-app
totalProcessedOrderCount: 0
=== explicit numeric conversion ===
productAmount: 35000, type: int
shippingFee: 3000, type: int64
finalAmount: 38000, type: int64
=== integer discount calculation ===
paymentAmount: 50000
discountAmount: 5000
finalPaymentAmount: 45000
=== variable shadowing ===
inside block: 상품 상세
outside block: 상품 목록리터럴, 변수, 상수와 Zero Value
리터럴
리터럴은 소스 코드에 직접 작성된 값입니다.
"PRODUCT-1001"
35000
4.5
true종류를 구분하면 다음과 같습니다.
| 코드 | 종류 |
|---|---|
"PRODUCT-1001" |
문자열 리터럴 |
35000 |
정수 리터럴 |
4.5 |
부동소수점 리터럴 |
true |
boolean 리터럴 |
다음 코드에서 productName은 변수이고 "무선 키보드"는 문자열 리터럴입니다.
productName := "무선 키보드"변수
변수는 값을 저장하기 위해 이름을 붙인 공간입니다.
var productPrice int = 35000| 구성 | 의미 |
|---|---|
var |
변수 선언 키워드 |
productPrice |
변수명 |
int |
변수 타입 |
35000 |
초기값 |
한 번 int로 선언된 변수에는 int 값을 대입해야 합니다.
var productPrice int = 35000
productPrice = 40000다음 코드는 문자열을 int 변수에 대입하므로 컴파일되지 않습니다.
productPrice = "무료"상수
상수는 const로 선언하며 실행 중에 값을 변경할 수 없습니다.
const maxLoginRetryCount = 5다음 코드는 상수에 다시 값을 대입하므로 컴파일되지 않습니다.
maxLoginRetryCount = 10상수는 다음과 같이 고정된 규칙을 표현할 때 사용할 수 있습니다.
const maxLoginRetryCount = 5
const defaultPageSize = 20
const orderStatusCompleted = "COMPLETED"함수 호출 결과는 상수로 선언할 수 없습니다.
// 컴파일 오류
const applicationName = os.Getenv("APPLICATION_NAME")함수를 실행해야 값이 결정되는 경우에는 변수를 사용합니다.
applicationName := os.Getenv("APPLICATION_NAME")Zero Value
Go에서는 초기값을 지정하지 않은 변수에 타입별 zero value가 들어갑니다.
var productCount int
var paymentCompleted bool
var customerName string주요 타입의 zero value는 다음과 같습니다.
| 타입 | zero value |
|---|---|
| 정수 타입 | 0 |
| 부동소수점 타입 | 0.0 |
bool |
false |
string |
"" |
| 포인터 | nil |
| slice | nil |
| map | nil |
| channel | nil |
| function | nil |
| interface | nil |
zero value가 실제 값과 구분되어야 하는 경우도 있습니다.
예를 들어 할인 금액이 0이라면 다음 두 의미가 섞일 수 있습니다.
- 할인이 적용되지 않음
- 아직 할인 금액을 계산하지 않음
별도의 상태 필드로 구분할 수 있습니다.
type DiscountResult struct {
Amount int64
Calculated bool
}또는 포인터로 값의 존재 여부를 표현할 수 있습니다.
type OrderRequest struct {
DiscountAmount *int64
}이 경우 nil은 값이 없음을 나타내고, 0을 가리키는 포인터는 계산 결과가 할인 없음임을 나타낼 수 있습니다.
포인터는
nil확인과 역참조가 필요합니다. 값의 부재를 반드시 구분해야 하는 경우에만 사용하는 것이 좋습니다.
변수 선언 방식과 Scope
Go에서 자주 사용하는 변수 선언 방식은 다음과 같습니다.
타입만 선언
var retryCount int초기값을 지정하지 않았으므로 retryCount는 0입니다.
타입과 초기값 선언
var paymentAmount int64 = 50000사용할 타입을 코드에 명확히 표시할 수 있습니다.
초기값으로 타입 추론
var productName = "무선 키보드"오른쪽 값이 문자열이므로 변수 타입은 string입니다.
짧은 변수 선언
productID := "PRODUCT-1001"
productPrice := 35000:=도 오른쪽 값을 기준으로 구체적인 타입을 결정합니다.
fmt.Printf("%T\n", productID) // string
fmt.Printf("%T\n", productPrice) // int:=는 함수 내부에서만 사용할 수 있습니다.
package main
var applicationName = "shopping-app"
func main() {
productID := "PRODUCT-1001"
}다음과 같이 package scope에서 :=를 사용할 수 없습니다.
package main
// 컴파일 오류
applicationName := "shopping-app"
func main() {
}재대입과 재선언
이미 선언된 변수의 값을 변경할 때는 =를 사용합니다.
orderStatus := "CREATED"
orderStatus = "PAID"같은 scope에서 새 변수 없이 :=를 다시 사용하면 컴파일되지 않습니다.
orderStatus := "CREATED"
// 컴파일 오류: 새 변수가 없음
orderStatus := "PAID"왼쪽에 새 변수가 하나 이상 포함되면 기존 변수와 함께 :=를 사용할 수 있습니다.
orderStatus := "CREATED"
orderStatus, paidAt := "PAID", "2026-07-10T15:30:00+09:00"여기서 orderStatus에는 새 값이 대입되고, paidAt은 새로 선언됩니다.
변수 Shadowing
안쪽 블록에서 같은 이름을 :=로 선언하면 바깥 변수와 다른 새 변수가 만들어집니다.
pageTitle := "상품 목록"
if showDetail {
pageTitle := "상품 상세"
fmt.Println(pageTitle)
}
fmt.Println(pageTitle)안쪽에서는 상품 상세가 출력되지만, 바깥 변수는 여전히 상품 목록입니다.
바깥 변수의 값을 변경하려면 =를 사용합니다.
pageTitle := "상품 목록"
if showDetail {
pageTitle = "상품 상세"
}
fmt.Println(pageTitle)수치 타입 변환과 변수명
서로 다른 수치 타입
Go에서는 int, int32, int64, float32, float64가 각각 서로 다른 타입입니다.
다음 코드는 int와 int64를 바로 더하므로 컴파일되지 않습니다.
var productAmount int = 35000
var shippingFee int64 = 3000
// 컴파일 오류
finalAmount := productAmount + shippingFee한쪽 값을 명시적으로 변환해야 합니다.
finalAmount := int64(productAmount) + shippingFee결과 타입은 int64입니다.
fmt.Printf("%T\n", finalAmount)int64int64를 int로 변환할 수도 있지만, 값이 int 범위를 초과할 가능성을 확인해야 합니다.
finalAmount := productAmount + int(shippingFee)같은 의미의 값은 가능한 한 하나의 타입으로 통일하는 편이 좋습니다.
var productAmount int64 = 35000
var shippingFee int64 = 3000
finalAmount := productAmount + shippingFee정수와 부동소수점
int와 float64도 바로 연산할 수 없습니다.
productPrice := 35000
discountRate := 0.1
// 컴파일 오류
discountAmount := productPrice * discountRate명시적으로 변환하면 연산할 수 있습니다.
discountAmount := float64(productPrice) * discountRate원화처럼 최소 단위가 정수로 표현되는 금액은 정수 연산으로 처리할 수 있습니다.
var paymentAmount int64 = 50000
var discountPercent int64 = 10
discountAmount := paymentAmount * discountPercent / 100
finalPaymentAmount := paymentAmount - discountAmount이 결과는 다음과 같습니다.
discountAmount: 5000
finalPaymentAmount: 45000소수점이 필요한 금액 계산은 반올림 방식과 정밀도 기준을 먼저 정해야 합니다.
의미가 분명한 변수명
다음 이름은 코드가 커지면 의미를 구분하기 어렵습니다.
id := "PRODUCT-1001"
value := 35000
count := 3
status := "PAID"
flag := true값이 무엇을 의미하는지 이름에 포함합니다.
productID := "PRODUCT-1001"
productPrice := 35000
productCount := 3
orderStatus := "PAID"
shouldSendEmail := true자주 사용하는 이름 예시는 다음과 같습니다.
| 구분 | 예 |
|---|---|
| 식별자 | productID, customerID, orderID |
| 금액 | productPrice, shippingFee, discountAmount |
| 개수 | productCount, retryCount, orderCount |
| 상태 | orderStatus, paymentStatus, accountStatus |
| boolean | isAvailable, hasAddress, canRetry, shouldSendEmail |
| 시간·단위 | timeoutSeconds, productWeightGrams, retryIntervalMilliseconds |
Go 코드에서는 일반적으로 Id보다 ID, Url보다 URL, Http보다 HTTP처럼 널리 알려진 약어의 대문자 형태를 유지합니다.
productID
apiURL
httpClient정리
리터럴은 코드에 직접 작성한 값입니다.
"PRODUCT-1001"
35000
true변수는 값을 저장하고 변경할 수 있습니다.
var retryCount int
retryCount = 1상수는 실행 중에 변경할 수 없습니다.
const maxLoginRetryCount = 5:=는 오른쪽 값으로 변수 타입을 추론하며 함수 내부에서만 사용할 수 있습니다.
productPrice := 35000값을 지정하지 않은 변수에는 타입별 zero value가 들어갑니다.
var productCount int // 0
var isAvailable bool // false
var customerName string // ""서로 다른 수치 타입은 명시적으로 변환해야 합니다.
var productAmount int = 35000
var shippingFee int64 = 3000
finalAmount := int64(productAmount) + shippingFee같은 scope에서 기존 변수의 값을 변경할 때는 =를 사용합니다. :=는 새 변수가 하나 이상 포함된 선언에서만 다시 사용할 수 있습니다.
참고 자료
- Go 언어 명세: https://go.dev/ref/spec
- Go 시작하기: https://go.dev/doc/tutorial/getting-started
- Go 코드 작성 방법: https://go.dev/doc/code
- Effective Go: https://go.dev/doc/effective_go
- Go 명령어 문서: https://pkg.go.dev/cmd/go
'프로그래밍 > Go' 카테고리의 다른 글
| Go if, for, switch와 break, continue (0) | 2026.07.11 |
|---|---|
| Go fmt의 %v와 %q (0) | 2026.07.11 |
| Go package main과 패키지 구조 이해하기 (0) | 2026.07.10 |
| Ubuntu 24.04에서 Go 1.26.1과 VS Code 설치하기 (1) | 2026.07.03 |
| Go 모듈과 패키지 이해하기 (0) | 2026.07.03 |
댓글