Go 언어 스타터의 팁 몇가지 정리(feat. 노마드, Go 언어 프로그래밍 서적)

Alli_Eunbi·2022년 5월 25일
0

Go를 하는 이유

속도가 매우 빠르다. JAVA, Python, Javascript 보다도 빠른 속도를 보여줍니다.


Go의 파일 정리

go의 위치를 파악하기 위해 아래를 터미널에 입력하면 위치를 바로 파악 할 수 있다.

$which go

/usr/local/go/bin/go 로 위치가 잘 나오면 /usr/local/go/src 아래에 실행 코드 파일을 넣도록 하자.
단, src 안에 url과 동일하게 파일을 만들어야 한다.
예를 들어, https://github.com/alli-eunbi/go-lang-practice 의 코드를 src 안에 넣는다고 하면 /usr/local/go/src/github.com/alli-eunbi/go-lang-practice 와 같은 형식으로 저장해야한다.


package main 이란?

go를 처음 배우면 아래 코드부터 시작하게 됩니다.

package main

func main(){}

go는 package main가 작성되어 있는 파일만 컴파일(실행)프로그램으로 인식합니다.
package main 밑에는 main 함수가 작성되어 있어야 합니다. go가 package main 에서 실행을 위해 찾는 함수가 main 이기 때문입니다.

go가 실행되지 않는 에러 발생시,
1. go: inconsistent vendoring in C:\Go\src
위와 같은 에러가 발생할때는 터미널에서 아래 명령어를 입력 해보면 된다.

$ go get -u 

위를 입력했는데 아래처럼 나온다면

go: . (/usr/local/go/src/github.com/alli-eunbi/learngo) is not within module rooted at /usr/local/go/src

아래를 터미널에 입력하면 된다.

$ go mod init {앱 이름}

// 예시) go mod init learngo

그러면 go mod가 설정 된다.

go: creating new go.mod: module learngo
go: to add module requirements and sums:
        go mod tidy

이렇게 뜨면 잘 설정 된 거지만, 읽어보면 go mod tidy를 실행해달라고 한다.
(go mod tidy는 go.mod 파일이 모듈의 소스 코드와 일치하는지 확인합니다. 현재 모듈의 패키지 및 종속성을 빌드하는 데 필요한 누락된 모듈 요구 사항을 추가합니다. )

$ go mod tidy

이렇게 까지 하고 나면 go 실행이 잘 될것 같았으나 아래와 같이 뜬다.

$ go run main.go 
main.go:1:1: expected 'package', found 'EOF'

이거는 아주 단순하게, 파일을 저장하지 않아서 그런 것이니 main.go 에서 저장 한번 해주면 됩니다.

저장하려는데 아래 에러가 발생하는 경우

'main.go': Insufficient permissions. Select 'Retry as Sudo' to retry as superuser.

이럴때는 파일의 권한을 변경해줘야하는데 두가지 방법이 있다.

sudo chmod -R 777 {파일명}

위는 아주 빠르게 해결을 할 수 있지만, 보안 문제에 최악이다.
chmod -R 777을 사용하게 되면 전체 사용자가 실행, 읽기, 쓰기가 가능해진다.

따라서 특정 사용자에게만 권한을 줄수 있도록 해야한다.

$ sudo chmod -R username directoryname

혹시라도 현재 유저명을 모른다면 아래로 확인하면 된다.

$ echo $USER
alli

//현재 디렉토리 사용권한 바꾸기
$ sudo chmod -R alli .

(여담으로 이 문제가 왜 발생하는지 몰랐는데.. 나라 기관에서 서류 다운받으려면 안랩의 보안 프로그램들이 깔리게 된다. 그러면 그 안랩 프로그램이 모든 실행과 인터넷 연결을 지멋대로 관리해버리기 때문에 다 권한을 뺏어가버린다.(욕나오는..ㅎ)

혹시라도 권한문제가 갑자기 vscode상에서 발생하거나 개발 프로그램 설치에 문제가 생긴다면 내가 국가 기관 사이트들을 방문한 적이 있는지 확인해보는게 좋다..ㅎ
있다면, 안랩 관련 프로그램 다 삭제해버리면 된다.)


fmt란?

fmt는 출력(print)과 같은 기본적인 함수들을 제공하는 패키지이다.

package main

import (
	"fmt"
	"main/something"
)

func main(){
	fmt.Println("hello")
    }

이렇게 입력하고 go run . 을 하면 hello가 잘 뜨는걸 볼 수 있다.


Go 에서 함수를 대문자로 사용하는 이유

GO는 주로 카멜케이스를 사용한다.
하지만 함수에 있어서는 대문자를 사용하는 경우가 많다.
Go에서는 함수가 대문자로 되어 있어야 export 가 되기 때문이다.
예를 들어, 자바스크립트에서는 export default 함수명 을 해줘야 모듈이나 함수가 외부에서도 사용 가능했으나 go는 대문자로 되어 있으면 된다는 점이 다르다.
시작이 소문자로 된 함수는 export가 안된다.
따라서 아래와 같이 함수를 작성 하면 된다.

//something.go
//export할 함수 작성
package something

