[Go] A Tour of Go - Basics (Packages, variables, and functions)
A Tour of Go
A Tour of Go
go.dev
다음주 수요일까지 이 튜토리얼을 쭉 따라가면서 코드를 따라 쳐보면서 개념을 익혀보기로 했다.
강의를 듣는 것도 좋지만, 강의는 이 튜토리얼을 한 번 쭉 해보고 듣는 것도 나쁘지 않을 것 같다.
일단.. 좀 많이 코드를 쳐보면서 쭉쭉 진도를 빼야 할 것 같다.
오늘 정리하는 부분은 Basics의 Packages, variables, and functions 부분이다.
헷갈리거나 잘 몰랐던 부분만 정리해보도록 하겠다.
Package
Golang에서 패키지는 코드를 묶는 기본 단위이다. 따라서 모든 코드는 반드시 패키지로 묶어야 한다.
또한 Golang으로 프로그램을 작성한다면, 반드시 main 패키지가 존재해야 하며, main 패키지안에 main 함수가 정의되어야 한다. Golang에서는 이 main 패키지의 main 함수가 프로그램의 시작 위치가 된다.
Module
모듈은 패키지(Package)의 모음으로써, 한 개의 모듈은 다수의 패키지를 포함할 수 있다.
이런 모듈을 통해 Golang은 패키지들의 종속성을 관리할 수 있으며, 모듈은 패키지 관리 시스템으로써 활용이 된다.
모듈은 패키지를 트리 형식으로 관리하며, 루트(root) 폴더에 go.mod 파일을 생성하여 모듈을 정의하고, 종속성 정보를 관리하게 된다.
Exported Names
import 한 패키지 안의 메소드나 상수 등을 사용하고 싶으면 항상 대문자로 가져와야 한다.
package main
import (
"fmt"
"math"
)
func main() {
// fmt.Println(math.pi)
// when you export from the package, you should start with capital letter
// error undefined: math.pi (unexported)
fmt.Println(math.Pi)
}
Multiple Results
한 함수는 몇 개의 결과든 반환 가능 (하나 또는 아무 것도 반환 안하는 C 언어와 다른 점)
return문을 사용하려면 반드시 Return type을 함수 선언부에 명시해야 함 (생략 불가)
package main
import "fmt"
func swap(x, y string) (string, string) {
return y, x // 두 개의 string 반환
}
func main() {
a, b := swap("hello", "world")
fmt.Println(a, b)
}
Named Return values
Go 프로그래밍 언어에서 함수는 리턴값이 없을 수도, 리턴값이 하나 일 수도, 또는 리턴값이 복수 개일 수도 있다.
C 언어에서 void 혹은 하나의 값만을 리턴하는 것과 대조적으로 Go 언어는 복수개의 값을 리턴할 수 있다.
Go에서 Named Return Parameter도 있다.
리턴되는 값들을 (함수에 정의된) 리턴 파라미터들에 할당할 수 있는 기능이다.
이는 리턴되는 값들이 여러 개일 때, 코드 가독성을 높이는 장점이 있다.
가독성을 왜 높인다는 것이냐?
단지 함수의 signature만을 읽고도 return parameter들을 알 수 있기 때문이다.
쓰는 방식은 함수 정의에서 return 변수의 명시적 이름과 타입을 지정하고 이를 리턴 가능한데,
반드시 return을 써줘야 하고, return 만 써주면 반환이 된다.
그러나, := 이 사용 불가하다.
Go 컴파일러에 의해 선언부에 있는 return parameter들이 이미 선언되고 초기화되었기 때문에 오류가 발생하는 것이다.
따라서 (=)을 사용하여 명명된 반환 매개변수에 값을 할당 가능하다.
package main
import "fmt"
func split(sum int) (x, y int) { // 리턴해주는 것에 변수명과 타입을 반드시 지정해야 함
x = sum * 4 / 9
y = sum - x
return // named return values를 통해 return만 쓰면 선언부의 return 값들이 반환됨
}
func main() {
fmt.Println(split(17))
}
Type Safety
타입 안정성
프로그래밍 언어에서 변수나 표현식의 타입이 명확하게 지정되고, 타입 규칙을 준수해야 하는 특성을 말한다.
타입 안정성은 잠재적인 타입 오류를 방지하고 프로그램의 안정성과 신뢰성을 높이는데 도움을 준다.
(타입 안정성 있는 언어에서는 변수 타입을 미리 지정함!)
타입 안정성이 있는 언어에서는 변수의 타입을 선언하거나 할당 시에 명시적으로 지정하며, 해당 타입과 일치하지 않는 타입의 값을 할당하거나 사용하려고 하면 "컴파일 오류"가 발생한다. 이는 변수와 값을 사용하는 시점에서 타입 호환성을 검사하여 타입 오류로 인한 문제를 "사전에 방지"한다.
Go 언어는 타입 안정성을 가진 언어로서,
변수의 타입은 명시적으로 선언되거나 컴파일러가 타입을 추론하여 결정한다.
Variables
Go에서 변수와 상수는 함수 밖에서도 사용할 수 있다.
또한 함수 밖에서의 패키지 레벨에서는 := (Short Assignment Statement)는 사용 못하고 var을 사용해야 한다.
함수 밖에서는 모든 선언이 키워드(var, func, 기타 등등)로 시작하기 때문이다.
만약 선언된 변수가 Go 프로그램 내에서 사용되지 않는다면, 에러를 발생시킨다. 따라서 사용되지 않는 변수는 프로그램에서 삭제한다.
동일한 타입의 변수가 복수 개 있을 경우, 변수들을 나열하고 마지막에 타입을 한번만 지정할 수 있다.
할당되는 값을 보고 그 타입을 추론하는 기능이 자주 사용된다. (타입 명시 없이 알아서 초기화 값을 보고 데이터 타입 지정해줌)
package main
import "fmt"
var c, python, java bool
func main() {
var i int
fmt.Println(i, c, python, java) // 선언하고 초기화하지 않으면 default 값 : 0, false, ""
var j, k int = 2, 3
fmt.Println(j, k)
var s = "Hi" // 타입 선언 안해도 할당되는 값 보고 타입 추론해줌
var z = 1
fmt.Println(s, z)
var p = 1
// p := 1 // 변수 p가 이미 선언되었으므로 여기서 또 선언해줄 수 없음
fmt.Println(p)
q := 2
fmt.Println(q)
}
Data Types
Integers
Signed Integers
- int
- 양수 값과 음수 값을 모두 저장할 수 있습니다.
package main
import ("fmt")
func main() {
var x int = 500
var y int = -4500
fmt.Printf("Type: %T, value: %v", x, x)
fmt.Printf("Type: %T, value: %v", y, y)
int | Depends on platform: 32 bits in 32 bit systems and 64 bit in 64 bit systems |
-2147483648 to 2147483647 in 32 bit systems and -9223372036854775808 to 9223372036854775807 in 64 bit systems |
int8 | 8 bits/1 byte | -128 to 127 |
int16 | 16 bits/2 byte | -32768 to 32767 |
int32 | 32 bits/4 byte | -2147483648 to 2147483647 |
int64 | 64 bits/8 byte | -9223372036854775808 to 9223372036854775807 |
Unsigned Integers
- uint
- 음수가 아닌 값만 저장할 수 있습니다.
package main
import ("fmt")
func main() {
var x uint = 500
var y uint = 4500
fmt.Printf("Type: %T, value: %v", x, x)
fmt.Printf("Type: %T, value: %v", y, y)
}
uint | Depends on platform: 32 bits in 32 bit systems and 64 bit in 64 bit systems |
0 to 4294967295 in 32 bit systems and 0 to 18446744073709551615 in 64 bit systems |
uint8 | 8 bits/1 byte | 0 to 255 |
uint16 | 16 bits/2 byte | 0 to 65535 |
uint32 | 32 bits/4 byte | 0 to 4294967295 |
uint64 | 64 bits/8 byte | 0 to 18446744073709551615 |
Format Specifier
형식 지시자.
package main
import "fmt"
func main() {
var i int
var f float64
var b bool
var s string
fmt.Printf("%v %v %v %q\n", i, f, b, s) // 0 0 false ""
}
- %v
- %v는 정수, 부동 소수점, 문자열, 배열, 맵 등의 값이 주어지면 해당 값을 기본 형식으로 출력한다.
- 이는 Go의 기본 형식 출력 방식이다.
- %q
- 이 형식 지시자는 문자열 값을 출력할 때 사용된다.
Type conversions
명시적 타입 간 변환만 가능
C와 달리 Go는 다른 type의 요소들 간의 할당에는 명시적인 변환을 필요로 함!
즉, 작은 데이터 타입에서 범위가 큰 데이터 타입으로 데이터 타입 변환 시 자동 현변환이 안되는 것이다.
예를 들어 정수형 int에서 uint로 변환할 때, 암묵적(implicit) 변환이 일어나지 않으므로 uint(i) 처럼 반드시 변환을 지정해 주어야 한다.
만약 명시적 지정이 없이 변환이 일어나면 런타임 에러가 발생한다.
C 언어의 경우 암시적인 데이터 타입 변환이 가능한데, 아래 사례를 보면 알 수 있다.
int a = 5;
double b = a; // 암시적인 타입 변환: int를 double로 변환하여 대입
int a = 5;
float b = 2.5;
float result = a + b; // 암시적인 타입 변환: int를 float로 변환하여 연산 수행
그러나 GO 언어의 경우 모든 type conversion에 대해 명시적으로 표기를 반드시 해줘야 한다.
예시 코드는 아래와 같다.
package main
import (
"fmt"
"math"
)
func main() {
var x, y int = 3, 4
var f float64 = math.Sqrt(float64(x*x + y*y)) // int -> float64
var z uint = uint(f) // float64 -> unit
fmt.Println(x, y, z)
}
Type Inference
타입 유추
:= 혹은 var = 표현을 이용해 명시적인 type을 정의하지 않고 변수를 선언할 때, 그 변수 type은 오른편에 있는 값으로부터 유추된다.
하지만, 오른 편에 type이 정해지지 않은 숫자 상수가 올 때, 새 변수는 그 상수의 정확도에 따라 int 혹은 float64, complex128 이 된다.
이게 무슨 뜻이냐면, Go 언어에서 숫자 상수는 타입이 없는 상태로 존재한다. 근데 값에 따라 자동으로 Go 컴파일러는 변수의 타입을 결정한다는 것이다.
예를 들어,
42 는 int 타입으로 인지하고
3.14 는 float64 타입으로 인지하고
2 + 3i 는 complex128 타입으로 인지한다.
Constants
상수
상수는 := 를 사용하지 못한다.
그 이유는 상수의 경우 선언과 초기화가 동시에 한 줄에서 이루어져야 하기 때문인데, 이는 값의 불변성을 유지하기 위해서이다.
따라서 상수는 = 만 사용하여 선언과 동시에 초기화가 일어나야 한다.
이렇게 선언된 상수는 값을 변경할 수 없고, 컴파일 타임에 초기화되어 고정된 값을 가지게 된다.
상수는 변수처럼 선언되지만 const 키워드와 함께 선언됩니다.
상수는 character 혹은 string, boolean, 숫자 값이 될 수 있다.
package main
import "fmt"
const Pi = 3.14
func main() {
const World = "世界"
fmt.Println("Hello", World)
fmt.Println("Happy", Pi, "Day")
const Truth = true
fmt.Println("Go rules?", Truth)
const ( // var() 처럼 한 번에 여러 개의 변수 선언 가능!
visa = "visa"
master = "masterCard"
amx = "american express"
)
const (
Apple = iota
Grape
Orage
)
// visa = "hey"
// 상수이므로 다시 초기화 불가능
// cannot assign to visa (untyped string constant "visa")
fmt.Println(visa)
}