Go 변수 소개

GolangBeginner
지금 연습하기

소개

컴퓨터 프로그래밍 언어에는 왜 변수가 필요할까요? 오래전부터 이어진 질문입니다. 우리가 LabEx의 URL이 labex.io라는 사실을 기억할 수 있듯이, 컴퓨터 프로그램도 사용해야 할 데이터를 기억해야 합니다.

변수는 데이터를 나타내는 역할을 합니다. 이 섹션에서는 Go 언어에서 변수를 사용하는 방법을 살펴봅니다.

학습할 내용:

  • 변수 선언
  • 변수 초기화
  • 변수 사용
  • 변수 수명
  • 상수

변수란 무엇인가요?

이 단계에서는 변수가 무엇을 나타내는지, 그리고 Go가 변수 이름과 타입을 어떻게 제한하는지 배웁니다.

변수란 무엇일까요? 간단히 말해 변수는 변경 가능한 데이터를 저장하는 컨테이너입니다.

Go와 같은 컴파일 언어에서는 변수의 타입이 고정됩니다.

변수의 타입이 고정된다는 것은 무엇을 의미하나요?

변수는 한 가지 타입의 데이터만 저장할 수 있다는 뜻입니다. 즉, 변수라는 컨테이너에 저장할 수 있는 항목의 타입이 고정됩니다.

변수를 과일을 담는 용도로 사용한다면 과일만 담아야 합니다. 한 번 과일을 담은 컨테이너를 쿠키를 담는 데 사용할 수는 없습니다. 코드에서도 변수에 서로 다른 두 타입의 데이터를 할당할 수 없는 방식으로 나타납니다.

변수의 변경 가능성

이름에서 알 수 있듯이 변수의 값은 변경할 수 있습니다. 단, 변경된 값이 변수 타입에서 허용하는 범위를 벗어나지 않아야 합니다.

Go 언어의 변수에는 다음 규칙이 적용됩니다.

  • 이름은 문자, 숫자, 밑줄로만 구성해야 합니다.
  • 변수 식별자는 숫자로 시작할 수 없습니다.
  • 식별자는 예약어일 수 없습니다. 예약어 확인
  • 변수 이름은 대소문자를 구분하지만, 같은 이름의 두 변수를 대소문자만 다르게 작성하는 방법은 권장하지 않습니다.

일반적인 선언 방법

이 단계에서는 var 키워드를 사용해 타입이 지정된 변수를 선언하는 방법을 배웁니다.

Go 언어에서 일반적인 방식으로 변수를 선언할 때 사용하는 키워드는 var입니다.

선언 형식은 var identifier type이며, 다음과 같은 의미입니다.

var variableName variableType.

Go에서 자주 사용하는 변수 타입은 무엇일까요?

키워드 설명
int 정수입니다. 초등학교 수학에서 배우는 가장 일반적인 데이터 타입입니다.
string 문자열입니다. "hello,world"처럼 큰따옴표로 묶은 문자들의 모음입니다.
bool 불리언입니다. true 또는 false 중 하나의 값을 나타냅니다.

이 섹션에서는 변수 타입 자체가 주요 내용이 아니므로 가장 자주 사용하는 세 가지 타입만 소개합니다.

더 많은 타입은 다음 과정에서 설명합니다.

변수는 어떻게 선언하나요?

이제 a라는 이름의 정수 변수를 선언해 보겠습니다.

var a int

어떻게 기억하면 될까요? 다음과 같이 마음속으로 읽어 보세요.

Define a variable named `a`, which is of type int.

많은 전통적인 프로그래밍 언어와 달리 Go에서는 변수 타입을 변수 이름 뒤에 작성합니다.

이러한 변수 선언 방식은 코드를 왼쪽에서 오른쪽으로 읽기 쉽게 만들고, C 언어에서 사용하는 나선형 읽기 방식을 피할 수 있게 합니다. 자세한 내용은 공식 문서를 참고하세요.

변수에는 어떤 이름을 지정해야 하나요?

좋은 변수 이름은 해당 변수가 무엇을 의미하는지 분명하게 보여 줘야 합니다.

변수 이름을 정할 때는 의미가 잘 드러나는지에 주의하고, 약어 사용은 피해야 합니다.

여기서는 변수 이름을 정하는 기본 방법인 카멜 케이스 명명 규칙을 간단히 소개합니다.

카멜 케이스 명명 규칙은 대소문자를 섞어 변수 이름을 표현합니다. 첫 번째 단어는 소문자로 작성하고, 이어지는 각 단어의 첫 글자는 대문자로 작성합니다.

예를 들어 currentDate가 있습니다. 첫 번째 단어인 current는 소문자로 시작하고, 두 번째 단어인 Date는 대문자로 시작합니다.

이렇게 작성하면 현재 날짜를 나타내는 변수라는 사실을 쉽게 이해할 수 있습니다.