import "fmt"

func SayHello(){
	fmt.Println("Wassssssup")
	
}

// main.go
// main.go 에서 자동 import 해서 사용 가능
// something이라는 package가 autoimport 됨
package main

import (
	"fmt"
	"main/something"
)

func main(){
	fmt.Println("hello")
	something.SayHello()
}

go module이 사용이 안되는 경우

The "gopls" command is not available. Run "go get -v golang.org/x/tools/gopls" to install.

vscode에서 깔라는거 다 깔았는데도 계속해서 에러가 뜨는 경우가 있다. 이걸 해결하기 위해 거의 하루 종일을 시간을 썼는데 보통은 go 버전을 업데이트 하거나 gopath를 바꾸라고 하는 등 다양한 이야기가 있지만.. 다 해결이 안됐다.

그러다 vscode 버전 문제일 수 있다는 희미한 stackoverflow 댓글을 보고
vscode를 업데이트 했더니 해결됐다.

맨 밑에 세팅 버튼에서 버전 업데이트 하고 go extension도 다시 깔면 된다.

하지만 혹시라도 gopls라던가 다른 모듈을 install 하라는 warning도 안뜨고 모듈도 먹히지 않을때에는 vscode에서 command pallete에 들어간 다음 go: Install/Update tools 에서 모든 tools를 체크 해주면 된다.


Go에서 상수/변수 사용하기

Go는 typescript를 사용해본 사람이라면 비슷하다고 느낄 부분이 많은 언어 입니다. 매우매우 강한 강타입 언어이고 javascript 에서 var과 const와 동일한 부분이 있습니다.

그럼 상수(const)와 변수(var)은 어떤 차이가 있을까?
const는 var 서로 다른 메모리에 할당 됩니다. const같은 경우 한번 메모리에 저장되면 값이 변경 될 수 없는 메모리에 저장 되고 컴파일러 차원에서 변경이 불가능하게 막기 때문에 재할당 및 선언이 불가능합니다.

//기본 구조
const 상수명 타입 = 데이터
var 변수명 타입 = 데이터

//예시
const name string = "alli"
var number int = 123

매번 타입을 지정하는게 귀찮은 경우는 아래를 사용하면 됩니다.
다만, 조건이 함수 아래에서만 동작한다는 걸 기억하면 됩니다.

//좋은 예시
func main(){
	num := 1
}

이렇게 단축해서 사용하는 경우는 상수가 아닌 변수이고, 상수는 const로 반드시 명시해줘야 하기때문에 위와 같이 사용 불가하다.


Go 에서 함수 사용하기

go는 typescript와 비슷하게 매개변수 및 함수의 return 값의 타입을 지정해야한다.

func nameLen(name string) (string, int){
	return name, len(name)
}

func main(){
	fmt.Println(nameLen("alli"))
}

여기서, 만약 return 되는 name값은 사용을 안한다고 하면 어떻게 함수를 호출시키는게 좋을까?

func nameLen(name string) (string, int){
	return name, len(name)
}

func main(){
	name, _:= nameLen("alli")
}

위와 같이 사용을 안하는 return 값은 "_" 처리 하면 된다.

만약에 매개 변수가 많아진다면 어떻게 해야할까?

func allNameLen(name ...string){
	fmt.Println(name)
}

func main(){
	allNameLen("alli", "cj", "jerry", "eunbi")
}

//실행하면 아래가 print됨
[alli cj jerry eunbi]

위와 같이 ...을 활용하면 된다. 여러개로 받은 매개변수는 go에서 배열로 만드는 것을 확인할 수 있다.

하나 더, go에서는 naked return 이라는 것이 존재한다.
함수에서 return 값을 변수명과 함께 지정 해두면 "return 변수"를 "return" 만 써도 된다는 것이다.

func nakedReturn(name string) (nameLen int){
	nameLen = len(name)
	return
}

마지막으로 go 에서 제공하는 defer이 있다.
defer는 함수가 실행 완료했는지를 체크하는 키워드 이다.
여기서 과연 '키워드'가 무엇일까 궁금해지는데, 키워드는 아래와 같다.

키워드는 컴파일러가 사용자 코드를 이해하고 구문 분석하는 데 도움이 되는 특수 단어이다.
지금까지(Go 1.18) Go에는 25개의 키워드가 있다.

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

defer는 try catch finally 중 finally에 해당하는 키워드이다. defer를 사용하면 함수가 에러 발생 여부와 상관 없이 실행 종료 되었음을 알 수 있다. 이러한 defer문 실행을 '지연'한다고도 표현하는데, defer의 구문이 함수가 종료될때까지 지연시켰다가 종료되기 직전에 실행되는걸 의미한다.

func nakedReturn(name string) (nameLen int){
	defer fmt.Println("done")
	nameLen = len(name)
	return
}

func main(){
	fmt.Println(nakedReturn("alli"))
}

위와 같은 defer문은 먼저 done이 출력 된 이후에 4가 출력되는걸 확인할 수 있다.
실행되는 순서는 아래와 같다.
nakedReturn 함수에서 nameLen에 4가 할당->
defer의 done 출력->
main함수의 fmt.Println(NakedReturn("alli")) 결과 출력


