본문 바로가기
프로그래밍/Go

Go 리터럴, 변수, 상수와 Zero Value

by JLearn 2026. 7. 10.
반응형

Go는 정적 타입 언어입니다. 변수의 타입은 컴파일 과정에서 결정되며, 한 번 결정된 변수 타입은 실행 중에 다른 타입으로 바뀌지 않습니다.

:=로 변수를 선언하더라도 타입이 없는 것이 아닙니다. 오른쪽 값을 기준으로 컴파일러가 타입을 추론합니다. 또한 :=는 함수 내부에서만 사용할 수 있고, 함수 밖의 package scope에서는 var 또는 const를 사용해야 합니다.

이 글에서는 다음 내용을 예제로 확인합니다.

  • literal, variable, constant, zero value의 차이
  • var:=를 이용한 변수 선언
  • package scope와 function scope
  • 여러 변수 선언과 := 재선언 규칙
  • 서로 다른 수치 타입의 명시적 변환
  • 변수 shadowing
  • 의미가 분명한 변수명 작성 방법

예제 준비와 실행

디렉터리 구조는 다음과 같이 구성합니다.

sources/
└── hello-values/
    └── main.go

sources 디렉터리에서 실행합니다.

cd sources
go run ./hello-values

go 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

초기값을 지정하지 않았으므로 retryCount0입니다.

타입과 초기값 선언

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가 각각 서로 다른 타입입니다.

다음 코드는 intint64를 바로 더하므로 컴파일되지 않습니다.

var productAmount int = 35000
var shippingFee int64 = 3000

// 컴파일 오류
finalAmount := productAmount + shippingFee

한쪽 값을 명시적으로 변환해야 합니다.

finalAmount := int64(productAmount) + shippingFee

결과 타입은 int64입니다.

fmt.Printf("%T\n", finalAmount)
int64

int64int로 변환할 수도 있지만, 값이 int 범위를 초과할 가능성을 확인해야 합니다.

finalAmount := productAmount + int(shippingFee)

같은 의미의 값은 가능한 한 하나의 타입으로 통일하는 편이 좋습니다.

var productAmount int64 = 35000
var shippingFee int64 = 3000

finalAmount := productAmount + shippingFee

정수와 부동소수점

intfloat64도 바로 연산할 수 없습니다.

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에서 기존 변수의 값을 변경할 때는 =를 사용합니다. :=는 새 변수가 하나 이상 포함된 선언에서만 다시 사용할 수 있습니다.


참고 자료

반응형

댓글