기본 문법
Go 의 기본 문법은 매우 간단하고 이해하기 쉽습니다. 가장 간단한 예시부터 시작해보겠습니다.
package main
import "fmt"
func main() {
fmt.Println("Hello 세계!")
}package키워드는 현재 go 파일이 어느 패키지에 속하는지 선언합니다. 진입 파일은 반드시main패키지로 선언해야 하며, 진입 함수는main함수입니다. 사용자 정의 패키지와 함수 시 이와 중복되지 않도록 명명해야 합니다.import는导入 키워드로, 뒤에导入할 패키지 이름이 옵니다.func는 함수 선언 키워드로, 함수를 선언하는 데 사용됩니다.fmt.Println("Hello 세계!")는 문장으로,fmt패키지의Println함수를 호출하여 출력합니다.
이상으로 간단한 문법 소개를 마쳤으며, 이제里面的 개념을 좀 더 자세히 알아보겠습니다.
패키지
Go 에서 프로그램은 패키지를 링크하여 구축합니다. Go 에서导入의 기본 단위는 .go 파일이 아닌 패키지입니다. 패키지는 실제로 폴더이며, 영어로 package 라고 합니다. 패키지 내에서는 모든 변수, 상수 및 정의된 타입을 공유합니다. 패키지 명명 스타일은 모두 소문자를 사용하는 것이 좋으며 가능한 짧게 유지해야 합니다.
가시성
앞서 패키지 내에서는 모든 변수, 상수 및 정의된 타입을 공유한다고 언급했지만, 패키지 외부에서는 그렇지 않습니다. 때로는 다른 사람이 특정 타입에 액세스하지 못하도록 하려면 가시성을 제어해야 합니다. 다른 OOP 언어에서 Public, Private 등의 키워드를 본 적이 있을 것입니다. 그러나 Go 언어에는 이러한 키워드가 없으며 가시성을 제어하는 방식은 매우 간단합니다. 규칙은 다음과 같습니다.
- 이름이 대문자로 시작하면 공개 타입/변수/상수입니다.
- 이름이 소문자 또는 밑줄로 시작하면 비공개 타입/변수/상수입니다.
예를 들어 아래 예시에서 상수 MyName 은 공개이고 상수 mySalary 는 비공개입니다.
package example
// 공개
const MyName = "jack"
// 비공개
const mySalary = 20_000이 가시성 규칙은 Go 언어 전체의 모든 곳에 적용됩니다.
导入
패키지의 타입/메서드/함수/변수를 사용하려면 패키지를 导入합니다. 导入 문법은 import 에 패키지 이름을 추가하는 것입니다.
package main
import "example"여러 패키지를 导入할 때는 다음과 같이 작성할 수 있습니다.
package main
import "example"
import "example1"또는 괄호로 묶을 수도 있습니다. 아래 방법이 실제에서 더 자주 사용됩니다.
package main
import (
"example"
"example1"
)패키지 이름이 중복되거나 패키지 이름이 복잡한 경우 별명을 지을 수 있습니다.
package main
import (
e "example"
e1 "example1"
)별명이 밑줄 _ 일 때는 익명 导入입니다. 익명 导入된 패키지는 사용할 수 없으며, 이는 일반적으로 패키지 아래의 init 함수를 로드하기 위해 수행되지만 패키지의 타입을 사용할 필요는 없습니다. 일반적인 예로는 데이터베이스 드라이버 등록이 있지만 드라이버를 수동으로 사용할 필요는 없습니다.
package main
import (
e "example"
_ "mysql-driver-go"
)导入한 후 패키지의 타입에 액세스하려면 패키지명.식별자 를 통해 액세스하면 됩니다. 예를 들어 아래 예시에서 비공개 타입에 액세스하려고 하면 컴파일러가 액세스할 수 없다고 알려줍니다.
package main
import (
"example"
"fmt"
)
func main() {
fmt.Println(example.MyName)
}특수한 导入 방식으로는 패키지의 모든 타입을 현재 패키지 스코프로 导入하는 것입니다. 이 방식으로 导入된 타입은 더 이상 . 연산자를 사용하여 액세스할 필요가 없지만, 이름이 중복된 타입이 있으면 컴파일을 통과할 수 없습니다.
package main
import (
. "example"
)WARNING

