Go

Go 프로그램의 기본 구성 요소

구루싸 2021. 12. 13. 19:39
반응형
SMALL

이름

Go 프로그램에서 이름은 문자(유니코드에서 문자로 간주하는 것 모두)나 밑줄로 시작하고, 그 뒤에 임의의 개수의 부가적인 문자, 숫자, 밑줄을 쓸 수 있다.

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

기 정의된 내장 상수, 타입, 함수의 이름

true false iota nil int
int8 int16 int32 int64 uint
uint8 uint16 uint32 uint64 uintptr
float32 float64 complex128 complex64 bool
byte rune string error make
len cap new append copy
close delete complex real imag
panic recover      

위의 이름들은 예약어가 아니므로 선언에서 사용할 수 있다. 이러한 이름이 함수 안에 선언된 경우 함수 내로 국한되지만 함수 밖에서 선언하면 이름이 속하는 패키지의 모든 파일에서 볼 수 있다. 이름 첫 글자의 대소문자로 패키지 간의 가시성을 결정한다. 이름이 대문자로 시작하면 자신의 패키지 밖에서 볼 수 있거나 사용할 수 있으며  프로그램의 다른 부분에서도 참조할 수 있다. 패키지명은 항상 소문자이다. 이름의 길이에는 제한이 없지만 Go 프로그램에서는 관행과 스타일에 따라 짧은 이름을 선호하며 범위가 클수록 이름이 더 길고 의미가 있어야 한다. Go 개발자는 스타일상 단어를 조합해 이름을 지을 때 낙타 표기법(Camel case)을 사용하며, ASCII나 HTML 같은 약어나 두문자는 항상 동일한 대문자나 소문자로 표기한다.

선언

선언은 프로그램 개체와 일부 또는 모든 속성에 이름을 붙인다. Go 프로그램은 이름이 .go로 끝나는 하나 이상의 파일에 저장된다. 각 파일은 파일이 어느 패키지에 속하는지 알리는 package 선언으로 시작한다. package 선언 다음에는 import 선언이 올 수 있으며, 그 후에 타입, 변수, 상수, 함수 등의 패키지 수준 선언이 임의의 순서대로 따라온다.

변수

var 선언은 특정 타입의 변수를 만들고 이름을 붙인 뒤 초기 값을 설정한다.

var 이름 타입 = 표현식

"타입"이나 "= 표현식" 부분 중 하나는 생략 가능하지만, 둘 다 생략할 수는 없다. 타입을 생략하면 초기화 표현식에 의해 타입이 결정된다. 표현식을 생략하면 초기 값이 해당 타입의 제로 값이 되는데, 숫자는 0, 불리언은 false, 문자열은 "", 인터페이스와 참조 타입(슬라이스, 포인터, 맵, 채널, 함수)은 nil, 배열이나 구조체 같은 집합 타입의 제로 값은 타입 내의 모든 원소나 필드가 제로 값을 갖는다.

Go에는 초기화되지 않은 변수가 없어 코드가 단순해지며, 별도의 추가 작업 없이 경계 조건(boundary condition)에 맞게 동작한다.

짧은  변수 선언

이름 := 표현식

이름의 타입은 표현식에 타입에 의해 결정된다. 짧은 변수 선언에서 왼쪽에 있는 모든 변수를 반드시 선언할 필요는 없다. 그 중 일부가 이미 같은 어휘 블록에 선언돼 있는 경우에는 짧은 변수 선언이 해당 변수에 값을 할당한다. 그러나 짧은 변수 선언은 적어도 하나의 새로운 변수를 선언해야 한다.

포인터

포인터 값은 변수의 주소다. 따라서 포인터는 값이 저장돼 있는 위치다. 모든 값이 주소를 갖지는 않지만 모든 변수에는 주소가 있다. 변수명을 사용하지 않거나 심지어 변수명을 모르더라도 포인터를 통해 변수의 값을 간접적으로 읽거나 수정할 수 있다.

x := 1
p := &x
fmt.Println(*p) //*int 타입 p는 x를 가리킨다.
*p = 2
fmt.Println(x) //"2"

