Go 공식 문서: 개념, 예제, 실습
예제 중심의 간결한 Go 레퍼런스. 개념을 읽고 코드를 본 뒤 Coddy 여정에서 직접 연습하세요.
Go 가이드 학습 시작하기시작하기
- Go란?Go(흔히 Golang이라고 부름)는 빠르고 안정적인 서버와 도구를 만들기 위해 Google에서 설계한 정적 타입 컴파일 언어입니다. Go 언어가 무엇인지, 어디에 쓰는지, 어디에서 부족한지를 다룹니다.
- Go 설치Windows, macOS, Linux에서 Go를 내려받아 설치하고, go version으로 설치를 확인하고, GOPATH를 이해하고, "go: command not found" 오류를 고치는 방법을 다룹니다.
- Hello WorldGo hello world 프로그램을 한 줄씩 살펴봅니다. package main, import 블록, func main, fmt.Println, 그리고 실행 방법과 초보자가 가장 먼저 만나는 컴파일 오류까지 다룹니다.
- go run과 go buildgo run, go build, go install이 각각 하는 일, Go가 바이너리 이름을 정하는 방식, GOOS와 GOARCH로 크로스 컴파일하는 법, 커밋 전에 돌릴 go fmt와 go vet 검사를 다룹니다.
- Go 모듈Go 모듈이 동작하는 방식: go mod init으로 모듈 만들기, 모듈 경로 정하기, go get으로 의존성 추가하기, go mod tidy로 정리하기, go.sum의 용도, 그리고 로컬 개발을 위한 replace를 다룹니다.
- 패키지와 importGo 패키지가 동작하는 방식: package 절, 표준 패키지와 모듈 패키지 import, 공개 이름과 비공개 이름, 패키지를 여러 파일로 나누기, internal 패키지, import 별칭, 빈 import, 그리고 go run main.go에서 나오는 "undefined" 오류를 다룹니다.
기초
- 변수Go에서 var와 := 짧은 형태로 변수를 선언하는 방법, 제로 값의 의미, 다중 대입, 그리고 Go 입문자를 괴롭히는 섀도잉과 사용하지 않는 변수 오류를 다룹니다.
- 상수Go에서 const가 동작하는 방식: 상수 선언, 타입 있는 상수와 타입 없는 상수의 차이, 임의 정밀도의 상수 표현식, 그리고 Go에 상수 슬라이스, 맵, 구조체가 없는 이유를 다룹니다.
- 열거형과 iotaGo에는 enum 키워드가 없습니다. 관용적인 대체법인 이름 있는 타입과 iota를 쓴 const 블록, 그리고 String(), 검증, 파싱, 비트 플래그, JSON 지원을 추가하는 방법을 보여 줍니다.
- 자료형Go의 내장 타입: 정수 크기와 범위, float32와 float64, bool, string, byte와 rune, 복소수, 제로 값, 그리고 %T로 값의 타입을 출력하는 방법을 다룹니다.
- 타입 변환Go에서 타입을 변환하는 방법: strconv로 int를 string으로, string을 int로 바꾸기, 실수를 정수로, T(v)로 하는 숫자 변환, 바이트와 룬, string(65) 함정, 변환 오류 처리를 다룹니다.
- 문자열Go에서 문자열 다루기: strings 패키지(Split, Join, Contains, Replace, Fields, TrimSpace, ToUpper), strings.Builder로 효율적으로 문자열 만들기, 여러 줄 원시 문자열, 불변성, 그리고 len이 바이트를 세는 이유를 다룹니다.
- rune과 byteGo에서 rune과 byte의 의미, 문자열이 UTF-8을 저장하는 방식, len이 바이트를 세는 이유, range가 룬을 디코딩하는 방식, string, []byte, []rune 사이의 변환, bytes 패키지 사용법을 다룹니다.
- fmt.Printf와 SprintfGo의 fmt 패키지가 값을 출력하고 형식화하는 방식: Println, Printf, Sprintf, Errorf의 차이, 포맷 동사 전체 표(%v, %+v, %d, %s, %q, %f, %T, %w 등), 너비, 정밀도, 채우기를 다룹니다.
제어 흐름
- if / elseGo에서 if, else if, else가 동작하는 방식: 괄호 없음, 필수 중괄호, 초기화 구문이 있는 if와 그 스코프, if err != nil 관용구, 깊은 중첩 대신 이른 반환을 다룹니다.
- for 반복문Go에는 반복 키워드가 for 하나뿐이고, 이것으로 모든 것을 합니다. 세 절로 된 카운터 반복문, 조건만 있는 while 반복문, 무한 루프, 그리고 슬라이스, 맵, 문자열, 채널, 정수, 함수에 대한 range. 레이블이 붙은 break와 continue, Go 1.22의 반복 변수 변경도 다룹니다.
- while 반복문Go에는 while 키워드가 없습니다. 조건만 있는 for 반복문이 Go의 while문이고, 조건이 없는 for는 무한 루프이며, do-while은 끝에서 조건을 검사하는 무한 루프입니다.
- range 반복문for range가 슬라이스, 배열, 문자열, 맵, 채널, 정수, 이터레이터 함수에 대해 내놓는 값, 값 변수가 복사본인 이유, 순회 중에 슬라이스나 맵을 수정하면 일어나는 일, 그리고 Go 1.23의 함수에 대한 range를 다룹니다.
- switch 문Go에서 switch가 동작하는 방식: case는 기본적으로 다음으로 넘어가지 않고, case 하나에 여러 값을 나열할 수 있으며, 조건 없는 switch는 if/else 체인을 대신합니다. 초기화 구문, fallthrough 키워드, 타입 스위치도 간단히 봅니다.
- 삼항 연산자Go에는 삼항 연산자(cond ? a : b)가 없습니다. 그 이유와 관용적인 if/else 대체법, 기본값을 위한 cmp.Or, 제네릭 헬퍼 함수, 그리고 진짜 삼항 연산자와 다르게 만드는 함정을 설명합니다.
함수
- 함수Go에서 함수를 선언하고 호출하는 방법: 매개변수, 타입을 공유하는 매개변수, 반환 값, 값에 의한 전달, 그리고 저장하고 넘길 수 있는 값으로서의 함수를 다룹니다.
- 다중 반환 값Go 함수는 값을 여러 개 반환할 수 있습니다. 문법, (value, error) 관례, comma-ok 관용구, 이름 있는 결과, naked return, 빈 식별자를 다룹니다.
- 가변 인자 함수Go의 가변 인자(variadic) 함수는 한 타입의 인자를 끝에 몇 개든 받습니다. ...T 문법, s...로 슬라이스 넘기기, 슬라이스 공유 함정, fmt.Println이 ...any를 쓰는 방식을 알아봅니다.
- 클로저Go의 익명 함수는 주변 스코프의 변수를 캡처할 수 있습니다. 이것이 클로저이며, 자기 상태를 가진 카운터, 제너레이터, 미들웨어, 콜백을 만듭니다.
- defer 문defer는 감싸는 함수가 반환될 때 실행할 호출을 예약합니다. LIFO 순서, 인자가 평가되는 시점, 파일 닫기와 뮤텍스 해제, 반복문 안의 defer, 이름 있는 결과 바꾸기를 알아봅니다.
- 메서드메서드는 리시버가 있는 함수입니다. 메서드를 선언하는 법, 값 리시버와 포인터 리시버를 고르는 기준, 메서드 집합과 인터페이스, 구조체가 아닌 타입의 메서드를 알아봅니다.
컬렉션
- 배열Go 배열은 길이가 고정되어 있고 그 길이가 타입의 일부이며, 대입할 때 복사됩니다. 배열을 선언하고, 순회하고, 비교하고, 넘기는 방법과 대부분의 Go 코드가 대신 슬라이스를 쓰는 이유를 알아봅니다.
- 슬라이스슬라이스는 Go에서 매일 쓰는 리스트 타입입니다. make, append와 용량 증가, len과 cap의 차이, 슬라이싱이 기반 배열을 공유하는 방식(과 그로 인한 버그), copy, slices 패키지, 2차원 슬라이스, nil과 빈 슬라이스를 알아봅니다.
- 맵Go 맵은 키-값 쌍을 저장하고 빠르게 조회합니다. 맵을 만들고, comma-ok로 키가 있는지 확인하고, 삭제하고, (무작위 순서로) 순회하고, 키를 정렬하고, 구조체를 저장하는 방법과 nil 맵 및 동시 쓰기 패닉을 피하는 방법을 알아봅니다.
- 집합(Set)Go에는 내장 집합 타입이 없습니다. 표준 관용구는 빈 구조체를 값으로 쓰는 맵입니다. 추가, 확인, 삭제와 합집합, 교집합, 차집합, 그리고 작은 제네릭 Set을 만드는 법을 알아봅니다.
- 정렬slices.Sort와 slices.SortFunc로 Go 슬라이스를 정렬하고, cmp.Compare로 구조체를 필드 하나나 여러 개로 정렬하며, 안정 정렬로 같은 요소의 순서를 유지하고, 예전의 sort.Slice 코드를 읽는 법을 알아봅니다.
구조체, 인터페이스, 제네릭
- 구조체구조체는 이름 있는 필드를 하나의 타입으로 묶습니다. 정의와 초기화, 제로 값, 구조체 포인터, 익명 구조체, 비교, 구조체 태그, NewX 생성자 관례를 알아봅니다.
- 포인터포인터는 값의 주소를 담습니다. &와 *, new, 구조체 포인터, 지역 변수의 포인터를 안전하게 반환할 수 있는 이유, 포인터를 쓸 때, 그리고 nil 포인터 역참조 패닉을 알아봅니다.
- 인터페이스Go 인터페이스는 암묵적으로 만족됩니다. 알맞은 메서드를 가진 타입이라면 무엇이든 구현한 것입니다. io.Reader와 fmt.Stringer 같은 작은 인터페이스, 빈 인터페이스 any, nil 인터페이스 함정, 그리고 인터페이스를 받고 구조체를 반환하라는 원칙을 알아봅니다.
- 구조체 임베딩임베딩은 필드 이름 없이 한 타입을 다른 타입 안에 넣어서, 그 필드와 메서드를 바깥 타입으로 승격시킵니다. 승격의 동작 방식, 인터페이스 임베딩, 이름 충돌, 그리고 임베딩이 상속이 아닌 이유를 알아봅니다.
- 타입 단언타입 단언은 인터페이스에서 구체적인 값을 꺼냅니다. x.(T), 패닉이 나지 않는 comma-ok 형태, 다른 인터페이스로 단언하기, 타입 스위치, 감싼 오류를 위한 errors.As를 알아봅니다.
- 제네릭제네릭(Go 1.18 이상)을 쓰면 함수나 타입 하나가 타입 안전성을 유지하면서 여러 타입에 동작합니다. 타입 매개변수, any, comparable, cmp.Ordered 제약, ~를 쓴 커스텀 제약, 제네릭 타입, 그리고 쓰지 말아야 할 때를 알아봅니다.
에러
- 에러 처리Go는 오류를 함수가 반환하는 평범한 값으로 다룹니다. error 인터페이스, if err != nil, errors.New와 fmt.Errorf, 맥락을 붙여 오류 반환하기, errors.Is와 errors.As로 확인하기, 그리고 오류를 한 번만 처리하는 법을 알아봅니다.
- 커스텀 오류센티널 오류와 커스텀 오류 타입을 정의하고, %w로 오류를 감싸고, errors.Is와 errors.As로 확인하고, errors.Join으로 여러 개를 합치고, 필요할 때 Unwrap과 Is 메서드를 작성하는 법을 알아봅니다.
- panic과 recover패닉은 정상 실행을 멈추고 스택을 되감으면서 지연된 호출을 실행합니다. 패닉의 원인, 지연 함수 안의 recover로 패닉을 멈추는 법, 자주 보는 런타임 오류 메시지, 그리고 패닉이 올바른 선택인 경우를 알아봅니다.
동시성
- 고루틴go 키워드로 함수를 동시에 실행하고, 끝나기를 기다리고, 결과를 돌려받는 방법, 그리고 고루틴이 쉽게 만드는 데이터 레이스, 누수, 충돌을 피하는 방법을 알아봅니다.
- 채널Go 채널이 고루틴 사이에 값을 전달하는 방식: 버퍼 없는 채널과 버퍼 있는 채널, 닫기와 range, 방향 타입, 교착 상태 오류, 그리고 채널로 만든 파이프라인을 다룹니다.
- select 문select가 여러 채널 연산을 한 번에 기다리는 방식: 준비된 case 중에서 고르기, default로 블록되지 않는 송수신, time.After로 타임아웃, 종료 채널이나 컨텍스트로 반복문 멈추기를 다룹니다.
- WaitGroupsync.WaitGroup이 여러 고루틴이 끝나기를 기다리는 방식: Add, Done, Wait 규칙, 포인터로 넘겨야 하는 이유, 결과와 오류 모으기, 그리고 이를 기반으로 만든 워커 풀을 다룹니다.
- 뮤텍스sync.Mutex와 sync.RWMutex로 고루틴 사이의 공유 상태를 보호하는 방법, sync/atomic으로 충분한 경우, sync.Once로 초기화를 정확히 한 번 실행하는 법, 그리고 교착 상태를 일으키는 잠금 실수를 다룹니다.
- context 패키지context.Context가 Go 프로그램 전체로 취소, 마감 시간, 요청 범위의 값을 전달하는 방법: Background, WithCancel, WithTimeout, WithValue, select 안의 ctx.Done, HTTP 서버와 클라이언트의 컨텍스트를 다룹니다.
표준 라이브러리
- time 패키지Go에서 날짜와 시간을 다루는 방법: time.Now와 time.Sleep, Duration 산술, Format과 Parse를 위한 2006-01-02 15:04:05 기준 레이아웃, 시간대, Unix 타임스탬프, 타이머와 티커를 다룹니다.
- JSONencoding/json으로 Go에서 JSON을 인코딩하고 디코딩하는 방법: Marshal과 Unmarshal, omitempty와 omitzero 같은 구조체 태그, 보기 좋게 출력하기, map[string]any로 디코딩하기, 알 수 없는 필드 거부하기, Decoder로 스트리밍하기를 다룹니다.
- 파일 읽기와 쓰기Go에서 파일을 읽고 쓰는 방법: os.ReadFile과 os.WriteFile, bufio.Scanner로 한 줄씩 읽기, os.OpenFile로 이어 쓰기, 파일이 있는지 확인하기, 디렉터리 다루기를 알아봅니다.
- HTTP 서버Go 표준 net/http 패키지로 웹 서버를 만드는 방법: 핸들러, 메서드와 경로 와일드카드를 쓰는 ServeMux 라우팅(Go 1.22), JSON 응답, 상태 코드, 미들웨어, 타임아웃, 정상 종료를 다룹니다.
- HTTP 클라이언트net/http로 Go에서 HTTP 요청을 보내는 방법: http.Get, 본문 읽기와 닫기, 상태 코드 확인, http.Client 타임아웃, 컨텍스트와 헤더가 있는 요청, 쿼리 매개변수, JSON POST를 다룹니다.
- 명령줄 인자Go 프로그램이 명령줄을 읽는 방법: os.Args, 타입이 있는 옵션을 위한 flag 패키지, FlagSet으로 만드는 하위 명령, os.Getenv와 os.LookupEnv로 읽는 환경 변수, os.Exit로 정하는 종료 코드를 다룹니다.
- 정규 표현식regexp 패키지로 Go에서 정규 표현식을 쓰는 방법: MustCompile, MatchString, FindString과 FindAllString, 캡처 그룹과 이름 있는 그룹, ReplaceAllString, 그리고 후방 탐색이 없는 등 RE2 문법의 한계를 다룹니다.
- 로깅Go에서 로그를 남기는 방법: 플래그와 log.Fatal이 있는 전통적인 log 패키지, 그리고 레벨, 키-값 속성, 텍스트와 JSON 핸들러, With로 맥락을 담는 로거를 갖춘 구조화된 로깅용 log/slog(Go 1.21)를 다룹니다.
테스트와 프로젝트 구조
- 테스트표준 testing 패키지와 go test로 Go 코드를 테스트하는 방법: _test.go 파일, TestXxx 함수, t.Errorf와 t.Fatalf의 차이, t.Run을 쓴 테이블 기반 테스트, 헬퍼와 임시 디렉터리, 커버리지, b.Loop를 쓴 벤치마크, 예제 테스트를 다룹니다.
- 프로젝트 구조Go 프로젝트를 배치하는 방법: 평평하게 시작하고, 이유가 생기면 패키지로 나누고, 여러 바이너리에는 cmd/를, 아무도 import하면 안 되는 코드에는 internal/을 쓰고, 패키지 이름을 잘 짓고, 테스트는 코드 옆에 둡니다.