Go
|
Go는 구글이 개발한 프로그래밍 언어이다. 가비지 컬렉션 기능이 있고, 병행성(concurrent)을 잘 지원하는 컴파일 언어다.
Go의 초기 디자인은 2007년 9월 21일에 로버트 그리즈머, 롭 파이크, 켄 톰슨이 Inferno 분산 운영체제와 관련된 작업을 하다가 시작되었다. 화이트 보드에 새로운 언어에 대한 스케치를 하면서 초기 20% 파트타임 프로젝트로 시작하였다가 2008년 1월 켄 톰슨이 C 코드를 만들어내는 컴파일러를 만들기 시작했고, 2008년 중반 풀타임 프로젝트로 승격되었다. 2008년 5월 이안 테일러가 Go 스펙의 초안을 이용해서 GCC 프론트엔드를 만들기 시작했고, 2008년 말 러스 콕스가 참여하면서 프로토타입에서 실질적인 언어와 라이브러리들을 만들기 시작했다. 2009년 11월 10일에 리눅스와 Mac OS X 플랫폼을 대상으로 공식 발표되었다. Go가 처음 런칭되었을 때는 실무적인 소프트웨어를 만들기에는 준비가 좀 덜 된 상태였지만, 2010년 5월 롭 파이크는 구글에서 실제로 사용되고 있는 부분이 있다고 공개적으로 알리게 되었다.
Categories
- Effective Go
- Go:Warnings - 코드에서 나는 고약한 냄새 지우는 방법
- Go:Receiver
- Go:Embedding
- Go:Interface
- Go:Generics
- Go:Modernize
- Go:StructTags
- Go:Fix (go fix)
- Go:encoding/json
- cgo
Modules
- go.mod와 go.sum 도 커밋해야 할까? - 기계인간 John Grib -> 결론은 "Yes".
Environment
- GOROOT
- Go가 설치된 디렉토리(윈도우즈의 경우 디폴트로
C:\Go)를 가리키며, Go 실행파일은GOROOT/bin폴더에, Go 표준 패키지들은GOROOT/pkg폴더에 있다. (윈도우즈에 GO 설치시 시스템 환경변수로 자동으로 설정된다)
- GOPATH
- Go는 표준 패키지 이외의 3rd Party 패키지나 사용자 정의 패키지들을 이
GOPATH에서 찾는다. 복수 개의 경로를 지정한 경우, 3rd Party 패키지는 처음 경로에 설치된다.
Libraries
- TinyGo - A Go Compiler For Small Places
WebRTC
Web Frameworks
Go에서 웹 프로그래밍을 쉽게 도와주는 여러 Web Framework들이 현재 개발 중에 있다. 간단한 웹 서버를 만들기 위해 net/http 패키지를 사용하면 되겠지만, 복잡하고 다양한 기능을 요하는 웹 서버인 경우 여러 기능들이 탑재된 Web Framework을 사용하는 것이 좋다.
일반적으로 Web Framework은 Request와 Handler를 매핑하는 Routing 기능, Request 파라미터들은 핸들러의 파라미터에 연결하는 데이타바인딩 기능, Request 상태를 유지하는 컨텍스트 기능, 핸들러들에서 자주 쓰이는 공통된 기능을 제공하는 미들웨어 기능 등 실무에서 자주 요구되는 기능들을 갖추고 있다.
다음은 현재 사용되는, 혹은 개발되고 있는 Go Web Framework들이다. 각 프레임워크마다 제공되는 기능이 다르므로, 필요한 기능이 이미 구현되었는지 아니면 개발 예정인지를 미리 체크해야 한다.
- Revel: https://revel.github.io
- Beego: http://beego.me
- Martini: https://github.com/go-martini/martini
- Gin: https://gin-gonic.github.io/gin
- GoCraft: https://github.com/gocraft/web
- Traffic: https://github.com/pilu/traffic
- Gorilla: http://www.gorillatoolkit.org
- Echo Web Framework
- Copper - Go용 Web App 툴킷
- PocketBase - 단 하나의 파일로 구성된 오픈소스 벡엔드
- Goravel - Laravel에서 영감을 받은 Go 프레임워크
- Pennybase - 초경량 파일 기반 오픈소스 BaaS
GUI
TUI/Terminal/Console
- GOCUI (gocui) - Go Console User Interface
- Huh - 터미널용 인터랙티브 폼/프롬프트 작성용 Go 라이브러리
- Cobra - 강력한 Go 기반 CLI 앱 개발 라이브러리
- Fang - CLI 스타터 키트
- Whosthere - Go로 작성된 현대적 TUI 기반 LAN 탐색 도구
Git
- lazygit - simple terminal UI for git commands
Web Debugging
- GoVisual - Go용 실시간 HTTP 요청 시각화 및 디버깅 도구
Testing
Debugging
- Delve is a debugger for the Go programming language.
Linting
Logging
- zap - Blazing fast, structured, leveled logging in Go.
- lumberjack - lumberjack is a log rolling package for Go
Monitoring
Performance
- pprof++ - A Go Profiler with Hardware Performance Monitoring
Desktop
- Wails - 데스크톱 응용 프로그램 개발 프레임 워크
Machine Learning
Compiler
- gARM - ARM64에 특화된 고성능 Go 컴파일러
ETC
Hello, World!
아래와 같이 main.go파일을 생성한다.
아래와 같이 컴파일 한 후 실행하면 된다.
또는 아래와 같이 바로 실행할 수 있다.
Basic
주석과 함께 정리한다.
package main // 패키지명을 적는다.
func main() { // 함수명을 적는다.
var a int = 11 // 변수 문법: var [변수명] [타입] [ = 초기값]
const b int = 11 // 상수 문법: const [상수명] [타입] [ = 값]
println("a value:", a) // stdout 출력.
println("b value:", b) // stdout 출력.
var c = "haha" // 이런식으로 타입 추론이 가능하다. (상수도 가능)
println("c value:", c)
var d, e int = 33, 44 // 연달아 적는 것도 가능하다.
var f, g = 55, "Six" // 연달아 적을 때 추론이 가능하며, 다른 타입을 적는 것도 가능하다.
println("d, e value:", d, e)
println("f, g value:", f, g)
// 아래와 같이 상수를 열거할 수도 있다.
const (
Visa = "Visa"
Master = "MasterCard"
Amex = "American Express"
)
const (
Apple = iota // 0
Grape // 1
Orange // 2
)
}
%v 출력
특정 struct type 의 %v 포맷을 재정의하고 싶다면 String() method 를 구현하면 된다.
Debugging
- Debugging Go Code with GDB
- Debugging Go Code with LLDB
- Debug Golang applications: LLDB
- Debugging With Vim Go - vim/go/vim-go Debugging
Coding Guideline
Function
여러 줄일 경우 하나씩 넣는다.
func NewBypassClient(
address string,
modbusHost string,
modbusPort int,
modbusInterval float64,
) (*EdgeDataProxy, error) {
// ...
}
Naming Convention
Go는 단순히 대문자로 시작하면 Public, 소문자로 시작하면 Private으로 선언된다.
Constant Variable
Go에서는 상수 네이밍에 대해 다음 컨벤션을 사용합니다:
일반 상수
CamelCase 사용 (첫 글자 대문자로 public, 소문자로 private)
열거형 상수 (iota)
CamelCase 또는 PascalCase
전역 상수나 패키지 레벨
PascalCase (대문자 시작)
주요 특징
- SCREAMING_SNAKE_CASE는 사용하지 않음 (C/C++와 다름)
- 첫 글자 대문자 = public (다른 패키지에서 접근 가능)
- 첫 글자 소문자 = private (같은 패키지 내에서만 접근)
- 약어는 모두 대문자 (HTTP, URL, JSON 등)
최적화
임시값(temporary value)에 대한 주소 연산 규칙
Go 언어 스펙에서는 "함수의 반환값은 주소를 취할 수 없는 값(non-addressable value)"으로 정의되어 있습니다.
// ❌ 컴파일 에러: cannot take the address of random.GenerateRandomProgramName()
name := &random.GenerateRandomProgramName()
함수 반환값은 임시 메모리 위치에 존재하며, 컴파일러가 최적화 과정에서 이 값을 레지스터에 저장하거나 다른 방식으로 처리할 수 있기 때문에 직접적인 주소 참조가 불가능합니다.
func stringPtr(s string) *string {
// 1. 함수 호출 시: random.GenerateRandomProgramName()의 반환값이
// 매개변수 s로 복사됨 (call by value)
// 2. s는 함수의 지역 변수로 메모리에 할당됨
// 3. 컴파일러의 escape analysis가 &s를 감지
// 4. s를 힙에 할당 (스택 대신)
return &s // 5. 힙 주소를 반환 (함수 종료 후에도 유효)
}
// ✅ 가능
name := stringPtr(random.GenerateRandomProgramName())
이 경우는 가능한 이유:
- 함수 매개변수는 주소 참조 가능(addressable)
- s는 함수의 지역 변수(local variable)로 메모리에 할당됨
- 명확한 메모리 주소를 가지므로 &s가 가능
- Go 컴파일러는 &s가 함수 외부로 반환되는 것을 감지
- s를 스택이 아닌 힙(heap)에 할당하여 함수 반환 후에도 유효하도록 보장
- 가비지 컬렉터가 관리하므로 안전
Generic 으로 일반화 하면 (Go 1.18+):
rune
Go에서 rune은 int32의 별칭(alias)으로, 단일 유니코드 문자를 표현해:
파일 경로 비교
Go에서 정확히 동일한 파일 경로인지 확인하는 방법:
Go:filepath 패키지 사용 방법
filepath.EvalSymlinks + filepath.Clean 조합으로 가능:
package main
import (
"fmt"
"os"
"path/filepath"
)
// SamePath checks if two paths point to the same location
// 두 경로가 동일한 위치를 가리키는지 확인
func SamePath(path1, path2 string) (bool, error) {
// Resolve symlinks and get absolute paths
// 심볼릭 링크를 해석하고 절대 경로를 얻음
abs1, err := filepath.EvalSymlinks(path1)
if err != nil {
return false, err
}
abs2, err := filepath.EvalSymlinks(path2)
if err != nil {
return false, err
}
// Clean the paths to remove redundant elements
// 경로를 정규화하여 중복 요소 제거
clean1 := filepath.Clean(abs1)
clean2 := filepath.Clean(abs2)
return clean1 == clean2, nil
}
func main() {
path1 := "."
path2 := "./test/.."
same, err := SamePath(path1, path2)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("'%s' and '%s' are same: %v\n", path1, path2, same)
}
Go:os#SameFile 사용 방법
os.SameFile을 사용한 방법 (권장)
package main
import (
"fmt"
"os"
)
// SameFileCheck checks if two paths refer to the same file
// 두 경로가 동일한 파일을 참조하는지 확인 (inode 비교)
func SameFileCheck(path1, path2 string) (bool, error) {
// Get file info for both paths
// 두 경로의 파일 정보를 가져옴
info1, err := os.Stat(path1)
if err != nil {
return false, err
}
info2, err := os.Stat(path2)
if err != nil {
return false, err
}
// Compare using os.SameFile (compares inode on Unix, file index on Windows)
// os.SameFile로 비교 (Unix는 inode, Windows는 file index 사용)
return os.SameFile(info1, info2), nil
}
func main() {
path1 := "."
path2 := "./test/.."
same, err := SameFileCheck(path1, path2)
if err != nil {
fmt.Printf("Error: %v\n", err)
return
}
fmt.Printf("'%s' and '%s' are same: %v\n", path1, path2, same)
}
Python 대신 Go로 스크립팅하기
Go 파일을 실행 파일처럼 직접 실행할 수 있는 트릭 소개:
- 첫 줄에
//usr/local/go/bin/go run "$0" "$@"; exit를 두고 실행 권한을 주면./script.go로 실행 가능 - 이 방식은 shebang이 아니라 POSIX에서 ENOEXEC 발생 시 셸이
/bin/sh로 폴백하는 동작을 이용함 - 셸은 첫 줄을 명령으로 실행하고, Go 컴파일러는
//주석으로 인식해 무시함 - "$0"로 자기 자신 경로를 넘겨 go run이 스크립트를 빌드·실행하고 $@로 인자 전달
- Go의 강력한 표준 라이브러리와 하위 호환성 보장이 스크립팅 용도에 적합하며, Go 1.x 버전을 사용하는 한 스크립트가 수십 년간 동작 가능
- Python의 가상 환경, pip/poetry/uv 등 의존성 관리 복잡성을 피할 수 있음
- shebang(#!)은 execve 시스템 콜을 통해 인터프리터를 지정하는 방식이지만, 이 글에서 소개하는 기법은 shebang이 아님
See also
- Layered Design - 패키지 순환 참조 방지를 위한 설계 방법.
Troubleshooting
Undefined: sort.Slice
# github.com/go-delve/delve/pkg/proc
.golang/src/github.com/go-delve/delve/pkg/proc/fncall.go:338: undefined: sort.Slice
최신버전으로 업데이트하면 된다. (2019년 01월 21일, 1.11.4 버전으로 업데이트하여 해결했다)
Favorite site
- The Go Programming Language
- Wikipedia (en) GO에 대한 설명
- [추천] Python vs Go 비교 레퍼런스 (Python과 Go의 비교 레퍼런스 ~ 각기 버전은 Python 3.5+와 Go 1.7+ ~)
Tutorials
- Go를 향한 여행
- [추천] 예제로 배우는 GO 프로그래밍
- Go언어 시작하기
- Go 프로그래밍 입문(An Introduction to Programming in Go) - 간단하게 설명되어있다.
Guide
- [추천] 구조적 동시성에 대한 소고, 또는 Go 문의 해로움
- go를 위한 vim 환경설정 (vim-go)
- 컨테이너 내부 Go 애플리케이션 디버깅하기
- Golang 패키지 이름(Package Names)
Article
- Golang vs Rust 퍼포먼스 벤치마킹 썰 (Go, Rust)
- "러스트"와 "고"를 선택하는 방법 (Go, Rust)
- Go vs Bun, Go 언어는 정말 JS 런타임보다 빠를까? | GeekNews (go, bun)
- 저자가 Go 언어를 수년간 사용한 후 Java로 전환하게 되면서 느낀 Go 언어의 한계점과 문제점을 설명하는 글
- Go가 단순하고 지루한(boring) 언어라는 특징이 장점이 아닌 단점이 될 수 있다는 관점을 제시함
- Go의 철학: Google의 Go 설계 팀은 단순함과 제한성을 강조했지만, 이는 사용자가 직접 해결해야 하는 반복 작업을 야기함