new 함수

변수를 생성하는 또 다른 방법은 내장된 new 함수를 사용하는 것이다. 

new(T)

T 타입의 이름 없는 변수를 만들고 T의 제로 값으로 초기화한 후 *T 타입의 값인 변수의 주소를 반환한다.

변수의 수명

변수의 수명은 프로그램이 실행될 때 변수가 존재하는 시간의 길이다. 패키지 수준 변수의 수명은 프로그램의 전체 실행 기간과 같다. 반면에 지역 변수의 수명은 동적이다. 선언문이 실행될 때마다 새 인스턴스가 생성되며, 이 변수는 더 이상 접근할 수 없어서 해당 변수의 저장 공간이 재활용될 때까지 살아 있다. 함수 파라미터나 결과 값도 지역 변수다.

반응형

할당

변수 내의 값은 할당문에 의해 갱신되며, 할당문의 제일 단순한 형태는 = 기호의 왼쪽에 변수가 있고 오른쪽에 표현식이 있는 것이다.

튜플 할당

할당의 형태 중에는 여러 변수를 한 번에 할당할 수 있는 튜플 할당이 있다. 오른쪽의 모든 표현식은 변수가 갱신되기 전에 평가돼 특정 변수가 공교롭게 할당의 양쪽에 모두 나오는 경우에 유용하다.

/* 최대 공약수 */
func gcd(x, y int) int {
	for y != 0 {
    	x, y = y, x%y
    }
    return x
}
/* 피보나치 수 */
func fib(n int) int {
	x, y := 0, 1
    for i := 0; i < n; i++ {
    	x, y = y, x+y
    }
    return x
}

할당성

할당문은 할당의 명시적인 형태이지만, 프로그램 내에는 묵시적인 할당이 일어나는 곳이 많다. 함수 호출은 인자 값들을 그에 대응하는 파라미터들에 묵시적으로 할당한다. return문은 return문의 피연산자에 대응하는 반환 변수들을 묵시적으로 할당하고 슬라이스와 같은 복합 타입에 대한 리터럴 표현식은 묵시적으로 각 요소들을 할당한다. 또한, 맵과 채널은 일반적인 변수는 아니지만 이와 유사한 묵시적 할당 대상이다. 할당문은 명시적이든 묵시적이든 왼쪽과 오른쪽의 타입이 같으면 적법하다.

 

타입 선언

변수나 표현식의 타입은 크기, 내부 표현 방식, 수행 가능한 고유 작업, 연관된 메소드와 같은 해당 값의 특성을 정의한다.

 

type 이름 내부-타입

type 선언은 기존 타입과 같은 내부 타입을 갖는 새 명명된 타입을 정의한다. 모든 타입 T에는 그에 대응해 값 x를 타입 T로 변환하는 변환 명령 T(x)가 있다. 한 타입에서 다른 타입으로의 변환은 둘 다 동일한 내부 타입을 갖고 있거나 동일한 내부 타입의 변수를 가리키는 이름 없는 포인터일 때만 허용된다.

기본 타입

정수

Go의 rune 타입은 int32에 대한 동의어로 통상적으로 값이 유니코드 코드 포인트임을 나타낸다. uintptr 타입은 길이가 지정돼 있지 않지만 포인터 값의 모든 비트를 저장할 수 있는 부호 없는 정수 타입으로 저수준 프로그램에서만 쓰인다. int, uint, uintptr은 크기에 관계없이 명시적으로 크기가 주어진 타입들과는 다르다. 따라서 정수의 기본 크기가 32비트일 때에도 int는 int32와 다른 타입이며, int32가 필요한 경우에 int 값을 쓰기 위해서는 명시적인 변환이 필요하고, 그 반대도 마찬가지이다.

부동소수점 수

Go에는 두 가지 크기의 부동소수점 수 float32와 float64가 있다. 이 수의 산술적 속성은 모든 현대 CPU에 의해 구현된 IEEE 754 표준을 따른다. float32는 대략 6자리 10진 숫자의 정확도를 갖고, float64는 대략 15자리의 정확도를 갖는다. 대부분의 경우 float64를 사용해야 한다.

