Go 개발환경
'셸(Shell)'은 사용자의 명령어를 해석하고 운영체제가 알아들을 수 있게 지시해주는 것으로, 사용자와 커널을 이어주는 것이다.
'커널(kernel)'은 이와 같은 내용을 받아 하드웨어에 직접적으로 명령을 내려준다.
셸 : 사용자가 글자를 입력하여 컴퓨터에 명령할 수 있도록 하는 프로그램
터미널/콘솔 : 셸을 실행하기 위해 글자 입력을 받아 컴퓨터에 전달하거나 컴퓨터의 출력을 글자 화면에 쓰는 프로그램
1.3 go 명령어
바이너리(실행파일) 생성
go build hello.go
go build -o hello_world.exe
hello.go
1.3.2 서드 파티 Go 도구 설치
go는 다른 언어와 달리 자신의 코드 저장소를 공유하여 패키지를 직접 다운받고 설치 가능
예)
go install github.com/rakyll/hey@latest
1.3.3 코드 포매팅
1.4 린팅, 베팅
Go 위키의 코드 리뷰 규칙
linter
1.5 개발 도구 소개
1.5.1 비주얼 스튜디오 코드
1.5.3 Go Playground
1.6 Makefiles 텍스트 파일
chap. 02
2.1 내장 타입
https://go101.org/article/basic-types-and-value-literals.html
1) 불리언
2) 숫자
2.1.1 제로 값
2.1.2 리터럴
코드에 작성하는 값 그 자체
정수 리터럴
2.1.3 불리언
2.1.4 숫자 타입
2.1.5 문자열과 룬 맛보기
2.1.6 명시적 타입 변환
cf.) Explicit vs. Implicit
명시적 vs. 암시적(암묵적)
⇒ Manual vs. Automatic
2.2 var vs. :=
변수 선언 스타일에 따른 의도가 존재
var 키워드
[SYNTAX]
var 변수 타입 = 값(표현식)
2.3 const 사용
2.4 타입 지정 상수와 타입 미지정 상수
2.5 사용하지 않는 변수
2.6 변수와 상수 이름 짓기
chap. 03
3.1 배열(array)
선언 방법
1) 제로 값으로 선언: 배열의 크기와 배열 내 요소 타입 지정
2) 배열 초깃값 지정: 배열 리터럴 사용
3) 희소 배열: 대부분의 요소가 0인 배열
4) 배열 크기 지정하지 않고: 배열 리터럴 필요
5) 다차원 배열: []의 개수가 차원 수
배열 요소 접근
3.2 슬라이스
일련의 값(시퀀스)을 저장하는 자료 구조
순서 중요
슬라이스의 크기는 타입의 일부가 아니다 (배열과의 큰 차이점)
슬라이스 간 비교는 불가 (배열과 다른 점)
단, nil과는 비교 가능(==, !=)
슬라이스 선언 (슬라이스 크기를 지정하지 않음)
1) 슬라이스 초깃값 지정: 슬라이스 리터럴
2) 희소 슬라이스
3) 다차원 슬라이스
4) 제로 슬라이스: 슬라이스 리터럴 없이 선언만 하는 것
3.2.2 append
3.2.3 수용력(capacity)
3.2.4 make()
3.2.5 슬라이스 선언
var data []int => nil
var data = []int{} => nil(X)
슬라이스를 아래 방식 중 어떤 걸로 생성할 지 정리해 봅시다.(p.75)
3.2.6 슬라이싱의 슬라이싱
슬라이스 연산자를 사용하면 복사본을 만들지 않고 메모리를 공유함
슬라이싱과 append를 함께 사용하면 혼란이 가중됨
하위 슬라이스의 수용력
= 원본 슬라이스의 수용력 - 하위 슬라이스 시작 오프셋
하위 슬라이스와 append를 아무 생각 없이 사용하면 혼란이 가중된다.
3.2.7 배열을 슬라이스로 변환
3.2.8 copy
3.3 문자열과 룬 그리고 바이트
3.4 맵
4) make() 함수로 생성
3.4.1 맵 읽고 쓰기
3.4.2 콤마 OK 관용구(idiom)
3.4.3 맵 (요소) 삭제
3.4.4 맵을 셋(집합)으로 이용
3.5 구조체
3.5.1 익명 구조체
3.5.2 구조체 비교와 변환
chap. 04
4.1 블록(block)
4.1.1 섀도잉 변수
4.2 if 문
4.3 for 문
4.3.1 완전한 for 구문
1) 선언부는 한 번만 실행
2) 조건부가 참일 경우에만 for 블록 실행, 거짓이면 for 문 종료
3) 증감부는 for 블록 실행이 끝나고 다시 2)으로 가기전에 실행
4.3.2 조건식만 사용하는 for 문
4.3.3 for 문을 이용한 무한루프
4.3.4 break와 continue
4.3.5 for-range 문
(index, value) 쌍
cf.) Go에서는 반환되는 값을 사용할 의도나 필요가 없다면 _(underscore)로 받는다. (Python과 같은 개념)
4.3.7 4 가지 중 알맞은 for 문 선택
4.4 switch 문
case
}
4.5 공백 스위치
4.6 if 문과 switch 문 중 선택
4.7 goto 문
chap. 05
5.1 함수 선언과 호출
함수 선언 = 함수 헤더(시그니처) + 함수 몸체(바디)
1) func 키워드
2) 함수 이름
3) 입력 파라미터: 반드시 타입 명시 (Go는 정적 언어)
4) 반환값의 타입
⇒ 함수의 시그니처
return 키워드
여러 입력 파라미터가 같은 타입이라면 콤마로 파라미터를 구별하고 타입은 마지막에 기술할 수 있다.
5.1.1 이름이 지정된 파라미터(named paramters)와 선택적 파라미터 대응
5.1.2 가변 입력 파라미터와 슬라이스
임의 개수의 입력 파라미터 처리
가변 파라미터는 반드시 함수의 입력 파라미터 목록에서 마지막에 위치
타입 이름 앞에 ...
가변 파라미터는 함수 내에서는 해당 타입의 슬라이스이다
슬라이스를 파라미터로 Unpack하려면 슬라이스뒤에 ...을 붙여준다
5.1.3 다중 반환값
함수 정의시 반환값의 타입을 콤마로 구분하고 괄호로 묶어준다
return 시 반환값을 괄호로 묶지 않는다
일반적으로 다중 반환값을 받을 때는 := 사용
5.1.4 다중 반환값은 다중값
5.1.5 반환되는 값 무시
5.1.6 이름이 지정된 반환값(naked return)
5.1.7 빈 반환(blank return)
5.2 함수는 값이다
5.2.1 함수 타입 선언
5.2.2 익명 함수
5.3 클로저(Closure)
package main
func nextValue() func() int {
i := 0
return func() int {
i++
return i
}
}
func main() {
next := nextValue()
println(next()) // 1
println(next()) // 2
println(next()) // 3
anotherNext := nextValue()
println(anotherNext()) // 1 다시 시작
println(anotherNext()) // 2
}
5.3.1 파라미터로 함수를 전달
5.3.2 함수에서 함수 반환
5.4 defer
package main
import "os"
func main() {
f, err := os.Open("1.txt")
if err != nil {
panic(err)
}
// main 마지막에 파일 close 실행
defer f.Close()
// 파일 읽기
bytes := make([]byte, 1024)
f.Read(bytes)
println(len(bytes))
}
5.5 값에 의한 호출을 사용하는 Go
chap. 06
6.1 빠른 포인터 입문
주소 연산자(&)
간접 연산자(*, dereferencing operator)
모든 포인터는 어떤 타입을 가리키던간에 항상 같은 크기
포인터의 제로 값: nil
nil은 숫자 0이 아닙니다.
포인터 dereferencing 전에 nil인지 확인할 필요 있다.
포인터 타입
6.2 포인터를 두려워 말라
6.3 포인터는 변경 가능한 파라미터를 가리킨다
6.4 포인터는 최후의 수단
6.5 포인터로 성능 개선
6.6 제로 값과 값없음의 차이
0, ‘ ‘ vs. NULL, ‘’ ⇒ Go: nil
값이 있음 vs. 값이 없음
포인터를 이용하여 변수나 구조체의 항목의 값이 제로 값인지 없는 값인지 구분하는데 사용
할당되지 않은 변수나 구조체 항목에 nil 포인터를 사용
또한, 포인터는 변경가능함을 나타내므로 함수에서 nil 포인터를 직접 반환하는 것보다 콤마 OK 관용구를 사용하자
nil 포인터를 함수의 파라미터나 구조체의 항목의 값으로 담아서 함수의 인자로 넘기면 nil 포인터를 통해서는 값을 저장할 수 없으므로 함수 안에서 값을 설정(간접 연산자 *를 사용하는 것)할 수가 없음을 명심
6.7 맵과 슬라이스의 차이
6.8 버퍼 슬라이스
6.9 가비지 컬렉션 작업량 줄이기
chap. 07
7.1 Go의 타입
7.2 메서드
package main
//Rect - struct 정의
type Rect struct {
width, height int
}
//Rect의 area() 메소드
func (r Rect) area() int {
return r.width * r.height
}
func main() {
rect := Rect{10, 20}
area := rect.area() //메서드 호출
println(area)
}
7.2.1 포인터 리시버와 값 리시버
package main
import (
"fmt"
"time"
)
type Counter struct {
total int
lastUpdated time.Time
}
func (c *Counter) Increment() {
c.total++
c.lastUpdated = time.Now()
}
func (c Counter) String() string {
return fmt.Sprintf("total: %d, last updated: %v", c.total, c.lastUpdated)
}
func main() {
var c Counter
fmt.Println(c.String())
c.Increment()
fmt.Println(c.String())
}
package main
import (
"fmt"
"time"
)
type Counter struct {
total int
lastUpdated time.Time
}
func (c *Counter) Increment() {
c.total++
c.lastUpdated = time.Now()
}
func (c Counter) String() string {
return fmt.Sprintf("total: %d, last updated: %v", c.total, c.lastUpdated)
}
func doUpdateWrong(c Counter) {
c.Increment()
fmt.Println("in doUpdateWrong:", c.String())
}
func doUpdateRight(c *Counter) {
c.Increment()
fmt.Println("in doUpdateRight:", c.String())
}
func main() {
var c Counter
doUpdateWrong(c)
fmt.Println("in main:", c.String())
doUpdateRight(&c)
fmt.Println("in main:", c.String())
}
// 포인터 Receiver
func (r *Rect) area2() int {
r.width++
return r.width * r.height
}
func main() {
rect := Rect{10, 20}
area := rect.area2() //메서드 호출
println(rect.width, area) // 11 220 출력
}
7.2.2 nil 인스턴스를 위한 메서드 작성
package main
import (
"fmt"
)
type IntTree struct {
val int
left, right *IntTree
}
func (it *IntTree) Insert(val int) *IntTree {
if it == nil {
return &IntTree{val: val}
}
if val < it.val {
it.left = it.left.Insert(val)
} else if val > it.val {
it.right = it.right.Insert(val)
}
return it
}
func (it *IntTree) Contains(val int) bool {
switch {
case it == nil:
return false
case val < it.val:
return it.left.Contains(val)
case val > it.val:
return it.right.Contains(val)
default:
return true
}
}
func main() {
var it *IntTree
it = it.Insert(5)
it = it.Insert(3)
it = it.Insert(10)
it = it.Insert(2)
fmt.Println(it.Contains(2))
fmt.Println(it.Contains(12))
}
7.2.3 메서드도 함수이다
public class Sample {
int sum(int a, int b) {
return a + b;
}
public static void main(String[] args) {
int a = 3;
int b = 4;
Sample sample = new Sample();
int c = sample.sum(a, b);
System.out.println(c); // 7 출력
}
}
7.2.4 함수와 메서드 비교
7.2.5 타입 선언은 상속되지 않는다
7.2.6 타입은 실행가능한 문서
7.2.7 열거형을 위한 iota
package main
import "fmt"
type State uint
const (
StateCreated State = iota // 0
StateInitialized // 1
StateRunning // 2
StateStopped // 3
)
func main() {
var state = StateRunning
fmt.Printf("State: %v\n", state) // prints State: 2
}
package main
import "a"
// func from the outside that handles a.base via a.Baser
// since a.base is not exported, only exported bases that are created within package a may be used, like a.A, a.C, a.T. and a.G
func HandleBasers(b a.Baser) {
base := b.Base()
base.OtherMethod()
}
// func from the outside that returns a.A or a.C, depending of condition
func AorC(condition bool) a.Baser {
if condition {
return a.A
}
return a.C
}
7.3 구성(composition)을 위한 임베딩 사용
7.4 임베딩은 상속이 아니다
package main
import (
"fmt"
)
type Employee struct {
Name string
ID string
}
func (e Employee) Description() string {
return fmt.Sprintf("%s (%s)", e.Name, e.ID)
}
type Manager struct {
Employee
Reports []Employee
}
func (m Manager) FindNewEmployees() []Employee {
// do business logic
return nil
}
func main() {
m := Manager{
Employee: Employee{
Name: "Bob Bobson",
ID: "12345",
},
Reports: []Employee{},
}
var eFail Employee = m // compilation error!
var eOK Employee = m.Employee // ok!
}
package main
import "fmt"
type Inner struct {
A int
}
func (i Inner) IntPrinter(val int) string {
return fmt.Sprintf("Inner: %d", val)
}
func (i Inner) Double() string {
result := i.A * 2
return i.IntPrinter(result)
}
type Outer struct {
Inner
S string
}
func (o Outer) IntPrinter(val int) string {
return fmt.Sprintf("Outer: %d", val)
}
func main() {
o := Outer{
Inner: Inner{
A: 10,
},
S: "Hello",
}
fmt.Println(o.Double())
}
7.5 인터페이스
암묵적 인터페이스
: Go의 유일한 추상 타입
인터페이스의 목적/용도
interface 키워드
인터페이스 리터럴
위 ...에는 인터페이스를 만족시키기 위한 구체적 타입에서 반드시 구현(implement)해야 할 메서드 시그니처 나열
Go에서 관례적으로 인터페이스 이름은 끝에 ‘er’을 붙인다
인터페이스는 모든 블록에서 선언 가능
7.6 인터페이스는 타입에 안정적인 덕 타이핑이다.
function calculate(a, b, c) => return (a+b)*c
a = calculate (1, 2, 3)
b = calculate ([1, 2, 3], [4, 5, 6], 2)
c = calculate ('apples ', 'and oranges, ', 3)
print to_string a
print to_string b
print to_string c
7.7 임베딩과 인터페이스
7.8 인터페이스를 받고 구조체 반환하기
7.9 인터페이스와 nil
7.11 타입 단언과 타입 스위치
인터페이스 변수에 할당된 구체 타입 확인
구체 타입이 다른 인터페이스를 구현했는지 확인
타입 단언(Type Assertion)
chap. 10
10.1 동시성 사용 시점
10.2 고루틴
cf) 동기(synchronous) vs. 비동기(asynchronous)
ex) 동기 통신 vs 비동기 통신
10.3 채널