Golang/Basic

Zero 부터 시작하는 Golang - 기본문법

시바도지 2022. 2. 13. 16:44
반응형

변수


  • 변수는 Go 키워드 var를 사용하여 선언한다. var 키워드 뒤에 변수 명을 적고, 그 뒤에 변수 타입을 적는다.

예를 들어, 아래는 a라는 정수(int) 변수를 선언한 것이다.

var a int

변수 선언문에서 변수 초기 값을 할당할 수도 있다.

즉, float32 타입의 변수 f 에 11.0 이라는 초기 값을 할당하기 위해서는 아래와 같이 쓸 수 있다.

var f float32 = 11.0

선언된 변수는 그 뒤의 문장에서 해당 타입의 값을 할당할 수 있다.

a = 10
f = 12.0

만약 선언된 변수가 Go 프로그램 내에서 사용되지 않는다면, 에러를 발생 시킨다.

따라서 사용되지 않는 변수는 삭제하도록 한다.

동일한 타입의 변수가 여러 개 있을 경우, 변수들을 나열한 후 마지막에 타입을 한번만 지정할 수 있다.

var a, b, c int

여러 개의 변수들이 선언된 상황에서 초기 값을 지정할 수 있다. 초기 값은 순서대로 변수에 할당된다.

예를 들어 a는 1, b는 2, c는 3이 할당된다.

var a, b, c int = 1, 2, 3

변수를 선언하면서 초기 값을 지정하지 않으면, Go는 Zero Value 를 기본적으로 할당한다.

즉, 숫자형에는 0, bool 타입에는 false, string형에는 ""(빈문자열) 을 할당한다.

Go에서는 할당되는 값을 보고 그 타입을 추론하는 기능이 자주 사용된다.

즉, 아래의 코드에서 a는 정수형으로 1이 할당되고, s는 문자열로 Hello가 할당된다.

var a = 1

var s = "Hello"

변수를 선언하는 또 다른 방식으로 Short Assignment Statement( := )를 사용할 수 있다.

즉, var a = 1을 사용하는 대신 a := 1 이라고 var를 생략하고 사용할 수 있다.

하지만 이러한 표현은 함수(func) 내에서만 사용할 수 있으며, 함수 밖에서는 var를 사용해야 한다.

 

상수


  • 상수는 Go 키워드 const를 사용하여 선언한다.
  • 상수는 Short Assignment Statement( := )를 사용할 수 없다.
  • const 키워드 뒤에 상수 명을 적고, 그 뒤에 상수 타입, 그리고 상수 값을 할당한다.
const c int = 10

여러 개의 상수들을 묶어서 지정할 수 있다.

const (
	a = "Alpha"
	b = "Bravo"
	c = "Charlie"
)

iota라는 identifier를 사용하면, 상수 값을 0부터 순차적으로 부여할 수 있다.

예를 들어  iota가 지정된 a에는 0이 할당되고, 나머지 상수들을 순서대로 1씩 증가된 값을 부여 받는다.

const (
	a = iota    // 0
	b           // 1
	c           // 2
)

다음과 같이 첫 번째 선언 후의 상수의 선언을 생략하면 첫 번째 선언을 따르게 된다.

const (
	a int = 1
	b
)

 

 

데이터 타입


  • Go는 다음과 같은 기본적인 데이터 타입들이 있다.

문자열

  • 문자열 리터럴은 Back Quote(``) 혹은 Double Quote("")를 사용하여 표현할 수 있다.
  • Back Quote(``)로 둘러 싸인 문자열은 Raw String Literal이라 부르는데, 이 안에 있는 문자열은 별도로 해석되지 않고 Raw String 그대로의 값을 갖는다. 예를 들어, 문자열 안에 \n 이 있을 경우 이는 NewLine으로 해석되지 않는다. 또한, Back Quote은 복수 라인의 문자열을 표현할 때 자주 사용된다.
  • Double Quote("")로 둘러 싸인 문자열은 Interpreted String Literal이라 부르는데, 복수 라인에 걸쳐 쓸 수 없으며, 인용 부호 안의 Escape 문자열들은 특별한 의미로 해석된다. 예를 들어, 문자열 안에 \n 이 있을 경우 이는 NewLine으로 해석된다. Double Quote를 이용해 문자열을 여러 라인에 걸쳐 쓰기 위해서는 + 연산자를 이용해 결합하여 사용한다.

데이터 타입 변환 (Type Conversion)

  • 하나의 데이터 타입에서 다른 데이터 타입으로 변환하기 위해서는 type(v) 와 같이 표현하고 이를 Type Conversion이라 부르는데,여기서 T는 변환하고자 하는 타입을 표시하고, v는 변환될 값(value)을 지정한 것이다.

