소개
컴퓨터 프로그래밍 언어에서 변수가 왜 필요할까요? 이것은 오래된 질문입니다. LabEx 의 URL 이 labex.io라는 것을 기억하는 것처럼, 컴퓨터 프로그램도 사용을 위해 일부 데이터를 기억해야 합니다.
변수의 목적은 데이터 조각을 나타내는 것입니다. 이 섹션에서는 Go 언어에서 변수가 어떻게 사용되는지 살펴보겠습니다.
지식 포인트:
- 변수 선언
- 변수 초기화
- 변수 사용
- 변수 수명
- 상수
컴퓨터 프로그래밍 언어에서 변수가 왜 필요할까요? 이것은 오래된 질문입니다. LabEx 의 URL 이 labex.io라는 것을 기억하는 것처럼, 컴퓨터 프로그램도 사용을 위해 일부 데이터를 기억해야 합니다.
변수의 목적은 데이터 조각을 나타내는 것입니다. 이 섹션에서는 Go 언어에서 변수가 어떻게 사용되는지 살펴보겠습니다.
지식 포인트:
변수란 무엇일까요? 간단히 말해서, 변수는 변경 가능한 (mutable) 데이터 조각을 저장하고 보관하는 데 사용되는 컨테이너입니다.
Go 와 같은 컴파일 언어에서 변수의 타입은 고정되어 있습니다.
이는 변수가 한 가지 타입의 데이터만 담을 수 있다는 것을 의미합니다. 즉, 변수 컨테이너에 저장되는 항목은 고정되어 있습니다.
변수가 과일을 담는 데 사용된다면, 과일만 담아야 합니다. 컨테이너가 과일을 담은 후에는 쿠키를 담는 데 사용할 수 없습니다. 이는 코드에서 변수가 서로 다른 두 가지 타입의 데이터를 할당받을 수 없다는 것으로 나타납니다.
이름에서 알 수 있듯이, 변수의 값은 해당 타입에서 허용하는 범위를 초과하지 않는 한 변경될 수 있습니다.
Go 언어는 변수에 대해 다음과 같은 규칙을 가지고 있습니다.
Go 언어에서 일반적인 방법으로 변수를 선언하는 데 사용되는 키워드는 var입니다.
선언 형식은 다음과 같습니다: var identifier type, 이는 다음을 의미합니다:
var variableName variableType.
Go 에서 흔히 사용되는 변수 타입은 무엇일까요?
| 키워드 | 설명 |
|---|---|
int |
정수. 가장 일반적인 데이터 타입으로, 초등학교 수학에서 배웁니다. |
string |
문자열. 큰따옴표로 묶인 문자열, 예: "hello,world" |
bool |
부울. true 또는 false를 나타내며, 두 가지 가능한 값: true 또는 false를 가집니다. |
변수 타입은 이 섹션의 초점이 아니므로, 가장 흔히 사용되는 세 가지 타입만 나열했습니다.
더 많은 타입은 다음 강좌에서 설명하겠습니다.
이제 a라는 정수 변수를 선언해 보겠습니다.
var a int
어떻게 기억할까요? 마음속으로 이렇게 말할 수 있습니다:
`a`라는 변수를 정의하는데, 타입은 int입니다.
많은 전통적인 프로그래밍 언어와 달리, Go 에서 사용되는 변수 타입은 변수 이름 뒤에 위치합니다.
이러한 변수 선언 방식은 코드를 왼쪽에서 오른쪽으로 읽기 쉽게 만들고, C 언어의 나선형 읽기 논리를 피할 수 있게 해줍니다. 자세한 내용은 공식 문서를 참조하십시오.
좋은 변수 이름은 변수의 의미를 명확하게 나타내야 합니다.
변수 이름을 지을 때는 표현력에 주의하고 약어를 사용하지 않도록 해야 합니다.
여기서는 변수 명명의 기본 방법인 Camel Case Naming Convention 을 간략하게 소개합니다.
Camel case naming convention 은 혼합된 대소문자를 사용하여 변수를 나타냅니다. 첫 번째 단어는 소문자로 시작하고, 그 다음 단어의 첫 글자는 대문자로 시작합니다.
예를 들어: currentDate. 첫 번째 단어인 current는 소문자로 시작하고, 두 번째 단어인 Date는 대문자로 시작합니다.
이러한 방식으로 현재 날짜를 나타내는 변수를 쉽게 이해할 수 있습니다.
이 명명 규칙은 일반적으로 중요하고 자주 사용되는 변수에 사용되며, 임시 변수는 중복을 일으키지 않는 한 간소화될 수 있습니다.
이제 세 개의 변수를 선언해 보겠습니다:
var a int // a 라는 정수 변수 선언
var b int // b 라는 정수 변수 선언
var c int // c 라는 정수 변수 선언
관찰력이 뛰어난 학생들은 세 변수 a, b, c가 모두 int 타입임을 알아차렸을 것입니다.
이 경우, 쉼표를 사용하여 변수 이름을 연결하고 코드의 양을 줄일 수 있습니다.
var a, b, c int // a, b, c 세 변수를 정수로 선언
하지만 세 변수가 서로 다른 타입을 가진다면 어떻게 해야 할까요?
var a int // a 라는 정수 변수 선언
var b string // b 라는 문자열 변수 선언
var c bool // c 라는 부울 변수 선언
패키지를 가져올 때 비슷한 상황을 겪었던 것 같습니다. 서로 다른 이름을 가진 많은 패키지를 함께 가져와야 할 때, 유사한 작성 방식을 사용할 수 있습니다:
var (
a int
b string
c bool
)
패키지를 가져오는 것과 유사한 이러한 종류의 선언 동작은 일반적으로 전역 변수를 정의하는 데 사용됩니다.
Go 에서는 모든 변수가 선언될 때 초기값을 갖습니다. 변수의 초기값이 무엇인지 살펴보겠습니다!
~/project 디렉토리에 varExercise.go라는 파일을 생성합니다.
touch ~/project/varExercise.go
다음 코드를 파일에 작성합니다:
package main
import "fmt"
func main() {
var a int
var b string
var c bool
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
}
직접 실행해 보고 아래 표와 일치하는지 확인할 수 있습니다.
go run varExercise.go
초기값의 타입은 다음과 같이 요약됩니다:
| 키워드 | 설명 | 초기값 |
|---|---|---|
int |
정수 | 0 |
string |
문자열 | "" |
bool |
부울 | false |
변수의 타입은 초기값으로 결정될 수 있으므로, 기본값이나 이미 선언된 변수를 변경할 수 있을까요?
var a int = 1
var b string = "labex"
var c bool = true
a = 233
b = "labex"
c = false
위에서 보듯이, 변수를 선언한 후 =를 추가하고, 그 뒤에 변수 타입과 호환되는 초기값을 추가하면 됩니다. 값을 변경하려면 변수 이름 뒤에 =를 붙이고 동일한 타입의 다른 값을 사용하면 됩니다.
varExercise.go 파일을 수정합니다:
package main
import "fmt"
func main() {
// Declare and initialize
var a int = 1
var b string = "labex"
var c bool = true
// Print the variables
fmt.Println("Before modification:")
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
// Modify the variables
a = 233
b = "labex"
c = false
// Print the modified variables
fmt.Println("After modification:")
fmt.Println(a)
fmt.Println(b)
fmt.Println(c)
}
코드를 실행하면 다음과 같은 출력이 나타납니다:
$ go run varExercise.go
Before modification:
1
labex
true
After modification:
233
labex
false
직접 실행해 보고 초기값을 수정해 볼 수 있습니다.
방금 변수에 할당된 초기값은 변수 선언과 동일한 타입이어야 한다고 언급했습니다. 만약 타입이 다르면 어떻게 될까요?
예를 들어, a 변수에 "labex"를 초기값으로 할당해 보겠습니다:
package main
import "fmt"
func main() {
var a int = "labex"
fmt.Println(a)
}
코드를 실행합니다:
$ go run varExercise.go
## command-line-arguments
./varExercise.go:6:12: cannot use "labex" (type untyped string) as type int in assignment
그림과 같이, "labex"와 같은 문자열 타입을 int 타입의 변수에 할당할 수 없습니다. 이는 Go 가 강력한 타입의 컴파일 언어이기 때문에 컴파일될 수 없기 때문입니다.
Go 는 초기값으로 변수의 타입을 결정할 수 있으므로, 타입을 명시적으로 지정하는 단계를 제거하여 타입 선언 과정을 단순화할 수 있을까요?
package main
import "fmt"
func main() {
// var a int = 1
var a = 1 // Type is inferred
fmt.Println(a)
}
이제 변수를 정의하기 위해 var 키워드를 사용할 필요조차 없습니다.
이러한 변수 선언 및 초기화 방식은 일괄 선언 방법과 결합될 수도 있습니다:
a, b, c := 0
// Declare variables a, b, c as integers and assign an initial value of 0
a, b, c := 0, "", true
// Declare variables a, b, c as integer, string, and boolean, respectively
짧은 선언은 매우 편리하지만, :=는 할당 연산자가 아님에 유의해야 합니다. 이는 변수를 선언하는 방법이며, Go 에 고유하며, 함수 내부의 지역 변수를 선언하고 초기화하는 데 사용됩니다. 변수의 타입은 표현식을 기반으로 자동으로 추론됩니다.
때때로 다음과 같은 코드를 작성합니다:
func main() {
a := 1
println(a)
a := 2
println(a)
}
컴파일러는 변수 a가 재선언되었다는 오류를 알려줍니다. 그러나 다음과 같이 작성하면:
func main() {
a := 1
if true {
a := 2
println(a) // Output: 2
}
println(a) // Output: 1
}
이러한 출력이 나타나는 이유는 위에 있는 값이 1인 a와 아래에 있는 값이 2인 a가 동일한 변수 범위 (동일한 중괄호 내) 에 있지 않기 때문에 컴파일러가 이를 두 개의 다른 변수로 처리하기 때문입니다.
컴파일러는 여러분의 실수를 지적하지 않지만, 예상치 못한 출력이 발생할 수 있습니다.
Go 에서는 다음과 같이 규정되어 있습니다:
함수 외부의 각 문장은 키워드 (var, func 등) 로 시작해야 합니다.
따라서 짧은 변수 선언은 지역 변수를 선언하는 데만 사용할 수 있으며, 전역 변수를 선언하는 데는 사용할 수 없습니다.
그렇다면 전역 변수와 지역 변수는 무엇일까요?
이는 변수 수명 (variable lifetime) 의 개념과 관련이 있으며, 다음 섹션에서 설명하겠습니다.
변수 범위는 프로그램 내에서 변수가 유효한 범위, 즉 변수를 사용할 수 있는 범위를 의미합니다.
변수를 선언했지만 사용하지 않으면 코드가 컴파일되지 않는다는 것을 이미 아셨을 것입니다.
다시 말해, Go 가 컴파일될 때 각 변수가 사용되었는지, 즉 해당 범위 내에서 사용되었는지 확인합니다.
선언 위치를 기준으로 변수를 세 가지 유형으로 간단하게 나눌 수 있습니다:
이 섹션에서 우리가 정의하는 대부분의 변수는 지역 변수입니다:
package main
import "fmt"
func main() { // Function body
var a int = 1 // Local variable
fmt.Println(a)
}
지역 변수는 main 함수 내에서 정의된 a와 같이 함수 본문 내에서 정의됩니다. 변수 a의 범위는 main 함수 내로 제한됩니다.
동시에, 변수가 main 함수 내에서 사용되지 않으면 컴파일러는 오류를 발생시킵니다.
하지만 전역 변수를 정의하는 것도 가능합니다.
package main
import "fmt"
var a int = 1 // Global variable
func main() { // Function body
fmt.Println(a)
}
전역 변수는 함수 본문 외부에서 정의되며, 해당 범위는 전체 프로그램을 포괄합니다. 어떤 함수에서도 호출되지 않더라도 컴파일러는 오류를 보고하지 않습니다.
전역 변수가 호출되지 않더라도 왜 오류가 발생하지 않는지 생각해 볼 수 있습니다.
이는 전역 변수가 다른 패키지에서 호출될 수 있기 때문입니다.
형식 매개 변수 변수에 대한 자세한 내용은 이후의 함수 관련 강좌에서 설명하겠습니다.
날아가는 새는 그 자취를 숨기고, 교활한 토끼는 죽으면 사냥개가 삶아진다. - 사기 (史記)
변수가 목적을 완료하면 메모리 사용량을 줄이기 위해 파괴되어야 합니다.
이러한 설계는 Go 의 높은 성능과 효율적인 공간 사용의 핵심입니다.
변수의 수명 동안에는 재선언할 수 없습니다.
varExercise.go에서 다음과 같이 작성할 수 있습니다:
package main
import "fmt"
func main() {
var a int = 1 // Local variable, lifetime is limited to the entire main function
var a int = 2 // Redefinition
fmt.Println(a)
}
코드를 실행한 후:
go run varExercise.go
다음 오류 메시지가 표시됩니다:
./varExercise.go:7:9: a redeclared in this block
previous declaration at ./varExercise.go:6:9
컴파일러는 a가 다시 정의되었다고 알려줍니다.
인생의 많은 것들이 상수와 같습니다. 우리는 그것들을 인지할 수 있지만, 변경할 수는 없습니다.
프로그램 실행 시간 동안 변하지 않는 변수는 상수로 정의해야 합니다.
상수는 변수와 매우 유사하며, 변경 불가능한 값을 가진 변수로 생각할 수도 있습니다.
상수를 선언할 때는 var 키워드를 const 키워드로 바꾸기만 하면 됩니다.
const Pi = 3.14159 // 타입 추론 초기화 사용
~/project 디렉토리에 constExercise.go라는 파일을 생성합니다.
touch ~/project/constExercise.go
상수를 수정하려고 하면 어떻게 될까요?
package main
import "fmt"
func main() {
const Pi = 3.14159
Pi = 2 // Error: cannot assign to Pi
fmt.Println(Pi)
}
코드를 실행합니다.
$ go run constExercise.go
## command-line-arguments
./constExercise.go:7:8: cannot assign to Pi
컴파일러는 Pi의 값을 재할당할 수 없다고 알려줍니다.
상수를 선언할 때는 반드시 초기값을 제공해야 합니다.
그리고 상수에게 할당된 초기값은 컴파일 시간에 고정되어 있어야 합니다.
사용자 정의 함수 반환 값은 Go 에서 고정된 것으로 간주되지 않습니다.
var a int = 1
// 값은 고정되어 있으며, 선언은 유효합니다.
const Pi = 3.14159
// 계산된 값도 고정되어 있으며, 선언은 유효합니다.
const c = 1 / Pi
// 내장 함수에서 고정된 반환 값은 유효합니다.
const le = len("labex")
// 사용자 정의 함수의 반환 값은 고정되지 않으므로, 선언은 유효하지 않습니다.
const le = getLen("labby")
// `a` 는 고정되지 않은 변수 값이므로, 선언은 유효하지 않습니다.
const k = a
상수 선언의 유효성은 다음 표로 요약할 수 있습니다.
| 선언 유형 | 유효성 |
|---|---|
| 고정 값 및 고정 값 표현식 | 유효 |
| 비고정 값 (변수) 및 해당 표현식 | 무효 |
내장 함수 (len()) 가 고정 값 및 고정 값 표현식을 수신함 |
유효 |
| 사용자 정의 함수 | 무효 |
이번 랩에서 배운 내용을 복습해 보겠습니다:
이번 랩에서는 Go 에서 변수의 기본적인 사용법을 복습하고, 다양한 상황에서 변수를 선언하고 사용하는 방법을 시연했으며, 상수를 소개했습니다.