구글이 만들었고 DevOps가 애용하는 언어 중 하나인 Go 언어의 기초에 대해서 정리하였습니다.
Go 언어 설치
•
공식 홈페이지 에서 다운받아서 설치할 수 있습니다.
Go 언어 소개 및 특징
•
구글의 로버트 그리즈머, 롭 파이크, 켄 톰슨이 개발하였습니다.
•
2007년 개발을 시작하여 2012년 버전 1.0이 나왔고 2021년 2월 16일에 1.16이 릴리즈되었습니다.
•
Go는 일차적으로 시스템 프로그래밍을 위해 개발되었으며, C++, Java, Python의 장점들을 뽑아 만들어졌습니다. (다 덤벼)
◦
정적 타입(컴파일시 타입 결정), 강한 타입(암시적 형변환 불가)의 언어입니다.
◦
Java와 같이 Garbage Collection 기능을 제공합니다.
◦
문법이 간결하고 배우기 쉽습니다.
•
쓰레드보다 매우 가볍고 쉽게 이용이 가능한 Go Routine이 있고 channel을 통해서 동시성 프로그래밍을 구현할 수 있습니다.
•
컴파일하면 네이티브 바이너리(실행 파일)을 만들어냅니다. JVM, .NET 프레임워크 같은 가상 머신이나 각종 언어 플랫폼을 설치하지 않아도 바로 실행가능합니다.
Hello, Golang!
백문이 불여일견입니다. hello golang를 실행해보겠습니다.
// main.go
package main
func main() {
println("hello golang")
}
Go
복사
> go run main.go
hello golang
Bash
복사
GOROOT, GOPATH
GOROOT is a variable that defines where your Go SDK is located. Go가 설치된 디렉토리.
GOPATH is a variable that defines the root of your workspace. Go의 표준 패키지 이외의 3rd Party 패키지나 사용자 정의 패키지를 찾는 위치.
go의 환경변수는 go env로 확인할 수 있습니다.
> go env
# ...
GOROOT="/home/user/.go"
GOPATH="/home/user/go"
# ...
Bash
복사
변수와 상수
1. 변수
변수는 var를 사용하여 선언합니다. var 뒤에 변수명, 변수타입을 적습니다.
var a int //초기값 없음
var f float32 = 11 //초기값 할당
a = 11 //선언된 변수에 값 할당
f = 12.0 //선언된 변수에 값 할당
var i, j, k int //동일한 타입의 변수들은 한 줄로 선언 가능
var i, j, k int = 1, 2, 3 //동일한 타입의 변수들은 한 줄로 선언 및 할당 가능
var i = 1 //타입없이 추론 (정수형)
var s = "Hi" //타입없이 추론 (문자열)
Go
복사
변수를 선언하면서 초기값을 지정하지 않으면, Go는 Zero Value를 기본적으로 할당합니다. 즉, 숫자형에는 0, bool 타입에는 false, 그리고 string 형에는 ""(빈문자열)을 할당합니다.
Go 언어는 변수를 선언만 하고 사용하지 않을 경우 에러를 발생시킵니다. _ 이름을 가진 변수는 선언되고 사용되지 않아도 컴파일 에러가 나지 않습니다.
var a, b = 1, 2
println(a) //a만 사용되고 b는 사용되지 않았으므로 에러 발생
var _ = 3 //선언되고 사용하지 않아도 에러나지 않음
print(b)
Go
복사
선언과 할당을 하는 Short Assignment Statement(:=) 도 가능합니다. 단, Short Assignment Statement은 함수 내에서만 사용할 수 있습니다.
a := 1 // Error
func main() {
b := 2 // OK
print(b)
}
Go
복사
2. 상수
상수는 const 키워드를 사용합니다.
const c int = 10
const s string = "Hi"
s = "Hello" //상수에 할당시 에러
// 여러 개의 상수들을 묶어서 지정 가능
const (
Visa = "Visa"
Master = "MasterCard"
Amex = "American Express"
)
// iota를 사용하면 iota를 지정한 상수부터 0이 할당되고 순서대로 1씩 증가한 값을 부여
const (
Apple = iota // 0
Grape // 1
Orange // 2
)
Go
복사
상수는 변수와는 다르게 선언/할당하고 사용하지 않아도 에러가 나지 않습니다.
3. 예약어
참고로 다음의 25개의 예약어는 변수/상수/함수의 이름으로 사용할 수 없습니다.
break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
Plain Text
복사
Go 데이터 타입
1. 데이터 타입
1.
불리언 타입
•
bool
2.
문자열 타입
•
string: string은 한번 생성되면 수정될 수 없는 Immutable 타입
3.
정수형 타입
•
int int8 int16 int32 int64
•
uint uint8 uint16 uint32 uint64 uintptr
4.
Float 및 복소수 타입
•
float32 float64 complex64 complex128
5.
기타 타입
•
byte: uint8과 동일하며 바이트 코드에 사용
•
rune: int32과 동일하며 유니코드 코드포인트에 사용
2. 문자열
문자열에는 Raw String Literal(``)과 Interpreted String Literal("")이 있습니다.
func main() {
// Raw String Literal
rawLiteral := `아리랑\n
아리랑\n
아라리요`
// Interpreted String Literal
interLiteral := "아리랑아리랑\n아리리요"
// 아래와 같이 +를 사용하여 두 라인에 걸쳐 사용할 수도 있다.
// interLiteral := "아리랑아리랑\n" +
// "아리리요"
fmt.Println(rawLiteral)
fmt.Println()
fmt.Println(interLiteral)
}
/* 출력
// Raw String Literal
아리랑\n
아리랑\n
아라리요
// Interpreted String Literal
아리랑아리랑
아리리요
*/
Go
복사
데이터 타입 변환
하나의 데이타 타입에서 다른 데이타 타입으로 변환하기 위해서는 T(v) 와 같이 표현하고 이를 Type Conversion이라고 부릅니다.
func main() {
var i int = 100
var u uint = uint(i) // int -> uint
var f float32 = float32(i) // uint -> float
println(f, u) // 출력결과: +1.000000e+002 100
str := "ABC"
bytes := []byte(str) // 문자열 -> 바이트 배열
str2 := string(bytes) // 바이트 배열 -> 문자열
println(bytes, str2) // 출력결과: [3/32]0xc000034748 ABC
}
Go
복사
연산자
1. 산술연산자
산술연산자에는 사칙연산자(+, -, *, /, %(Modulus))와 증감연산자(++, --)가 있습니다.
c = (a + b) / 5;
i++
Go
복사
2. 관계연산자
관계연산자는 서로의 크기를 비교하거나 동일함을 체크하는데 사용됩니다.
a == b
a != c
a >= b
Go
복사
3. 논리연산자
AND(&&), OR(||), NOT(!)을 표현하는데 사용됩니다.
A && B
A || !(C && B)
Go
복사
4. Bitwise 연산자
바이너리 AND, OR, XOR와 바이너리 쉬프트 연산자가 있습니다.
// << : 현재 값의 비트를 특정 횟수만큼 왼쪽으로 이동합니다
c = (a & b) << 5
Go
복사
5. 할당연산자
값을 할당하는 = 연산자 외에 사칙연산, 비트연산을 축약한 +=, &=, <<= 같은 연산자들도 있습니다.
a = 100
a *= 10
a >>= 2
a |= 1
Go
복사
6. 포인터연산자
C++와 같이 & 혹은 * 을 사용하여 해당 변수의 주소를 얻어내거나 이를 반대로 Dereference 할 때 사용합니다.
var k int = 10
var p = &k //k의 주소를 할당
println(*p) //p가 가리키는 주소에 있는 실제 내용을 출력
Go
복사
조건문
1. if 문
if 문은 해당 조건이 맞으면 { } 블럭안의 내용을 실행합니다. Go의 if 조건문은 아래 예제에서 보듯이 조건식을 괄호( )로 둘러 싸지 않아도 됩니다. 그리고 반드시 조건 블럭 시작 브레이스({)를 if문과 같은 라인에 두어야 하며 그렇지 않으면 에러가 발생합니다. 이를 다음 라인에 두게 되면 에러를 발생시킵니다. 더불어 한가지 주목할 점은 C/C++ 같은 다른 언어들이 조건식에 1, 0 과 같은 숫자를 쓸 수 있는 것과 대조적으로 if 문의 조건식은 반드시 Boolean 식으로 표현되어야 한다는 점입니다.
// if 문
if k == 1 { //같은 라인
println("One")
}
// if, else if, else 문
if k == 1 {
println("One")
} else if k == 2 { //같은 라인
println("Two")
} else { //같은 라인
println("Other")
}
Go
복사
다음과 같이 조건식을 사용하기 전에 간단한 문장(Optional Statement)을 실행할 수 있습니다. 물론 Optional Statement에서 사용된 변수를 scope를 벗어나면 에러가 발생합니다.
// if OptionalStatement;조건식 {...}
if val := i * 2; val < max {
println(val)
}
// 아래 처럼 사용하면 Scope 벗어나 에러가 발생
val++
Go
복사
2. switch 문
여러 값을 비교해야 하는 경우 혹은 다수의 조건식을 체크해야 하는 경우 switch 문을 사용합니다. 복수개의 case 값들이 있을 경우는 아래 예제에서 보듯이 case 3,4 처럼 콤마를 써서 나열할 수 있습니다.
package main
func main() {
var name string
var category = 1
switch category {
case 1:
name = "Paper Book"
case 2:
name = "eBook"
case 3, 4:
name = "Blog"
default:
name = "Other"
}
println(name)
}
// Paper Book
Go
복사
switch문 조건식에 연산 Expression을 사용할 수도 있습니다.
package main
func main() {
var name string
var category = 1
// x에 category의 값을 대입; x+1의 값으로 조건식 판별
switch x := category; x + 1 {
case 1:
name = "Paper Book"
case 2:
name = "eBook"
case 3, 4:
name = "Blog"
default:
name = "Other"
}
println(name)
}
// eBook
Go
복사
Go의 switch 문에서 한가지 특징적인 용법은 switch 뒤에 조건변수 혹은 Expression을 적지 않는 용법입니다. 이 경우 각 case 조건문들을 순서대로 검사하여 조건에 맞는 경우 해당 case 블럭을 실행하고 switch문을 나올 수 있습니다. 이 용법은 복잡한 if...else if...else if... 구조를 단순화하는데 유용합니다.
package main
func main() {
score := 75
// `switch 변수/Expression`의 형태가 아님
// 각 case에서 검사
switch {
case score >= 90:
println("A")
case score >= 80:
println("B")
case score >= 70:
println("C")
case score >= 60:
println("D")
default:
println("No Hope")
}
}
// 결과: C
Go
복사
Go의 또 다른 용법은 switch 변수의 타입을 검사하는 Type switch가 있습니다. 아래 예제는 변수 i의 타입을 체크한 후 해당 case 블럭을 실행하는 예입니다.
package main
import "fmt"
func whatAmI(i interface{}) {
switch t := i.(type) {
case bool:
fmt.Println("I'm a bool")
case int:
fmt.Println("I'm an int")
default:
fmt.Printf("Don't know type %T \n", t)
}
}
func main() {
whatAmI(true)
whatAmI(1)
whatAmI("hey")
}
/*
I'm a bool
I'm an int
Don't know type string
*/
Go
복사
fallthrough를 사용하면 이하의 case와 default를 모두 실행할 수 있습니다.
package main
import "fmt"
func main() {
check(2)
}
func check(val int) {
switch val {
case 1:
fmt.Println("1 이하")
fallthrough
case 2:
fmt.Println("2 이하")
fallthrough
case 3:
fmt.Println("3 이하")
fallthrough
default:
fmt.Println("default 도달")
}
}
/*
2 이하
3 이하
default 도달
*/
Go
복사
반복문
Go 언어에서는 반복문은 for 루프 밖에 없습니다.
1. for 문
가장 기본이 되는 for문입니다. for 초기값; 조건식; 증감 { ... } 형식을 따르고 있습니다.
// 1부터 100까지 더하는 예제
package main
func main() {
sum := 0
for i := 1; i <= 100; i++ {
sum += i
}
println(sum)
}
Go
복사
2. for 문 - 조건식만 쓰는 for 루프
초기값과 증감식을 생략하고 조건식만 사용할 수 있습니다. 다른 언어의 while 문처럼 사용할 수 있습니다.
package main
func main() {
n := 1
for n < 100 {
n *= 2
// 중간에 조건을 주고 break 가능
//if n > 50 {
// break
//}
}
println(n)
}
Go
복사
3. for 문 - 무한루프
"초기값; 조건식; 증감" 모두를 생략하면 무한루프를 만들 수 있습니다.
package main
func main() {
for {
println("Infinite loop")
}
}
Go
복사
4. for range 문
for range 문은 컬렉션으로 부터 한 요소(element)씩 가져와 차례로 for 블럭의 문장들을 실행합니다. 다른 언어의 foreach와 유사합니다. for range 문은 for 인덱스,요소값 := range 컬렉션 형식을 가지고 있으며 인덱스와 요소를 차례대로 가져옵니다
names := []string{"홍길동", "이순신", "강감찬"}
for index, name := range names {
println(index, name)
}
/*
0 홍길동
1 이순신
2 강감찬
*/
Go
복사
5. break, continue, goto 문
경우에 따라 for 루프내에서 즉시 빠져나올 필요가 있는데, 이때 break 문을 사용합니다. 만약 for 루프의 중간에서 나머지 문장들을 실행하지 않고 for 루프 시작부분으로 바로 가려면 continue문을 사용합니다. 그리고 기타 임의의 문장으로 이동하기 위해 goto 문 을 사용할 수 있습니다. goto문은 for 루프와 관련없이 사용될 수 있습니다.
break문은 for 루프 이외에 switch문이나 select문에서도 사용할 수 있지만 continue문은 for 루프와 연관되어서만 사용됩니다.
package main
func main() {
var a = 1
for a < 15 {
if a == 5 {
a += a
continue // for루프 시작으로
}
a++
if a > 10 {
break //루프 빠져나옴
}
}
if a == 11 {
goto END //goto 사용예
}
println(a)
END:
println("End")
}
Go
복사
break문은 보통 단독으로 사용되지만, 경우에 따라 break 레이블 과 같이 사용하여 반복문을 빠져나와서 지정된 레이블로 이동할 수도 있습니다. 다시 말해서 break의 "레이블"은 보통 현재의 for 루프를 바로 위에 적게 되는데, 이러한 break 레이블은 현재의 루프를 빠져나와 지정된 레이블로 이동하고, break문의 직속 for 루프 전체의 다음 문장을 실행하게 합니다. 아래 예제는 언뜻 보기에 무한루프인 것 같지만, 실제로는 OK를 출력하고 정상 종료합니다. 이는 "break L1" 문이 for 루프를 빠져나와 L1 레이블로 이동한 후, break가 있는 현재 for 루프를 건너뛰고 다음 문장인 println() 으로 이동하기 때문입니다.
package main
func main() {
i := 0
// 2. L1 도착
L1:
for {
if i == 0 {
break L1
// 1. 반복문 탈출하고 L1으로 가즈아~
}
}
// 3. break를 사용하였으니 반복문은 건너뛰고 println 실행
println("OK")
}
Go
복사