GO

[Go]Golang 기본개념

[Go]Golang 개본개념

Go 파일 이해하기

Go 파일의 특징

  1. 컴파일을 하기 위해서는 main.go가 필요하다.
  2. Go 파일은 package를 선언해줘야 한다.
  3. 컴파일시에 function을 찾아간다.(없으면 오류)

Go 파일의 구성

package main

import "fmt" // fmt 사용시 자동 생성, 사용하지 않을 시에 자동 제거(vscode)

func main() {
  fmt.Println("Hello World!")
}

fmt : Go가 내장하고 있는 패키지

Go 파일 실행

go run 파일명.go

export와 import

function 이름이 대문자로 시작하면 public

function 이름이 소문자로 시작하면 private

package main

import (
  "fmt" // fmt 사용시 자동 생성

  "github.com/saichoi/learngo/someting" // someting 사용시 자동 생성
)

func main() {
  fmt.Println("Hello World!")
  someting.SayHello()
  someting.sayBye() // private 사용하면 에러남
}
package someting

import "fmt"

// Private 함수
func sayBye() {
  fmt.Println("Bye")
}

// Public 함수
func SayHello() {
  fmt.Println("Hello")
}

변수(Variables)와 상수(Constants)

변수 : 값 변경 가능(JS의 let)

상수 : 값 변경 불가능(JS의 const)

Go는 Type 언어로 해당 변수가 어떤 타입인지 알려줘야하는 언어이다. 하지만 func 내부에서는 Go가 타입을 추론해준다.

package main

import "fmt"

func main() {
  // 타입이 없는 상수 (Constant)
  const name1 = "dahye"

  // 타입이 있는 상수(Constant)
  const name2 string = "dahye"

  // 타입이 없는 변수 (Variables)
  var age1 = 30 // 미사용시 에러
  fmt.Println(age1)

  // 타입이 있는 변수 (Variables)
  var age2 int = 25 // 미사용시 에러
  fmt.Println(age2)

  // 축약형 (func 내부에서만 사용 가능)
  name3 := "dahye" // 미사용시 에러
  fmt.Println(name3)
  age3 := 17 // 미사용시 에러
  fmt.Println(age3)

  // 값변경이 불가능한 상수
  name1 = "choi" // 에러
  name2 = "choi" // 에러

  // 값변경이 가능한 변수
  age1 = 21
  age2 = 34
  age3 = 13
}
Go 언어의 타입
타입 종류
string type string
bool type bool
numeric types int8, unit8(byte), int16, unit16,(rune), unit32, int64, unit64, int, unit, unitptr,
float32, float64,
complex64, complex128

함수(Function)

1. 리턴 타입 명시

Go에서는 변수는 물론 함수에도 return 타입을 알려줘야한다.

func 함수명(인수 인수타입) 함수타입{ 실행코드 }

package main

import "fmt"

// 숫자 타입의 인수 a, b를 받아 a * b를 반환하는 함수
func multipy(a int, b int) int {
  return a * b
}

func main() {
  fmt.Println(multipy(2, 2))
}

2. 복수의 리턴값

Go에서는 함수가 여러개의 값을 return 할 수 있다.

package main

import (
  "fmt"
  "strings"
)

// 이름을 받아 -> 이름의 길이를 세고 이름을 대문자로 변환하는 함수
func lenAndUpper(name string) (int, string) {
  return len(name), strings.ToUpper(name)
}

func main() {
  totalLenght, upperName := lenAndUpper("dahye")
  fmt.Println(totalLenght, upperName)
}

// _(underscore)로 value 무시
func main() {
  totalLenght, _ := lenAndUpper("dahye")
  fmt.Println(totalLenght)
}

3. 복수의 인수값

Go에서는 원하는 만큼 함수의 인수를 받을 수 있다.

package main

import "fmt"
// 받아온 여러개의 인수를 모두 출력하는 함수
func repeatMe(words ...string) {
  fmt.Println(words)
}

func main() {
  repeatMe("dahye", "dayeon", "minsu", "jaewook")
}