Go 에서는 순환 导入가 불가능하며, 직접적이든 간접적이든 마찬가지입니다. 예를 들어 패키지 A 가 패키지 B 를 导入하고 패키지 B 도 패키지 A 를 导入하면 직접 순환 导入입니다. 패키지 A 가 패키지 C 를 导入하고 패키지 C 가 패키지 B 를 导入하고 패키지 B 가 다시 패키지 A 를 导入하면 간접 순환 导入입니다. 순환 导入가 있으면 컴파일을 통과할 수 없습니다.
내부 패키지
Go 에서는 관례적으로 패키지 내 이름이 internal 인 패키지를 내부 패키지로 하며, 외부 패키지는 내부 패키지의 어떤 내용에도 액세스할 수 없으며 그렇지 않으면 컴파일이 통과되지 않습니다. 아래 예시를 보겠습니다.
/home/user/go/
src/
crash/
bang/ (go code in package bang)
b.go
foo/ (go code in package foo)
f.go
bar/ (go code in package bar)
x.go
internal/
baz/ (go code in package baz)
z.go
quux/ (go code in package main)
y.go파일 구조에서 알 수 있듯이 crash 패키지는 baz 패키지의 타입에 액세스할 수 없습니다.
주석
Go 는 한 줄 주석과 여러 줄 주석을 지원합니다. 주석과 내용 사이에는 한 칸의 공백을 두는 것이 좋습니다. 예를 들어
// 이는 main 패키지입니다
package main
// fmt 패키지를 导入했습니다
import "fmt"
/*
*
이는 시작 함수인 main 함수입니다
*/
func main() {
// 이는 문장입니다
fmt.Println("Hello 세계!")
}식별자
식별자는 패키지 명명, 함수 명명, 변수 명명 등에 사용되는 이름입니다. 명명 규칙은 다음과 같습니다.
- 문자, 숫자, 밑줄로만 구성될 수 있습니다.
- 문자와 밑줄로만 시작할 수 있습니다.
- 대소문자를 엄격히 구분합니다.
- 기존 식별자와 중복될 수 없으며, 즉 패키지 내에서 유일해야 합니다.
- Go 의 내장 키워드와 충돌할 수 없습니다.
아래는 모든 내장 키워드 목록입니다. 더 많은 세부사항은 참조 매뉴얼 - 식별자 에서 확인할 수 있습니다.
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연산자
아래는 Go 언어에서 지원하는 연산자 우선 순위 목록입니다. 더 많은 세부사항은 참조 매뉴얼 - 연산자 에서 확인할 수 있습니다.
Precedence Operator
5 * / % << >> & &^
4 + - | ^
3 == != < <= > >=
2 &&
1 ||한 가지 주의할 점은 Go 언어에서는 ~ 를 부정 연산자로 사용하지 않고 ^ 기호를 재사용한다는 점입니다. 두 숫자에 ^ 를 사용할 때 (예: a^b) 이는 XOR 연산자이며, 하나의 숫자에만 사용할 때 (예: ^a) 부정 연산자입니다. Go 는 증강 할당 연산자도 지원합니다. 예를 들어
a += 1
a /= 2
a &^= 2TIP
Go 언어에는 증분 및 감분 연산자가 없으며, 이들은 문장 statement 로 강등되었으며 피연산자 뒤에 위치하도록 규정되었습니다. 따라서 i++ 와 ++i 와 같은 문제를 고민할 필요가 없습니다.
a++ // 올바름
++a // 오류
a-- // 올바름또한 더 이상 반환 값이 없으므로 a = b++ 와 같은 문장은 잘못되었습니다.
리터럴
리터럴은 컴퓨터 과학 용어에 따르면 소스 코드에서 고정 값을 표현하는 기호로, 리터럴 값이라고도 합니다. 두 명칭은 모두 같은 의미로, 무엇을 작성했는지 값이 그대로 "글자 그대로의" 값입니다.
정수 리터럴
가독성을 위해 밑줄 _ 를 사용하여 숫자를 구분할 수 있지만, 접두사 기호 이후와 숫자 사이에서만 허용됩니다.
24 // 24
024 // 24
2_4 // 24
0_2_4 // 24
10_000 // 10k
100_000 // 100k
0O24 // 20
0b00 // 0
0x00 // 0
0x0_0 // 0부동소수점 리터럴
서로 다른 접두사를 통해 서로 다른 진수의 부동소수점을 표현할 수 있습니다.
0.
72.40
072.40 // == 72.40
2.71828
1.e+0
6.67428e-11
1E6
.25
.12345E+5
1_5. // == 15.0
0.15e+0_2 // == 15.0
0x1p-2 // == 0.25
0x2.p10 // == 2048.0
0x1.Fp+0 // == 1.9375
0X.8p-0 // == 0.5
0X_1FFFP-16 // == 0.1249847412109375
0x15e-2 // == 0x15e - 2 (정수 뺄셈)복소수 리터럴
0i
0123i // == 123i
0o123i // == 0o123 * 1i == 83i
0xabci // == 0xabc * 1i == 2748i
0.i
2.71828i
1.e+0i
6.67428e-11i
1E6i
.25i
.12345E+5i
0x1p-2i // == 0x1p-2 * 1i == 0.25i문자 리터럴
문자 리터럴은 반드시 작은따옴표 '' 로 묶어야 하며, Go 의 문자는 utf8 를 완전히 지원합니다.
'a'
'ä'
'你'
'\t'
'\000'
'\007'
'\377'
'\x07'
'\xff'
'\u12e4'
'\U00101234'이스케이프 문자
Go 에서 사용 가능한 이스케이프 문자
\a U+0007 벨 기호
\b U+0008 백스페이스 기호
\f U+000C 폼 피드 기호
\n U+000A 줄 바꿈 기호
\r U+000D 캐리지 리턴 기호
\t U+0009 수평 탭 기호
\v U+000B 수직 탭 기호
\\ U+005C 백슬래시 이스케이프
\' U+0027 작은따옴표 이스케이프 (이 이스케이프는 문자 내에서만 유효합니다)
\" U+0022 큰따옴표 이스케이프 (이 이스케이프는 문자열 내에서만 유효합니다)문자열 리터럴
문자열 리터럴은 큰따옴표 "" 또는 백쿼트 ``로 묶어야 합니다 (백쿼트 문자열은 이스케이프를 허용하지 않음).
`abc` // "abc"
`\n
\n` // "\\n\n\\n"
"\n"
"\"" // `"`
"Hello, world!\n"
"오늘 날씨가 좋습니다"
"日本語"
"\u65e5 本\U00008a9e"
"\xff\u00FF"함수
Go 에서 함수 선언은 func 키워드를 통해 수행되며, 대부분의 언어와 유사합니다.
func main() {
println(1)
}그러나 Go 의 함수에는 두 가지 차이점이 있습니다. 첫 번째는 매개변수 타입이 뒤에 온다는 점입니다. 예를 들어
func Hello(name string) {
fmt.Println(name)
}두 번째 차이점은 여러 반환 값이며 이름을 붙일 수도 있습니다.
func Pos() () (x, y float64) {
...
}스타일
코딩 스타일에 대해 Go 는 모든 사람을 동일한 스타일로 통일하도록 강제합니다. Go 공식은 포맷팅 도구 gofmt 를 제공하며, 명령줄을 통해 사용할 수 있습니다. 이 포맷팅 도구는 전달할 수 있는 포맷팅 매개변수가 없으며,仅有的 두 매개변수도 포맷팅 프로세스를 출력하는 것뿐이므로 사용자 정의가 전혀 지원되지 않습니다. 즉, 이 도구를 통해 포맷팅된 모든 코드는 동일한 코드 스타일이며, 이는 유지보수 인력의 심리적 부담을 크게 줄여줍니다. 따라서 이 부분에서 개성을 추구하는 것은 현명하지 않은 선택입니다.
아래에는 몇 가지 규칙을 간단히 소개하겠습니다. 평소 코드를 작성할 때 주의할 수 있습니다.
함수 중괄호 줄 바꿈
함수 뒤의 중괄호를 줄 바꿈해야 하는지에 대해 거의 모든 프로그래머가 자신의 이유를 말할 수 있습니다. Go 에서는 모든 중괄호를 줄 바꿈하지 않아야 합니다.
// 올바른 예시
func main() {
fmt.Println("Hello 세계!")
}만약 다음과 같이 작성했다면
// 잘못된 예시
func main()
{
fmt.Println("Hello 세계!")
}이러한 코드는 컴파일조차 통과할 수 없으므로 Go 는 모든 프로그래머에게 함수 뒤의 중괄호를 줄 바꿈하지 않도록 강제합니다.
코드 들여쓰기
Go 는 기본적으로 Tab(탭) 을 사용하여 들여쓰기를 하며, 일부 특수한 경우에만 공백을 사용합니다.
코드 간격
Go 에서 대부분의 간격은 의미가 있습니다. 어떤 면에서는 컴파일러가 코드를 어떻게 보는지를 나타냅니다. 예를 들어 아래 수학 연산을 보십시오.
2*9 + 1/3*2아시다시피 곱셈의 우선순위가 덧셈보다 높습니다. 포맷팅 후 * 기호 사이의 간격이 더 조밀해져 우선 연산이 수행됨을 의미하고, + 기호 근처의 간격은 더 커져 나중에 연산이 수행됨을 나타냅니다.
중괄호 생략
다른 언어에서 if 와 for 문은 보통 다음과 같이 간략히 작성할 수 있습니다.
for (int i=0; i < 10; i++) printf("%d", i)그러나 Go 에서는 불가능합니다. 한 줄만 작성할 수 있지만 중괄호를 추가해야 합니다.
for i := 0; i < 10; i++ {fmt.Println(i)}삼항 표현식
Go 에는 삼항 표현식이 없으므로 아래 코드는 컴파일을 통과할 수 없습니다.
var c = a > b ? a : b이 문서를 통해 Go 문법에 대한 초기 인식을 가질 수 있으며, 후속 내용에서 더 자세히 설명하겠습니다.
