변수선언
var 키워드 사용
- Golang에서는 var 키워드를 사용합니다.
- `var {변수_이름} 타입` 의 형태를 따릅니다.
var name string
var age int
var isStudent bool
short variable declaration
- Golang에서 타입추론이 가능합니다.
- `:=`구문은 암묵적으로 타입 할당문을 사용할 수 있습니다.
- `:=`구문은 함수 내에서만 사용할 수 있습니다. 패키지 레벨에서는 사용할 수 없습니다.
함수 내부에서 암묵적 유형의 선언 :=대신 짧은 할당 문을 사용할 수 있습니다.
name := "Gopher"
age := 25
isStudent := true
패키지 레벨 선언의 제한사항
- 명확성과 일관성
- Go는 코드의 명확성과 일관성을 매우 중요하게 여깁니다. := 연산자는 변수의 선언과 초기화를 동시에 수행하는 짧은 선언 문법입니다. 이 문법을 함수 내부로 제한함으로써, 전역 변수와 지역 변수의 선언 방식을 명확히 구분할 수 있습니다.
- 언어 설계의 단순성
- Go는 언어 자체를 단순하게 유지하려고 노력합니다. 패키지 레벨과 함수 레벨에서 다른 규칙을 적용함으로써, 전체적인 코드 가독성을 더 간단하게 만들 수 있습니다.
package main
var globalVar = "I'm global" // 올바른 사용
// globalVar := "I'm global" // 컴파일 에러
변수명만 선언
- Go에서는 변수를 선언할 때 초기값을 지정하지 않으면, 해당 타입의 제로값(zero value)으로 자동 초기화됩니다.
- 이 방식은 변수를 선언만 하고 나중에 값을 할당하고자 할 때 유용합니다.
- Go의 제로값 초기화 특성 덕분에 변수를 사용하기 전에 반드시 초기화할 필요가 없어 편리합니다.
- 주의: Go에서는 변수명만 선언하고 타입을 생략하는 것은 불가능합니다.
var name string // 빈 문자열 ""로 초기화
var age int // 0으로 초기화
var isStudent bool // false로 초기화
var f float64 // 0.0으로 초기화
var ptr *int // nil로 초기화
여러 변수 동시 선언
- 타입 일치: 같은 타입의 변수들을 선언할 때는 타입을 한 번만 명시
- 병렬 할당: 여러 변수에 동시에 값을 할당할 수 있습니다
// 타입 일치: 같은 타입의 변수들을 선언할 때는 타입을 한 번만 명시
var a, b, c int
a, b, c = 1, 2, 3
// 병렬 할당: 여러 변수에 동시에 값을 할당
var x, y int
x, y = 10, 20
x, y = y, x // 값 교환
블럭과 스코프
블럭
블록이란?
블록은 중괄호 { } 로 둘러싸인 코드의 부분입니다.
{
// 이 안은 하나의 '방'입니다.
}
블록의 특징
- 독립된 공간: 각 블록은 자신만의 독립된 공간입니다.
- 규칙이 있는 공간: 블록 안에서 만든 규칙(변수)은 그 블록 안에서만 통해요.
- 중첩 가능: 블록 안에 또 다른 블록을 만들 수 있어요. 방 안에 작은 방을 만드는 것과 비슷해요.
func 집() {
var 거실물건 = "TV"
{
// 이것은 '안방'이에요
var 안방물건 = "침대"
fmt.Println(거실물건) // TV 출력 가능
fmt.Println(안방물건) // 침대 출력 가능
}
fmt.Println(거실물건) // TV 출력 가능
// fmt.Println(안방물건) // 오류! 안방물건은 안방에서만 사용 가능해요
}
패키지 레벨 스코프
- 패키지 레벨에서 선언된 변수는 해당 패키지의 모든 파일에서 접근 가능합니다.
package main
var globalVar = "I'm global"
func main() {
// globalVar 사용 가능
}
함수 레벨 스코프
- 함수 내에서 선언된 변수는 해당 함수 내에서만 접근 가능합니다.
func exampleFunction() {
localVar := "I'm local"
// localVar는 이 함수 내에서만 사용 가능
}
블록 레벨 스코프
- 중괄호 {} 내에서 선언된 변수는 해당 블록 내에서만 접근 가능합니다.
func blockScopeExample() {
if x := 10; x > 5 {
y := 20
// x와 y 모두 사용 가능
}
// 여기서는 x와 y 모두 사용 불가
}
변수 섀도잉
- Go에서는 내부 스코프에서 외부 스코프와 같은 이름의 변수를 선언할 수 있습니다. 이를 변수 섀도잉이라고 합니다.
- 참고: shadowing-variables
func shadowingExample() {
x := 10
if true {
x := 20 // 외부의 x를 가림
fmt.Println(x) // 20 출력
}
fmt.Println(x) // 10 출력
}
변수의 수명
변수의 수명은 그 변수가 메모리에 존재하는 기간을 의미합니다.
- 지역 변수: 함수 호출이 끝나면 소멸
- 전역 변수: 프로그램이 종료될 때까지 존재
예제를 통해 힙메모리 변화를 살펴 보면서 변수의 수명을 테스트 해보겠습니다.
package main
import (
"fmt"
"runtime"
"time"
)
type exampleStruct struct {
data string
}
func main() {
fmt.Println("프로그램 시작")
// 초기 메모리 통계
printMemStats("초기 상태")
// 많은 객체 생성
createObjects()
// 가비지 컬렉션 전 메모리 통계
printMemStats("객체 생성 후")
// 가비지 컬렉션 실행
runtime.GC()
// 가비지 컬렉션 후 메모리 통계
printMemStats("가비지 컬렉션 후")
fmt.Println("프로그램 종료")
}
func createObjects() {
var objects []*exampleStruct
for i := 0; i < 1000000; i++ {
objects = append(objects, &exampleStruct{data: "some data"})
}
fmt.Println("1,000,000개의 객체 생성됨")
// objects는 이 함수가 종료되면 접근 불가능해짐
}
func printMemStats(stage string) {
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("\n--- 메모리 통계 (%s) ---\n", stage)
fmt.Printf("Alloc = %v MiB\n", bToMb(m.Alloc))
fmt.Printf("TotalAlloc = %v MiB\n", bToMb(m.TotalAlloc))
fmt.Printf("Sys = %v MiB\n", bToMb(m.Sys))
fmt.Printf("NumGC = %v\n", m.NumGC)
time.Sleep(time.Second) // GC에 시간을 주기 위한 지연
}
func bToMb(b uint64) uint64 {
return b / 1024 / 1024
}
프로그램 시작
--- 메모리 통계 (초기 상태) ---
Alloc = 0 MiB
TotalAlloc = 0 MiB
Sys = 6 MiB
NumGC = 0
1,000,000개의 객체 생성됨
--- 메모리 통계 (객체 생성 후) ---
Alloc = 30 MiB
TotalAlloc = 58 MiB
Sys = 56 MiB
NumGC = 7
--- 메모리 통계 (가비지 컬렉션 후) ---
Alloc = 0 MiB
TotalAlloc = 58 MiB
Sys = 56 MiB
NumGC = 8
프로그램 종료
- Alloc: 현재 할당된 힙 메모리
- TotalAlloc: 프로그램 시작 이후 총 할당된 메모리
- Sys: 시스템에서 얻은 총 메모리
- NumGC: 가비지 컬렉션 실행 횟수
결과:
- 객체 생성 후 Alloc 값이 크게 증가합니다.
- 가비지 컬렉션 실행 후 Alloc 값이 감소합니다.
- NumGC 값이 증가하여 가비지 컬렉션이 실행되었음을 확인할 수 있습니다.
정리
Go 언어에서 변수와 스코프를 제대로 이해하고 활용하면 더 효율적이고 안전한 코드를 작성할 수 있습니다. 변수의 가시성을 적절히 제어하고, 필요한 곳에서만 접근할 수 있도록 하는 것이 중요합니다. 독립적인 블록의 사용은 가능하지만, 대부분의 경우 불필요하며 더 명확한 코드 구조를 위해 함수, 메서드, 제어문 등을 활용하는 것이 좋습니다.
'프로그래밍 > Golang' 카테고리의 다른 글
Golang type(타입) 키워드 탐구 : Named Type과 Type Alias의 차이와 활용 (0) | 2024.07.21 |
---|---|
Golang Constants(상수) (0) | 2024.07.20 |
Golang Module(모듈) 발행 가이드: GitHub에 패키지 배포 (0) | 2024.07.18 |
Golang Package(패키지) 종속성 관리와 모듈화 (0) | 2024.07.17 |
Golang Package(패키지) 네이밍 가이드: 효과적인 패키지 이름 짓기 12가지 팁 (0) | 2024.07.16 |