4. naked return

return 값이 명시되지 않을 경우 변수가 같은 값을 자동으로 return 해준다.

package main

import (
  "fmt"
  "strings"
)

// 이름을 받아 -> 이름의 길이를 세고 이름을 대문자로 변환하는 함수
func lenAndUpper(name string) (lenght int, uppercase string) {
  lenght = len(name)
  uppercase = strings.ToUpper(name)
  return // 변수가 같은 값을 자동으로 리턴해준다.
}

func main() {
  totalLenght, upperName := lenAndUpper("dahye")
  fmt.Println(totalLenght, upperName)
}

5. defer

함수 종료 후에 추가적으로 동작할 수 있도록 해주는 기능이다.

package main

import (
  "fmt"
  "strings"
)

func lenAndUpper(name string) (lenght int, uppercase string) {
  // lenAndUppder 함수가 실행된 후에 추가적으로 코드가 실행된다.
  defer fmt.Println("I'm done.")
  lenght = len(name)
  uppercase = strings.ToUpper(name)
  return 
}

func main() {
  totalLenght, upperName := lenAndUpper("dahye")
  fmt.Println(totalLenght, upperName)
}

반복문(Loop)

1. for / range

range는 변수 안의 값을 하나씩 가져오는 역할을 하며 기본적으로 index 값을 가진다. index 사용하지 않는 경우 _(undersocore)로 대신할 수 있다.

package main

import "fmt"

func superAdd(numbers ...int) int {
  for index, number := range numbers {
    fmt.Println(index, number)
  }
  return 1
}

func main() {
  superAdd(1, 2, 3, 4, 5, 6)
}

2. for

()가 없는 for 반복문으로 작성할 수 있다.

package main

import "fmt"

func superAdd(numbers ...int) int {
  for i := 0; i < len(numbers); i++ {
    fmt.Println(numbers[i])
  }
  return 1
}

func main() {
  superAdd(1, 2, 3, 4, 5, 6)
}
예제
package main

import "fmt"

// 들어온 인자의 값을 전부 더하는 함수
func superAdd(numbers ...int) int {
  total := 0
  for _, number := range numbers { // index를 사용하지 않기 위해 _로 대체
    total += number
  }
  return total
}

func main() {
  result := superAdd(1, 2, 3, 4, 5, 6) // 함수에 여러개의 인자를 넣음
  fmt.Println(result)
}

조건문

1. if

()가 없는 if 조건문을 사용할 수 있다.

package main

import "fmt"

func canIDrink(age int) bool {
  if age < 18 {
    return false
  } else {
    return true
  }
}

func main() {
  fmt.Println(canIDrink(18))
}
variable expression

조건문 내에서만 사용하는 변수를 생성할 수 있다.

package main

import "fmt"

func canIDrink(age int) bool {
  if koreanAge := age + 2; koreanAge < 20 {
    return false
  }
  return true
}

func main() {
  fmt.Println(canIDrink(17))
}

2. switch

package main

import "fmt"

func canIDrink(age int) bool {
  switch {
  case age < 18:
    return false
  case age == 18:
    return true
  case age > 50:
    return false
  }
  return false
}

func main() {
  fmt.Println(canIDrink(18))
}
variable expression

조건문 내에서만 사용하는 변수를 생성할 수 있다.

package main

import "fmt"

func canIDrink(age int) bool {
  switch koreaAge := age + 2; {
  case koreaAge < 20:
    return false
  case koreaAge == 20:
    return true
  case koreaAge > 50:
    return false
  }
  return false
}

func main() {
  fmt.Println(canIDrink(18))
}

Pointers

메모리에 접근하여 메모리 주소와 저장된 값을 볼 수 있다. 이 방법을 이용하여 같은 값을 공유하는 것으로 메모리 저장 공간을 줄여주고 실행 속도를 빠르게 할 수 있다.

& : 메모리 주소 보는 법

* : 메모리 안의 값을 살펴보는 법

package main

import "fmt"