아래의 예제 코드는 변수 f를 변수 z에 타입을 변환하여 담아낸다.

package main

import "fmt"

func main() {
	var f float64 = 5
	var z uint = uint(f)
	fmt.Println(z)
}

연산자

  1. 산술 연산자 산술 연산자는 사칙연산자(+, -, *, /, %)와 증감연산자(++, -)를 사용한다.
  2. 관계 연산자관계 연산자는 서로의 크기를 비교하거나 동일함을 체크하는데 사용된다.
  3. 논리 연산자논리 연산자는 AND, OR, NOT을 표현하는데 사용된다.
  4. Bitwise 연산자Bitwise 연산자는 비트단위 연산을 위해 사용되는데, 바이너리 AND, OR, XOR과 바이너리 쉬프트 연산자가 있다.
  5. 할당 연산자할당 연산자는 값을 할당하는 = 연산자 외에 사칙연산, 비트연산을 축약한 +=, &=, <<= 같은 연산자들도 있다.
  6. 포인터 연산자포인터 연산자는 C++과 같이 &혹은 *를 사용하여 해당 변수의 주소를 얻어내거나 이를 반대로 Dereference 할 때 사용한다.Go는 비록 포인터 연산자를 제공하지만 포인터 산술 즉, 포인터에 더하고 빼는 기능은 제공하지 않는다.
var a int = 10 
var b = &a  // a의 주소를 할당 
println(*b) // b가 가리키는 주소에 있는 실제 내용을 출력

 

 

조건문