복소수

Go에는 float32와 float64로 구성된 두 가지 크기의 복소수 complex64와 complex128을 제공한다. 내장된 complex 함수는 실수부와 허수부로 복소수를 생성하며 내장된 real과 imag 함수는 각각 실수와 허수를 추출한다. 

불리언

bool이나 불리언 타입은 값으로 true와 false 두 가지만이 허용된다. 

문자열

문자열은 불변의 바이트 시퀀스다. 문자열에는 0 값으로 이뤄진 바이트들과 같은 임의의 데이터를 넣을 수 있지만 보통 사람이 읽을 수 있는 텍스트를 담고 있다. 텍스트 문자열은 통상적으로 유니코드 코드 포인트를 UTF-8로 인코딩한 시퀀스로 해석한다. 문자열 값은 큰따옴표로 묶인 바이트 시퀀스인 문자열 리터럴로 쓸 수 있다. 큰따옴표로 묶인 문자열 리터럴 안에서는 백 슬래시 \로 시작하는 이스케이프 시퀀스를 사용해 임의의 바이트 값을 문자열에 삽입할 수 있다. 원시 문자열은 큰따옴표 대신 백쿼트(`)를 사용한다. 원시 문자열 리터럴은 보통 백슬래시를 많이 사용하는 정규 표현식, HTML, JSON 등을 작성하기에 편리하다. 

상수

상수는 컴파일러가 값을 알고 있으며, 실행 시가 아니라 컴파일 시 평가되는 표현식이다. const 선언은 변수와 유사한 문법으로 명명된 값을 선언하지만 이 값은 상수이며 실수로 프로그램 실행 중 값이 바뀌는 것을 방지한다. const 선언에서 상수 생성기 iota를 사용해 연관된 값들을 하나하나 명시하지 않고 생성할 수 있다. iota의 값은 0에서 시작하며 상수 목록의 각 항목마다 하나씩 증가한다. Go의 상수는 int나 float64 등의 기본 데이터 타입과 time.Duration과 같은 명명된 기본 타입을 모두 사용할 수 있지만 특정 타입으로 지정되지 않은 상수도 많다. 타입 없는 상수는 타입 지정을 연기해 이후에도 높은 정밀도를 유지할 뿐만 아니라 변환 없이 타입이 지정된 상수보다 더 많은 표현식에 사용할 수 있다.

이항 연산자

* / % << >> & &^
+ - | ^      
== != < <= > >=  
&&            
||            

패키지와 파일

Go의 패키지는 다른 언어의 라이브러리나 모듈과 마찬가지로 모듈화, 캡슐화, 분할 컴파일 및 재사용 등을 지원한다. 패키지의 소스코드는 하나 이상의 .go로 끝나는 파일 내에 있으며 일반적으로 임포트 경로의 마지막 이름과 같은 디렉토리 안에 있다. 각 패키지는 선언에 대한 별도의 이름 공간 역할을 한다. 또한 패키지는 패키지 외부에서 보이거나 익스포트되는 이름을 제어해 정보를 숨길 수 있게 한다. Go에서 익스포트되는 식별자는 대문자로 시작한다. Go 프로그램 안의 모든 패키지는 임포트 경로라는 고유한 문자열로 식별된다. 패키지 초기화는 시작 시 패키지 수준 변수를 선언된 순서대로 초기화하며 의존성이 있을 때는 의존하는 변수부터 초기화한다.

func init() { /* ... */ }

init 함수는 호출하거나 참조할 수 없다는 것 외에는 일반 함수와 같다. 프로그램이 시작할 때는 각 파일 내의 init 함수들이 선언된 순서대로 자동으로 실행된다. 한 패키지는 프로그램 안에서 의존하는 패키지들이 초기화된 후 임포트 순서에 따라 초기화되며, 따라서 q를 임포트하는 p 패키지에서는 p의 초기화 과정이 시작되기 전에 q의 초기화가 완료된다는 것을 확신할 수 있다. main 패키지는 가장 마지막에 초기화된다.

 

반응형
LIST