- 컴파일을 하려면 main.go의 이름 변경 불가
- 목적에 따라 컴파일을 할 필요가 없다면 main.go 사용하지 않아도 괜찮음
- 컴파일 할 필요가 없는 경우 : 오픈소스 기여, 라이브러리 개발...
main.go
- 컴파일을 위해 필요
- 컴파일러는 main package와 그 안에 있는 main func을 먼저 찾고 실행시킴
// 내가 어떤 패키지를 사용하는지 작성해줘야 함
package main
// 프로그램의 시작점이 되는 부분
func main() {
...
}
go의 구성
- Package declaration
- Import packages
- Functions
- Statements and expressions
Package and Import
package main
// formatting을 위한 패키지, 텍스트 출력/인쇄에 사용
import "fmt"
func main() {
fmt.Println("Hello World")
}
- go에서는 모든 프로그램이 패키지의 일부임
- go의 문장은 행 끝(enter) or 세미콜론(;)으로 구분
(enter 누르면 암묵적으로 ; 사용, 소스에는 표시 x)
- { 는 문장의 선두에 올 수 없음
func main()
{ // Error 발생
fmt.Println("Hello World!")
}
변수와 상수
상수
- 변수지만 값을 바꿀 수 없음
- JS와 비교 : const
package main
func main() {
// 아래와 같이 선언시 타입이 없는 상수가 선언됨
const name = "jisu"
}
package main
func main() {
const name string = "jisu" // 상수
}
변수
- 말 그대로 변수, 값 변경 가능
- JS와 비교 : let
- type : int / uint8 / float32 / complex64 / string / bool ... (다양함)
- 변수 선언 방법
1. var 키워드 사용
// var 변수이름 type = 값
var name string = "jisu" // 변수
2. := 기호 사용
// 변수이름 := 값
name := "jisu"
- 기호를 사용하면 값을 통해 타입을 추론 (컴파일러가 타입 결정)
- 정해진 타입은 내가 임의로 변경 불가
- 기호는 오로지 func안에서만 사용 가능하고 변수에서만 적용 가능
var와 := 의 차이점
var | := |
func의 안 또는 밖 어디서든 사용 가능 | func 안에서만 사용 가능 |
함수 선언과 값 할당 별도로 가능 (함수 선언 먼저 하고 나중에 값 할당 가능) |
함수 선언과 값 할당 별도로 불가능 (함수 선언과 값 할당을 같은 라인해서 해야함) |
초기값이 없는 변수 선언
- 모든 변수가 초기화 됨
- 초기값을 지정하지 않고 변수를 선언하면 기본값으로 설정 됨
func main() {
var a string
var b int
var c bool
fmt.Println(a) // ""
fmt.Println(b) // 0
fmt.Println(c) // false
}
다중 변수 선언
- 여러 변수를 한 줄에 선언 가능
var a, b, c, d int = 1, 3, 5, 7
블록 변수 선언
- 여러 변수 선언을 블록으로 그룹화 가능
var (
a int
b int = 1
c string = "hello"
)
Functions
- 페이지가 로드될 때 자동으로 실행되지 않고 호출에 의해 실행 됨
func 함수이름 { ... }
package something
import "fmt"
// private 함수, 호출 안됨
func sayBye() {
fmt.Println("Bye")
}
// 호출 가능
func SayHello() {
fmt.Println("Hello")
}
- 첫 문자가 소문자로 이름이 지어진 함수는 private
sayBye() : 다른 패키지에서 호출 불가능
SayHello() : 다른 패키지에서 호출 가능
- 첫 문자가 대문자로 작성된 함수는 다른 패키지로부터 호출된 함수라고 봐도 됨
Parameters
func mul(a int, b int) {
fmt.Println( a * b )
}
// 파라미터의 타입이 같다면 아래와 같이 사용 가능
func mul(a, b int) {
fmt.Println( a * b )
}
- 원하는 만큼 파라미터를 전달하는 방법
func repeatMe(words ...string) {
fmt.Println(words)
}
func main() {
repeatMe("jisu", "lee", "ji", "su")
}
// result, array 형태로 나옴
[jisu lee ji su]
반환값 (Returns)
- 반환값이 있을때 반환형을 알려줘야함
func mul(a int, b int) int {
return a * b
}
- 여러개의 반환값을 가질 수도 있음
// 반환 값이 int, string 2개임
func lenAndUpper(name string) (int, string) {
return len(name), strings.ToUpper(name)
}
ignored value
- _ 는 무시된 value → 컴파일할때 무시함
// 두개의 값을 반환하는 함수에서 값을 하나만 받으면 Error 발생
func lenAndUpper(name string) (int, string) {
return len(name), strings.ToUpper(name)
}
func main() {
totalLength := lenAndUpper("jisu") // Error
fmt.Println(totalLength, upperName)
}
// value 값을 무시하는 코드를 작성하면 Error 사라짐
func main() {
totalLength, _ := lenAndUpper("jisu")
fmt.Println(totalLength, upperName)
}
naked returns
- 값을 반환하는 또다른 방법
- return 할 변수를 명시 안해도 됨
func lenAndUpper(name string) (length int, uppercase string) {
// = 을 쓰는 이유는 length, uppercase를 업데이트 하고 있기 때문
length = len(name)
uppercase = strings.ToUpper(name)
return
}
defer
- func 끝났을때 추가적으로 무엇인가 동작하도록 할 수 있음
func lenAndUpper(name string) (length int, uppercase string) {
defer fmt.Println("I'm done") // func 끝나고 나서 실행 됨
length = len(name)
uppercase = strings.ToUpper(name)
return
}
for, range, ... args
- 반복문은 for만 사용 가능
- range : index를 줌, for 안에서 적용 가능
func superAdd(numbers int) int {
// numbers안에서 조건에 따라 반복실행을 하도록 해줌
for number := range numbers {
fmt.Println(number)
}
return 1
}
func main() {
superAdd(1, 2, 3, 4, 5, 6)
}
// result
0
1
2
3
4
5
func superAdd(numbers int) int {
// 첫번째는 인덱스, 두번째는 값
for _, number := range numbers {
fmt.Println(number)
}
return 1
}
func main() {
superAdd(1, 2, 3, 4, 5, 6)
}
// result
1
2
3
4
5
6
// 아래와 같이도 가능
for i:=0; i < len(numbers); i++ {
fmt.Println(numbers[i])
}
// result
1
2
3
4
5
6
if with a twist
package main
import "fmt"
func canIDrink(age int) bool {
if age < 18 {
return false
}
return true
}
func main() {
fmt.Println(canIDrink(16))
}
+ variable expression
- if를 쓰는 순간에 변수 생성 가능
func canIDrink(age int) bool {
// if else 조건에서만 사용하기위해 변수 생성
if koreanAge := age+2; koreanAge < 18 {
return false
}
return true
}
// 위 코드는 아래 코드와 같은거임
func canIDrink(age int) bool {
koreanAge := age+2
if koreanAge < 18 {
return false
}
return true
}
Switch
switch {
case age < 18 :
return false
case age == 18 :
return true
case age > 50 :
return false
}
+ variable expression
switch koreanAge := age + 2; koreanAge {
case 10 :
return false
case 18 :
return true
}
Pointers
- Low level programming
- 메모리로 접근해서 메모리의 주소 볼 수 있음
- 메모리 주소에 저장된 값도 볼 수 있음
func main() {
a := 2
b := a // 값이 복사됨
a = 10
fmt.Println(a, b)
}
// result
10 2
// 값이 복사되는걸 원하지 않는 경우 , 실제로 원하는 값: 메모리 주소
func main() {
a := 2
b := 5
fmt.Println(&a, &b) // 메모리 주소 출력
}
// 위 코드와 같은 결과 나옴
func main() {
a := 2
b := &a
fmt.Println(&a, b)
}
- & : Address
- * : Pointer
func main() {
a := 2
b := &a
fmt.Println(*b)
}
// result
2
func main() {
a := 2
b := &a
a = 5
fmt.Println(*b)
}
// result
5
// b는 a의 메모리 주소에 연결되어 있음
// 그래서 a가 바뀌면 같이 바뀜
func main() {
a := 2
b := &a // b는 a를 살펴보는 pointer
*b = 20
fmt.Println(a)
}
// result
20
// b는 a의 주소를 가지고 있기 때문에 a의 값을 바꿀 수 있음
Arrays
- 동일한 유형의 여러 값을 단일 변수에 저장하는데 사용
- 선언 방법
1. var 키워드
// var 배열명 = [길이]데이터 타입 { 값 }
func main() {
var name = [5]string{"jisu", "lee", "ji"}
name[3] = "lalala"
name[4] = "lalala"
name[5] = "lalala" // Error
fmt.Println(name)
}
2. := 기호
// 배열명 := [길이]데이터 타입 { 값 }
func main() {
name := [5]string{"jisu", "lee", "ji"}
name[3] = "lalala"
name[4] = "lalala"
name[5] = "lalala" // Error
fmt.Println(name)
}
- 배열 초기화
// 초기화 되지 않은 경우 지정된 타입의 기본값이 할당 됨
arr1 := [5]int{} //not initialized
arr2 := [5]int{1,2} //partially initialized
// result
[0 0 0 0 0]
[1 2 0 0 0]
- len() : 배열 길이
arr1 := [4]string{"Volvo", "BMW", "Ford", "Mazda"}
fmt.Println(len(arr1))
// result
4
Slices
- 동일한 유형의 여러 값을 단일 변수에 저장하는데 사용
- 배열과 비슷하지만 더 강력하고 유연함
- 길이를 필요에 따라 늘리거나 줄일 수 있음
// 슬라이스명 := []데이터 타입 { 값 }
myslice1 := []int{}
myslice2 := []int{1,2,3}
- 배열 슬라이스
// 배열을 슬라이스하여 슬라이스 생성 가능
arr1 := [6]int{10, 11, 12, 13, 14,15}
myslice := arr1[2:4] // [12, 13]
- append() : 슬라이스에 아이템 추가
func main() {
names := []string{"jisu", "lee", "ji"}
// 2개의 인자를 요구함 1: slice, 2: 추가할 값
append(names, "lalala")
fmt.Println(names[2])
}
- len() : 슬라이스의 길이 (요소의 수) 반환
- cap() : 슬라이스의 용량 반환 (늘리거나 줄일 수 있는 요수의 수)
myslice := []string{"Go", "Slices", "Are", "Powerful"}
fmt.Println(len(myslice)) // 4
fmt.Println(cap(myslice)) // 4
Maps
- key : value 로 값 저장
- 중복 x, 순서 x
func main() {
// map 생성, key=string, value=string
jisu := map[string]string{"name":"jisu", "age":"7"}
fmt.Println(jisu)
}
// result
map[age:12 name:jisu]
- 추가
// 맵이름[key] = value
jisu["year"] = "1998"
- 삭제
// delete(맵이름, key)
delete(jisu, "year")
- 특정 값 찾기
// val, ok := 맵이름[key]
val, ok := jisu["name"] // jisu true
val2, ok2 := jisu["aa"] // false
* val : 값
* ok : 키가 존재하면 true, 키가 없으면 false
- range
func main() {
jisu := map[string]string{"name":"jisu", "age":"7"}
for key, value := range jisu {
fmt.Println(key, value)
}
}
// result
name jisu
age 7
Structs
- 서로 다른 데이터 유형의 멤버 집합을 변수로 만드는데 사용
- 데이터를 그룹화하여 레코드 작성 가능
- type, struct 키워드 사용
type person struct {
// 안에는 구조체의 형태를 만들어주면 됨
name string
age int
favFood []string
}
func main() {
favFood := []string{"rice", "kimchi"}
// 1. 순서대로 기입
jisu := person{"leejisu", 7, favFood }
fmt.Println(jisu)
fmt.Println(jisu.name)
}
// result
{leejisu7 [rice jimchi]}
leejisu
// 이렇게도 작성 가능
func main() {
favFood := []string{"rice", "kimchi"}
// 2. 위 코드보다 명확하게 알아보기 쉬움
jisu := person{name: "leejisu", age: 7, favFood: favFood}
fmt.Println(jisu)
}
출처 :
'Golang' 카테고리의 다른 글
간단한 Job Scrapper 구현 (2) | 2023.02.14 |
---|---|
golang을 이용한 간단한 실습 (1) | 2023.02.13 |