Golang

Go를 3년간 사용하면서 느낀 개인적인 장단점

장대한 삽질 이야기 2024. 4. 9. 20:06

"문법이 간단하고 배우기 쉬우며..." 같은 표면적인 장단점 말고

내가 쓰면서 느꼈던 점 위주로 정리해보려 한다.

내 사견에 앞서 회사에서 Go를 선택한 이유는 아래와 같다.

  • cgo를 통해 C++와 연동이 가능하다.
    • 영상처리를 위해 쓰이는 OpenGL과 의료에서 필수적으로 쓰이는 DCMTK가 C++다.
  • 대부분 C++를 하던 인력이라 Java보단 Go가 문법적으로 친숙했다.
  • Java는 단점이 많다.
    • JVM은 무겁고 느리며
    • Java의 Annotation이 어떻게 동작하는지도 모르겠고 사용하기 싫었다.... (실제로 하신 말)
    • Java와 달리 필요한 부분을 A to Z로 직접 구현해서 사용하는게 매력적이었다.
      • 그래서 그런지 사내에서는 kfaka나 resdis도 사용하지 않고 직접 구현해서 쓰는데 좀 썼으면... 싶다...

go gopher

 

장점

개발하기 편한 환경을 제공함

  • 개발하다 보면 빌드시 집중력이 깨지는 경우가 정말 많다. 하지만 Go는 빌드가 빨라서 집중력을 계속 유지하며 몰입감을 깨지 않고 개발할 수 있다.
  • 간편한 테스트 환경을 제공하여 디버깅 시간도 많이 절약된다. 다만 cgo를 테스트하려면 설정이 필요하다.

강력한 기능들

  • 아래의 10여줄로 서버의 구현이 가능하다. 그만큼 간결한 문법과 강력한 라이브러리를 제공한다.
package main
import (
    "fmt"
    "log"
    "net/http"
)
func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "URL: %v", r.URL.Path)
		})
    log.Fatal(http.ListenAndServe(":8080", nil))
}

  • 고루틴을 통해 손쉬운 병렬 처리를 제공한다. Go를 잘한다는건 고루틴을 잘 사용한다는 말이 아닐까 생각한다.
    • 아래의 코드처럼 함수앞에 "go"만 붙이면 그 함수를 병렬로 수행할 수 있다.
    • 이러한 병렬 구축이 쉬워지면서 polling을 해오던, channel로 Queue를 구현하여 손쉽게 event-driven형식을 취할수도 있다
// goroutines.go
go func() {
	for i := 0; i < 3; i++ {
        fmt.Println(i)
		time.Sleep(time.Second)
    }
}()
fmt.Println("golang")
$ go run goroutines.go
0
golang
1
2

효율성 중심의 문법

  • Go는 효율적인 문법을 제공하기 위해 디자인 되었다
    • Go attempts to reduce the amount of typing in both senses of the word.

  • Short Assignment Statement(:=)로 선언이 가능해서 코드의 양과 가독성이 좋다.
foo := &Foo{} // foo.Foo* myFoo = new(foo.Foo)

  • 변수의 대소문자로 접근지시자 설정이 가능하다
struct ( Foo // public foo // private )

  • 복수의 변수를 return 할 수 있다. 따라서 리턴값을 dto로 묶거나 함수를 너무 쪼갤 필요가 없어졌다.
    • Spring처럼 한 함수를 수정할때 4~5개의 파일을 왔다갔다하며 개발하는 경우가 거의 없어졌다.
  • 또한 에러의 리턴을 강제화 하고 있다. 에러를 명시적으로 리턴할 수 있으니 핸들링도 편하다.
// MyFunction은 3개의 리턴값을 가짐 (str1, str2, err) func MyFunction() (str1, str2 string, err error) { return }

단점

너무 차별화 되어 호불호가 갈림

  • 이분야 단골은 에러를 명식적으로 return해 줘야 한다는 부분이다.
  • OOP라기엔 Class와 상속 없이 interface를 통한 implement만 지원해서 어색하다.
  • 묵시적 형변환도 허용하지 않는다.
  • 객체 선언 시 하위 객체를 초기화 해 주지 않는다. 따라서 주의를 기울이지 않으면 panic이 발생하여 서버가 죽는다. 개인적으론 제일 불편한 부분이다.

없는게 많음

  • Gradle같은 중앙 Library관리소가 없이 git을 이용한다
  • 공식적인 IDE가 없다
    • 이 들이 합쳐지면 생각보다 큰 단점이 되는데, 자바처럼 IDE단에서 Import할 Package를 지정할 수 없다.
    • 그래서 package name으로 import를 해줘햐 하는데 (ex. errors) 그런데 git에 수많은 동명의 Package가 있는 경우 내가 원하는 것을 한번에 import하기가 힘들다.
    • 고로 Libarary를 넓은 범위로 사용하려면 아무리 잘 wrapping 된 라이브러리도 재차 감싸줘야 한다.
    • 아래와 같은 기능을 제공해 줄 수 있을까? 잘 모르겠다.
    • 빌드도 번거롭다. "딸깍" 한번에 IDE에서 빌드와 서버까지 올려줬다면, Go는 콘솔을 열고 빌드 명령어를 입력해 줘야한다.
      • 서버가 가벼운 Go의 특성상 다수의 서버를 다루는데 이들을 동시에 빌드하고 스크립트를 써서 통합빌드환경을 만들어서 빌드하고 있다.

  • Spring같은 강력한 프레임워크가 없음
    • Gin이 있긴 하지만 강력한 기본 Libarary를 제공하다 보니 효용성이 떨어지는 느낌이다.
    • Package간 순환참조를 허용하지 않는데다가, go.mod와 go.work로 빌드 구조 짜기가 생각보다 힘들다. 기초적인건 자동으로 잡아주면 좋겠건만...

  • 한국에서 많이 안쓴다.
    • 이번에 SMS 전송 시스템과 연동하려니 Go를 지원하지 않아서 node서버 를 구현했다.
    • 이직하고싶다. Go좀 뽑아달라.

정리

상단에도 적었지만 Go는 워낙 개성있는 언어라 호불호가 많이 갈릴 수 있다고 생각한다.

개인적으론 '호'에 속하는데 내가 느끼는 장단점은 아래와 같다.

  • 장점 : 개발할때 집중력 안 끊기고 할 수 있다.
  • 단점 : 안쓴다. 써도 대규모 시스템에서 사용하기엔 애매한 부분이 있다. 한정적인 분야에서 활용 될 듯 하다.