이 명명 규칙은 일반적으로 중요하고 자주 사용하는 변수에 사용합니다. 임시 변수는 이름이 중복되지 않는다면 더 간단하게 작성할 수 있습니다.

여러 변수 선언 방법

이 단계에서는 여러 변수를 한 번에 선언하는 방법을 배웁니다.

이제 변수 세 개를 선언해 보겠습니다.

var a int // Declare an integer variable named a
var b int // Declare an integer variable named b
var c int // Declare an integer variable named c

주의 깊게 살펴보면 세 변수 a, b, c의 타입이 모두 int라는 것을 알 수 있습니다.

이 경우 쉼표로 변수 이름을 연결해 코드를 줄일 수 있습니다.

var a, b, c int // Declare three variables a, b, c as integers

그렇다면 세 변수의 타입이 서로 다르면 어떻게 해야 할까요?

var a int    // Declare an integer variable named a
var b string // Declare a string variable named b
var c bool   // Declare a boolean variable named c

패키지를 가져올 때와 비슷한 상황입니다. 이름이 서로 다른 여러 패키지를 함께 가져와야 할 때 유사한 작성 방식을 사용할 수 있습니다.

var (
    a int
    b string
    c bool
)

패키지를 가져올 때와 비슷한 이 선언 방식은 일반적으로 전역 변수를 정의할 때 사용합니다.

기본 초기화

이 단계에서는 Go가 변수에 할당하는 기본값을 확인합니다.

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, 0, 0
// Declare variables a, b, c as integers and assign 0 to each variable.

서로 다른 타입의 값으로 변수를 초기화할 수도 있습니다.

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
}

이와 같은 결과가 나오는 이유는 위의 값이 1a와 아래의 값이 2a가 같은 변수 범위(같은 중괄호 내부)에 있지 않기 때문입니다. 따라서 컴파일러는 두 변수를 서로 다른 변수로 처리합니다.

컴파일러는 이 코드를 오류로 지적하지 않지만, 예상하지 못한 출력이 나타납니다.

Go에서는 다음 규칙을 따릅니다.

함수 외부의 각 문장은 키워드(var, func 등)로 시작해야 합니다.

따라서 짧은 변수 선언은 지역 변수를 선언할 때만 사용할 수 있으며, 전역 변수를 선언하는 데는 사용할 수 없습니다.

그렇다면 전역 변수와 지역 변수는 무엇일까요?

이 내용은 변수 수명 개념과 관련이 있으며, 다음 섹션에서 설명합니다.

변수 범위

이 단계에서는 지역 변수, 전역 변수 및 형식 매개변수의 범위를 비교합니다.

변수 범위란 프로그램에서 변수가 유효한 범위, 즉 변수를 사용할 수 있는 범위를 의미합니다.

변수를 선언하고 사용하지 않으면 코드가 컴파일되지 않는다는 사실을 이미 확인했을 것입니다.

다시 말해 Go는 컴파일할 때 각 변수가 사용되었는지, 즉 해당 변수의 범위 안에서 사용되었는지 확인합니다.

변수는 선언된 위치에 따라 다음 세 가지 유형으로 나눌 수 있습니다.

  • 함수 내부에 정의된 변수: 지역 변수
  • 함수 외부에 정의된 변수: 전역 변수
  • 함수 정의에 포함된 변수: 형식 매개변수

지역 변수

이 섹션에서 정의하는 대부분의 변수는 지역 변수입니다.

package main

import "fmt"

func main() { // Function body
    var a int = 1 // Local variable
    fmt.Println(a)
}

지역 변수는 함수 본문 내부에 정의됩니다. 예를 들어 amain 함수 안에 정의되어 있습니다. 따라서 변수 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 // Using type inference initialization

~/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
// Value is fixed, declaration is valid.
const Pi = 3.14159
// The calculated value is also fixed, the declaration is valid.
const c = 1 / Pi
// Fixed return value from built-in function is valid.
const le = len("labex")
// The return value of the user-defined function is not fixed, the declaration is invalid.
const le = getLen("labby")
// `a` is a variable value that is not fixed, the declaration is invalid.
const k = a

상수 선언의 유효 여부는 다음 표와 같이 정리할 수 있습니다.

선언 유형 유효 여부
고정된 값 및 고정된 값으로 이루어진 식 유효
고정되지 않은 값(변수) 및 해당 값으로 이루어진 식 유효하지 않음
내장 함수(len())가 고정된 값과 고정된 값으로 이루어진 식을 받는 경우 유효
사용자 정의 함수 유효하지 않음

요약

이 실습에서 배운 내용을 복습합니다.

  • 변수 선언 방법
  • 변수 초기화 방법
  • 변수 수명 개념
  • 상수 사용 방법과 상수 선언의 유효 여부

이 실습에서는 Go 변수의 기본적인 사용 방법을 복습하고, 다양한 상황에서 변수를 선언하고 사용하는 방법을 살펴보았으며, 상수를 소개했습니다.