1.다른 페키지 export 하기
python 에서 내가 만든 프로젝트의 함수를 import, export 하듯
go에서도 비슷한 개념으로 export를 해볼 것이다.
package ex_game
type User struct {
Id string
Pass string
Score int
}
package main
import (
"fmt"
"./ex_game"
)
func main() {
user1 := ex_game.User{Id: "logcat", Pass: "1234", Score: 0}
fmt.Println(user1)
}
결과:
{logcat 1234 0}
메인 프로젝트에서 익트포트 할 프로젝트의 페키지명인 "ex_game"을 import 하여
그 안에 있는 User 구조체를 사용하였다.
여기서 중요한 점은
다른 프로젝트에서 사용하는 public 접근제어를 걸기 위해서는 이름 첫글자가 대문자여야 하고 필드 변수들도 앞글자가 대문장여야 한다.
<export가 안될때>
"ex_game"
처럼 임포트 할때 오류가 생길 수 있다.
제가 그 억까에 걸려서 꼬박 3시간 동안 구글링 + 제부팅을 반복한 결과 대략 적인 해결법 두가지를 알아냈다
개인적으로 2번 해결법을 추천
1. "./" 추가
위 코드의 "./ex_game" 처럼 ./를 붙여주면 에러 없이 실행이 가능하다.
2. mod 생성
우선 "시스템 환경변수 편집" 에 들어가서
환경변수 - 사용자에 대한 환경변수 에서
새로 만들기
이름 : GOPATH 값 : 자신의 GO 프로젝트가 저장되는 파일
여기서 중요한 점은 파일 정리를 c:/.../src / 프로젝트저장파일 처럼 한 다음에
GOPATH의 값을 C:/.../src 처럼 해주면 편하다
이름 : GOROOT 값 : C:/program files/GO (내 GO가 깔린 위치)
그런 다음 VSCODE터미널로 가서 내 프로젝트 파일로 이동한 다음
#go mod init (이름) 을 입력한다
여기서 (이름)은 아무거나 상관없다.
이러면 내 프로젝트 파일에 go.mod가 생성된다
여기서 또다시 중요한점
좀 전에 만든 GOPATH의 위치와 go.mod의 저장 위치가 달라야 한다.
안그러면 GOPATH/go.mod exits but should not 이라는 에러가 뜬다 .
이것이 파일 정리를 위처럼 하라고 한 이유이다.
GOPATH는 내 소스코드가 있는 폴더의 상위폴더, go.mod는 내 소스코드가 있는 폴더로 저장해 놓으면 된다,
그 다음 터미널에
#go mod tidy
를 입력하면 go.sum 파일이 생성되고 익스포트가 정상적으로 될 것이다.
만약 go.mod를 여러번 설치, 삭제 하고 그 이전에 뭘 많이 만졌다면 go.mod의 저장 위치를 vscode가 이상한 곳(아마 내가 맨 처음에 저장한 곳)으로 착각하여 go mod tidy가 실행되지 않는다.
여기서 또또다시 중요한점
이 상황에서 1번 해결법을 쓸때에는 설치되어 있는 go.mod를 삭제해야 한다.
만약 go mod tidy 가 실행이 안되고
Error loading workspace: You are outside of a module and outside of $GOPATH.... 라는 에러가 뜰때의 해결법을 아신다면
댓글 부탁드립니다.
2.생성자 만들기
go에서는 class가 없고 생성자도 없다.
그래서 수동으로 생성자를 만드는 방식을 많이 사용한다.
package ex_game
type User struct {
id string
pass string
score int
}
func NewUsers(id string, pass string) *User {
user := User{id: id, pass: pass, score: 0}
return &user
}
package main
import (
"fmt"
"./ex_game"
)
func main() {
fmt.Println(*ex_game.NewUsers("logcat2", "1234"))
}
결과
{logcat2 1234 0}
우리에게 익숙한 생성자의 모습이 보인다.
User 구조체에 있는 필드변수들은 private라 접근할 수 없지만 유사 생성자를 사용하여 생성이 가능하다
NewUser 함수는 *User(User 타입 변수의 주소값을 저장하는 변수)를 리턴하기 때문에 &user(user의 주소값)을 넘겼고
main에서는 받아온 주소에 들어있는 값을 출력하기 위해 *을 붙였다
이렇게 하면 ex_game 에서 만든 변수를 복사하지 않고 그대로 main에서 사용 가능하여 메모리를 아낄 수 있다.
3.메소드 생성
계속 말하지만 golang은 클레스가 없다.
하지만 class와 같은 동작을 할 수 있다.
일단 메소드를 만드는 방법은 아주 쉽다
func (a User) ScoreEditer(int newscore) {}
여기서 func와 메소드명 사이에 쓴것을 reciver 라고 부른다.
위의 리시버는 a라는 리시버를 User 타입으로 만든다는 의미이다.
클레스가 없어도 구조체를 중심으로 생성자와 필드변수, 메소드까지 엮어 class를 구현한다.
예제로 자세하게 알아보자
package ex_game
type User struct {
id string
pass string
score int
}
func NewUsers(id string, pass string) *User {
user := User{id: id, pass: pass, score: 0}
return &user
}
func (a User) Scoreediter(newscore int) {
a.score = newscore
}
package main
import (
"fmt"
"./ex_game"
)
func main() {
user1 := *ex_game.NewUsers("logcat2", "1234")
user1.Scoreediter(100)
fmt.Println(user1)
}
결과
{logcat2 1234 0}
??? 분명히 Scoreediter 메소드를 호출하여 user1의 score을 100으로 바꾸었는데
0이 출력된다.
뭐가 문제일까?
그 이유는 Scoreediter 메소드의 리시버a는 user1을 복사하여 사용하기 때문이다.
그래서 원래 있던 user1은 변화가 없었던 것이다.
Scoreediter 메소드의 (a User) 을 (a *User)로 수정해 주면
{logcat2 1234 100}
우리가 원한 값이 출력된다.
리시버를 포인터로 지정하면 호출받은 User을 복사하지 않고 그대로 수행한다.
4.error
go에서는 try except 같은 예외 체크 구문이 없다.
따라서 오류를 직접 체크해 줘야 한다
package ex_game
import "errors"
type User struct {
id string
pass string
score int
}
func NewUsers(id string, pass string) *User {
user := User{id: id, pass: pass, score: 0}
return &user
}
func (a *User) Scoreediter(newscore int) error {
if newscore < 0 {
return errors.New("do not")
}
a.score = newscore
return nil
}
newscore가 0보다 작으면 에러를 리턴하여 수행을 못하게 하는 코드이다.
부울 타입에는 true와 false 가 있듯 error타입에는 두가지 값이 있다.
errors 와 nil이다
여기서 nil은 null과 비슷한 개념이다.
go에서는 return에 대하여 if가 씌였을때 else가 필요없다.
return을 만나면 바로 함수가 끝나기 때문이다.
package main
import (
"fmt"
"log"
"./ex_game"
)
func main() {
user1 := *ex_game.NewUsers("logcat2", "1234")
push_error := user1.Scoreediter(-10)
if push_error != nil {
log.Fatalln(push_error)
}
fmt.Println(user1)
}
Scoreediter의 리턴값이 nil이 아니면
프로그렘을 종료시켜버리는 log.Fatalln() 함수를 호출한다.
결과 :
2023/01/09 21:21:13 do not
exit status 1