func main() {

  // a의 복사본을 만들면 같은 메모리의 값을 공유하는 것이 아니라 새로운 메모리에 똑같은 값을 넣게 된다.
  a := 2
  b := a

  a = 10
  a = 12

  fmt.Println(a, b) // 12  2 -> 아무리 a의 값을 변경하여도 a 값을 복사한 값을 가진 b의 값은 아무런 영향을 주지 않는다.

  // 메모리 주소 보는 방법
  fmt.Println(&a, &b) // 0xc0000b2008 0xc0000b2010 -> 주소가 다른 것을 확인할 수 있다.

}
package main

import "fmt"

func main() {

  a := 2
  b := &a           // b에 a의 메모리를 저장
  fmt.Println(a, b) // 2 0xc0000b2008 -> 값과 메모리 주소를 확인 할 수 있다.

  // 메모리 안을 살펴보는 방법
  fmt.Println(*b) // 2 -> b에 저장된 값이 a의 값과 같다는 것을 확인할 수 있다.

}
package main

import "fmt"

func main() {

  a := 2
  b := &a // b에 a의 메모리를 저장
  a = 5
  fmt.Println(*b) // 5 -> a의 값을 공유하고 있는 b의 값도 함께 변경되는 것을 확인할 수 있다.

}
package main

import "fmt"

func main() {

  a := 2
  b := &a
  *b = 20        // a의 메모리 주소를 공유하는 b의 값을 변경할 수 있다.
  fmt.Println(a) // 20 -> a는 b와 값을 공유하기 때문에 b의 값이 변경되면 a의 값도 변경된다.
}

Arrays & Slices

Arrays

만들어둔 배열의 개수만큼 값을 넣을 수 있다.

package main

import "fmt"

func main() {
  names := [5]string{"dahye", "dayeon", "minsu"}
  names[3] = "lalala"
  names[4] = "lululu"
  names[5] = "nonono" // 정해진 배열의 크기 만큼만 넣을 수 있기 때문에 에러가 난다.
  fmt.Println(names)
}

Slices

배열의 개수 제한 없이 값을 넣을 수 있다.

package main

import "fmt"

func main() {
  // slice는 array에서 길이를 넣지 않은 것이다.
  names := []string{"dahye", "dayeon", "minsu"}

  // slice 배열에 값 추가하는 법
  // slice는 array와 같지만 값을 추가하는 방법은 다르다.
  // names[3] = "lalala" // -> 이렇게 하면 에러남
  names = append(names, "lalala")

  fmt.Println(names)

}

Maps

Go에서 map은 key와 value를 지정할 때 타입을 알려줘야한다.

package main

import "fmt"

func main() {

  // key와 value에 타입을 지정한다. 첫번째 string은 key의 타입, 두번째 string은 value의 타입이다.
  dahye := map[string]string{"name": "dahye", "age": "20"}
  fmt.Println(dahye)

  // 반복문으로 map 출력하기
  for key, value := range dahye {
    fmt.Println(key, value)
  }

  // key를 제외하고 value만 출력
  for _, value := range dahye {
    fmt.Println(value)
  }

  // value를 제외하고 key만 출력
  for key, _ := range dahye {
    fmt.Println(key)
  }

}

초기화되지 않은 map에 값을 할당할 수 없다. 초기화 하지 않은 map은 nil이 된다. 

// map 초기화하는 방법
var results = map[string]string{}
var results = make(map[string]string)

Structs(구조체)

Go에서는 자바의 class, python과 JS의 object 대신에 structs(구조체)를 사용한다. 또한 다른 언어와 달리 method와 constructor가 존재하지 않기 때문에 직접 constructor를 실행해야한다.

package main

import "fmt"

// 구조체 만드는 법
type person struct {
  name    string
  age     int
  favFood []string // string 타입의 slice
}

func main() {
  favFood := []string{"tteokbokki", "sushi"}
  // dahye := person{"dahye", 30, favFood}
  dahye := person{name: "dahye", age: 30, favFood: favFood} // key : value를 더욱 직관적으로 볼 수 있다.
  fmt.Println(dahye)
}
최신글