if문

  • if문은 해당 조건이 참(True)이면 { } 내의 코드를 실행한다.
  • if문의 조건식은 반드시 Boolean 식으로 표현되어야 한다. 이점은 C/C++ 같은 다른 언어들이 조건식에 1, 0과 같은 숫자를 쓸 수 있는 것과 같다.
  • Go의 if문은 반드시 Curly Braket( { )이 if문과 같은 라인에 있어야 한다. 그렇지 않으면 에러를 발생한다.
// 잘못된 예시
if a == 1
{
	println("True")
}

// 올바른 예시
if a == 1 {
	println("True")
}

 

if문은 else if 또는 else 문을 함께 사용할 수 있다.

if a == 1 {
	println("a == 1")
	} else if a == 2 {
		println("a == 2")
	} else {
		println("Other")
}

switch 문

  • 다수의 조건식을 체크해야 하는 경우 switch 문을 사용한다.
package main func main() {
	var fruit string
	var category = 1

	switch category {
		case 1:
		fruit = "Apple"

		case 2:
		fruit = "Orange"

		case 3:
		fruit = "Grape"

		default :
		fruit = "Other"
	}

	println(fruit)
}

 

반복문


for문

  • Go는 반복문에 for 하나 밖에 없다.
  • for 루프는 다른 언어와 비슷하게 "for 초기값; 조건식; 증감{ ... }"의 형식을 따른다.
  • 초기값, 조건식, 증감식 등은 경우에 따라 생략할 수 있다.

for문 - 무한 루프

  • for 루프로 무한 루프를 만들려면 "초기값; 증감" 모두를 생략하면 된다.
  • 아래 예제는 무한 루프를 만든 예제이다.
package main
func maiun() {
	for {
		println("Infinite Loop")
	}
}

for range 문

  • 콜렉션으로 부터 한 요소(element)씩 가져와 차례 for 블럭의 문장들을 실행한다. 이는 다른 언어의 foreach롸 비슷하다.
  • for range문은 "for 인덱스, 요소 값 := 컬렉션" 같이 for 루프를 구성하는데, range 키워드 다음의 컬렉션으로부터 하나씩 요소를 반환해서 그 요소의 위치 인덱스와 값을 for 키워드 다음의 2개의 변수에 각각 할당한다.
  • 아래 예제는 3명의 이름을 갖는 문자열 배열에서 문자열 인덱스(0, 1, 2)와 해당 이름을 차례로 가져와서 출력한다.
names := []string{"Kim", "Lee", "Park"}

for index, name := range names {
	println(index, name)
}

break, continue, goto 문

  • 경우에 따라 for 루프 내에서 즉시 빠져나올 필요가 있다. 이때 break문을 사용한다.
  • 만약 for 루프의 중간에서 나머지 문장들을 실행하지 않고 for 루프 시작 부분으로 바로 가려면 continue문을 사용한다. 그리고 기타 임의의 문장으로 이동하기 위해 goto 문을 사용할 수 있다.
  • goto 문은 for 루프와 관련 없이 사용될 수 있다.
  • break 문은 for 루프 이외에 switch문이나 select 문에서도 사용할 수 있다. 하지만, continue문은 for 루프와 연관되어 사용된다.
package main

func main() {
	var a = 1
	for a < 15 {
        if a == 5 {
            a += a
            continue // for 루프 시작으로 이동
        }
        
        a++
        if a > 10 {
       		break    // 루프에서 빠져 나온다.
        }
        
    }
    
    if a == 11 {
    	goto END     // goto의 사용 예
	}
	println(a)
    
	END:
	println("End")
}

break문은 보통 단독으로 사용되지만, 경우에 따라 "break 레이블"과 같이 사용하여 지정된 레이블로 이동할 수도 있다.

break의 "레이블"은 보통 현재의 for 루프를 바로 위에 적게 되는데, 이러한 "break 레이블"은 현재의 루프를 빠져나와 지정된 레이블로 이동하고,

break문의 직속 for 루프 전체의 다음 문장을 실행하게 한다.

 

아래 예제는 언뜻 보기에 무한루프를 돌 것 같지만, 실제로는 OK를 출력하고 프로그램을 정상 종료한다.

이는 "break L1" 문이 for 루프를 빠져나와 L1 레이블로 이동한 후,

break가 있는 현재 for 루프를 건너뛰고 다음 문장인 println() 으로 이동하기 때문이다.

package main

func main() {
	i := 0

	L1:
	for {
		if i == 0 {
			break L1
		}
	}

	println("OK")
}

 

 

함수


  • Go에서 함수는 func 키워드를 사용하여 정의한다.
  • func 뒤에 함수명을 적고 ( ) 내에 그 함수에 전달하는 매개변수(Parameter)들을 적는다.
  • 매개변수(Parameter)는 0개 이상 사용할 수 있는데, 매개변수 뒤에 int, string등의 타입을 적어서 정의한다.

아래의 예제는 'greeting' 이라는 함수를 정의한 예제이다.greeting() 함수는 문자열 msg 매개변수를 하나 갖고 있으며,

반환 값이 없으므로 별도의 리턴 타입(Return Type)을 정의하지 않았다.

package main

func main() {
	msg := "Hello!"
	greeting(msg)
}

func greeting(param string) {
	println(param)
}

두 개 이상의 연속된 매개변수가 같은 타입일 때는 마지막 변수를 제외한 매개변수들의 타입을 생략할 수 있다.

func f(x int, y int){ } ➡ func f(x, y int){ }

Pass By Reference

  • Go에서 매개변수를 전달하는 방식은 크게 Pass By Value와 Pass By Reference로 나뉜다.
  1. Pass By Value위의 예제에서는 msg의 값 "Hello!" 문자열이 복사 되어 함수 greeting()에 전달된다.  만약 매개변수 param의 값이 greeting() 함수 내에서 변경된다 하더라도 호출 함수 main() 에서의 msg 변수는 변함이 없다.
  2. Pass By Reference아래의 예제에서 처럼 msg 변수앞에 &(Ampersand) 부호를 붙이면 msg 변수의 주소를 표시하게 된다. 흔히 포인터라 불리는 이 용법을 사용하면 함수에 msg 변수의 주소를 표시하게 된다. 함수 greeting()에서는 *string 과 같이 매개변수가 포인터임을 표시하고 이때 greeting() 함수의 param은 문자열이 아니라 문자열을 갖는 메모리 영역의 주소를 갖게 된다. msg 주소에 데이터를 쓰기 위해서는 *msg = "" 와 같이 앞에 *를 붙이는데 이를 흔히 Dereferencing(역참조) 이라 한다.

아래의 예제의 경우  main 함수의 msg 변수의 값이 greeting 함수에서 changed로 변경되었으므로 main 함수의 println()에서 변경된 값이 출력된다.

package main

func main() {
	msg := "Hello!"
	greeting(&msg)
	println(msg)    // 변경된 메세지 출력
}

func greeting(msg *string) {
	println(*msg)
	*msg = "Changed"  // 메세지 변경
}

Variadic Function (가변 인자 함수)

  • 함수에 고정된 수의 매개변수들을 전달하지 않고 다양한 숫자의 매개변수를 전달하고자 할 때 가변 매개변수를 나타내는 ...(3개의 마침표)를 사용한다.즉, 문자열 가변 매개변수를 나타내기 위해 ...string과 같이 표현한다.
  • 가변 매개변수를 갖는 함수를 호출할 때는 0개, 1개, 2개, ... n개의 동일한 타입의 매개변수를 전달할 수 있다.

아래의 예제는 say 함수에 4개의 문자열을 전달할 수도 있고, 1개의 문자열을 전달할 수도 있다.

package main

func main() {
	say("He", "llo", "Wor", "ld!")
	say("Hello World!")
}

func say(param ...string) {
	for _, s := range param {
		println(s)
	}
}

함수 반환 값

  • Go 언어에서 함수는 반환 값이 없을 수도, 하나 일 수도 또는 여러 개일 수도 있다.
  • Go 언어는 Named Return Parameter 라는 기능을 제공하는데, 이는 함수에 정의된 반환 되는 값들을 반환 매개변수들에 할당할 수 있는 기능이다.
  • 함수에서 반환 값이 있는 경우는 func 문의 마지막에 반환 값의 타입을 정의해 준다. 그리고 값을 반환하기 위해 함수 내에서 return 키워드를 사용한다.

아래의 예제는 sum() 함수의 리턴 타입이 int임을 표시하고 있고, sum 함수 마지막에 return s와 같이 정수 s의 값을 반환하고 있다.

package main

func main() {
	total := sum(1, 3, 5, 7, 9)
	println(total)
}

func sum(nums ...int) int {
	s := 0

	for _, n := range nums {
		s += n
	}

	return s
}

Go에서 여러 개의 값을 반환하기 위해서는 해당 리턴 타입 들을 괄호( ) 안에 적어준다.

예를 들어, 처음 반환 값이 int이고 두 번째 반환 값이 string인 경우 (int, string)과 같이 적어 준다.

아래의 예제는 sum() 함수에 가변 인수로 숫자들이 전달될 때, 그 숫자들의 개수와 합계를 함께 반환하는 코드다.

package main

func main() {
	count, total := sum(1, 7, 3, 5, 9)
	println(count, total)
}

func sum(nums ...int) (int, int) {
	s := 0      // 합계
	count := 0  // 요소 갯수

	for _, n := range nums {
		s += n
		count++
	}

	return count, s
}

Go에서 named Return Parameter에 반환 값들을 할당하여 반환할 수 있는데, 이는 반환 되는 값들이 여러 개일 때, 코드 가독성을 높이는 장점이 있다.

위의 sum() 함수를 Named Return Parameter를 이용하여 고쳐 쓰면 다음과 같다.

아래 예제에서 func 라인의 마지막 리턴 타입 정의 부분을 보면 (int, int)가 아니라 (count int, total int) 처럼 정의되어 있음을 볼 수 있다.

즉, 반환 매개변수 명과 그 타입을 함께 정의한 것이다. 그리고 함수 내에서는 이 count, total에 결과 값을 직접 할당하고 있음을 볼 수 있다.

또한 마지막에 return 문이 있는 것을 볼 수 있는데, 실제 return 문에는 아무 값들을 반환하지 않지만, 그래도 반환 되는 값이 있을 경우에는

빈 return 문을 반드시 써 주어야 한다. 만약 이를 생략하면 에러가 발생한다.

func sum(nums ...int)(count int, total int) {
	for _, n := range nums {
		total += n
	}

	count = len(nums)

	return
}

Anonymous Function (익명 함수)

  • 함수명을 갖지 않는 함수를 익명 함수라 한다.
  • 일반적으로 익명 함수는 그 함수 전체를 변수에 할당하거나 다른 함수의 매개변수에 직접 정의되어 사용되곤 한다.

아래의 예제는 main() 함수 내에 익명 함수가 선언되어 sum 이라는 변수에 할당되고 있음을 보여준다.

익명 함수가 변수에 할당된 이후에는 변수명이 함수명과 같이 취급되며 "변수명(매개변수들)" 형식으로 함수를 호출할 수 있다.

package main

func main() {
	sum := func(n ...int)int {    // 익명 함수 정의
		s := 0
	
		for _, i := range n {
			s += i
		}

		return s
	}

	result := sum(1, 2, 3, 4, 5)  // 익명 함수 호출
	println(result)
}

type문을 사용한 함수 원형 정의

  • type문은 struct, interface 등 Custom Type(또는 User Defined Type)을 정의하기 위해 사용한다.
  • type문은 또한 함수 원형을 정의하는데 사용된다.
// 원형 정의
type calculator func(int, int) int

// calculator 원형 사용
func calc(f calculator, a int, b int) int {
	result := f(a, b)
	return result
}

이렇게 함수의 원형을 정의하고 함수를 타 메소드에 전달하고 반환받는 기능을 타 언어에서 흔히 델리게이트(Delegate)라고 부른다.

Go는 이러한 Delegate 기능을 제공하고 있다.

 

 

 

반응형