Go 에서 for문 사용하기

Go는 자바스크립트의 map과 같은 함수가 존재하지 않는다.
기본 for문과 동일하게 사용 가능하다. 다만 아래를 조심할 필요가 있다.

func forArray(numbers ...int)(number int){
	for i := range numbers{
		number += i
	} 
}
func main(){
	fmt.Println(forArray(1,2,3))
}

//print 된 값
3

왜 6이 아닌 3이 출력 될까?
go에서 for문의 첫번째 인자는 배열의 인덱스를 가르킨다. 마치 자바스크립트 map 함수에서 array.map(n, i)가 n이 원소이고 i가 배열의 인덱스였듯이 go 에서는 n과 i의 위치만 반대로 된 것이다.

따라서 배열의 원소 덧셈 for문은 아래와 같이 작성한다.

func forArray(numbers ...int)(number int){
	for _, n := range numbers{
	 	number += n
	}
	return 
}

특별히 인덱스를 사용하지 않는다면 "_"로 표기하는 것이 좋다.
왜냐면 go에서는 변수가 선언은 됐는데 사용되지 않으면 error를 방출하기 때문이다.

func forArray(numbers ...int)(number int){
	for i:=0; i<len(numbers); i++{
		number += numbers[i]
	}
	return 
}

혹시라도 가장 기본적인 for문을 찾는다면 위와 같이 작성해도 상관 없다.

Go에서의 if/else 사용하기

go의 if else는 자바스크립트에서 괄호가 없는 버전이랑 비슷하다.

func areYouRich(money int) bool{
	if money>5000{
		return true
	}else{
		return false
	}
}

이런식으로 쓰면 되고 golint는 else문이 return 값이 있으므로 drop할 수 있어서 아래처럼 사용해도 된다.(go-lint에서 추천하는 방법, 린트는 소스 코드를 분석하여 프로그램 오류, 버그, 스타일 오류, 의심스러운 구조체에 표시를 달아놓기 위한 도구들을 가리킴. 이 용어는 C 언어 소스 코드를 검사하는 유닉스 유틸리티에서 기원한다)

func areYouRich(money int) bool{
	if money>5000{
		return true
	}
	return false
}

하지만 이걸하면 실행은 되나 아래와 같은 문구가 뜨는 밑줄이 생긴다.

should use 'return money > 5000' instead of 'if money > 5000 { return true }; return false' (S1008) go-staticcheck

go-staticcheck는 정적 분석을 사용하여 버그 및 성능 문제를 찾고 단순화를 제공하고 스타일 규칙을 적용한다.
따라서 가장 단순한 방법인 if else를 사용하지 않는 아래를 추천한다.

func areYouRich(money int) bool{
	return money>5000
}

(참고로 go-staticcheck를 비활성화 하면 아래와 같은 추천은 따로 뜨지 않을 것이다.)

여기서 잠시 go-lint와 go-staticcheck는 무슨 차이가 있나 궁금해질 것이다. 보면 둘다 동일한 기능을 하는거 같은데 말이다.

표에서 보다시피 go-lint의 경우 초반 인기는 go-staticcheck보다 높았으나 인기가 감소하고 있고 go-staticcheck는 인기가 증가하고 있다.

"It was archived in mid-2021 and it is being recommended Staticcheck be used as a replacement."
=> 코드베이스의 코드 스타일과 패턴을 확인하기 위해 Golang은 이미 golint가 설치된 상태로 제공되는데 2021년 중반부터는 Staticcheck 를 대체품 으로 사용하는 것을 추천한다고 한다.

(참조 : https://go.libhunt.com/compare-go-tools-dominikh-vs-lint
https://sourcelevel.io/blog/state-of-golang-linters-and-the-differences-between-them)

또 하나, go에서는 다른 언어에서는 제공하지 않는 if else만의 특이한 문법이 있는데 바로 if문 안에 변수를 선언할 수 있다는 점이다.

func areYouRich(money int) bool{
	if koreanMoney := money*1200 ; koreanMoney> 8000 {
		return true
	}
	return false
}

(나름 korean money라고 1200원 환율 적용을 해봤다ㅋ)
위와 같이 if else문 사이에 지역 변수를 만들어 사용할 수 있다.


Go에서 Switch Case 사용하기

func switchCases(money int)string{
	switch money {
	case 5000 : 
		return "오천원"
	
	case 6000 : 
		return "육천원"
	}
	return "얼만지 모르겠음"
}

이렇게 사용하면 money가 5000과 6000 일때는 "오천원"과 "육천원"이 나오고 나머지 숫자에서는 "얼만지 모르겠음"이 출력된다.

func switchCases(money int)bool{
	switch koreanMoney := money*1200; {
	case koreanMoney > 80000 : 
		return true
	
	default: 
		return false
	}
}

이렇게 if문 처럼도 사용 가능하고, switch 뒤에 변수 선언을 사용하여 활용도 가능하다.
default는 위에서 return false로 처리한 것과 동일하다.

profile
BACKEND

0개의 댓글