티스토리 뷰
함수
1. 기본 구조
func AplusB(a int, b int) int {
return a + b
}
func PrintAplusB(a int, b int) {
fmt.Print(a + b)
}
- 다른 언어와 다르게 타입의 위치만 반대로 된 형태이다.
2. 다중 반환 함수
func AcalB(a int, b int) (int, int, int) {
return a + b, a - b, a * b
}
func main() {
puls, minus, multi := AcalB(1032, 3124)
fmt.Print(puls, minus, multi) // 4156 -2092 3223968
}
- 반환 값이 2개 이상일 경우엔 return type을 괄호로 묶는 것은 필수이다.
func AcalB(a int, b int) (plus int, minus int, multi int) {
return a + b, a - b, a * b
}
- 위와 같이 반환 값이 여러 개일 경우에 각 반환값이 의미하는 바를 명시할 수도 있다.
- 반환 값의 이름을 명시하는 목적은 개발자를 위한 것일 뿐이다.
2. 에러값 생성
func AdivB(a int, b int) (int, error) {
if b <= 0 {
return 0, fmt.Errorf("b(%d) is invalid.", b)
}
return a / b, nil
}
func main() {
div, err := AdivB(8, 0)
if err != nil {
fmt.Print(err) // b(0) is invalid.
} else {
fmt.Print(div)
}
}
- 에러 값이 없을 경우엔 보통 nil을 반환한다.
cf. error 타입은 go에서 지정된 키워드이기 때문에 변수명으로 사용하지 않는 것이 좋다.
cf. Go의 함수는 오버로딩이 지원되지 않는다.
Call-By-Value
go는 기본적으로 call-by-vlaue(pass-by-value) 언어이다. 따라서 함수가 호출될 때 인자값이 그대로 복사된다.
포인터: Call-By-reference
C/C++과 마찬가지로 & 연산자를 통해 변수의 주소값을 가져올 수 있다. & 연산자는 모든 변수에 적용할 수 있다. 즉, Go에서는 타입과 관계 없이 모든 변수의 주소 값을 가져올 수 있다.
func main() {
var val1 int
var val2 string
var val3 float64
var val4 bool
fmt.Println(&val1, &val2, &val3, &val4) // 0xc000112000 0xc000110010 0xc000112008 0xc000112010
}
포인터 타입
- (복습) 포인터란 변수의 주소를 나타내는 값
- C에서와 마찬가지로 포인터라는 의미를 가지도록 별도의 타입이 존재 (* + <type명> 형태)
- 포인터 변수는 타입과 관계없이 같은 크기를 가진다. (컴파일러에 따라 크기는 달라짐)
- 아래 예시 코드에서는 모든 포인터 변수의 크기가 8 바이트이다.
func main() {
var val1 int
var val2 string
var val3 float64
var val4 bool
fmt.Println(reflect.TypeOf(&val1)) // *int
fmt.Println(reflect.TypeOf(&val2)) // *string
fmt.Println(reflect.TypeOf(&val3)) // *float64
fmt.Println(reflect.TypeOf(&val4)) // *bool
val1Ptr := &val1
val2Ptr := &val2
val3Ptr := &val3
val4Ptr := &val4
fmt.Println(unsafe.Sizeof(val1Ptr)) // 8
fmt.Println(unsafe.Sizeof(val2Ptr)) // 8
fmt.Println(unsafe.Sizeof(val3Ptr)) // 8
fmt.Println(unsafe.Sizeof(val4Ptr)) // 8
}
- 포인터 변수에 *를 붙이면 포인터가 가리키고 있는 변수의 값을 가져올 수 있다.
- *를 통해 값을 변경하면 원본 변수의 값도 같이 변경된다. (reference 참조 이기 때문)
func main() {
val1 := 4
val1ptr := &val1
fmt.Println(val1) // 4
fmt.Println(val1ptr) // 0xc000120000
fmt.Println(*val1ptr) // 4
*val1ptr = 8
fmt.Println(*val1ptr) // 8
fmt.Println(val1) // 8 <- reference 참조이기 때문에 원래 변수도 변한다.
}
- 포인터 변수는 함수의 return type으로도 사용될 수 있다.
- 아래와 같이 Go는 다른 언어와 달리 함수의 로컬 변수 포인터를 반환할 수 있다. 함수의 스코프는 벗어나지만 해당 변수의 포인터를 가지고 있는 동안에는 해당 변수의 값에 접근할 수 있다.
func createPointer() *int {
val := 26
return &val
}
func main() {
var ptr *int = createPointer()
fmt.Println(ptr) // 0xc00001c030
fmt.Println(*ptr) // 26
}
- 함수의 인자로 포인터를 전달할 때에는 매개변수 타입을 포인터 타입으로 지정하면 된다.
func printPointer(num *int) {
fmt.Println(*num) // 10
}
func main() {
var val int = 10
printPointer(&val)
}
Package
공통 로직을 수행하는 함수들을 별도의 패키지로 분리할 수 있다. 공통 로직을 패키지로 분리시켜 모듈화해 두면 서로 다른 프로그램끼리 동일한 코드를 사용할 수 있게 된다. 즉, 코드 공유(함수 공유)를 위해서 패키지화하는 목적도 있다.
workspace
go의 기본 workspace는 홈 디렉토리 아래 위치한다.
cf. OS별 홈 디렉토리
- 윈도우: C:\User\<username>
- Mac: /User/<username>
- Linux: /home/<name>
Go 기본 디렉토리
1) bin: '컴파일된' 실행 가능한 바이너리 프로그램(실행파일)
2) pkg: '컴파일된' 바이너리 '패키지' 파일
3) src: 소스코드 (패키지 소스코드 등)
* 각 패키지의 코드는 src 디렉토리 하위 패키지명의 디렉토리로 생성된다. 예를 들어 fmt 패키지는 src/fmt/에 생성된다. 각 패키지 디렉토리는 하나 이상의 소스코드 파일(.go)을 포함해야 한다.
* import 하는 패키지 코드를 찾을 때에는 항상 src 디렉토리에서 찾게 된다.
패키지 생성 실습
1. GOROOT 또는 GOPATH로 설정된 디렉토리의 src/ 하위에 이동
2. 생성할 패키지명과 동일한 디렉토리 생성
- 예: go/src/greeting
3. go/src/greeting 디렉토리에 go file 작성
- greeting.go
package greeting
import "fmt"
func Hello() {
fmt.Println("Hello!")
}
4. 새로 만든 패키지 가져오기
package main
import "greeting"
func main() {
greeting.Hello() // Hello!
}
- 위 코드는 GOROOT 상에 존재하는 프로젝트가 아니어도 상관없음.
- go run 커맨드로 컴파일 및 실행
cf. GOROOT 환경변수는 go env 커맨드로 확인 가능
- 기본설정 시 리눅스의 경우 GOROOT는 $HOME/go로 설정됨.
The GOPATH environment variable is used to specify directories outside of $GOROOT that contain the source for Go projects and their binaries.
* 동작원리
- 위와 같이 패키지를 만들고 실행하면 import 문의 "greeting"을 보고 GOROOT의 src/greeting 패키지에서 소스코드를 찾는다. 그리고 해당 코드가 컴파일되고 import 되면 greeting 패키지의 함수가 호출된다.
- 따라서 반드시 패키지 명과 디렉토리 명을 동일하게 작성해야 한다.
cf. 패키지명 컨벤션
- 패키지명은 소문자로만 작성한다.
- 두 단어 이상이 될 경우 카멜 케이스도, 스네이크 케이스도 사용하지 않으며 두 단어를 붙인다 (ex. strconv)
- 축약어를 사용한다. (ex. fmt: format의 약자)
cf. 패키지 그룹화
src 디렉토리 하위에 디렉토리 경로 자체가 import 할 패키지 경로가 된다. 예를 들어, archive 패키지의 tar 패키지는 src/archive/tar 디렉토리에 위치한다.
패키지 실행파일 생성(go install)
- go install 커맨드는 go build 커맨드와 비슷하게 .go 파일을 컴파일한다.
- go install 커맨드는 패키지 코드를 컴파일하며 결과물을 일반 패키지는 pkg 디렉토리에, 실행파일은 bin 디렉토리에 저장한다.
- 커맨드: go install 또는 go install $GOROOT/go/src/<package_name>
- 저장될 패키지 go 파일 경로: $GOROOT/go/src/<package_name>
- 저장될 패키지 실행파일 경로: $GOROOT/go/pkg/<package_name>
- pkg 디렉토리에는 컴파일된 실행파일이 생성된다.
cf. go build vs go install
- go build는 소스파일(.go)명을 그대로 실행파일 이름을 지정한다.
- go install은 .go 파일이 포함된 디렉토리의 이름을 기반으로 실행파일 이름을 지정한다.
- go build는 실행파일을 현재 디렉토리에 생성한다.
- go install은 bin 디렉토리에 실행파일을 생성한다.
- go install은 커맨드를 실행하는 위치와 무관하게 $GOROOT/go/src의 소스코드를 컴파일한다.
패키지 배포 및 네이밍
보통 Git과 같은 원격 저장소에 패키지를 배포한다고 가정했을 때, 이를 사용하는 사람 입장에서는 패키지 명을 직접 지정해줘야 한다. 만약 사용자가 이전에 만들어 둔 패키지 명과 중복될 수도 있다. 따라서 보통의 경우엔 원격 저장소의 디렉토리 구조를 그대로 패키지 구조로 사용한다. (사실 이러한 방식은 보편적으로 사용되는 방식이다)
[예시]
- 원격 저장소 주소: http://github.com/example1/package1
- 패키지 구조: go/src/github.com/example1/package1
- import 작성: import "github.com/example1/package1"
패키지 다운로드(go get)
원격 저장소에 있는 패키지를 쉽게 다운로드할 수 있는 go get 커맨드가 제공된다.
* 예시: go get github.com/example1/package1
- go get 커맨드는 git 커맨드 기반으로 동작하기 때문에 git이 설치되어 있어야 한다.
- go get 커맨드를 통해 https:// 부분은 생략할 수 있다.
- go get 커맨드는 작업 공간의 src 디렉토리 하위에 자동으로 패키지를 설정한다.
패키지 주석과 함수 주석
1. 패키지 주석
- 소스코드 상에서 package 선언문 위에 작성된 주석 (//)
- 주석 내용은 "Package"라는 단어로 시작해야 함
2. 함수 주석
- 소스코드의 함수 바로 위에 작성된 주석 (//)
- 주석 내용은 "함수 이름"으로 시작해야 함
* 패키지 주석과 함수 주석은 go doc 커맨드를 사용할 때 보여진다.
- 예시1 - 함수 주석: go doc strconv ParseFloat
- 예시2 - 패키지 주석: go doc <github_url>
GOPATH
코드가 기본 작업 공간(GOROOT) 외 디렉토리에 저장된 경우, Go 컴파일러가 해당 위치에서 코드를 찾을 수 있도록 설정해주어야 한다. 즉, GOPATH 환경변수 설정으로 작업 공간 위치를 변경할 수 있다.
- Mac, Linux: .bashrc 파일에 작업 디렉토리를 명시하여 GOPATH 노출
ex) echo "export GOPATH=<작업공간>" >> ~/.bashrc
상수(constant)
- 다른 언어와 마찬가지로 상수(constant)를 가진다.
- 상수는 이름을 가진 불변(immutable)의 값이다.
- var 키워드 대신 const 키워드를 사용한다.
- 선언과 동시에 반드시 초기화해야 한다.
- 변수처럼 := 키워드를 사용할 수 없다.
- 변수와 마찬가지로 타입을 생략할 수 있다. (타입추론)
- 함수 내에서 선언할 수도 있지만 대부분 패키지 레벨에서 선언한다.
- 상수 이름이 대문자로 시작할 경우 함수와 마찬가지로 패키지 외부에 노출시킬 수 있다.
package main
import "fmt"
const DaysInWeek int = 7
func weeksToDay(weeks int) int {
return weeks * DaysInWeek
}
func main() {
fmt.Println(weeksToDay(2))
}
Reference
- Go docs, https://go.dev/doc/tutorial/compile-install
'[ DevOps ] > [ Golang ]' 카테고리의 다른 글
[Go] 고루틴(goroutine)과 채널(channel) (0) | 2023.02.20 |
---|---|
[Go] CDK8s (CDK for k8s) (0) | 2023.02.18 |
[Go] 한 번에 정리하는 Golang 기본 개념 - 3편 (0) | 2022.11.08 |
[Go] 한 번에 정리하는 Golang 기본 개념 - 1편 (0) | 2022.10.28 |
[Go] Golang 이란 (0) | 2022.08.15 |
- Total
- Today
- Yesterday
- GitOps
- jvm
- Stream
- rolling update
- golang
- 카프카
- RDB
- ci/cd
- spring
- Linux
- LFCS
- go
- kafka
- docker
- ubuntu
- CICD
- 컨트롤러
- argocd
- Controller
- 코틀린
- 우분투
- github actions
- container
- 쿠버네티스
- db
- Non-Blocking
- Kubernetes
- Java
- K8s
- helm
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |